diff --git a/go.mod b/go.mod index 60e8603b0..4ad7b54cc 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module github.com/Axway/agent-sdk go 1.25.0 require ( - github.com/elastic/beats/v7 v7.17.29 github.com/emicklei/proto v1.9.2 github.com/fsnotify/fsnotify v1.5.4 github.com/getkin/kin-openapi v0.144.0 @@ -30,78 +29,43 @@ require ( google.golang.org/protobuf v1.36.11 gopkg.in/h2non/gock.v1 v1.1.2 gopkg.in/yaml.v3 v3.0.1 - k8s.io/apimachinery v0.22.7 ) require ( - github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/armon/go-radix v1.0.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect - github.com/elastic/elastic-agent-client/v7 v7.17.2 // indirect - github.com/elastic/elastic-agent-libs v0.21.5 // indirect - github.com/elastic/go-licenser v0.4.2 // indirect - github.com/elastic/go-sysinfo v1.15.3 // indirect - github.com/elastic/go-ucfg v0.8.8 // indirect - github.com/elastic/go-windows v1.0.2 // indirect - github.com/fatih/color v1.18.0 // indirect github.com/go-openapi/jsonpointer v0.22.5 // indirect github.com/go-openapi/swag/jsonname v0.25.5 // indirect github.com/goccy/go-json v0.10.2 // indirect - github.com/gofrs/uuid v4.4.0+incompatible // indirect github.com/golang/protobuf v1.5.4 // indirect - github.com/google/pprof v0.0.0-20250630185457-6e76a2b096b5 // indirect github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect - github.com/hashicorp/errwrap v1.1.0 // indirect - github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/jcchavezs/porto v0.7.0 // indirect - github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 // indirect github.com/lestrrat-go/blackmagic v1.0.2 // indirect github.com/lestrrat-go/httpcc v1.0.1 // indirect github.com/lestrrat-go/httprc v1.0.5 // indirect github.com/lestrrat-go/iter v1.0.2 // indirect github.com/lestrrat-go/option v1.0.1 // indirect github.com/magiconair/properties v1.8.6 // indirect - github.com/mattn/go-colorable v0.1.14 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mitchellh/hashstructure v1.1.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/oasdiff/yaml v0.1.1 // indirect github.com/oasdiff/yaml3 v0.0.14 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml/v2 v2.0.2 // indirect - github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/procfs v0.17.0 // indirect - github.com/santhosh-tekuri/jsonschema v1.2.4 // indirect github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect github.com/segmentio/asm v1.2.0 // indirect github.com/spf13/afero v1.10.0 // indirect github.com/spf13/cast v1.5.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect - go.elastic.co/apm v1.15.0 // indirect - go.elastic.co/ecszap v1.0.3 // indirect - go.elastic.co/fastjson v1.5.1 // indirect - go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.27.0 // indirect golang.org/x/crypto v0.54.0 // indirect - golang.org/x/lint v0.0.0-20241112194109-818c5a804067 // indirect - golang.org/x/mod v0.37.0 // indirect - golang.org/x/sync v0.22.0 // indirect golang.org/x/sys v0.47.0 // indirect - golang.org/x/telemetry v0.0.0-20260625142307-59b4966ccb57 // indirect - golang.org/x/tools v0.47.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 // indirect gopkg.in/ini.v1 v1.66.6 // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - howett.net/plist v1.0.1 // indirect ) -replace github.com/Shopify/sarama => github.com/elastic/sarama v1.19.1-0.20210823122811-11c3ef800752 - retract ( // errored versions [v1.1.35, v1.1.36] [v1.1.21, v1.1.23] diff --git a/go.sum b/go.sum index 43010bde7..d1bdc234a 100644 --- a/go.sum +++ b/go.sum @@ -39,16 +39,7 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7 github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= 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/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= -github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= -github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= -github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= @@ -59,7 +50,6 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -67,30 +57,6 @@ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etly github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7 h1:Cvj7S8I4Xpx78KAl6TwTmMHuHlZ/0SM60NUneGJQ7IE= -github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/elastic/beats/v7 v7.17.29 h1:xZD9AOJSheSUy96zjPo3K1UlwM2ROihnosUfTctGmv8= -github.com/elastic/beats/v7 v7.17.29/go.mod h1:/XyHz2xF8aRiFcb+G3NTvbh3TR2Vka3Ts+/wgYScIic= -github.com/elastic/elastic-agent-client/v7 v7.17.2 h1:Cl2TeABqWZgW40t5fchGWT/sRk4MDDLWA0d8iHHOxLA= -github.com/elastic/elastic-agent-client/v7 v7.17.2/go.mod h1:5irRFqp6HLqtu1S+OeY0jg8x7K6PLL+DW+PwVk1vJnk= -github.com/elastic/elastic-agent-libs v0.21.5 h1:YTMwaBPgOPvQnxBPv7fLpUqjw2HgY3ymC9TwdzdOD8U= -github.com/elastic/elastic-agent-libs v0.21.5/go.mod h1:xSeIP3NtOIT4N2pPS4EyURmS1Q8mK0lWZ8Wd1Du6q3w= -github.com/elastic/go-licenser v0.3.1/go.mod h1:D8eNQk70FOCVBl3smCGQt/lv7meBeQno2eI1S5apiHQ= -github.com/elastic/go-licenser v0.4.2 h1:bPbGm8bUd8rxzSswFOqvQh1dAkKGkgAmrPxbUi+Y9+A= -github.com/elastic/go-licenser v0.4.2/go.mod h1:W8eH6FaZDR8fQGm+7FnVa7MxI1b/6dAqxz+zPB8nm5c= -github.com/elastic/go-sysinfo v1.1.1/go.mod h1:i1ZYdU10oLNfRzq4vq62BEwD2fH8KaWh6eh0ikPT9F0= -github.com/elastic/go-sysinfo v1.15.3 h1:W+RnmhKFkqPTCRoFq2VCTmsT4p/fwpo+3gKNQsn1XU0= -github.com/elastic/go-sysinfo v1.15.3/go.mod h1:K/cNrqYTDrSoMh2oDkYEMS2+a72GRxMvNP+GC+vRIlo= -github.com/elastic/go-ucfg v0.8.8 h1:54KIF/2zFKfl0MzsSOCGOsZ3O2bnjFQJ0nDJcLhviyk= -github.com/elastic/go-ucfg v0.8.8/go.mod h1:4E8mPOLSUV9hQ7sgLEJ4bvt0KhMuDJa8joDT2QGAEKA= -github.com/elastic/go-windows v1.0.0/go.mod h1:TsU0Nrp7/y3+VwE82FoZF8gC/XFg/Elz6CcloAxnPgU= -github.com/elastic/go-windows v1.0.2 h1:yoLLsAsV5cfg9FLhZ9EXZ2n2sQFKeDYrHenkcivY4vI= -github.com/elastic/go-windows v1.0.2/go.mod h1:bGcDpBzXgYSqM0Gx3DM4+UxFj300SZLixie9u9ixLM8= -github.com/elastic/pkcs8 v1.0.0 h1:HhitlUKxhN288kcNcYkjW6/ouvuwJWd9ioxpjnD9jVA= -github.com/elastic/pkcs8 v1.0.0/go.mod h1:ipsZToJfq1MxclVTwpG7U/bgeDtf+0HkUiOxebk95+0= -github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= -github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/proto v1.9.2 h1:YX2MPuUfUi/h8v+yt4WD8cdj6bt9P3475d2zrL0iogM= github.com/emicklei/proto v1.9.2/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -99,13 +65,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= -github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/getkin/kin-openapi v0.144.0 h1:hIRcTH+KjLfkLpYU6bSSfdFpi0fZi1fp+hSPi4aQu9Y= @@ -115,17 +76,12 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= -github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.22.5 h1:8on/0Yp4uTb9f4XvTrM2+1CPrV05QPZXu+rvu2o9jcA= github.com/go-openapi/jsonpointer v0.22.5/go.mod h1:gyUR3sCvGSWchA2sUBJGluYMbe1zazrYWIkWPjjMUY0= -github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag/jsonname v0.25.5 h1:8p150i44rv/Drip4vWI3kGi9+4W9TdI3US3uUYSFhSo= github.com/go-openapi/swag/jsonname v0.25.5/go.mod h1:jNqqikyiAK56uS7n8sLkdaNY/uq6+D2m2LANat09pKU= github.com/go-openapi/testify/v2 v2.4.0 h1:8nsPrHVCWkQ4p8h1EsRVymA2XABB4OT40gcvAu+voFM= @@ -133,10 +89,6 @@ github.com/go-openapi/testify/v2 v2.4.0/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16p github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= -github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gofrs/uuid/v5 v5.2.0 h1:qw1GMx6/y8vhVsx626ImfKMuS5CvJmhIKKtuyvfajMM= -github.com/gofrs/uuid/v5 v5.2.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= @@ -166,8 +118,6 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -181,11 +131,8 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -199,49 +146,29 @@ 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-20250630185457-6e76a2b096b5 h1:xhMrHhTJ6zxu3gA4enFM9MLn9AY7613teCdFnlUVbSQ= -github.com/google/pprof v0.0.0-20250630185457-6e76a2b096b5/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.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/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= -github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75 h1:f0n1xnMSmBLzVfsMMvriDyA75NB/oBgILX2GcHXIQzY= github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75/go.mod h1:g2644b03hfBX9Ov0ZBDgXXens4rxSxmqFBbhvKv2yVA= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= -github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= -github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 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/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 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/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/invopop/yaml v0.3.1 h1:f0+ZpmhfBSS4MhG+4HYseMdJhoeeopbSKbq5Rpeelso= github.com/invopop/yaml v0.3.1/go.mod h1:PMOp3nn4/12yEZUFfmOuNHJsZToEEOwoWsT+D81KkeA= -github.com/jcchavezs/porto v0.1.0/go.mod h1:fESH0gzDHiutHRdX2hv27ojnOVFco37hg1W6E9EZF4A= -github.com/jcchavezs/porto v0.7.0 h1:VncK84yxV7QZD4GdvoslzjnieSuruztGxLCmFi/Eu28= -github.com/jcchavezs/porto v0.7.0/go.mod h1:tQ1cJ85cNzzZg/58VuZWOLbmrjcH1wPxkWgeBjvOq5o= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 h1:rp+c0RAYOWj8l6qbCUTSiRLG/iKnW3K3/QfPPuSsBt4= -github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901/go.mod h1:Z86h9688Y0wesXCyonoVr47MasHilkuLMqGhRZ4Hpak= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= @@ -250,7 +177,6 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -271,39 +197,14 @@ github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNB github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= -github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mitchellh/hashstructure v1.1.0 h1:P6P1hdjqAAknpY/M1CGipelZgp+4y9ja9kmUZPXP+H0= -github.com/mitchellh/hashstructure v1.1.0/go.mod h1:xUDAozZz0Wmdiufv0uyhnHkUTN6/6d8ulp4AwfLKrmA= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4= github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/oasdiff/yaml v0.1.1 h1:6nHx+pn9gBRM6YpBlFZFQGCCd1nuvqOBtTD3KKTgGxY= github.com/oasdiff/yaml v0.1.1/go.mod h1:EYJNoyktvWMJ0Hmhx+6qTaqMOsalUaRGT8Sj1hNcegU= github.com/oasdiff/yaml3 v0.0.14 h1:aLJee3hxBK2H5wdXd9iPcIXb93Nty1Ge0pT171eHtkw= github.com/oasdiff/yaml3 v0.0.14/go.mod h1:csto2xfDjYccdUn/yw/bPjj/cYTdp6HtFA0J4TWG+gg= -github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -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.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -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/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= @@ -311,23 +212,16 @@ github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3v github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.0.2 h1:+jQXlF3scKIcSEKkdHzXhCTDLPFi5r1wnK6yPS+49Gw= github.com/pelletier/go-toml/v2 v2.0.2/go.mod h1:MovirKjgVRESsAvNZlAjtFwV867yGuwRkXbG66OzopI= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/procfs v0.0.0-20190425082905-87a4384529e0/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0= -github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/santhosh-tekuri/jsonschema v1.2.4 h1:hNhW8e7t+H1vgY+1QeEQpveR6D4+OwKPXCfD2aieJis= -github.com/santhosh-tekuri/jsonschema v1.2.4/go.mod h1:TEAUOeZSmIxTTuHatJzrvARHiuO9LYd+cIxzgEHCQI4= github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ= github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= @@ -347,12 +241,10 @@ github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= -github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.12.0 h1:CZ7eSOd3kZoaYDLbXnmzgQI5RlciuXBMA+18HwHRfZQ= github.com/spf13/viper v1.12.0/go.mod h1:b6COn30jlNxbm/V2IqWiNWkJ+vZNiMNksliPCiuKtSI= -github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -375,18 +267,6 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.elastic.co/apm v1.15.0 h1:uPk2g/whK7c7XiZyz/YCUnAUBNPiyNeE3ARX3G6Gx7Q= -go.elastic.co/apm v1.15.0/go.mod h1:dylGv2HKR0tiCV+wliJz1KHtDyuD8SPe69oV7VyK6WY= -go.elastic.co/apm/module/apmhttp v1.7.2 h1:2mRh7SwBuEVLmJlX+hsMdcSg9xaielCLElaPn/+i34w= -go.elastic.co/apm/module/apmhttp/v2 v2.6.0 h1:s8UeNFQmVBCNd4eoz7KDD9rEFhQC0HeUFXz3z9gpAmQ= -go.elastic.co/apm/module/apmhttp/v2 v2.6.0/go.mod h1:D0GLppLuI0Ddwvtl595GUxRgn6Z8L5KaDFVMv2H3GK0= -go.elastic.co/apm/v2 v2.6.0 h1:VieBMLQFtXua2YxpYxaSdYGnmmxhLT46gosI5yErJgY= -go.elastic.co/apm/v2 v2.6.0/go.mod h1:33rOXgtHwbgZcDgi6I/GtCSMZQqgxkHC0IQT3gudKvo= -go.elastic.co/ecszap v1.0.3 h1:RQtagS3uSftE8mPZ3msqb6mVI67jgcDuy1PUqiMv8ow= -go.elastic.co/ecszap v1.0.3/go.mod h1:fM1RLWDU25TB/L48RUJgz5Le2AnoCeY/g0zf2op8gDU= -go.elastic.co/fastjson v1.1.0/go.mod h1:boNGISWMjQsUPy/t6yqt2/1Wx4YNPSe+mZjlyw9vKKI= -go.elastic.co/fastjson v1.5.1 h1:zeh1xHrFH79aQ6Xsw7YxixvnOdAl3OSv0xch/jRDzko= -go.elastic.co/fastjson v1.5.1/go.mod h1:WtvH5wz8z9pDOPqNYSYKoLLv/9zCWZLeejHWuvdL/EM= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -406,14 +286,8 @@ go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHS go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -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.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -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.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= -go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -446,8 +320,6 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRu golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20241112194109-818c5a804067 h1:adDmSQyFTCiv19j015EGKJBoaa7ElV0Q1Wovb/4G7NA= -golang.org/x/lint v0.0.0-20241112194109-818c5a804067/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= @@ -458,11 +330,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 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/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ= -golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0= 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= @@ -473,7 +342,6 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -484,7 +352,6 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/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= @@ -495,7 +362,6 @@ 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.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o= golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -517,10 +383,7 @@ 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.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= -golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= 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= @@ -530,11 +393,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-20191025021431-6c3a3bfe00ae/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= @@ -548,7 +407,6 @@ golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -561,14 +419,10 @@ golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7w 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-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -golang.org/x/telemetry v0.0.0-20260625142307-59b4966ccb57 h1:nwGZBCt+FnXUrGsj5vjzAsEmkcaFvd82BbOjECiFYZc= -golang.org/x/telemetry v0.0.0-20260625142307-59b4966ccb57/go.mod h1:3AWMyWHS+caVoiEXpiq6+tzKA40J4vQT3MYr80ZtQpc= 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= @@ -617,7 +471,6 @@ golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjs golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= @@ -633,8 +486,6 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210106214847-113979e3529a/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.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q= -golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA= 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= @@ -698,7 +549,6 @@ google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= @@ -735,38 +585,23 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= 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.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 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/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY= gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0= -gopkg.in/hjson/hjson-go.v3 v3.0.1/go.mod h1:X6zrTSVeImfwfZLfgQdInl9mWjqPqgH90jom9nym/lw= -gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.66.6 h1:LATuAqN/shcYAOkv3wl2L4rkaKqkcgTBQjOyYDvcPKI= gopkg.in/ini.v1 v1.66.6/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 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.2.8/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-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/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= @@ -776,18 +611,6 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0= -howett.net/plist v1.0.1 h1:37GdZ8tP09Q35o9ych3ehygcsL+HqKSwzctveSlarvM= -howett.net/plist v1.0.1/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g= -k8s.io/apimachinery v0.22.7 h1:k+/12sD4rvlHQIYhWVruk7bQXl+ztlfQc8CWZJPRy4I= -k8s.io/apimachinery v0.22.7/go.mod h1:ZvVLP5iLhwVFg2Yx9Gh5W0um0DUauExbRhe+2Z8I1EU= -k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= -k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= -k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= -sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/pkg/agent/resource/manager_test.go b/pkg/agent/resource/manager_test.go index 936ada4a4..80b10462f 100644 --- a/pkg/agent/resource/manager_test.go +++ b/pkg/agent/resource/manager_test.go @@ -1,6 +1,7 @@ package resource import ( + "encoding/json" "fmt" "strconv" "testing" @@ -16,7 +17,6 @@ import ( management "github.com/Axway/agent-sdk/pkg/apic/apiserver/models/management/v1" "github.com/Axway/agent-sdk/pkg/config" "github.com/stretchr/testify/assert" - "k8s.io/apimachinery/pkg/util/json" ) func createDiscoveryAgentRes(id, name, dataplane, teamID string) *v1.ResourceInstance { diff --git a/pkg/cmd/cmd_test.go b/pkg/cmd/cmd_test.go index 43ddae745..93da73fb1 100644 --- a/pkg/cmd/cmd_test.go +++ b/pkg/cmd/cmd_test.go @@ -31,6 +31,98 @@ import ( corecfg "github.com/Axway/agent-sdk/pkg/config" ) +// central config property keys, flag names, descriptions and defaults. These are hared by the +// Discovery/Compliance/Traceability asserted in TestRootCmdFlags. +const ( + pathCentralDeployment = "central.deployment" + flagCentralDeployment = "centralDeployment" + descCentralDeployment = "Amplify Central" + pathCentralSSLMaxVersion = "central.ssl.maxVersion" + flagCentralSSLMaxVersion = "centralSslMaxVersion" + descCentralSSLMaxVersion = "Maximum acceptable SSL/TLS protocol version" + defaultSSLMaxVersion = "0" + + pathCentralURL = "central.url" + pathCentralPlatformURL = "central.platformURL" + pathCentralSingleURL = "central.singleURL" + pathCentralOrganizationID = "central.organizationID" + pathCentralAuthPrivateKey = "central.auth.privateKey" + pathCentralAuthPublicKey = "central.auth.publicKey" + pathCentralAuthKeyPassword = "central.auth.keyPassword" + pathCentralAuthURL = "central.auth.url" + pathCentralAuthRealm = "central.auth.realm" + pathCentralAuthClientID = "central.auth.clientId" + pathCentralAuthTimeout = "central.auth.timeout" + pathCentralSSLNextProtos = "central.ssl.nextProtos" + pathCentralSSLInsecureSkipVerify = "central.ssl.insecureSkipVerify" + pathCentralSSLCipherSuites = "central.ssl.cipherSuites" + pathCentralSSLMinVersion = "central.ssl.minVersion" + + flagCentralURL = "centralUrl" + flagCentralPlatformURL = "centralPlatformURL" + flagCentralSingleURL = "centralSingleURL" + flagCentralOrganizationID = "centralOrganizationID" + flagCentralAuthPrivateKey = "centralAuthPrivateKey" + flagCentralAuthPublicKey = "centralAuthPublicKey" + flagCentralAuthKeyPassword = "centralAuthKeyPassword" + flagCentralAuthURL = "centralAuthUrl" + flagCentralAuthRealm = "centralAuthRealm" + flagCentralAuthClientID = "centralAuthClientId" + flagCentralAuthTimeout = "centralAuthTimeout" + flagCentralSSLNextProtos = "centralSslNextProtos" + flagCentralSSLInsecureSkipVerify = "centralSslInsecureSkipVerify" + flagCentralSSLCipherSuites = "centralSslCipherSuites" + flagCentralSSLMinVersion = "centralSslMinVersion" + + descCentralURL = "URL of Amplify Central" + descCentralPlatformURL = "URL of the platform" + descCentralSingleURL = "Alternate Connection for Agent if using static IP" + descCentralOrganizationID = "Tenant ID for the owner of the environment" + descCentralAuthPrivateKey = "Path to the private key for Amplify Central Authentication" + descCentralAuthPublicKey = "Path to the public key for Amplify Central Authentication" + descCentralAuthKeyPassword = "Path to the password file required by the private key for Amplify Central Authentication" + descCentralAuthURL = "Amplify Central authentication URL" + descCentralAuthRealm = "Amplify Central authentication Realm" + descCentralAuthClientID = "Client ID for the service account" + descCentralAuthTimeout = "Timeout waiting for AxwayID response" + descCentralSSLNextProtos = "List of supported application level protocols, comma separated" + descCentralSSLInsecureSkipVerify = "Controls whether a client verifies the server's certificate chain and host name" + descCentralSSLCipherSuites = "List of supported cipher suites, comma separated" + descCentralSSLMinVersion = "Minimum acceptable SSL/TLS protocol version" + + defaultAuthPrivateKeyPath = "/etc/private_key.pem" + defaultAuthPublicKeyPath = "/etc/public_key" + + errStringPropNotSet = "agentConfig: String prop not set" + errOrganizationIDUnset = "Error central.organizationID not set in config" + errIncorrectErrorReturned = "Incorrect error returned: %s" + + testDataPath = "./testdata" + + pathAgentBool = "agent.bool" + pathAgentDuration = "agent.duration" + pathAgentInt = "agent.int" + pathAgentString = "agent.string" + pathAgentStringSlice = "agent.stringSlice" + pathAgentObjectSlice = "agent.objectSlice" + + descAgentBoolProperty = "Agent Bool Property" + descAgentDurationProperty = "Agent Duration Property" + descAgentDurationInvalidUpper = "Agent Duration Property - invalid upper limit" + descAgentIntProperty = "Agent Int Property" + descAgentStringProperty = "Agent String Property" + descAgentStringSliceProperty = "Agent String Slice Property" + + testPrivateKeyPath = "../transaction/testdata/private_key.pem" + testPublicKeyPath = "../transaction/testdata/public_key" + testLogPath = "./tmplogs/test_with_non_defaults.log" + + secretInvalidCachedKey = "@Secret.invalidSecret.cachedSecretKey" + secretAgentKey = "@Secret.agentSecret.secretKey" + + testAuthToken = "{\"access_token\":\"somevalue\",\"expires_in\": 12235677}" +) + func getPFlag(cmd AgentRootCmd, flagName string) *flag.Flag { return cmd.RootCmd().Flags().Lookup(flagName) } @@ -76,7 +168,7 @@ type agentConfig struct { func (a *agentConfig) ValidateCfg() error { a.agentValidationCalled = true if a.sProp == "" { - return errors.New("agentConfig: String prop not set") + return errors.New(errStringPropNotSet) } return nil } @@ -109,65 +201,65 @@ type configWithNoValidation struct { func TestRootCmdFlags(t *testing.T) { // Discovery Agent rootCmd := NewRootCmd("Test", "TestRootCmd", nil, nil, corecfg.DiscoveryAgent) - assertStringCmdFlag(t, rootCmd, "central.url", "centralUrl", "", "URL of Amplify Central") // assert to empty "" - set by region settings - assertStringCmdFlag(t, rootCmd, "central.platformURL", "centralPlatformURL", "", "URL of the platform") // assert to empty "" - set by region settings - assertStringCmdFlag(t, rootCmd, "central.singleURL", "centralSingleURL", "", "Alternate Connection for Agent if using static IP") - assertStringCmdFlag(t, rootCmd, "central.organizationID", "centralOrganizationID", "", "Tenant ID for the owner of the environment") + assertStringCmdFlag(t, rootCmd, pathCentralURL, flagCentralURL, "", descCentralURL) // assert to empty "" - set by region settings + assertStringCmdFlag(t, rootCmd, pathCentralPlatformURL, flagCentralPlatformURL, "", descCentralPlatformURL) // assert to empty "" - set by region settings + assertStringCmdFlag(t, rootCmd, pathCentralSingleURL, flagCentralSingleURL, "", descCentralSingleURL) + assertStringCmdFlag(t, rootCmd, pathCentralOrganizationID, flagCentralOrganizationID, "", descCentralOrganizationID) assertStringCmdFlag(t, rootCmd, "central.team", "centralTeam", "", "Team name for creating catalog") assertStringCmdFlag(t, rootCmd, "central.environment", "centralEnvironment", "", "The Environment that the APIs will be associated with in Amplify Central") - assertStringCmdFlag(t, rootCmd, "central.auth.privateKey", "centralAuthPrivateKey", "/etc/private_key.pem", "Path to the private key for Amplify Central Authentication") - assertStringCmdFlag(t, rootCmd, "central.auth.publicKey", "centralAuthPublicKey", "/etc/public_key", "Path to the public key for Amplify Central Authentication") - assertStringCmdFlag(t, rootCmd, "central.auth.word", "centralAuthKeyPassword", "", "Path to the password file required by the private key for Amplify Central Authentication") - assertStringCmdFlag(t, rootCmd, "central.auth.url", "centralAuthUrl", "", "Amplify Central authentication URL") // assert to empty "" - set by region settings - assertStringCmdFlag(t, rootCmd, "central.auth.realm", "centralAuthRealm", "Broker", "Amplify Central authentication Realm") - assertStringCmdFlag(t, rootCmd, "central.auth.clientId", "centralAuthClientId", "", "Client ID for the service account") - assertDurationCmdFlag(t, rootCmd, "central.auth.timeout", "centralAuthTimeout", 10*time.Second, "Timeout waiting for AxwayID response") - assertStringSliceCmdFlag(t, rootCmd, "central.ssl.nextProtos", "centralSslNextProtos", []string{}, "List of supported application level protocols, comma separated") - assertBooleanCmdFlag(t, rootCmd, "central.ssl.insecureSkipVerify", "centralSslInsecureSkipVerify", false, "Controls whether a client verifies the server's certificate chain and host name") - assertStringSliceCmdFlag(t, rootCmd, "central.ssl.cipherSuites", "centralSslCipherSuites", corecfg.TLSDefaultCipherSuitesStringSlice(), "List of supported cipher suites, comma separated") - assertStringCmdFlag(t, rootCmd, "central.ssl.minVersion", "centralSslMinVersion", corecfg.TLSDefaultMinVersionString(), "Minimum acceptable SSL/TLS protocol version") - assertStringCmdFlag(t, rootCmd, "central.ssl.maxVersion", "centralSslMaxVersion", "0", "Maximum acceptable SSL/TLS protocol version") + assertStringCmdFlag(t, rootCmd, pathCentralAuthPrivateKey, flagCentralAuthPrivateKey, defaultAuthPrivateKeyPath, descCentralAuthPrivateKey) + assertStringCmdFlag(t, rootCmd, pathCentralAuthPublicKey, flagCentralAuthPublicKey, defaultAuthPublicKeyPath, descCentralAuthPublicKey) + assertStringCmdFlag(t, rootCmd, pathCentralAuthKeyPassword, flagCentralAuthKeyPassword, "", descCentralAuthKeyPassword) + assertStringCmdFlag(t, rootCmd, pathCentralAuthURL, flagCentralAuthURL, "", descCentralAuthURL) // assert to empty "" - set by region settings + assertStringCmdFlag(t, rootCmd, pathCentralAuthRealm, flagCentralAuthRealm, "Broker", descCentralAuthRealm) + assertStringCmdFlag(t, rootCmd, pathCentralAuthClientID, flagCentralAuthClientID, "", descCentralAuthClientID) + assertDurationCmdFlag(t, rootCmd, pathCentralAuthTimeout, flagCentralAuthTimeout, 10*time.Second, descCentralAuthTimeout) + assertStringSliceCmdFlag(t, rootCmd, pathCentralSSLNextProtos, flagCentralSSLNextProtos, []string{}, descCentralSSLNextProtos) + assertBooleanCmdFlag(t, rootCmd, pathCentralSSLInsecureSkipVerify, flagCentralSSLInsecureSkipVerify, false, descCentralSSLInsecureSkipVerify) + assertStringSliceCmdFlag(t, rootCmd, pathCentralSSLCipherSuites, flagCentralSSLCipherSuites, corecfg.TLSDefaultCipherSuitesStringSlice(), descCentralSSLCipherSuites) + assertStringCmdFlag(t, rootCmd, pathCentralSSLMinVersion, flagCentralSSLMinVersion, corecfg.TLSDefaultMinVersionString(), descCentralSSLMinVersion) + assertStringCmdFlag(t, rootCmd, pathCentralSSLMaxVersion, flagCentralSSLMaxVersion, defaultSSLMaxVersion, descCentralSSLMaxVersion) assertBooleanCmdFlag(t, rootCmd, "central.migration.cleanInstances", "centralMigrationCleanInstances", false, "Set this to clean all but latest instance, per stage, within an API Service") // Compliance Agent rootCmd = NewRootCmd("Test", "TestRootCmd", nil, nil, corecfg.ComplianceAgent) - assertStringCmdFlag(t, rootCmd, "central.deployment", "centralDeployment", "", "Amplify Central") // assert to empty "" - set by region settings - assertStringCmdFlag(t, rootCmd, "central.url", "centralUrl", "", "URL of Amplify Central") // assert to empty "" - set by region settings - assertStringCmdFlag(t, rootCmd, "central.platformURL", "centralPlatformURL", "", "URL of the platform") // assert to empty "" - set by region settings - assertStringCmdFlag(t, rootCmd, "central.singleURL", "centralSingleURL", "", "Alternate Connection for Agent if using static IP") - assertStringCmdFlag(t, rootCmd, "central.organizationID", "centralOrganizationID", "", "Tenant ID for the owner of the environment") - assertStringCmdFlag(t, rootCmd, "central.auth.privateKey", "centralAuthPrivateKey", "/etc/private_key.pem", "Path to the private key for Amplify Central Authentication") - assertStringCmdFlag(t, rootCmd, "central.auth.publicKey", "centralAuthPublicKey", "/etc/public_key", "Path to the public key for Amplify Central Authentication") - assertStringCmdFlag(t, rootCmd, "central.auth.keyPassword", "centralAuthKeyPassword", "", "Path to the password file required by the private key for Amplify Central Authentication") - assertStringCmdFlag(t, rootCmd, "central.auth.url", "centralAuthUrl", "", "Amplify Central authentication URL") // assert to empty "" - set by region settings - assertStringCmdFlag(t, rootCmd, "central.auth.realm", "centralAuthRealm", "Broker", "Amplify Central authentication Realm") - assertStringCmdFlag(t, rootCmd, "central.auth.clientId", "centralAuthClientId", "", "Client ID for the service account") - assertDurationCmdFlag(t, rootCmd, "central.auth.timeout", "centralAuthTimeout", 10*time.Second, "Timeout waiting for AxwayID response") - assertStringSliceCmdFlag(t, rootCmd, "central.ssl.nextProtos", "centralSslNextProtos", []string{}, "List of supported application level protocols, comma separated") - assertBooleanCmdFlag(t, rootCmd, "central.ssl.insecureSkipVerify", "centralSslInsecureSkipVerify", false, "Controls whether a client verifies the server's certificate chain and host name") - assertStringSliceCmdFlag(t, rootCmd, "central.ssl.cipherSuites", "centralSslCipherSuites", corecfg.TLSDefaultCipherSuitesStringSlice(), "List of supported cipher suites, comma separated") - assertStringCmdFlag(t, rootCmd, "central.ssl.minVersion", "centralSslMinVersion", corecfg.TLSDefaultMinVersionString(), "Minimum acceptable SSL/TLS protocol version") - assertStringCmdFlag(t, rootCmd, "central.ssl.maxVersion", "centralSslMaxVersion", "0", "Maximum acceptable SSL/TLS protocol version") + assertStringCmdFlag(t, rootCmd, pathCentralDeployment, flagCentralDeployment, "", descCentralDeployment) // assert to empty "" - set by region settings + assertStringCmdFlag(t, rootCmd, pathCentralURL, flagCentralURL, "", descCentralURL) // assert to empty "" - set by region settings + assertStringCmdFlag(t, rootCmd, pathCentralPlatformURL, flagCentralPlatformURL, "", descCentralPlatformURL) // assert to empty "" - set by region settings + assertStringCmdFlag(t, rootCmd, pathCentralSingleURL, flagCentralSingleURL, "", descCentralSingleURL) + assertStringCmdFlag(t, rootCmd, pathCentralOrganizationID, flagCentralOrganizationID, "", descCentralOrganizationID) + assertStringCmdFlag(t, rootCmd, pathCentralAuthPrivateKey, flagCentralAuthPrivateKey, defaultAuthPrivateKeyPath, descCentralAuthPrivateKey) + assertStringCmdFlag(t, rootCmd, pathCentralAuthPublicKey, flagCentralAuthPublicKey, defaultAuthPublicKeyPath, descCentralAuthPublicKey) + assertStringCmdFlag(t, rootCmd, pathCentralAuthKeyPassword, flagCentralAuthKeyPassword, "", descCentralAuthKeyPassword) + assertStringCmdFlag(t, rootCmd, pathCentralAuthURL, flagCentralAuthURL, "", descCentralAuthURL) // assert to empty "" - set by region settings + assertStringCmdFlag(t, rootCmd, pathCentralAuthRealm, flagCentralAuthRealm, "Broker", descCentralAuthRealm) + assertStringCmdFlag(t, rootCmd, pathCentralAuthClientID, flagCentralAuthClientID, "", descCentralAuthClientID) + assertDurationCmdFlag(t, rootCmd, pathCentralAuthTimeout, flagCentralAuthTimeout, 10*time.Second, descCentralAuthTimeout) + assertStringSliceCmdFlag(t, rootCmd, pathCentralSSLNextProtos, flagCentralSSLNextProtos, []string{}, descCentralSSLNextProtos) + assertBooleanCmdFlag(t, rootCmd, pathCentralSSLInsecureSkipVerify, flagCentralSSLInsecureSkipVerify, false, descCentralSSLInsecureSkipVerify) + assertStringSliceCmdFlag(t, rootCmd, pathCentralSSLCipherSuites, flagCentralSSLCipherSuites, corecfg.TLSDefaultCipherSuitesStringSlice(), descCentralSSLCipherSuites) + assertStringCmdFlag(t, rootCmd, pathCentralSSLMinVersion, flagCentralSSLMinVersion, corecfg.TLSDefaultMinVersionString(), descCentralSSLMinVersion) + assertStringCmdFlag(t, rootCmd, pathCentralSSLMaxVersion, flagCentralSSLMaxVersion, defaultSSLMaxVersion, descCentralSSLMaxVersion) // Traceability Agent rootCmd = NewRootCmd("Test", "TestRootCmd", nil, nil, corecfg.TraceabilityAgent) - assertStringCmdFlag(t, rootCmd, "central.deployment", "centralDeployment", "", "Amplify Central") // assert to empty "" - set by region settings - assertStringCmdFlag(t, rootCmd, "central.url", "centralUrl", "", "URL of Amplify Central") // assert to empty "" - set by region settings - assertStringCmdFlag(t, rootCmd, "central.platformURL", "centralPlatformURL", "", "URL of the platform") // assert to empty "" - set by region settings - assertStringCmdFlag(t, rootCmd, "central.singleURL", "centralSingleURL", "", "Alternate Connection for Agent if using static IP") - assertStringCmdFlag(t, rootCmd, "central.organizationID", "centralOrganizationID", "", "Tenant ID for the owner of the environment") - assertStringCmdFlag(t, rootCmd, "central.auth.privateKey", "centralAuthPrivateKey", "/etc/private_key.pem", "Path to the private key for Amplify Central Authentication") - assertStringCmdFlag(t, rootCmd, "central.auth.publicKey", "centralAuthPublicKey", "/etc/public_key", "Path to the public key for Amplify Central Authentication") - assertStringCmdFlag(t, rootCmd, "central.auth.keyPassword", "centralAuthKeyPassword", "", "Path to the password file required by the private key for Amplify Central Authentication") - assertStringCmdFlag(t, rootCmd, "central.auth.url", "centralAuthUrl", "", "Amplify Central authentication URL") // assert to empty "" - set by region settings - assertStringCmdFlag(t, rootCmd, "central.auth.realm", "centralAuthRealm", "Broker", "Amplify Central authentication Realm") - assertStringCmdFlag(t, rootCmd, "central.auth.clientId", "centralAuthClientId", "", "Client ID for the service account") - assertDurationCmdFlag(t, rootCmd, "central.auth.timeout", "centralAuthTimeout", 10*time.Second, "Timeout waiting for AxwayID response") - assertStringSliceCmdFlag(t, rootCmd, "central.ssl.nextProtos", "centralSslNextProtos", []string{}, "List of supported application level protocols, comma separated") - assertBooleanCmdFlag(t, rootCmd, "central.ssl.insecureSkipVerify", "centralSslInsecureSkipVerify", false, "Controls whether a client verifies the server's certificate chain and host name") - assertStringSliceCmdFlag(t, rootCmd, "central.ssl.cipherSuites", "centralSslCipherSuites", corecfg.TLSDefaultCipherSuitesStringSlice(), "List of supported cipher suites, comma separated") - assertStringCmdFlag(t, rootCmd, "central.ssl.minVersion", "centralSslMinVersion", corecfg.TLSDefaultMinVersionString(), "Minimum acceptable SSL/TLS protocol version") - assertStringCmdFlag(t, rootCmd, "central.ssl.maxVersion", "centralSslMaxVersion", "0", "Maximum acceptable SSL/TLS protocol version") + assertStringCmdFlag(t, rootCmd, pathCentralDeployment, flagCentralDeployment, "", descCentralDeployment) // assert to empty "" - set by region settings + assertStringCmdFlag(t, rootCmd, pathCentralURL, flagCentralURL, "", descCentralURL) // assert to empty "" - set by region settings + assertStringCmdFlag(t, rootCmd, pathCentralPlatformURL, flagCentralPlatformURL, "", descCentralPlatformURL) // assert to empty "" - set by region settings + assertStringCmdFlag(t, rootCmd, pathCentralSingleURL, flagCentralSingleURL, "", descCentralSingleURL) + assertStringCmdFlag(t, rootCmd, pathCentralOrganizationID, flagCentralOrganizationID, "", descCentralOrganizationID) + assertStringCmdFlag(t, rootCmd, pathCentralAuthPrivateKey, flagCentralAuthPrivateKey, defaultAuthPrivateKeyPath, descCentralAuthPrivateKey) + assertStringCmdFlag(t, rootCmd, pathCentralAuthPublicKey, flagCentralAuthPublicKey, defaultAuthPublicKeyPath, descCentralAuthPublicKey) + assertStringCmdFlag(t, rootCmd, pathCentralAuthKeyPassword, flagCentralAuthKeyPassword, "", descCentralAuthKeyPassword) + assertStringCmdFlag(t, rootCmd, pathCentralAuthURL, flagCentralAuthURL, "", descCentralAuthURL) // assert to empty "" - set by region settings + assertStringCmdFlag(t, rootCmd, pathCentralAuthRealm, flagCentralAuthRealm, "Broker", descCentralAuthRealm) + assertStringCmdFlag(t, rootCmd, pathCentralAuthClientID, flagCentralAuthClientID, "", descCentralAuthClientID) + assertDurationCmdFlag(t, rootCmd, pathCentralAuthTimeout, flagCentralAuthTimeout, 10*time.Second, descCentralAuthTimeout) + assertStringSliceCmdFlag(t, rootCmd, pathCentralSSLNextProtos, flagCentralSSLNextProtos, []string{}, descCentralSSLNextProtos) + assertBooleanCmdFlag(t, rootCmd, pathCentralSSLInsecureSkipVerify, flagCentralSSLInsecureSkipVerify, false, descCentralSSLInsecureSkipVerify) + assertStringSliceCmdFlag(t, rootCmd, pathCentralSSLCipherSuites, flagCentralSSLCipherSuites, corecfg.TLSDefaultCipherSuitesStringSlice(), descCentralSSLCipherSuites) + assertStringCmdFlag(t, rootCmd, pathCentralSSLMinVersion, flagCentralSSLMinVersion, corecfg.TLSDefaultMinVersionString(), descCentralSSLMinVersion) + assertStringCmdFlag(t, rootCmd, pathCentralSSLMaxVersion, flagCentralSSLMaxVersion, defaultSSLMaxVersion, descCentralSSLMaxVersion) // Log yaml properties and command flags assertStringCmdFlag(t, rootCmd, "log.level", "logLevel", "info", "Log level (trace, debug, info, warn, error)") @@ -182,28 +274,29 @@ func TestRootCmdConfigFileLoad(t *testing.T) { err := rootCmd.Execute() - // should be FileNotFound error + // a missing config file is now ok. The resulting error, if any, comes from downstream + // config validation (nothing is set here), not from ConfigFileNotFoundError assert.NotNil(t, err, err.Error()) if err != nil { _, ok := err.(viper.ConfigFileNotFoundError) - assert.True(t, ok, "Incorrect error returned: %s", err.Error()) + assert.False(t, ok, errIncorrectErrorReturned, err.Error()) } rootCmd = NewRootCmd("test_no_overide", "test_no_overide", nil, nil, corecfg.DiscoveryAgent) - viper.AddConfigPath("./testdata") + viper.AddConfigPath(testDataPath) err = rootCmd.Execute() // should NOT be FileNotFound error assert.NotNil(t, err, err.Error()) if err != nil { _, ok := err.(viper.ConfigFileNotFoundError) - assert.False(t, ok, "Incorrect error returned: %s", err.Error()) + assert.False(t, ok, errIncorrectErrorReturned, err.Error()) } errBuf := new(bytes.Buffer) rootCmd.RootCmd().SetErr(errBuf) - assert.Contains(t, "Error central.organizationID not set in config", errBuf.String()) + assert.Contains(t, errOrganizationIDUnset, errBuf.String()) } func TestRootCmdConfigDefault(t *testing.T) { @@ -214,8 +307,8 @@ func TestRootCmdConfigDefault(t *testing.T) { assert.Equal(t, "https://login.axway.com/auth/realms/Broker/protocol/openid-connect/token", centralConfig.GetAuthConfig().GetTokenURL()) assert.Equal(t, "cccc", centralConfig.GetAuthConfig().GetClientID()) assert.Equal(t, "Broker", centralConfig.GetAuthConfig().GetRealm()) - assert.Equal(t, "/etc/private_key.pem", centralConfig.GetAuthConfig().GetPrivateKey()) - assert.Equal(t, "/etc/public_key", centralConfig.GetAuthConfig().GetPublicKey()) + assert.Equal(t, defaultAuthPrivateKeyPath, centralConfig.GetAuthConfig().GetPrivateKey()) + assert.Equal(t, defaultAuthPublicKeyPath, centralConfig.GetAuthConfig().GetPublicKey()) assert.Equal(t, "", centralConfig.GetAuthConfig().GetKeyPassword()) assert.Equal(t, 10*time.Second, centralConfig.GetAuthConfig().GetTimeout()) return centralConfig, errors.New("Test return error from init config handler") @@ -227,8 +320,8 @@ func TestRootCmdConfigDefault(t *testing.T) { assert.Equal(t, "https://login.axway.com/auth/realms/Broker/protocol/openid-connect/token", centralConfig.GetAuthConfig().GetTokenURL()) assert.Equal(t, "cccc", centralConfig.GetAuthConfig().GetClientID()) assert.Equal(t, "Broker", centralConfig.GetAuthConfig().GetRealm()) - assert.Equal(t, "/etc/private_key.pem", centralConfig.GetAuthConfig().GetPrivateKey()) - assert.Equal(t, "/etc/public_key", centralConfig.GetAuthConfig().GetPublicKey()) + assert.Equal(t, defaultAuthPrivateKeyPath, centralConfig.GetAuthConfig().GetPrivateKey()) + assert.Equal(t, defaultAuthPublicKeyPath, centralConfig.GetAuthConfig().GetPublicKey()) assert.Equal(t, "", centralConfig.GetAuthConfig().GetKeyPassword()) assert.Equal(t, 10*time.Second, centralConfig.GetAuthConfig().GetTimeout()) return centralConfig, errors.New("Test return error from init config handler") @@ -236,14 +329,14 @@ func TestRootCmdConfigDefault(t *testing.T) { // Discovery rootCmd := NewRootCmd("test_with_non_defaults", "test_with_non_defaults", discoveryInitConfigHandler, nil, corecfg.DiscoveryAgent) - viper.AddConfigPath("./testdata") + viper.AddConfigPath(testDataPath) err := rootCmd.Execute() // should NOT be FileNotFound error assert.NotNil(t, err, err.Error()) if err != nil { _, ok := err.(viper.ConfigFileNotFoundError) - assert.False(t, ok, "Incorrect error returned: %s", err.Error()) + assert.False(t, ok, errIncorrectErrorReturned, err.Error()) } errBuf := new(bytes.Buffer) @@ -252,14 +345,14 @@ func TestRootCmdConfigDefault(t *testing.T) { // Compliance rootCmd = NewRootCmd("test_with_non_defaults", "test_with_non_defaults", traceabilityInitConfigHandler, nil, corecfg.ComplianceAgent) - viper.AddConfigPath("./testdata") + viper.AddConfigPath(testDataPath) err = rootCmd.Execute() // should NOT be FileNotFound error assert.NotNil(t, err, err.Error()) if err != nil { _, ok := err.(viper.ConfigFileNotFoundError) - assert.False(t, ok, "Incorrect error returned: %s", err.Error()) + assert.False(t, ok, errIncorrectErrorReturned, err.Error()) } errBuf = new(bytes.Buffer) @@ -268,14 +361,14 @@ func TestRootCmdConfigDefault(t *testing.T) { // Traceability rootCmd = NewRootCmd("test_with_non_defaults", "test_with_non_defaults", traceabilityInitConfigHandler, nil, corecfg.TraceabilityAgent) - viper.AddConfigPath("./testdata") + viper.AddConfigPath(testDataPath) err = rootCmd.Execute() // should NOT be FileNotFound error assert.NotNil(t, err, err.Error()) if err != nil { _, ok := err.(viper.ConfigFileNotFoundError) - assert.False(t, ok, "Incorrect error returned: %s", err.Error()) + assert.False(t, ok, errIncorrectErrorReturned, err.Error()) } errBuf = new(bytes.Buffer) @@ -295,18 +388,18 @@ func TestRootCmdAgentConfigValidation(t *testing.T) { CentralCfg: centralConfig, AgentCfg: &agentConfig{ agentValidationCalled: false, - bProp: rootCmd.GetProperties().BoolPropertyValue("agent.bool"), - dProp: rootCmd.GetProperties().DurationPropertyValue("agent.duration"), - iProp: rootCmd.GetProperties().IntPropertyValue("agent.int"), - sProp: rootCmd.GetProperties().StringPropertyValue("agent.string"), - ssProp: rootCmd.GetProperties().StringSlicePropertyValue("agent.stringSlice"), + bProp: rootCmd.GetProperties().BoolPropertyValue(pathAgentBool), + dProp: rootCmd.GetProperties().DurationPropertyValue(pathAgentDuration), + iProp: rootCmd.GetProperties().IntPropertyValue(pathAgentInt), + sProp: rootCmd.GetProperties().StringPropertyValue(pathAgentString), + ssProp: rootCmd.GetProperties().StringSlicePropertyValue(pathAgentStringSlice), }, } return cfg, nil } - os.Setenv("CENTRAL_AUTH_PRIVATEKEY", "../transaction/testdata/private_key.pem") - os.Setenv("CENTRAL_AUTH_PUBLICKEY", "../transaction/testdata/public_key") + os.Setenv("CENTRAL_AUTH_PRIVATEKEY", testPrivateKeyPath) + os.Setenv("CENTRAL_AUTH_PUBLICKEY", testPublicKeyPath) os.Setenv("CENTRAL_AUTH_CLIENTID", "serviceaccount_1234") os.Setenv("CENTRAL_AUTH_URL", s.URL) os.Setenv("CENTRAL_URL", s.URL) @@ -314,13 +407,13 @@ func TestRootCmdAgentConfigValidation(t *testing.T) { os.Setenv("CENTRAL_PLATFORMURL", s.URL) rootCmd = NewRootCmd("test_with_non_defaults", "test_with_non_defaults", initConfigHandler, nil, corecfg.DiscoveryAgent) - viper.AddConfigPath("./testdata") + viper.AddConfigPath(testDataPath) - rootCmd.GetProperties().AddBoolProperty("agent.bool", false, "Agent Bool Property") - rootCmd.GetProperties().AddDurationProperty("agent.duration", 10*time.Second, "Agent Duration Property", properties.WithLowerLimit(10*time.Second)) - rootCmd.GetProperties().AddIntProperty("agent.int", 0, "Agent Int Property") - rootCmd.GetProperties().AddStringProperty("agent.string", "", "Agent String Property") - rootCmd.GetProperties().AddStringSliceProperty("agent.stringSlice", nil, "Agent String Slice Property") + rootCmd.GetProperties().AddBoolProperty(pathAgentBool, false, descAgentBoolProperty) + rootCmd.GetProperties().AddDurationProperty(pathAgentDuration, 10*time.Second, descAgentDurationProperty, properties.WithLowerLimit(10*time.Second)) + rootCmd.GetProperties().AddIntProperty(pathAgentInt, 0, descAgentIntProperty) + rootCmd.GetProperties().AddStringProperty(pathAgentString, "", descAgentStringProperty) + rootCmd.GetProperties().AddStringSliceProperty(pathAgentStringSlice, nil, descAgentStringSliceProperty) err := rootCmd.Execute() @@ -328,7 +421,7 @@ func TestRootCmdAgentConfigValidation(t *testing.T) { assert.NotNil(t, err, err.Error()) if err != nil { _, ok := err.(viper.ConfigFileNotFoundError) - assert.False(t, ok, "Incorrect error returned: %s", err.Error()) + assert.False(t, ok, errIncorrectErrorReturned, err.Error()) } errBuf := new(bytes.Buffer) @@ -350,31 +443,31 @@ func TestRootCmdAgentConfigChildValidation(t *testing.T) { CentralCfg: centralConfig, AgentCfg: &agentConfig{ agentValidationCalled: false, - bProp: rootCmd.GetProperties().BoolPropertyValue("agent.bool"), - dProp: rootCmd.GetProperties().DurationPropertyValue("agent.duration"), - iProp: rootCmd.GetProperties().IntPropertyValue("agent.int"), - sProp: rootCmd.GetProperties().StringPropertyValue("agent.string"), - ssProp: rootCmd.GetProperties().StringSlicePropertyValue("agent.stringSlice"), + bProp: rootCmd.GetProperties().BoolPropertyValue(pathAgentBool), + dProp: rootCmd.GetProperties().DurationPropertyValue(pathAgentDuration), + iProp: rootCmd.GetProperties().IntPropertyValue(pathAgentInt), + sProp: rootCmd.GetProperties().StringPropertyValue(pathAgentString), + ssProp: rootCmd.GetProperties().StringSlicePropertyValue(pathAgentStringSlice), }, } return cfg, nil } - os.Setenv("CENTRAL_AUTH_PRIVATEKEY", "../transaction/testdata/private_key.pem") - os.Setenv("CENTRAL_AUTH_PUBLICKEY", "../transaction/testdata/public_key") + os.Setenv("CENTRAL_AUTH_PRIVATEKEY", testPrivateKeyPath) + os.Setenv("CENTRAL_AUTH_PUBLICKEY", testPublicKeyPath) os.Setenv("CENTRAL_AUTH_CLIENTID", "serviceaccount_1234") os.Setenv("CENTRAL_AUTH_URL", s.URL) os.Setenv("CENTRAL_URL", s.URL) os.Setenv("CENTRAL_SINGLEURL", s.URL) rootCmd = NewRootCmd("test_with_non_defaults", "test_with_non_defaults", initConfigHandler, nil, corecfg.DiscoveryAgent) - viper.AddConfigPath("./testdata") + viper.AddConfigPath(testDataPath) - rootCmd.GetProperties().AddBoolProperty("agent.bool", false, "Agent Bool Property") - rootCmd.GetProperties().AddDurationProperty("agent.duration", 10*time.Second, "Agent Duration Property", properties.WithLowerLimit(10*time.Second)) - rootCmd.GetProperties().AddIntProperty("agent.int", 0, "Agent Int Property") - rootCmd.GetProperties().AddStringProperty("agent.string", "", "Agent String Property") - rootCmd.GetProperties().AddStringSliceProperty("agent.stringSlice", nil, "Agent String Slice Property") + rootCmd.GetProperties().AddBoolProperty(pathAgentBool, false, descAgentBoolProperty) + rootCmd.GetProperties().AddDurationProperty(pathAgentDuration, 10*time.Second, descAgentDurationProperty, properties.WithLowerLimit(10*time.Second)) + rootCmd.GetProperties().AddIntProperty(pathAgentInt, 0, descAgentIntProperty) + rootCmd.GetProperties().AddStringProperty(pathAgentString, "", descAgentStringProperty) + rootCmd.GetProperties().AddStringSliceProperty(pathAgentStringSlice, nil, descAgentStringSliceProperty) err := rootCmd.Execute() @@ -382,12 +475,12 @@ func TestRootCmdAgentConfigChildValidation(t *testing.T) { assert.NotNil(t, err, err.Error()) if err != nil { _, ok := err.(viper.ConfigFileNotFoundError) - assert.False(t, ok, "Incorrect error returned: %s", err.Error()) + assert.False(t, ok, errIncorrectErrorReturned, err.Error()) } errBuf := new(bytes.Buffer) rootCmd.RootCmd().SetErr(errBuf) - assert.Contains(t, "agentConfig: String prop not set", errBuf.String()) + assert.Contains(t, errStringPropNotSet, errBuf.String()) assert.Equal(t, false, cfg.configValidationCalled) assert.Equal(t, true, cfg.AgentCfg.(*agentConfig).agentValidationCalled) } @@ -403,8 +496,8 @@ func TestRootCmdHandlersWithError(t *testing.T) { return nil } - os.Setenv("CENTRAL_AUTH_PRIVATEKEY", "../transaction/testdata/private_key.pem") - os.Setenv("CENTRAL_AUTH_PUBLICKEY", "../transaction/testdata/public_key") + os.Setenv("CENTRAL_AUTH_PRIVATEKEY", testPrivateKeyPath) + os.Setenv("CENTRAL_AUTH_PUBLICKEY", testPublicKeyPath) os.Setenv("CENTRAL_AUTH_CLIENTID", "serviceaccount_1234") os.Setenv("CENTRAL_AUTH_URL", s.URL) os.Setenv("CENTRAL_URL", s.URL) @@ -413,22 +506,20 @@ func TestRootCmdHandlersWithError(t *testing.T) { rootCmd := NewRootCmd("Test", "TestRootCmd", initConfigHandler, cmdHandler, corecfg.DiscoveryAgent) err := rootCmd.Execute() - // should be FileNotFound error - assert.NotNil(t, err, err.Error()) if err != nil { _, ok := err.(viper.ConfigFileNotFoundError) - assert.True(t, ok, "Incorrect error returned: %s", err.Error()) + assert.False(t, ok, errIncorrectErrorReturned, err.Error()) } rootCmd = NewRootCmd("test_no_overide", "test_no_overide", initConfigHandler, cmdHandler, corecfg.DiscoveryAgent) - viper.AddConfigPath("./testdata") + viper.AddConfigPath(testDataPath) err = rootCmd.Execute() // should NOT be FileNotFound error assert.NotNil(t, err, err.Error()) if err != nil { _, ok := err.(viper.ConfigFileNotFoundError) - assert.False(t, ok, "Incorrect error returned: %s", err.Error()) + assert.False(t, ok, errIncorrectErrorReturned, err.Error()) } } @@ -444,12 +535,12 @@ func TestRootCmdHandlers(t *testing.T) { CentralCfg: centralConfig, AgentCfg: &agentConfig{ agentValidationCalled: false, - bProp: rootCmd.GetProperties().BoolPropertyValue("agent.bool"), - dProp: rootCmd.GetProperties().DurationPropertyValue("agent.duration"), - iProp: rootCmd.GetProperties().IntPropertyValue("agent.int"), - sProp: rootCmd.GetProperties().StringPropertyValue("agent.string"), - ssProp: rootCmd.GetProperties().StringSlicePropertyValue("agent.stringSlice"), - osProp: rootCmd.GetProperties().ObjectSlicePropertyValue("agent.objectSlice"), + bProp: rootCmd.GetProperties().BoolPropertyValue(pathAgentBool), + dProp: rootCmd.GetProperties().DurationPropertyValue(pathAgentDuration), + iProp: rootCmd.GetProperties().IntPropertyValue(pathAgentInt), + sProp: rootCmd.GetProperties().StringPropertyValue(pathAgentString), + ssProp: rootCmd.GetProperties().StringSlicePropertyValue(pathAgentStringSlice), + osProp: rootCmd.GetProperties().ObjectSlicePropertyValue(pathAgentObjectSlice), }, } return cfg, nil @@ -460,8 +551,8 @@ func TestRootCmdHandlers(t *testing.T) { return nil } - os.Setenv("CENTRAL_AUTH_PRIVATEKEY", "../transaction/testdata/private_key.pem") - os.Setenv("CENTRAL_AUTH_PUBLICKEY", "../transaction/testdata/public_key") + os.Setenv("CENTRAL_AUTH_PRIVATEKEY", testPrivateKeyPath) + os.Setenv("CENTRAL_AUTH_PUBLICKEY", testPublicKeyPath) os.Setenv("CENTRAL_AUTH_CLIENTID", "serviceaccount_1234") os.Setenv("CENTRAL_AUTH_URL", s.URL) os.Setenv("CENTRAL_URL", s.URL) @@ -480,21 +571,21 @@ func TestRootCmdHandlers(t *testing.T) { os.Setenv("AGENT_OBJECTSLICE_NAMETITLE_3", "osp3_title") rootCmd = NewRootCmd("test_with_agent_cfg", "test_with_agent_cfg", initConfigHandler, cmdHandler, corecfg.DiscoveryAgent) - viper.AddConfigPath("./testdata") - - rootCmd.GetProperties().AddBoolProperty("agent.bool", false, "Agent Bool Property") - rootCmd.GetProperties().AddDurationProperty("agent.duration", 10*time.Second, "Agent Duration Property", properties.WithLowerLimit(10*time.Second)) - rootCmd.GetProperties().AddIntProperty("agent.int", 0, "Agent Int Property") - rootCmd.GetProperties().AddStringProperty("agent.string", "", "Agent String Property") - rootCmd.GetProperties().AddStringSliceProperty("agent.stringSlice", nil, "Agent String Slice Property") - rootCmd.GetProperties().AddObjectSliceProperty("agent.objectSlice", []string{"index", "name", "namevalue", "nametitle"}) + viper.AddConfigPath(testDataPath) + + rootCmd.GetProperties().AddBoolProperty(pathAgentBool, false, descAgentBoolProperty) + rootCmd.GetProperties().AddDurationProperty(pathAgentDuration, 10*time.Second, descAgentDurationProperty, properties.WithLowerLimit(10*time.Second)) + rootCmd.GetProperties().AddIntProperty(pathAgentInt, 0, descAgentIntProperty) + rootCmd.GetProperties().AddStringProperty(pathAgentString, "", descAgentStringProperty) + rootCmd.GetProperties().AddStringSliceProperty(pathAgentStringSlice, nil, descAgentStringSliceProperty) + rootCmd.GetProperties().AddObjectSliceProperty(pathAgentObjectSlice, []string{"index", "name", "namevalue", "nametitle"}) err := rootCmd.Execute() // should NOT be FileNotFound error assert.Nil(t, err, "An unexpected error returned") if err != nil { _, ok := err.(viper.ConfigFileNotFoundError) - assert.False(t, ok, "Incorrect error returned: %s", err.Error()) + assert.False(t, ok, errIncorrectErrorReturned, err.Error()) } errBuf := new(bytes.Buffer) @@ -530,15 +621,15 @@ func TestRootCommandLoggerStdout(t *testing.T) { initConfigHandler := noOpInitConfigHandler cmdHandler := noOpCmdHandler - os.Setenv("CENTRAL_AUTH_PRIVATEKEY", "../transaction/testdata/private_key.pem") - os.Setenv("CENTRAL_AUTH_PUBLICKEY", "../transaction/testdata/public_key") + os.Setenv("CENTRAL_AUTH_PRIVATEKEY", testPrivateKeyPath) + os.Setenv("CENTRAL_AUTH_PUBLICKEY", testPublicKeyPath) os.Setenv("CENTRAL_AUTH_CLIENTID", "serviceaccount_1234") os.Setenv("CENTRAL_AUTH_URL", s.URL) os.Setenv("CENTRAL_URL", s.URL) os.Setenv("CENTRAL_SINGLEURL", s.URL) rootCmd := NewRootCmd("test_with_non_defaults", "test_with_non_defaults", initConfigHandler, cmdHandler, corecfg.DiscoveryAgent) - viper.AddConfigPath("./testdata") + viper.AddConfigPath(testDataPath) rescueStdout := os.Stdout r, w, _ := os.Pipe() @@ -550,7 +641,7 @@ func TestRootCommandLoggerStdout(t *testing.T) { assert.Nil(t, err, "An unexpected error was received") if err != nil { _, ok := err.(viper.ConfigFileNotFoundError) - assert.False(t, ok, "Incorrect error returned: %s", err.Error()) + assert.False(t, ok, errIncorrectErrorReturned, err.Error()) } w.Close() @@ -590,15 +681,15 @@ func TestRootCommandLoggerFile(t *testing.T) { config.AgentVersion = "1.2.3-abc123" SDKBuildVersion = "1.0.0" - os.Setenv("CENTRAL_AUTH_PRIVATEKEY", "../transaction/testdata/private_key.pem") - os.Setenv("CENTRAL_AUTH_PUBLICKEY", "../transaction/testdata/public_key") + os.Setenv("CENTRAL_AUTH_PRIVATEKEY", testPrivateKeyPath) + os.Setenv("CENTRAL_AUTH_PUBLICKEY", testPublicKeyPath) os.Setenv("CENTRAL_AUTH_CLIENTID", "serviceaccount_1234") os.Setenv("CENTRAL_AUTH_URL", s.URL) os.Setenv("CENTRAL_URL", s.URL) os.Setenv("CENTRAL_SINGLEURL", s.URL) rootCmd := NewRootCmd("test_with_non_defaults", "test_with_non_defaults", initConfigHandler, cmdHandler, corecfg.DiscoveryAgent) - viper.AddConfigPath("./testdata") + viper.AddConfigPath(testDataPath) rootCmd.RootCmd().SetArgs([]string{ "--logOutput", "file", @@ -609,14 +700,14 @@ func TestRootCommandLoggerFile(t *testing.T) { }, ) // Make sure to delete file - os.RemoveAll("./tmplogs/test_with_non_defaults.log") + os.RemoveAll(testLogPath) fExecute := func() { rootCmd.Execute() } assert.NotPanics(t, fExecute) - dat, err := ioutil.ReadFile("./tmplogs/test_with_non_defaults.log") + dat, err := ioutil.ReadFile(testLogPath) assert.Nil(t, err, "failed to read file") scanner := bufio.NewScanner(bytes.NewReader(dat)) @@ -646,15 +737,15 @@ func TestRootCommandLoggerStdoutAndFile(t *testing.T) { s := newTestServer() defer s.Close() - os.Setenv("CENTRAL_AUTH_PRIVATEKEY", "../transaction/testdata/private_key.pem") - os.Setenv("CENTRAL_AUTH_PUBLICKEY", "../transaction/testdata/public_key") + os.Setenv("CENTRAL_AUTH_PRIVATEKEY", testPrivateKeyPath) + os.Setenv("CENTRAL_AUTH_PUBLICKEY", testPublicKeyPath) os.Setenv("CENTRAL_AUTH_CLIENTID", "serviceaccount_1234") os.Setenv("CENTRAL_AUTH_URL", s.URL) os.Setenv("CENTRAL_URL", s.URL) os.Setenv("CENTRAL_SINGLEURL", s.URL) rootCmd := NewRootCmd("test_with_non_defaults", "test_with_non_defaults", initConfigHandler, cmdHandler, corecfg.DiscoveryAgent) - viper.AddConfigPath("./testdata") + viper.AddConfigPath(testDataPath) rootCmd.RootCmd().SetArgs([]string{ "--logOutput", "both", @@ -672,7 +763,7 @@ func TestRootCommandLoggerStdoutAndFile(t *testing.T) { rootCmd.Execute() } // Make sure to delete file - os.Remove("./tmplogs/test_with_non_defaults.log") + os.Remove(testLogPath) assert.NotPanics(t, fExecute) w.Close() out, _ := ioutil.ReadAll(r) @@ -680,7 +771,7 @@ func TestRootCommandLoggerStdoutAndFile(t *testing.T) { var logData map[string]string json.Unmarshal([]byte(out), &logData) - dat, err := ioutil.ReadFile("./tmplogs/test_with_non_defaults.log") + dat, err := ioutil.ReadFile(testLogPath) assert.Nil(t, err) assert.Equal(t, out, dat) } @@ -715,7 +806,7 @@ func TestRootCmdHandlerWithSecretRefProperties(t *testing.T) { s := httptest.NewServer(http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { if strings.Contains(req.RequestURI, "/auth") { - token := "{\"access_token\":\"somevalue\",\"expires_in\": 12235677}" + token := testAuthToken resp.Write([]byte(token)) return } @@ -748,9 +839,9 @@ func TestRootCmdHandlerWithSecretRefProperties(t *testing.T) { CentralCfg: centralConfig, AgentCfg: &agentConfig{ agentValidationCalled: false, - sProp: rootCmd.GetProperties().StringPropertyValue("agent.string"), + sProp: rootCmd.GetProperties().StringPropertyValue(pathAgentString), sPropExt: rootCmd.GetProperties().StringPropertyValue("agent.stringExt"), - osProp: rootCmd.GetProperties().ObjectSlicePropertyValue("agent.objectSlice"), + osProp: rootCmd.GetProperties().ObjectSlicePropertyValue(pathAgentObjectSlice), }, } return cfg, nil @@ -763,18 +854,18 @@ func TestRootCmdHandlerWithSecretRefProperties(t *testing.T) { os.Setenv("CENTRAL_AUTH_URL", s.URL+"/auth") os.Setenv("CENTRAL_AUTH_CLIENTID", "serviceaccount_1234") - os.Setenv("CENTRAL_AUTH_PRIVATEKEY", "../transaction/testdata/private_key.pem") - os.Setenv("CENTRAL_AUTH_PUBLICKEY", "../transaction/testdata/public_key") + os.Setenv("CENTRAL_AUTH_PRIVATEKEY", testPrivateKeyPath) + os.Setenv("CENTRAL_AUTH_PUBLICKEY", testPublicKeyPath) os.Setenv("CENTRAL_URL", s.URL) os.Setenv("CENTRAL_SINGLEURL", s.URL) os.Setenv("CENTRAL_ENVIRONMENT", "test") rootCmd = NewRootCmd("test_with_agent_cfg", "test_with_agent_cfg", initConfigHandler, cmdHandler, corecfg.DiscoveryAgent) - viper.AddConfigPath("./testdata") + viper.AddConfigPath(testDataPath) - rootCmd.GetProperties().AddStringProperty("agent.string", "", "Agent String Property") - rootCmd.GetProperties().AddStringSliceProperty("agent.stringSlice", nil, "Agent String Slice Property") - rootCmd.GetProperties().AddObjectSliceProperty("agent.objectSlice", []string{"prop1", "prop2", "prop3"}) + rootCmd.GetProperties().AddStringProperty(pathAgentString, "", descAgentStringProperty) + rootCmd.GetProperties().AddStringSliceProperty(pathAgentStringSlice, nil, descAgentStringSliceProperty) + rootCmd.GetProperties().AddObjectSliceProperty(pathAgentObjectSlice, []string{"prop1", "prop2", "prop3"}) // Case 1 : No secret resolution - use the value in config os.Setenv("AGENT_STRING", "testValue") @@ -795,23 +886,23 @@ func TestRootCmdHandlerWithSecretRefProperties(t *testing.T) { // Case 2 : Invalid secret resolution - secret ref with invalid secret name, // config value will be set to empty string rootCmd = NewRootCmd("test_with_agent_cfg", "test_with_agent_cfg", initConfigHandler, cmdHandler, corecfg.DiscoveryAgent) - viper.AddConfigPath("./testdata") + viper.AddConfigPath(testDataPath) - rootCmd.GetProperties().AddStringProperty("agent.string", "", "Agent String Property") - rootCmd.GetProperties().AddStringSliceProperty("agent.stringSlice", nil, "Agent String Slice Property") - rootCmd.GetProperties().AddObjectSliceProperty("agent.objectSlice", []string{"prop1", "prop2", "prop3"}) + rootCmd.GetProperties().AddStringProperty(pathAgentString, "", descAgentStringProperty) + rootCmd.GetProperties().AddStringSliceProperty(pathAgentStringSlice, nil, descAgentStringSliceProperty) + rootCmd.GetProperties().AddObjectSliceProperty(pathAgentObjectSlice, []string{"prop1", "prop2", "prop3"}) cfg = nil agentCfg.agentValidationCalled = false cmdHandlerInvoked = false os.Setenv("AGENT_STRING", "@Secret.invalidSecret.secretKey") - os.Setenv("AGENT_STRINGEXT", "@Secret.invalidSecret.cachedSecretKey") + os.Setenv("AGENT_STRINGEXT", secretInvalidCachedKey) os.Setenv("AGENT_OBJECTSLICE_PROP1_1", "@Secret.invalidSecret.secretKey") - os.Setenv("AGENT_OBJECTSLICE_PROP1_2", "@Secret.invalidSecret.cachedSecretKey") + os.Setenv("AGENT_OBJECTSLICE_PROP1_2", secretInvalidCachedKey) err = rootCmd.Execute() assert.NotNil(t, err) - assert.Equal(t, "agentConfig: String prop not set", err.Error()) + assert.Equal(t, errStringPropNotSet, err.Error()) agentCfg = cfg.AgentCfg.(*agentConfig) assert.Equal(t, true, agentCfg.agentValidationCalled) assert.Equal(t, "", agentCfg.sProp) @@ -823,23 +914,23 @@ func TestRootCmdHandlerWithSecretRefProperties(t *testing.T) { // Case 3 : Invalid secret resolution - secret ref with invalid key in secret // config value will be set to empty string rootCmd = NewRootCmd("test_with_agent_cfg", "test_with_agent_cfg", initConfigHandler, cmdHandler, corecfg.DiscoveryAgent) - viper.AddConfigPath("./testdata") + viper.AddConfigPath(testDataPath) - rootCmd.GetProperties().AddStringProperty("agent.string", "", "Agent String Property") - rootCmd.GetProperties().AddStringSliceProperty("agent.stringSlice", nil, "Agent String Slice Property") - rootCmd.GetProperties().AddObjectSliceProperty("agent.objectSlice", []string{"prop1", "prop2", "prop3"}) + rootCmd.GetProperties().AddStringProperty(pathAgentString, "", descAgentStringProperty) + rootCmd.GetProperties().AddStringSliceProperty(pathAgentStringSlice, nil, descAgentStringSliceProperty) + rootCmd.GetProperties().AddObjectSliceProperty(pathAgentObjectSlice, []string{"prop1", "prop2", "prop3"}) cfg = nil agentCfg.agentValidationCalled = false cmdHandlerInvoked = false os.Setenv("AGENT_STRING", "@Secret.agentSecret.invalidKey") - os.Setenv("AGENT_STRINGEXT", "@Secret.invalidSecret.cachedSecretKey") - os.Setenv("AGENT_OBJECTSLICE_PROP1_1", "@Secret.agentSecret.secretKey") - os.Setenv("AGENT_OBJECTSLICE_PROP1_2", "@Secret.invalidSecret.cachedSecretKey") + os.Setenv("AGENT_STRINGEXT", secretInvalidCachedKey) + os.Setenv("AGENT_OBJECTSLICE_PROP1_1", secretAgentKey) + os.Setenv("AGENT_OBJECTSLICE_PROP1_2", secretInvalidCachedKey) err = rootCmd.Execute() assert.NotNil(t, err) - assert.Equal(t, "agentConfig: String prop not set", err.Error()) + assert.Equal(t, errStringPropNotSet, err.Error()) agentCfg = cfg.AgentCfg.(*agentConfig) assert.Equal(t, true, agentCfg.agentValidationCalled) assert.Equal(t, "", agentCfg.sProp) @@ -852,18 +943,18 @@ func TestRootCmdHandlerWithSecretRefProperties(t *testing.T) { // Case 4 : Successful secret resolution - use value in secret key // config value will be set to specified key in secret rootCmd = NewRootCmd("test_with_agent_cfg", "test_with_agent_cfg", initConfigHandler, cmdHandler, corecfg.DiscoveryAgent) - viper.AddConfigPath("./testdata") + viper.AddConfigPath(testDataPath) - rootCmd.GetProperties().AddStringProperty("agent.string", "", "Agent String Property") - rootCmd.GetProperties().AddStringSliceProperty("agent.stringSlice", nil, "Agent String Slice Property") - rootCmd.GetProperties().AddObjectSliceProperty("agent.objectSlice", []string{"prop1", "prop2", "prop3"}) + rootCmd.GetProperties().AddStringProperty(pathAgentString, "", descAgentStringProperty) + rootCmd.GetProperties().AddStringSliceProperty(pathAgentStringSlice, nil, descAgentStringSliceProperty) + rootCmd.GetProperties().AddObjectSliceProperty(pathAgentObjectSlice, []string{"prop1", "prop2", "prop3"}) cfg = nil agentCfg.agentValidationCalled = false cmdHandlerInvoked = false - os.Setenv("AGENT_STRING", "@Secret.agentSecret.secretKey") + os.Setenv("AGENT_STRING", secretAgentKey) os.Setenv("AGENT_STRINGEXT", "@Secret.agentSecret.cachedSecretKey") - os.Setenv("AGENT_OBJECTSLICE_PROP1_1", "@Secret.agentSecret.secretKey") + os.Setenv("AGENT_OBJECTSLICE_PROP1_1", secretAgentKey) os.Setenv("AGENT_OBJECTSLICE_PROP1_2", "@Secret.agentSecret.cachedSecretKey") err = rootCmd.Execute() assert.Nil(t, err) @@ -879,11 +970,11 @@ func TestRootCmdHandlerWithSecretRefProperties(t *testing.T) { // Case 5 : Successful secret resolution with key separate with dots(.) - use value in secret key // config value will be set to specified key in secret rootCmd = NewRootCmd("test_with_agent_cfg", "test_with_agent_cfg", initConfigHandler, cmdHandler, corecfg.DiscoveryAgent) - viper.AddConfigPath("./testdata") + viper.AddConfigPath(testDataPath) - rootCmd.GetProperties().AddStringProperty("agent.string", "", "Agent String Property") - rootCmd.GetProperties().AddStringSliceProperty("agent.stringSlice", nil, "Agent String Slice Property") - rootCmd.GetProperties().AddObjectSliceProperty("agent.objectSlice", []string{"prop1", "prop2", "prop3"}) + rootCmd.GetProperties().AddStringProperty(pathAgentString, "", descAgentStringProperty) + rootCmd.GetProperties().AddStringSliceProperty(pathAgentStringSlice, nil, descAgentStringSliceProperty) + rootCmd.GetProperties().AddObjectSliceProperty(pathAgentObjectSlice, []string{"prop1", "prop2", "prop3"}) cfg = nil agentCfg.agentValidationCalled = false @@ -940,7 +1031,7 @@ func newTestServer() *httptest.Server { s := httptest.NewServer(http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { if strings.Contains(req.RequestURI, "/auth") { - token := "{\"access_token\":\"somevalue\",\"expires_in\": 12235677}" + token := testAuthToken resp.Write([]byte(token)) return } @@ -951,7 +1042,7 @@ func newTestServer() *httptest.Server { } if strings.Contains(req.RequestURI, "/realms/Broker/protocol/openid-connect/token") { - token := "{\"access_token\":\"somevalue\",\"expires_in\": 12235677}" + token := testAuthToken resp.Write([]byte(token)) return } @@ -990,7 +1081,7 @@ func TestLowerAndUpperLimitDurations(t *testing.T) { { // valid range name: "Agent Duration Property - valid range", - durationProperty: "agent.duration", + durationProperty: pathAgentDuration, defaultDuration: 25 * time.Second, description: "Agent Duration Property - valid range", lowerLimit: 20 * time.Second, @@ -1003,7 +1094,7 @@ func TestLowerAndUpperLimitDurations(t *testing.T) { {"level":"warning","message":"config agentDuration has been set to the the default value of 25s.","time":"2022-07-26T14:42:54-07:00"} */ name: "Agent Duration Property - invalid lower limit", - durationProperty: "agent.duration", + durationProperty: pathAgentDuration, defaultDuration: 40 * time.Second, description: "Agent Duration Property - invalid lower limit", lowerLimit: 40 * time.Second, @@ -1011,10 +1102,10 @@ func TestLowerAndUpperLimitDurations(t *testing.T) { }, { // default lower than lower limit - name: "Agent Duration Property - invalid upper limit", - durationProperty: "agent.duration", + name: descAgentDurationInvalidUpper, + durationProperty: pathAgentDuration, defaultDuration: 5 * time.Second, - description: "Agent Duration Property - invalid upper limit", + description: descAgentDurationInvalidUpper, lowerLimit: 10 * time.Second, upperLimit: 20 * time.Second, expectPanic: true, @@ -1025,29 +1116,29 @@ func TestLowerAndUpperLimitDurations(t *testing.T) { {"level":"warning","message":"value 30s is higher than the supported higher limit (20s) for configuration agentDuration","time":"2022-07-26T14:42:54-07:00"} {"level":"warning","message":"config agentDuration has been set to the the default value of 30s.","time":"2022-07-26T14:42:54-07:00"} */ - name: "Agent Duration Property - invalid upper limit", - durationProperty: "agent.duration", + name: descAgentDurationInvalidUpper, + durationProperty: pathAgentDuration, defaultDuration: 20 * time.Second, - description: "Agent Duration Property - invalid upper limit", + description: descAgentDurationInvalidUpper, lowerLimit: 10 * time.Second, upperLimit: 20 * time.Second, }, { // default higher than upper limit - name: "Agent Duration Property - invalid upper limit", - durationProperty: "agent.duration", + name: descAgentDurationInvalidUpper, + durationProperty: pathAgentDuration, defaultDuration: 40 * time.Second, - description: "Agent Duration Property - invalid upper limit", + description: descAgentDurationInvalidUpper, lowerLimit: 10 * time.Second, upperLimit: 20 * time.Second, expectPanic: true, }, { // upper lower than lower limit - name: "Agent Duration Property - invalid upper limit", - durationProperty: "agent.duration", + name: descAgentDurationInvalidUpper, + durationProperty: pathAgentDuration, defaultDuration: 15 * time.Second, - description: "Agent Duration Property - invalid upper limit", + description: descAgentDurationInvalidUpper, lowerLimit: 10 * time.Second, upperLimit: 5 * time.Second, expectPanic: true, @@ -1067,14 +1158,14 @@ func TestLowerAndUpperLimitDurations(t *testing.T) { CentralCfg: centralConfig, AgentCfg: &agentConfig{ agentValidationCalled: false, - dProp: rootCmd.GetProperties().DurationPropertyValue("agent.duration"), + dProp: rootCmd.GetProperties().DurationPropertyValue(pathAgentDuration), }, } return cfg, nil } - os.Setenv("CENTRAL_AUTH_PRIVATEKEY", "../transaction/testdata/private_key.pem") - os.Setenv("CENTRAL_AUTH_PUBLICKEY", "../transaction/testdata/public_key") + os.Setenv("CENTRAL_AUTH_PRIVATEKEY", testPrivateKeyPath) + os.Setenv("CENTRAL_AUTH_PUBLICKEY", testPublicKeyPath) os.Setenv("CENTRAL_AUTH_CLIENTID", "serviceaccount_1234") os.Setenv("CENTRAL_AUTH_URL", s.URL) os.Setenv("CENTRAL_URL", s.URL) @@ -1082,7 +1173,7 @@ func TestLowerAndUpperLimitDurations(t *testing.T) { os.Setenv("AGENT_DURATION", "30s") rootCmd = NewRootCmd("test_with_non_defaults", "test_with_non_defaults", initConfigHandler, nil, corecfg.DiscoveryAgent) - viper.AddConfigPath("./testdata") + viper.AddConfigPath(testDataPath) fExecute := func() { rootCmd.GetProperties().AddDurationProperty(test.durationProperty, test.defaultDuration, test.description, properties.WithLowerLimit(test.lowerLimit), properties.WithUpperLimit(test.upperLimit)) } @@ -1152,14 +1243,14 @@ func TestIntLowerAndUpperLimits(t *testing.T) { CentralCfg: centralConfig, AgentCfg: &agentConfig{ agentValidationCalled: false, - iProp: rootCmd.GetProperties().IntPropertyValue("agent.int"), + iProp: rootCmd.GetProperties().IntPropertyValue(pathAgentInt), }, } return cfg, nil } - os.Setenv("CENTRAL_AUTH_PRIVATEKEY", "../transaction/testdata/private_key.pem") - os.Setenv("CENTRAL_AUTH_PUBLICKEY", "../transaction/testdata/public_key") + os.Setenv("CENTRAL_AUTH_PRIVATEKEY", testPrivateKeyPath) + os.Setenv("CENTRAL_AUTH_PUBLICKEY", testPublicKeyPath) os.Setenv("CENTRAL_AUTH_CLIENTID", "serviceaccount_1234") os.Setenv("CENTRAL_AUTH_URL", s.URL) os.Setenv("CENTRAL_URL", s.URL) @@ -1167,9 +1258,9 @@ func TestIntLowerAndUpperLimits(t *testing.T) { os.Setenv("AGENT_INT", tc.intProp) rootCmd = NewRootCmd("test_with_non_defaults", "test_with_non_defaults", initConfigHandler, nil, corecfg.DiscoveryAgent) - viper.AddConfigPath("./testdata") + viper.AddConfigPath(testDataPath) fExecute := func() { - rootCmd.GetProperties().AddIntProperty("agent.int", tc.defaultInt, "", properties.WithLowerLimitInt(tc.lowerLimit), properties.WithUpperLimitInt(tc.upperLimit)) + rootCmd.GetProperties().AddIntProperty(pathAgentInt, tc.defaultInt, "", properties.WithLowerLimitInt(tc.lowerLimit), properties.WithUpperLimitInt(tc.upperLimit)) } if tc.expectPanic { assert.Panics(t, fExecute) @@ -1203,13 +1294,13 @@ func TestNewCmd(t *testing.T) { return nil } newCmd := NewCmd(rootCmd, "traceability", "TestRootCmd", initConfigHandler, cmdHandler, corecfg.TraceabilityAgent) - viper.AddConfigPath("./testdata") + viper.AddConfigPath(testDataPath) assert.NotNil(t, newCmd) os.Setenv("CENTRAL_AUTH_URL", s.URL) os.Setenv("CENTRAL_AUTH_CLIENTID", "serviceaccount_1234") - os.Setenv("CENTRAL_AUTH_PRIVATEKEY", "../transaction/testdata/private_key.pem") - os.Setenv("CENTRAL_AUTH_PUBLICKEY", "../transaction/testdata/public_key") + os.Setenv("CENTRAL_AUTH_PRIVATEKEY", testPrivateKeyPath) + os.Setenv("CENTRAL_AUTH_PUBLICKEY", testPublicKeyPath) os.Setenv("CENTRAL_URL", s.URL) os.Setenv("CENTRAL_SINGLEURL", s.URL) os.Setenv("CENTRAL_ORGANIZATIONID", " orgid") @@ -1225,5 +1316,5 @@ func TestNewCmd(t *testing.T) { errBuf := new(bytes.Buffer) rootCmd.SetErr(errBuf) - assert.Contains(t, "Error central.organizationID not set in config", errBuf.String()) + assert.Contains(t, errOrganizationIDUnset, errBuf.String()) } diff --git a/pkg/cmd/properties/properties.go b/pkg/cmd/properties/properties.go index de58fffdd..09b90d249 100644 --- a/pkg/cmd/properties/properties.go +++ b/pkg/cmd/properties/properties.go @@ -67,6 +67,7 @@ type Properties interface { // Log Properties MaskValues(name string) DebugLogProperties() + SetAliasKeyPrefix(aliasKeyPrefix string) } @@ -122,8 +123,20 @@ func WithUpperLimitInt(upper int) IntOpt { } } +// aliasKeyPrefix - when set, properties are looked up under "." first, +// falling back to the plain "". var aliasKeyPrefix string +// SetAliasKeyPrefix - +func SetAliasKeyPrefix(keyPrefix string) { + aliasKeyPrefix = keyPrefix +} + +// GetAliasKeyPrefix - +func GetAliasKeyPrefix() string { + return aliasKeyPrefix +} + type properties struct { Properties rootCmd *cobra.Command @@ -162,16 +175,6 @@ func NewPropertiesWithSecretResolver(rootCmd *cobra.Command, secretResolver Secr return cmdprops } -// SetAliasKeyPrefix - -func SetAliasKeyPrefix(keyPrefix string) { - aliasKeyPrefix = keyPrefix -} - -// GetAliasKeyPrefix - -func GetAliasKeyPrefix() string { - return aliasKeyPrefix -} - func (p *properties) bindOrPanic(key string, flg *flag.Flag) { if err := viper.BindPFlag(key, flg); err != nil { panic(err) @@ -441,6 +444,18 @@ func (p *properties) parseStringValueForKey(key string) string { return s } +func (p *properties) parseStringValue(key string) string { + var s string + if aliasKeyPrefix != "" { + s = p.parseStringValueForKey(aliasKeyPrefix + "." + key) + } + // If no alias or no value parsed for alias key + if s == "" { + s = p.parseStringValueForKey(key) + } + return s +} + func (p *properties) parseSlice(s string, expSlice [][]byte) string { rtnS := s envVar := string(expSlice[1]) @@ -465,18 +480,6 @@ func (p *properties) parseSlice(s string, expSlice [][]byte) string { return rtnS } -func (p *properties) parseStringValue(key string) string { - var s string - if aliasKeyPrefix != "" { - s = p.parseStringValueForKey(aliasKeyPrefix + "." + key) - } - // If no alias or no value parsed for alias key - if s == "" { - s = p.parseStringValueForKey(key) - } - return s -} - func (p *properties) resolveSecretReference(cfgName, cfgValue string) string { if p.secretResolver != nil { secretValue, err := p.secretResolver.ResolveSecret(cfgValue) diff --git a/pkg/cmd/properties/properties_test.go b/pkg/cmd/properties/properties_test.go new file mode 100644 index 000000000..f1093304f --- /dev/null +++ b/pkg/cmd/properties/properties_test.go @@ -0,0 +1,53 @@ +package properties + +import ( + "bytes" + "testing" + + "github.com/spf13/cobra" + "github.com/spf13/viper" + "github.com/stretchr/testify/assert" +) + +// TestAliasKeyPrefixResolvesNestedYamlValue guards against a regression where yaml nested under +// an agent's own name (e.g. "traceability_agent:") silently stopped resolving. +func TestAliasKeyPrefixResolvesNestedYamlValue(t *testing.T) { + yamlContent := []byte(` +traceability_agent: + status: + port: 8990 +`) + + tests := map[string]struct { + aliasKeyPrefix string + expectedPort int + }{ + "no alias prefix set, falls back to flag default": { + aliasKeyPrefix: "", + expectedPort: 8989, + }, + "alias prefix set, resolves nested yaml value": { + aliasKeyPrefix: "traceability_agent", + expectedPort: 8990, + }, + } + + for name, tc := range tests { + tc := tc + t.Run(name, func(t *testing.T) { + viper.Reset() + SetAliasKeyPrefix(tc.aliasKeyPrefix) + defer SetAliasKeyPrefix("") + + rootCmd := &cobra.Command{Use: "test"} + props := NewPropertiesWithSecretResolver(rootCmd, nil) + props.AddIntProperty("status.port", 8989, "test port property") + + viper.SetConfigType("yaml") + err := viper.ReadConfig(bytes.NewReader(yamlContent)) + assert.Nil(t, err) + + assert.Equal(t, tc.expectedPort, props.IntPropertyValue("status.port")) + }) + } +} diff --git a/pkg/cmd/root.go b/pkg/cmd/root.go index 9ef529784..ef90f7f45 100644 --- a/pkg/cmd/root.go +++ b/pkg/cmd/root.go @@ -15,6 +15,7 @@ import ( "github.com/Axway/agent-sdk/pkg/cmd/properties/resolver" "github.com/Axway/agent-sdk/pkg/config" "github.com/Axway/agent-sdk/pkg/jobs" + "github.com/Axway/agent-sdk/pkg/traceability" "github.com/Axway/agent-sdk/pkg/util" "github.com/Axway/agent-sdk/pkg/util/errors" hc "github.com/Axway/agent-sdk/pkg/util/healthcheck" @@ -134,6 +135,11 @@ func NewRootCmd(exeName, desc string, initConfigHandler InitConfigHandler, comma } c.props = properties.NewPropertiesWithSecretResolver(c.rootCmd, c.secretResolver) + if agentType == config.TraceabilityAgent || agentType == config.ComplianceAgent { + properties.SetAliasKeyPrefix(c.agentName) + } else { + properties.SetAliasKeyPrefix("") + } c.addBaseProps(agentType) config.AddLogConfigProperties(c.props, fmt.Sprintf("%s.log", exeName)) config.AddMetricLogConfigProperties(c.props, agentType) @@ -179,6 +185,8 @@ func NewCmd(rootCmd *cobra.Command, exeName, desc string, initConfigHandler Init c.props = properties.NewPropertiesWithSecretResolver(c.rootCmd, c.secretResolver) if agentType == config.TraceabilityAgent || agentType == config.ComplianceAgent { properties.SetAliasKeyPrefix(c.agentName) + } else { + properties.SetAliasKeyPrefix("") } c.addBaseProps(agentType) @@ -243,6 +251,14 @@ func (c *agentRootCommand) initialize(cmd *cobra.Command, args []string) error { _, beatsConfigFilePath = c.props.StringFlagValue(beatsPathConfigFlag) } + // Most agents never set --pathConfig (it defaults to "."), so fall back to "./data" + // Instead of dumping files in the working directory, default pkg/agent/cache already uses. + dataDirPath := agentConfigFilePath + if dataDirPath == "" || dataDirPath == "." { + dataDirPath = "./data" + } + traceability.SetDataDirPath(dataDirPath) + viper.SetConfigName(c.agentName) // viper.SetConfigType("yaml") //Comment out since yaml, yml is a support extension already. We need an updated story to take into account the other supported extensions @@ -255,9 +271,9 @@ func (c *agentRootCommand) initialize(cmd *cobra.Command, args []string) error { viper.AutomaticEnv() err = viper.ReadInConfig() if err != nil { - if envFile == "" { - return err - } else if _, ok := err.(viper.ConfigFileNotFoundError); !ok { + // Config can be read entirely from env vars. + // Any other error (e.g. malformed YAML) still fails startup. + if _, ok := err.(viper.ConfigFileNotFoundError); !ok { return err } } @@ -418,14 +434,6 @@ func (c *agentRootCommand) run(cmd *cobra.Command, args []string) (err error) { WithField("sdkVersion", SDKBuildVersion). Infof("Starting %s", c.rootCmd.Short) if c.commandHandler != nil { - // Setup logp to use beats logger. - // Setting up late here as log entries for agent/command initialization are not logged - // as the beats logger is initialized only when the beat instance is created. - if c.agentType == config.TraceabilityAgent || c.agentType == config.ComplianceAgent { - properties.SetAliasKeyPrefix(c.agentName) - log.SetIsLogP() - } - c.healthCheckTicker() if util.IsNotTest() && c.agentFeaturesCfg.AgentStatusUpdatesEnabled() && !c.centralCfg.GetUsageReportingConfig().IsOfflineMode() { diff --git a/pkg/config/centralconfig.go b/pkg/config/centralconfig.go index 7f01da1f1..19503abf5 100644 --- a/pkg/config/centralconfig.go +++ b/pkg/config/centralconfig.go @@ -66,8 +66,8 @@ var regionalSettingsMap = map[Region]regionalSettings{ CentralURL: "https://apicentral.axway.com", AuthURL: "https://login.axway.com/auth", PlatformURL: "https://platform.axway.com", - TraceabilityHost: "ingestion.datasearch.axway.com:5044", - TraceabilityProtocol: "tcp", + TraceabilityHost: "phoenix.datasearch.axway.com:443", + TraceabilityProtocol: "https", Deployment: "prod", }, EU: { @@ -75,8 +75,8 @@ var regionalSettingsMap = map[Region]regionalSettings{ CentralURL: "https://central.eu-fr.axway.com", AuthURL: "https://login.axway.com/auth", PlatformURL: "https://platform.axway.com", - TraceabilityHost: "ingestion.visibility.eu-fr.axway.com:5044", - TraceabilityProtocol: "tcp", + TraceabilityHost: "phoenix.visibility.eu-fr.axway.com:443", + TraceabilityProtocol: "https", Deployment: "prod-eu", }, AP: { @@ -84,8 +84,8 @@ var regionalSettingsMap = map[Region]regionalSettings{ CentralURL: "https://central.ap-sg.axway.com", AuthURL: "https://login.axway.com/auth", PlatformURL: "https://platform.axway.com", - TraceabilityHost: "ingestion.visibility.ap-sg.axway.com:5044", - TraceabilityProtocol: "tcp", + TraceabilityHost: "phoenix.visibility.ap-sg.axway.com:443", + TraceabilityProtocol: "https", Deployment: "prod-ap", }, EU2: { diff --git a/pkg/config/centralconfig_test.go b/pkg/config/centralconfig_test.go index 45e06afa2..428733c17 100644 --- a/pkg/config/centralconfig_test.go +++ b/pkg/config/centralconfig_test.go @@ -313,3 +313,25 @@ func cleanupFiles(fileName string) { // cleanup files os.Remove("./" + fileName) } + +func TestRegionalTraceabilityDefaults(t *testing.T) { + tests := map[string]struct { + region Region + expectHost string + expectProto string + }{ + "US": {region: US, expectHost: "phoenix.datasearch.axway.com:443", expectProto: "https"}, + "EU": {region: EU, expectHost: "phoenix.visibility.eu-fr.axway.com:443", expectProto: "https"}, + "AP": {region: AP, expectHost: "phoenix.visibility.ap-sg.axway.com:443", expectProto: "https"}, + "EU2": {region: EU2, expectHost: "phoenix.eu-fr.axway.com:443", expectProto: "https"}, + } + + for name, tc := range tests { + tc := tc + t.Run(name, func(t *testing.T) { + settings := regionalSettingsMap[tc.region] + assert.Equal(t, tc.expectHost, settings.TraceabilityHost) + assert.Equal(t, tc.expectProto, settings.TraceabilityProtocol) + }) + } +} diff --git a/pkg/config/logconfig.go b/pkg/config/logconfig.go index 23a2e78ec..ca20500e2 100644 --- a/pkg/config/logconfig.go +++ b/pkg/config/logconfig.go @@ -1,14 +1,11 @@ package config import ( - "fmt" "path/filepath" "strings" "github.com/Axway/agent-sdk/pkg/cmd/properties" "github.com/Axway/agent-sdk/pkg/util/log" - "github.com/elastic/beats/v7/libbeat/cfgfile" - "github.com/elastic/beats/v7/libbeat/common" "github.com/sirupsen/logrus" ) @@ -184,125 +181,106 @@ const ( logFilePermissionsPath = "logging.files.permissions" ) +// ConditionalOverride replaces libbeat's cfgfile.ConditionalOverride. +type ConditionalOverride struct { + Check func(cfg *LogConfiguration) bool + Config map[string]interface{} +} + // LogConfigOverrides - override the filebeat config options -func LogConfigOverrides() []cfgfile.ConditionalOverride { - overrides := make([]cfgfile.ConditionalOverride, 0) +func LogConfigOverrides() []ConditionalOverride { + overrides := make([]ConditionalOverride, 0) overrides = setLogLevel(overrides) return overrideLogLevel(overrides) } -func setLogLevel(overrides []cfgfile.ConditionalOverride) []cfgfile.ConditionalOverride { +func setLogLevel(overrides []ConditionalOverride) []ConditionalOverride { // Set level to info - overrides = append(overrides, cfgfile.ConditionalOverride{ - Check: func(cfg *common.Config) bool { - aliasKeyPrefix := properties.GetAliasKeyPrefix() - output, _ := cfg.String(fmt.Sprintf("%s.%s", aliasKeyPrefix, pathLogLevel), 0) - level, err := logrus.ParseLevel(output) - if err == nil && level == logrus.InfoLevel { - return true - } - return false + overrides = append(overrides, ConditionalOverride{ + Check: func(cfg *LogConfiguration) bool { + level, err := logrus.ParseLevel(cfg.Level) + return err == nil && level == logrus.InfoLevel }, - Config: common.MustNewConfigFrom(map[string]interface{}{ + Config: map[string]interface{}{ logLevelYAMLPath: "info", - }), + }, }) // Set level to warn - overrides = append(overrides, cfgfile.ConditionalOverride{ - Check: func(cfg *common.Config) bool { - aliasKeyPrefix := properties.GetAliasKeyPrefix() - output, _ := cfg.String(fmt.Sprintf("%s.%s", aliasKeyPrefix, pathLogLevel), 0) - level, err := logrus.ParseLevel(output) - if err == nil && level == logrus.WarnLevel { - return true - } - return false + overrides = append(overrides, ConditionalOverride{ + Check: func(cfg *LogConfiguration) bool { + level, err := logrus.ParseLevel(cfg.Level) + return err == nil && level == logrus.WarnLevel }, - Config: common.MustNewConfigFrom(map[string]interface{}{ + Config: map[string]interface{}{ logLevelYAMLPath: "warn", - }), + }, }) // Set level to error - overrides = append(overrides, cfgfile.ConditionalOverride{ - Check: func(cfg *common.Config) bool { - aliasKeyPrefix := properties.GetAliasKeyPrefix() - output, _ := cfg.String(fmt.Sprintf("%s.%s", aliasKeyPrefix, pathLogLevel), 0) - level, err := logrus.ParseLevel(output) - if err == nil && level == logrus.ErrorLevel { - return true - } - return false + overrides = append(overrides, ConditionalOverride{ + Check: func(cfg *LogConfiguration) bool { + level, err := logrus.ParseLevel(cfg.Level) + return err == nil && level == logrus.ErrorLevel }, - Config: common.MustNewConfigFrom(map[string]interface{}{ + Config: map[string]interface{}{ logLevelYAMLPath: "error", - }), + }, }) return overrides } -func overrideLogLevel(overrides []cfgfile.ConditionalOverride) []cfgfile.ConditionalOverride { +func overrideLogLevel(overrides []ConditionalOverride) []ConditionalOverride { // Override the level to debug, if trace or debug - overrides = append(overrides, cfgfile.ConditionalOverride{ - Check: func(cfg *common.Config) bool { - aliasKeyPrefix := properties.GetAliasKeyPrefix() - output, _ := cfg.String(fmt.Sprintf("%s.%s", aliasKeyPrefix, pathLogLevel), 0) - level, err := logrus.ParseLevel(output) - if err == nil && (level == logrus.TraceLevel || level == logrus.DebugLevel) { - return true - } - return false + overrides = append(overrides, ConditionalOverride{ + Check: func(cfg *LogConfiguration) bool { + level, err := logrus.ParseLevel(cfg.Level) + return err == nil && (level == logrus.TraceLevel || level == logrus.DebugLevel) }, - Config: common.MustNewConfigFrom(map[string]interface{}{ + Config: map[string]interface{}{ logLevelYAMLPath: "debug", - }), + }, }) // Override the log output format - overrides = append(overrides, cfgfile.ConditionalOverride{ - Check: func(cfg *common.Config) bool { - aliasKeyPrefix := properties.GetAliasKeyPrefix() - output, _ := cfg.String(fmt.Sprintf("%s.%s", aliasKeyPrefix, pathLogFormat), 0) - return strings.ToLower(output) == "json" + overrides = append(overrides, ConditionalOverride{ + Check: func(cfg *LogConfiguration) bool { + return strings.ToLower(cfg.Format) == "json" }, - Config: common.MustNewConfigFrom(map[string]interface{}{ + Config: map[string]interface{}{ logJSONYAMLPath: true, - }), + }, }) // Override the log output stream - overrides = append(overrides, cfgfile.ConditionalOverride{ - Check: func(cfg *common.Config) bool { - aliasKeyPrefix := properties.GetAliasKeyPrefix() - output, _ := cfg.String(fmt.Sprintf("%s.%s", aliasKeyPrefix, pathLogOutput), 0) - return strings.ToLower(output) == "stdout" + overrides = append(overrides, ConditionalOverride{ + Check: func(cfg *LogConfiguration) bool { + return strings.ToLower(cfg.Output) == "stdout" }, - Config: common.MustNewConfigFrom(map[string]interface{}{ + Config: map[string]interface{}{ logSTDERRYAMLPath: true, logFileYAMLPath: false, - }), + }, }) // Override the log output to file - overrides = append(overrides, cfgfile.ConditionalOverride{ - Check: func(cfg *common.Config) bool { - aliasKeyPrefix := properties.GetAliasKeyPrefix() - output, _ := cfg.String(fmt.Sprintf("%s.%s", aliasKeyPrefix, pathLogOutput), 0) - if strings.ToLower(output) == "file" || strings.ToLower(output) == "both" { - if strings.ToLower(output) == "both" { + overrides = append(overrides, ConditionalOverride{ + Check: func(cfg *LogConfiguration) bool { + output := strings.ToLower(cfg.Output) + if output == "file" || output == "both" { + if output == "both" { log.Warn("Traceability agent can only log to one output type, setting to file output") } return true } return false }, - Config: common.MustNewConfigFrom(map[string]interface{}{ + Config: map[string]interface{}{ logFileYAMLPath: true, logSTDERRYAMLPath: false, logFilePermissionsPath: "0600", - }), + }, }) return overrides diff --git a/pkg/config/logconfig_test.go b/pkg/config/logconfig_test.go index 5b5ca100b..60d9d9dc5 100644 --- a/pkg/config/logconfig_test.go +++ b/pkg/config/logconfig_test.go @@ -257,3 +257,67 @@ func TestLogConfigValidations(t *testing.T) { }) } } + +func TestLogConfigOverrides(t *testing.T) { + tests := map[string]struct { + cfg *LogConfiguration + wantOverrides map[string]interface{} // subset of override Config expected to match + }{ + "info level": { + cfg: &LogConfiguration{Level: "info", Format: "json", Output: "stdout"}, + wantOverrides: map[string]interface{}{logLevelYAMLPath: "info"}, + }, + "warn level": { + cfg: &LogConfiguration{Level: "warn"}, + wantOverrides: map[string]interface{}{logLevelYAMLPath: "warn"}, + }, + "error level": { + cfg: &LogConfiguration{Level: "error"}, + wantOverrides: map[string]interface{}{logLevelYAMLPath: "error"}, + }, + "debug level overrides to debug": { + cfg: &LogConfiguration{Level: "debug"}, + wantOverrides: map[string]interface{}{logLevelYAMLPath: "debug"}, + }, + "trace level overrides to debug": { + cfg: &LogConfiguration{Level: "trace"}, + wantOverrides: map[string]interface{}{logLevelYAMLPath: "debug"}, + }, + "json format": { + cfg: &LogConfiguration{Format: "json"}, + wantOverrides: map[string]interface{}{logJSONYAMLPath: true}, + }, + "stdout output": { + cfg: &LogConfiguration{Output: "stdout"}, + wantOverrides: map[string]interface{}{logSTDERRYAMLPath: true, logFileYAMLPath: false}, + }, + "file output": { + cfg: &LogConfiguration{Output: "file"}, + wantOverrides: map[string]interface{}{logFileYAMLPath: true, logSTDERRYAMLPath: false, logFilePermissionsPath: "0600"}, + }, + "both output collapses to file output with a warning": { + cfg: &LogConfiguration{Output: "both"}, + wantOverrides: map[string]interface{}{logFileYAMLPath: true, logSTDERRYAMLPath: false, logFilePermissionsPath: "0600"}, + }, + } + + for name, tc := range tests { + tc := tc + t.Run(name, func(t *testing.T) { + overrides := LogConfigOverrides() + + matched := false + for _, override := range overrides { + if !override.Check(tc.cfg) { + continue + } + for k, v := range tc.wantOverrides { + if override.Config[k] == v { + matched = true + } + } + } + assert.True(t, matched, "expected at least one override to match and apply the expected config values") + }) + } +} diff --git a/pkg/event/event.go b/pkg/event/event.go new file mode 100644 index 000000000..a959bec83 --- /dev/null +++ b/pkg/event/event.go @@ -0,0 +1,29 @@ +// Package event replaces libbeat's beat.Event, common.MapStr, publisher.Event, and +// publisher.Batch. +package event + +import "time" + +// MapStr is a flat string-keyed metadata/fields map, replacing libbeat's common.MapStr. +type MapStr map[string]interface{} + +// Event is a single event to publish, replacing libbeat's beat.Event/publisher.Event. +type Event struct { + Timestamp time.Time + Meta MapStr + Fields MapStr + Private interface{} +} + +// Batch is a collection of events to be published as a unit, replacing libbeat's +// publisher.Batch interface. +type Batch interface { + Events() []Event + SetEvents(events []Event) + ACK() + Drop() + Retry() + RetryEvents(events []Event) + Cancelled() + CancelledEvents(events []Event) +} diff --git a/pkg/traceability/backoff.go b/pkg/traceability/backoff.go new file mode 100644 index 000000000..64793cf5b --- /dev/null +++ b/pkg/traceability/backoff.go @@ -0,0 +1,80 @@ +package traceability + +import ( + "context" + "math/rand" + "time" + + "github.com/Axway/agent-sdk/pkg/event" +) + +// backoffClient replaces libbeat's outputs.WithBackoff/backoff.EqualJitterBackoff. +type backoffClient struct { + client NetworkClient + init time.Duration + max time.Duration + done chan struct{} + + duration time.Duration +} + +func withBackoff(client NetworkClient, init, max time.Duration) NetworkClient { + return &backoffClient{ + client: client, + init: init, + max: max, + done: make(chan struct{}), + // allow sleeping at least the init period on the first wait + duration: init * 2, + } +} + +func (b *backoffClient) Connect() error { + err := b.client.Connect() + b.waitOnError(err) + return err +} + +func (b *backoffClient) Close() error { + err := b.client.Close() + close(b.done) + return err +} + +func (b *backoffClient) Publish(ctx context.Context, batch event.Batch) error { + err := b.client.Publish(ctx, batch) + if err != nil { + b.client.Close() + } + b.waitOnError(err) + return err +} + +func (b *backoffClient) String() string { + return "backoff(" + b.client.String() + ")" +} + +// waitOnError resets the backoff duration if err is nil, otherwise blocks for the +// jittered backoff duration and grows it for the next call. +func (b *backoffClient) waitOnError(err error) { + if err == nil { + b.duration = b.init * 2 + return + } + b.wait() +} + +func (b *backoffClient) wait() { + half := int64(b.duration / 2) + sleep := time.Duration(half + rand.Int63n(half)) + + b.duration *= 2 + if b.duration > b.max { + b.duration = b.max + } + + select { + case <-b.done: + case <-time.After(sleep): + } +} diff --git a/pkg/traceability/backoff_test.go b/pkg/traceability/backoff_test.go new file mode 100644 index 000000000..bba67f31d --- /dev/null +++ b/pkg/traceability/backoff_test.go @@ -0,0 +1,119 @@ +package traceability + +import ( + "context" + "errors" + "testing" + "time" + + "github.com/Axway/agent-sdk/pkg/event" + "github.com/stretchr/testify/assert" +) + +type fakeNetworkClient struct { + connectErr error + publishErr error + closeCalls int +} + +func (f *fakeNetworkClient) Connect() error { return f.connectErr } +func (f *fakeNetworkClient) Close() error { + f.closeCalls++ + return nil +} +func (f *fakeNetworkClient) Publish(_ context.Context, _ event.Batch) error { return f.publishErr } +func (f *fakeNetworkClient) String() string { return "fake" } + +func TestBackoffClientWaitOnError(t *testing.T) { + init := 10 * time.Millisecond + max := 100 * time.Millisecond + + tests := map[string]struct { + publish bool // false calls Connect, true calls Publish + clientErr error + startDuration time.Duration // 0 means the default (init*2) + wantMinElapsed time.Duration + wantMaxElapsed time.Duration + wantDuration time.Duration + wantCloseCalls int + }{ + "connect success does not sleep and resets duration": { + wantMaxElapsed: init, + wantDuration: init * 2, + }, + "connect error sleeps at least half the jittered duration and grows it": { + clientErr: errors.New("connect failed"), + wantMinElapsed: init / 2, + wantDuration: init * 4, + }, + "duration is capped at max": { + clientErr: errors.New("connect failed"), + startDuration: max * 2, // simulate having already grown past max + wantDuration: max, + }, + "publish error closes the inner client and grows duration": { + publish: true, + clientErr: errors.New("publish failed"), + wantCloseCalls: 1, + wantDuration: init * 4, + }, + "publish success does not close the inner client and resets duration": { + publish: true, + startDuration: max, + wantDuration: init * 2, + }, + } + + for name, tc := range tests { + tc := tc + t.Run(name, func(t *testing.T) { + fake := &fakeNetworkClient{} + if tc.publish { + fake.publishErr = tc.clientErr + } else { + fake.connectErr = tc.clientErr + } + + client := withBackoff(fake, init, max).(*backoffClient) + if tc.startDuration > 0 { + client.duration = tc.startDuration + } + + start := time.Now() + var err error + if tc.publish { + err = client.Publish(context.Background(), &MockBatch{}) + } else { + err = client.Connect() + } + elapsed := time.Since(start) + + assert.Equal(t, tc.clientErr, err) + assert.Equal(t, tc.wantDuration, client.duration) + assert.Equal(t, tc.wantCloseCalls, fake.closeCalls) + if tc.wantMinElapsed > 0 { + assert.GreaterOrEqual(t, elapsed, tc.wantMinElapsed) + } + if tc.wantMaxElapsed > 0 { + assert.Less(t, elapsed, tc.wantMaxElapsed) + } + }) + } +} + +func TestBackoffClientClose(t *testing.T) { + fake := &fakeNetworkClient{} + client := withBackoff(fake, 10*time.Millisecond, 100*time.Millisecond).(*backoffClient) + + err := client.Close() + assert.Nil(t, err) + assert.Equal(t, 1, fake.closeCalls) + _, open := <-client.done + assert.False(t, open) +} + +func TestBackoffClientString(t *testing.T) { + fake := &fakeNetworkClient{} + client := withBackoff(fake, 10*time.Millisecond, 100*time.Millisecond).(*backoffClient) + assert.Equal(t, "backoff(fake)", client.String()) +} diff --git a/pkg/traceability/batch.go b/pkg/traceability/batch.go new file mode 100644 index 000000000..060d2f9b4 --- /dev/null +++ b/pkg/traceability/batch.go @@ -0,0 +1,50 @@ +package traceability + +import ( + "github.com/Axway/agent-sdk/pkg/event" + "github.com/Axway/agent-sdk/pkg/util/log" +) + +// eventBatch is a minimal event.Batch implementation that logs ACK/Drop/Retry/ +// Cancelled activity, for callers that publish a fixed slice of events and don't +// need custom batch-completion handling (e.g. forwarding to an upstream pipeline). +type eventBatch struct { + events []event.Event + logger log.FieldLogger +} + +// NewEventBatch builds an event.Batch around a fixed slice of events, for callers +// of Client.Publish that don't already have their own Batch implementation. +func NewEventBatch(events []event.Event) event.Batch { + return &eventBatch{ + events: events, + logger: log.NewFieldLogger().WithPackage("sdk.traceability").WithComponent("eventBatch"), + } +} + +func (b *eventBatch) Events() []event.Event { return b.events } +func (b *eventBatch) SetEvents(events []event.Event) { b.events = events } + +func (b *eventBatch) ACK() { + b.logger.WithField("count", len(b.events)).Info("published events") +} + +func (b *eventBatch) Drop() { + b.logger.WithField("count", len(b.events)).Warn("dropped events") +} + +func (b *eventBatch) Retry() { + b.logger.WithField("count", len(b.events)).Warn("retrying events") +} + +func (b *eventBatch) Cancelled() { + b.logger.WithField("count", len(b.events)).Warn("cancelled events") +} + +func (b *eventBatch) RetryEvents(events []event.Event) { + b.logger.WithField("count", len(events)).Warn("retrying events") +} + +func (b *eventBatch) CancelledEvents(events []event.Event) { + b.logger.WithField("count", len(events)).Warn("cancelled events") +} diff --git a/pkg/traceability/batch_test.go b/pkg/traceability/batch_test.go new file mode 100644 index 000000000..0af4ed217 --- /dev/null +++ b/pkg/traceability/batch_test.go @@ -0,0 +1,30 @@ +package traceability + +import ( + "testing" + + "github.com/Axway/agent-sdk/pkg/event" + "github.com/stretchr/testify/assert" +) + +func TestNewEventBatchEvents(t *testing.T) { + events := []event.Event{{}, {}} + batch := NewEventBatch(events) + + assert.Equal(t, events, batch.Events()) + + replacement := []event.Event{{}} + batch.SetEvents(replacement) + assert.Equal(t, replacement, batch.Events()) +} + +func TestNewEventBatchCompletionCallbacksDoNotPanic(t *testing.T) { + batch := NewEventBatch([]event.Event{{}}) + + assert.NotPanics(t, batch.ACK) + assert.NotPanics(t, batch.Drop) + assert.NotPanics(t, batch.Retry) + assert.NotPanics(t, batch.Cancelled) + assert.NotPanics(t, func() { batch.RetryEvents([]event.Event{{}}) }) + assert.NotPanics(t, func() { batch.CancelledEvents([]event.Event{{}}) }) +} diff --git a/pkg/traceability/config.go b/pkg/traceability/config.go index d8bf23832..1e78adfd8 100644 --- a/pkg/traceability/config.go +++ b/pkg/traceability/config.go @@ -1,46 +1,88 @@ package traceability import ( + "net" "net/url" + "os" + "regexp" + "strconv" + "strings" "time" + yaml "gopkg.in/yaml.v3" + "github.com/Axway/agent-sdk/pkg/agent" + "github.com/Axway/agent-sdk/pkg/cmd/properties" "github.com/Axway/agent-sdk/pkg/traceability/redaction" "github.com/Axway/agent-sdk/pkg/traceability/sampling" "github.com/Axway/agent-sdk/pkg/util/log" +) - "github.com/elastic/beats/v7/libbeat/beat" - "github.com/elastic/beats/v7/libbeat/common" - "github.com/elastic/beats/v7/libbeat/common/cfgwarn" - "github.com/elastic/beats/v7/libbeat/common/transport/tlscommon" +// legacy lumberjack/ingestion hostname prefixes no longer supported now that +// single-entry routes HTTPS via phoenix.* hostnames (AOPS-4119) +var removedIngestionHostPrefixes = []string{"ingestion.", "ingestion-http.", "ingestion-lumberjack."} + +// using the same env vars agents already use, e.g. pathHost -> TRACEABILITY_HOST. +const ( + pathHost = "traceability.host" + pathProtocol = "traceability.protocol" + pathPort = "traceability.port" // deprecated, presence-only check + pathLoadBalance = "traceability.loadbalance" + pathSlowStart = "traceability.slowstart" + pathBulkMaxSize = "traceability.bulkmaxsize" + pathClientTimeout = "traceability.clienttimeout" + pathTTL = "traceability.ttl" + pathPipelining = "traceability.pipelining" + pathCompressionLevel = "traceability.compressionlevel" + pathMaxRetries = "traceability.maxretries" + pathSSLVerification = "traceability.ssl.verificationmode" + pathSSLCipherSuites = "traceability.ssl.ciphersuites" + pathProxyURL = "traceability.proxyurl" + pathProxyLocalResolve = "traceability.proxyuselocalresolver" + pathBackoffInit = "traceability.backoff.init" + pathBackoffMax = "traceability.backoff.max" + pathEscapeHTML = "traceability.escapehtml" + pathExceptionList = "traceability.exception.list" + pathRedactionMasking = "traceability.redaction.masking.characters" + pathSamplingPercent = "traceability.sampling.percentage" + pathSamplingPerAPI = "traceability.sampling.per.api" + pathSamplingPerSub = "traceability.sampling.per.subscription" + pathSamplingOnlyErr = "traceability.sampling.onlyerrors" ) -// Config - -type HostConfig struct { - Protocol string `config:"protocol"` - Hosts []string `config:"hosts"` -} +// redaction lists still come from one env var each, holding a blob like [{"keyMatch":".*"}]. Same +// as before. Reason : mulesoft-agents relies on this exact format, so it's kept as-is. +const ( + envRedactionPathShow = "TRACEABILITY_REDACTION_PATH_SHOW" + envRedactionQueryArgShow = "TRACEABILITY_REDACTION_QUERYARGUMENT_SHOW" + envRedactionQueryArgSanitize = "TRACEABILITY_REDACTION_QUERYARGUMENT_SANITIZE" + envRedactionRequestHeaderShow = "TRACEABILITY_REDACTION_REQUESTHEADER_SHOW" + envRedactionRequestHeaderSanitize = "TRACEABILITY_REDACTION_REQUESTHEADER_SANITIZE" + envRedactionResponseHeaderShow = "TRACEABILITY_REDACTION_RESPONSEHEADER_SHOW" + envRedactionResponseHeaderSanitize = "TRACEABILITY_REDACTION_RESPONSEHEADER_SANITIZE" + envRedactionJMSPropertiesShow = "TRACEABILITY_REDACTION_JMSPROPERTIES_SHOW" + envRedactionJMSPropertiesSanitize = "TRACEABILITY_REDACTION_JMSPROPERTIES_SANITIZE" +) // Config - type Config struct { - Index string `config:"index"` - LoadBalance bool `config:"loadbalance"` - BulkMaxSize int `config:"bulk_max_size"` - SlowStart bool `config:"slow_start"` - Timeout time.Duration `config:"client_timeout" validate:"min=0"` - TTL time.Duration `config:"ttl" validate:"min=0"` - Pipelining int `config:"pipelining" validate:"min=0"` - CompressionLevel int `config:"compression_level" validate:"min=0, max=9"` - MaxRetries int `config:"max_retries" validate:"min=-1"` - TLS *tlscommon.Config `config:"ssl"` - Proxy ProxyConfig `config:",inline"` - Backoff Backoff `config:"backoff"` - EscapeHTML bool `config:"escape_html"` - Protocol string `config:"protocol"` - Hosts []string `config:"hosts"` - Redaction redaction.Config `config:"redaction" yaml:"redaction"` - Sampling sampling.Sampling `config:"sampling" yaml:"sampling"` - APIExceptionsList []string `config:"apiExceptionsList"` + LoadBalance bool + BulkMaxSize int + SlowStart bool + Timeout time.Duration + TTL time.Duration + Pipelining int + CompressionLevel int + MaxRetries int + TLS TLSConfig + Proxy ProxyConfig + Backoff Backoff + EscapeHTML bool + Protocol string + Hosts []string + Redaction redaction.Config + Sampling sampling.Sampling + APIExceptionsList []string } // ProxyConfig holds the configuration information required to proxy @@ -48,10 +90,10 @@ type Config struct { type ProxyConfig struct { // URL of the SOCKS proxy. Scheme must be socks5. Username and password can be // embedded in the URL. - URL string `config:"proxy_url"` + URL string // Resolve names locally instead of on the SOCKS server. - LocalResolve bool `config:"proxy_use_local_resolver"` + LocalResolve bool } // Backoff - @@ -84,27 +126,103 @@ func DefaultConfig() *Config { } } -func readConfig(cfg *common.Config, info beat.Info) (*Config, error) { - outputConfig = DefaultConfig() +// AddConfigProperties sets up all the traceability env vars. Call this once before ParseConfig. It's not automatic, since discovery-only agents don't need it. +func AddConfigProperties(props properties.Properties) { + def := DefaultConfig() - err := cfgwarn.CheckRemoved6xSettings(cfg, "port") - if err != nil { - return nil, err + props.AddStringSliceProperty(pathHost, def.Hosts, "Comma separated list of traceability hosts to publish to") + props.AddStringProperty(pathProtocol, def.Protocol, "Protocol used to publish traceability events") + props.AddStringProperty(pathPort, "", "Deprecated, use "+pathHost) + props.AddBoolProperty(pathLoadBalance, def.LoadBalance, "Enables round robin load balancing across traceability hosts") + props.AddBoolProperty(pathSlowStart, def.SlowStart, "Enables slow start for the traceability client") + props.AddIntProperty(pathBulkMaxSize, def.BulkMaxSize, "Maximum number of events published in a single traceability request") + props.AddDurationProperty(pathClientTimeout, def.Timeout, "Traceability client timeout") + props.AddDurationProperty(pathTTL, def.TTL, "Traceability client connection TTL", properties.WithLowerLimit(0)) + props.AddIntProperty(pathPipelining, def.Pipelining, "Traceability client pipelining", properties.WithLowerLimitInt(0)) + props.AddIntProperty(pathCompressionLevel, def.CompressionLevel, "Traceability client compression level", + properties.WithLowerLimitInt(0), properties.WithUpperLimitInt(9)) + props.AddIntProperty(pathMaxRetries, def.MaxRetries, "Maximum number of retries for a failed traceability request", + properties.WithLowerLimitInt(-1)) + props.AddStringProperty(pathSSLVerification, "", "TLS verification mode for the traceability client") + props.AddStringSliceProperty(pathSSLCipherSuites, []string{}, "Cipher suites allowed for the traceability client") + props.AddStringProperty(pathProxyURL, def.Proxy.URL, "SOCKS5 proxy URL for the traceability client") + props.AddBoolProperty(pathProxyLocalResolve, def.Proxy.LocalResolve, "Resolve names locally instead of on the SOCKS proxy server") + props.AddDurationProperty(pathBackoffInit, def.Backoff.Init, "Initial backoff duration for a failed traceability request", properties.WithLowerLimit(0)) + props.AddDurationProperty(pathBackoffMax, def.Backoff.Max, "Maximum backoff duration for a failed traceability request") + props.AddBoolProperty(pathEscapeHTML, def.EscapeHTML, "Escapes HTML characters in traceability events") + props.AddStringSliceProperty(pathExceptionList, def.APIExceptionsList, "APIs excluded from traceability logging") + + props.AddStringProperty(pathRedactionMasking, def.Redaction.MaskingCharacters, "Characters used to mask sanitized values") + + props.AddStringProperty(pathSamplingPercent, strconv.FormatFloat(def.Sampling.Percentage, 'f', -1, 64), + "Percentage of transactions to sample") + props.AddBoolProperty(pathSamplingPerAPI, def.Sampling.PerAPI, "Applies sampling per API") + props.AddBoolProperty(pathSamplingPerSub, def.Sampling.PerSub, "Applies sampling per subscription") + props.AddBoolProperty(pathSamplingOnlyErr, def.Sampling.OnlyErrors, "Only samples transactions that resulted in an error") +} + +// ParseConfig reads traceability config from env vars. AddConfigProperties must be called first. +func ParseConfig(props properties.Properties) (*Config, error) { + cfg := DefaultConfig() + + if props.StringPropertyValue(pathPort) != "" { + log.Warn("output.traceability.port is no longer supported; use output.traceability.hosts") } - if err := cfg.Unpack(outputConfig); err != nil { - return nil, err + cfg.Hosts = props.StringSlicePropertyValue(pathHost) + cfg.Protocol = props.StringPropertyValue(pathProtocol) + cfg.LoadBalance = props.BoolPropertyValue(pathLoadBalance) + cfg.SlowStart = props.BoolPropertyValue(pathSlowStart) + cfg.BulkMaxSize = props.IntPropertyValue(pathBulkMaxSize) + cfg.Timeout = props.DurationPropertyValue(pathClientTimeout) + cfg.TTL = props.DurationPropertyValue(pathTTL) + cfg.Pipelining = props.IntPropertyValue(pathPipelining) + cfg.CompressionLevel = props.IntPropertyValue(pathCompressionLevel) + cfg.MaxRetries = props.IntPropertyValue(pathMaxRetries) + cfg.EscapeHTML = props.BoolPropertyValue(pathEscapeHTML) + cfg.APIExceptionsList = props.StringSlicePropertyValue(pathExceptionList) + + cfg.TLS.VerificationMode = props.StringPropertyValue(pathSSLVerification) + cfg.TLS.CipherSuites = props.StringSlicePropertyValue(pathSSLCipherSuites) + + cfg.Proxy.URL = props.StringPropertyValue(pathProxyURL) + cfg.Proxy.LocalResolve = props.BoolPropertyValue(pathProxyLocalResolve) + + cfg.Backoff.Init = props.DurationPropertyValue(pathBackoffInit) + cfg.Backoff.Max = props.DurationPropertyValue(pathBackoffMax) + + cfg.Redaction.MaskingCharacters = props.StringPropertyValue(pathRedactionMasking) + cfg.Redaction.Path.Allowed = parseShowList(envRedactionPathShow) + cfg.Redaction.Args.Allowed = parseShowList(envRedactionQueryArgShow) + cfg.Redaction.Args.Sanitize = parseSanitizeList(envRedactionQueryArgSanitize) + cfg.Redaction.RequestHeaders.Allowed = parseShowList(envRedactionRequestHeaderShow) + cfg.Redaction.RequestHeaders.Sanitize = parseSanitizeList(envRedactionRequestHeaderSanitize) + cfg.Redaction.ResponseHeaders.Allowed = parseShowList(envRedactionResponseHeaderShow) + cfg.Redaction.ResponseHeaders.Sanitize = parseSanitizeList(envRedactionResponseHeaderSanitize) + cfg.Redaction.JMSProperties.Allowed = parseShowList(envRedactionJMSPropertiesShow) + cfg.Redaction.JMSProperties.Sanitize = parseSanitizeList(envRedactionJMSPropertiesSanitize) + + if percentage, err := strconv.ParseFloat(props.StringPropertyValue(pathSamplingPercent), 64); err == nil { + cfg.Sampling.Percentage = percentage } + cfg.Sampling.PerAPI = props.BoolPropertyValue(pathSamplingPerAPI) + cfg.Sampling.PerSub = props.BoolPropertyValue(pathSamplingPerSub) + cfg.Sampling.OnlyErrors = props.BoolPropertyValue(pathSamplingOnlyErr) + + return FinishConfig(cfg) +} + +// FinishConfig runs the setup shared by every Config, regardless of source - host/protocol +// fallback, redaction, sampling, proxy check, exception list. So v7_traceability_agent (still +// on YAML/go-ucfg) can reuse it instead of duplicating it. +func FinishConfig(cfg *Config) (*Config, error) { + outputConfig = cfg if agent.GetCentralConfig().GetTraceabilityHost() != "" && len(outputConfig.Hosts) == 0 { outputConfig.Protocol = agent.GetCentralConfig().GetTraceabilityProtocol() outputConfig.Hosts = []string{agent.GetCentralConfig().GetTraceabilityHost()} } - if outputConfig.Index == "" { - outputConfig.Index = info.IndexPrefix - } - // Setup the redaction regular expressions redaction.SetupGlobalRedaction(outputConfig.Redaction) @@ -114,6 +232,7 @@ func readConfig(cfg *common.Config, info beat.Info) (*Config, error) { } // Setup the sampling config, if central config can not be found assume online mode + var err error if agent.GetCentralConfig() != nil && agent.GetCentralConfig().GetUsageReportingConfig() != nil { err = sampling.SetupSampling(outputConfig.Sampling, agent.GetCentralConfig().GetUsageReportingConfig().IsOfflineMode(), agent.GetCentralConfig().GetAPICDeployment(), sampling.WithCacheAccess(agent.GetCacheManager())) } else { @@ -138,29 +257,54 @@ func readConfig(cfg *common.Config, info beat.Info) (*Config, error) { } // set up the api exceptions list for logging events - exception, err := setUpAPIExceptionList(outputConfig.APIExceptionsList) + exceptionValue, err := setUpAPIExceptionList(outputConfig.APIExceptionsList) if err != nil { - err = ErrInvalidRegex.FormatError("apiExceptionValue", exception, err) + err = ErrInvalidRegex.FormatError("apiExceptionValue", exceptionValue, err) log.Error(err) } return outputConfig, nil } -// IsHTTPTransport - Returns true if the protocol is set to http/https -func IsHTTPTransport() bool { - if outputConfig == nil { - return false +// for Mulesoft after removing go ucfg +var redactionListSpacing = regexp.MustCompile(`(keyMatch|valueMatch):(\S)`) + +func normalizeRedactionListValue(val string) string { + return redactionListSpacing.ReplaceAllString(val, "$1: $2") +} + +func parseShowList(envVar string) []redaction.Show { + shows := []redaction.Show{} + val := strings.TrimSpace(os.Getenv(envVar)) + if val == "" { + return shows } - return (outputConfig.Protocol == "https" || outputConfig.Protocol == "http") + if err := yaml.Unmarshal([]byte(normalizeRedactionListValue(val)), &shows); err != nil { + log.Warnf("could not parse %s, ignoring: %s", envVar, err.Error()) + return []redaction.Show{} + } + return shows } -// IsTCPTransport - Returns true if the protocol is set to tcp -func IsTCPTransport() bool { +func parseSanitizeList(envVar string) []redaction.Sanitize { + sanitize := []redaction.Sanitize{} + val := strings.TrimSpace(os.Getenv(envVar)) + if val == "" { + return sanitize + } + if err := yaml.Unmarshal([]byte(normalizeRedactionListValue(val)), &sanitize); err != nil { + log.Warnf("could not parse %s, ignoring: %s", envVar, err.Error()) + return []redaction.Sanitize{} + } + return sanitize +} + +// IsHTTPTransport - Returns true if the protocol is set to http/https +func IsHTTPTransport() bool { if outputConfig == nil { return false } - return outputConfig.Protocol == "tcp" + return (outputConfig.Protocol == "https" || outputConfig.Protocol == "http") } // GetMaxRetries - Returns the max retries configured for transport @@ -170,3 +314,32 @@ func GetMaxRetries() int { } return outputConfig.MaxRetries } + +// ValidateCfg - validates the config does not use the removed tcp/lumberjack transport +func (c *Config) ValidateCfg() error { + if c.Protocol == "tcp" { + return ErrTCPProtocolRemoved + } + for _, host := range c.Hosts { + if err := validateHost(host); err != nil { + return err + } + } + return nil +} + +func validateHost(host string) error { + h, p, err := net.SplitHostPort(host) + if err != nil { + return nil + } + if p == tcpPort { + return ErrPort5044Removed.FormatError(host) + } + for _, prefix := range removedIngestionHostPrefixes { + if strings.HasPrefix(h, prefix) { + return ErrIngestionHostRemoved.FormatError(host) + } + } + return nil +} diff --git a/pkg/traceability/errors.go b/pkg/traceability/errors.go index a034c990c..ba8e9e2eb 100644 --- a/pkg/traceability/errors.go +++ b/pkg/traceability/errors.go @@ -4,8 +4,13 @@ import "github.com/Axway/agent-sdk/pkg/util/errors" // Config errors var ( - ErrHTTPNotConnected = errors.New(1503, "http transport is not connected") - ErrJSONEncodeFailed = errors.New(1504, "failed to encode the json content") - ErrInvalidConfig = errors.Newf(1505, "invalid traceability config. Config error: %s") - ErrInvalidRegex = errors.Newf(1506, "could not compile the %s regex value (%v): %v") + ErrHTTPNotConnected = errors.New(1503, "http transport is not connected") + ErrJSONEncodeFailed = errors.New(1504, "failed to encode the json content") + ErrInvalidConfig = errors.Newf(1505, "invalid traceability config. Config error: %s") + ErrInvalidRegex = errors.Newf(1506, "could not compile the %s regex value (%v): %v") + ErrTCPProtocolRemoved = errors.New(1507, "protocol 'tcp' is no longer supported; set output.traceability.protocol to https") + ErrPort5044Removed = errors.Newf(1508, "host %s uses port 5044 (tcp/lumberjack), which is no longer supported. Use port 443 with protocol https") + ErrIngestionHostRemoved = errors.Newf(1509, "host %s uses a legacy ingestion.* address, which is no longer supported. Use the phoenix.* host for your region") + ErrNoConnectionConfigured = errors.New(1510, "no connection configured") + ErrNoActiveConnection = errors.New(1511, "no active connection") ) diff --git a/pkg/traceability/failover.go b/pkg/traceability/failover.go new file mode 100644 index 000000000..cad700431 --- /dev/null +++ b/pkg/traceability/failover.go @@ -0,0 +1,73 @@ +package traceability + +import ( + "context" + "math/rand" + "strings" + + "github.com/Axway/agent-sdk/pkg/event" +) + +// failoverClient replaces libbeat's outputs.failoverClient. +type failoverClient struct { + clients []NetworkClient + active int +} + +// newFailoverClient replaces libbeat's outputs.NewFailoverClient. +func newFailoverClient(clients []NetworkClient) NetworkClient { + if len(clients) == 1 { + return clients[0] + } + return &failoverClient{clients: clients, active: -1} +} + +func (f *failoverClient) Connect() error { + var next int + active := f.active + l := len(f.clients) + + switch { + case l == 0: + return ErrNoConnectionConfigured + case l == 1: + next = 0 + case l == 2 && active >= 0 && active <= 1: + next = 1 - active + default: + for { + // connect to a random server, to potentially spread the load when a + // large number of agents with the same set of hosts start up at once + next = rand.Int() % l + if next != active { + break + } + } + } + + f.active = next + return f.clients[next].Connect() +} + +func (f *failoverClient) Close() error { + if f.active < 0 { + return ErrNoActiveConnection + } + return f.clients[f.active].Close() +} + +func (f *failoverClient) Publish(ctx context.Context, batch event.Batch) error { + if f.active < 0 { + batch.Retry() + return ErrNoActiveConnection + } + return f.clients[f.active].Publish(ctx, batch) +} + +func (f *failoverClient) String() string { + names := make([]string, len(f.clients)) + for i, client := range f.clients { + names[i] = client.String() + } + return "failover(" + strings.Join(names, ",") + ")" +} diff --git a/pkg/traceability/failover_test.go b/pkg/traceability/failover_test.go new file mode 100644 index 000000000..bc4364f71 --- /dev/null +++ b/pkg/traceability/failover_test.go @@ -0,0 +1,97 @@ +package traceability + +import ( + "context" + "testing" + + "github.com/Axway/agent-sdk/pkg/event" + "github.com/stretchr/testify/assert" +) + +func TestNewFailoverClientSingleClient(t *testing.T) { + fake := &fakeNetworkClient{} + client := newFailoverClient([]NetworkClient{fake}) + assert.Same(t, NetworkClient(fake), client) +} + +func TestFailoverClientBeforeAnyConnect(t *testing.T) { + tests := map[string]struct { + clients []NetworkClient + op func(client NetworkClient) error + wantErr error + }{ + "connect with no clients configured": { + clients: nil, + op: func(client NetworkClient) error { return client.Connect() }, + wantErr: ErrNoConnectionConfigured, + }, + "publish before any connect": { + clients: []NetworkClient{&fakeNetworkClient{}, &fakeNetworkClient{}}, + op: func(client NetworkClient) error { return client.Publish(context.Background(), &MockBatch{}) }, + wantErr: ErrNoActiveConnection, + }, + "close before any connect": { + clients: []NetworkClient{&fakeNetworkClient{}, &fakeNetworkClient{}}, + op: func(client NetworkClient) error { return client.Close() }, + wantErr: ErrNoActiveConnection, + }, + } + + for name, tc := range tests { + tc := tc + t.Run(name, func(t *testing.T) { + client := newFailoverClient(tc.clients) + assert.Equal(t, tc.wantErr, tc.op(client)) + }) + } +} + +func TestFailoverClientNeverRepeatsActive(t *testing.T) { + tests := map[string]struct { + numClients int + }{ + "two clients": {numClients: 2}, + "three clients": {numClients: 3}, + "four clients": {numClients: 4}, + } + + for name, tc := range tests { + tc := tc + t.Run(name, func(t *testing.T) { + clients := make([]NetworkClient, tc.numClients) + for i := range clients { + clients[i] = &fakeNetworkClient{} + } + client := newFailoverClient(clients).(*failoverClient) + + // the first connect picks randomly since there's no active client yet; + // every connect after that must never repeat the previously active client + for i := 0; i < 50; i++ { + prevActive := client.active + assert.Nil(t, client.Connect()) + if prevActive >= 0 { + assert.NotEqual(t, prevActive, client.active) + } + } + }) + } +} + +func TestFailoverClientPublishAndCloseRouteToActiveOnly(t *testing.T) { + c0 := &fakeNetworkClient{} + c1 := &fakeNetworkClient{} + client := newFailoverClient([]NetworkClient{c0, c1}).(*failoverClient) + + client.active = 0 + assert.Nil(t, client.Publish(context.Background(), &MockBatch{})) + assert.Nil(t, client.Close()) + assert.Equal(t, 1, c0.closeCalls) + assert.Equal(t, 0, c1.closeCalls) +} + +func TestFailoverClientString(t *testing.T) { + client := newFailoverClient([]NetworkClient{&fakeNetworkClient{}, &fakeNetworkClient{}}).(*failoverClient) + assert.Equal(t, "failover(fake,fake)", client.String()) +} + +var _ event.Batch = (*MockBatch)(nil) diff --git a/pkg/traceability/healthcheck_test.go b/pkg/traceability/healthcheck_test.go index 2d72db582..02793d4ad 100644 --- a/pkg/traceability/healthcheck_test.go +++ b/pkg/traceability/healthcheck_test.go @@ -7,8 +7,8 @@ import ( "github.com/Axway/agent-sdk/pkg/agent" "github.com/Axway/agent-sdk/pkg/apic" + "github.com/Axway/agent-sdk/pkg/event" hc "github.com/Axway/agent-sdk/pkg/util/healthcheck" - "github.com/elastic/beats/v7/libbeat/publisher" "github.com/stretchr/testify/assert" ) @@ -20,7 +20,7 @@ func (m mockTransportClient) Close() error { return nil } -func (m mockTransportClient) Publish(context.Context, publisher.Batch) error { +func (m mockTransportClient) Publish(context.Context, event.Batch) error { return nil } diff --git a/pkg/traceability/httpclient.go b/pkg/traceability/httpclient.go index 342232cb4..4136aeba2 100644 --- a/pkg/traceability/httpclient.go +++ b/pkg/traceability/httpclient.go @@ -15,12 +15,8 @@ import ( "github.com/Axway/agent-sdk/pkg/agent" "github.com/Axway/agent-sdk/pkg/api" "github.com/Axway/agent-sdk/pkg/config" + "github.com/Axway/agent-sdk/pkg/event" "github.com/Axway/agent-sdk/pkg/util/log" - "github.com/elastic/beats/v7/libbeat/beat" - "github.com/elastic/beats/v7/libbeat/outputs" - "github.com/elastic/beats/v7/libbeat/outputs/outil" - "github.com/elastic/beats/v7/libbeat/outputs/transport" - "github.com/elastic/beats/v7/libbeat/publisher" ) const ( @@ -33,26 +29,21 @@ const ( // HTTPClient struct type HTTPClient struct { Connection - tlsConfig *transport.TLSConfig + tlsConfig config.TLSConfig compressionLevel int proxyURL *url.URL headers map[string]string - beatInfo beat.Info logger log.FieldLogger timeout time.Duration } // HTTPClientSettings struct type HTTPClientSettings struct { - BeatInfo beat.Info URL string Proxy *url.URL - TLS *transport.TLSConfig - Index outil.Selector - Pipeline *outil.Selector + TLS config.TLSConfig Timeout time.Duration CompressionLevel int - Observer outputs.Observer Headers map[string]string UserAgent string IsSingleEntry bool @@ -91,20 +82,17 @@ func NewHTTPClient(s HTTPClientSettings) (*HTTPClient, error) { opts = append(opts, api.WithSingleURL()) } - tlsCfg := config.NewTLSConfig().(*config.TLSConfiguration) - tlsCfg.LoadFrom(s.TLS.ToConfig()) - client := &HTTPClient{ Connection: Connection{ URL: s.URL, - api: api.NewClient(tlsCfg, s.Proxy.String(), opts...), + api: api.NewClient(s.TLS, s.Proxy.String(), opts...), encoder: encoder, userAgent: s.UserAgent, }, + tlsConfig: s.TLS, compressionLevel: compression, proxyURL: s.Proxy, headers: s.Headers, - beatInfo: s.BeatInfo, logger: logger, timeout: s.Timeout, } @@ -125,7 +113,7 @@ func (client *HTTPClient) Close() error { } // Publish sends events to the clients sink. -func (client *HTTPClient) Publish(_ context.Context, batch publisher.Batch) error { +func (client *HTTPClient) Publish(_ context.Context, batch event.Batch) error { events := batch.Events() err := client.publishEvents(events) if err == nil { @@ -144,7 +132,6 @@ func (client *HTTPClient) String() string { func (client *HTTPClient) Clone() *HTTPClient { c, _ := NewHTTPClient( HTTPClientSettings{ - BeatInfo: client.beatInfo, URL: client.URL, Proxy: client.proxyURL, TLS: client.tlsConfig, @@ -157,7 +144,7 @@ func (client *HTTPClient) Clone() *HTTPClient { } // publishEvents - posts all events to the http endpoint. -func (client *HTTPClient) publishEvents(data []publisher.Event) error { +func (client *HTTPClient) publishEvents(data []event.Event) error { if len(data) == 0 { return nil } @@ -172,12 +159,12 @@ func (client *HTTPClient) publishEvents(data []publisher.Event) error { var events = make([]json.RawMessage, len(data)) timeStamp := time.Now() - for i, event := range data { - events[i] = client.makeHTTPEvent(&event.Content) + for i, evt := range data { + events[i] = client.makeHTTPEvent(&evt) if i == 0 { - timeStamp = event.Content.Timestamp - allFields, err := event.Content.Fields.GetValue("fields") - if err != nil { + timeStamp = evt.Timestamp + allFields, ok := evt.Fields["fields"] + if !ok { delete(client.headers, FlowHeader) continue } @@ -274,7 +261,7 @@ func (conn *Connection) execHTTPRequest(req api.Request) (int, []byte, error) { return resp.Code, resp.Body, nil } -func (client *HTTPClient) makeHTTPEvent(v *beat.Event) json.RawMessage { +func (client *HTTPClient) makeHTTPEvent(v *event.Event) json.RawMessage { var eventData json.RawMessage msg := v.Fields["message"].(string) json.Unmarshal([]byte(msg), &eventData) diff --git a/pkg/traceability/httpclient_test.go b/pkg/traceability/httpclient_test.go index 43582e239..36824b34b 100644 --- a/pkg/traceability/httpclient_test.go +++ b/pkg/traceability/httpclient_test.go @@ -5,20 +5,18 @@ import ( "testing" "time" - "github.com/elastic/beats/v7/libbeat/beat" - "github.com/elastic/beats/v7/libbeat/common" - "github.com/elastic/beats/v7/libbeat/publisher" "github.com/stretchr/testify/assert" "github.com/Axway/agent-sdk/pkg/agent" "github.com/Axway/agent-sdk/pkg/api" + "github.com/Axway/agent-sdk/pkg/event" "github.com/Axway/agent-sdk/pkg/util/log" ) const metricFlowValue = "api-central-metric" -func makeFlowEvent(flow string) publisher.Event { - fields := common.MapStr{ +func makeFlowEvent(flow string) event.Event { + fields := event.MapStr{ "message": `{"event":"test"}`, } if flow != "" { @@ -26,11 +24,9 @@ func makeFlowEvent(flow string) publisher.Event { FlowHeader: flow, } } - return publisher.Event{ - Content: beat.Event{ - Timestamp: time.Now(), - Fields: fields, - }, + return event.Event{ + Timestamp: time.Now(), + Fields: fields, } } @@ -39,20 +35,20 @@ func makeFlowEvent(flow string) publisher.Event { func TestPublishEventsFlowHeader(t *testing.T) { cases := map[string]struct { preSeedHeaders map[string]string // simulate headers left by a prior batch - events []publisher.Event + events []event.Event wantFlowHeader string // "" means key must be absent from client.headers }{ "metric event sets flow header": { - events: []publisher.Event{makeFlowEvent(metricFlowValue)}, + events: []event.Event{makeFlowEvent(metricFlowValue)}, wantFlowHeader: metricFlowValue, }, "transaction event does not add flow header": { - events: []publisher.Event{makeFlowEvent("")}, + events: []event.Event{makeFlowEvent("")}, wantFlowHeader: "", }, "stale metric flow header cleared by subsequent transaction batch": { preSeedHeaders: map[string]string{FlowHeader: metricFlowValue}, - events: []publisher.Event{makeFlowEvent("")}, + events: []event.Event{makeFlowEvent("")}, wantFlowHeader: "", }, } diff --git a/pkg/traceability/sampling/globalsampling.go b/pkg/traceability/sampling/globalsampling.go index 68ff9ea58..20287f233 100644 --- a/pkg/traceability/sampling/globalsampling.go +++ b/pkg/traceability/sampling/globalsampling.go @@ -13,11 +13,11 @@ import ( v1 "github.com/Axway/agent-sdk/pkg/apic/apiserver/models/api/v1" management "github.com/Axway/agent-sdk/pkg/apic/apiserver/models/management/v1" "github.com/Axway/agent-sdk/pkg/apic/definitions" + "github.com/Axway/agent-sdk/pkg/event" "github.com/Axway/agent-sdk/pkg/jobs" transactionUtil "github.com/Axway/agent-sdk/pkg/transaction/util" "github.com/Axway/agent-sdk/pkg/util" "github.com/Axway/agent-sdk/pkg/util/log" - "github.com/elastic/beats/v7/libbeat/publisher" "github.com/shopspring/decimal" ) @@ -250,7 +250,7 @@ func IsSamplingEnabled() (bool, error) { } // FilterEvents - returns an array of events that are part of the sample -func FilterEvents(events []publisher.Event) ([]publisher.Event, error) { +func FilterEvents(events []event.Event) ([]event.Event, error) { if agentSamples == nil { return events, ErrGlobalSamplingCfg } diff --git a/pkg/traceability/sampling/sample.go b/pkg/traceability/sampling/sample.go index cc67252f1..ee1c61f1d 100644 --- a/pkg/traceability/sampling/sample.go +++ b/pkg/traceability/sampling/sample.go @@ -8,9 +8,9 @@ import ( management "github.com/Axway/agent-sdk/pkg/apic/apiserver/models/management/v1" "github.com/Axway/agent-sdk/pkg/apic/definitions" + "github.com/Axway/agent-sdk/pkg/event" "github.com/Axway/agent-sdk/pkg/transaction/util" "github.com/Axway/agent-sdk/pkg/util/log" - "github.com/elastic/beats/v7/libbeat/publisher" ) const apiAppKey = "apiAppKey" @@ -239,15 +239,15 @@ func (s *sample) ShouldSampleTransaction(details TransactionDetails) bool { } // FilterEvents - returns an array of events that are part of the sample -func (s *sample) FilterEvents(events []publisher.Event) []publisher.Event { +func (s *sample) FilterEvents(events []event.Event) []event.Event { if s.config.Percentage == countMax { return events // all events are sampled by default } - sampledEvents := make([]publisher.Event, 0) - for _, event := range events { - if _, sampled := event.Content.Meta[SampleKey]; sampled { - sampledEvents = append(sampledEvents, event) + sampledEvents := make([]event.Event, 0) + for _, evt := range events { + if _, sampled := evt.Meta[SampleKey]; sampled { + sampledEvents = append(sampledEvents, evt) } } diff --git a/pkg/traceability/sampling/sampling_test.go b/pkg/traceability/sampling/sampling_test.go index 9603945fe..ba4db6af9 100644 --- a/pkg/traceability/sampling/sampling_test.go +++ b/pkg/traceability/sampling/sampling_test.go @@ -12,9 +12,7 @@ import ( v1Time "github.com/Axway/agent-sdk/pkg/apic/apiserver/models/api/v1" management "github.com/Axway/agent-sdk/pkg/apic/apiserver/models/management/v1" "github.com/Axway/agent-sdk/pkg/config" - "github.com/elastic/beats/v7/libbeat/beat" - "github.com/elastic/beats/v7/libbeat/common" - "github.com/elastic/beats/v7/libbeat/publisher" + "github.com/Axway/agent-sdk/pkg/event" "github.com/stretchr/testify/assert" ) @@ -402,22 +400,22 @@ func TestShouldSample(t *testing.T) { } } -func createEvents(numberOfEvents int, samplePercent float64) []publisher.Event { - events := []publisher.Event{} +func createEvents(numberOfEvents int, samplePercent float64) []event.Event { + events := []event.Event{} count := 0 sampled := 0 countMax := 100 * int(math.Pow(10, float64(numberOfDecimals(samplePercent)))) limit := int(float64(countMax) * samplePercent / 100) for i := 0; i < numberOfEvents; i++ { - var event publisher.Event + var evt event.Event if count < limit { sampled++ - event = createEvent(true) + evt = createEvent(true) } else { - event = createEvent(false) + evt = createEvent(false) } - events = append(events, event) + events = append(events, evt) count++ if count == countMax { count = 0 @@ -427,21 +425,19 @@ func createEvents(numberOfEvents int, samplePercent float64) []publisher.Event { return events } -func createEvent(sampled bool) publisher.Event { - fieldsData := common.MapStr{ +func createEvent(sampled bool) event.Event { + fieldsData := event.MapStr{ "message": "message value", } - meta := common.MapStr{} + meta := event.MapStr{} if sampled { - meta.Put(SampleKey, true) + meta[SampleKey] = true } - return publisher.Event{ - Content: beat.Event{ - Timestamp: time.Now(), - Meta: meta, - Private: nil, - Fields: fieldsData, - }, + return event.Event{ + Timestamp: time.Now(), + Meta: meta, + Private: nil, + Fields: fieldsData, } } diff --git a/pkg/traceability/tlsconfig.go b/pkg/traceability/tlsconfig.go new file mode 100644 index 000000000..91cc0b8a2 --- /dev/null +++ b/pkg/traceability/tlsconfig.go @@ -0,0 +1,57 @@ +package traceability + +import ( + "github.com/Axway/agent-sdk/pkg/config" + "github.com/Axway/agent-sdk/pkg/util/log" +) + +// TLSConfig replaces libbeat's tlscommon.Config. +type TLSConfig struct { + // Enabled is accepted but not acted on: protocol already determines whether TLS + // applies. + Enabled *bool `config:"enabled"` + VerificationMode string `config:"verification_mode"` + CipherSuites []string `config:"cipher_suites"` + + SupportedProtocols []string `config:"supported_protocols"` + CertificateAuthorities []string `config:"certificate_authorities"` + CurveTypes []string `config:"curve_types"` + Renegotiation string `config:"renegotiation"` + CASha256 []string `config:"ca_sha256"` + KeyPassphrase string `config:"key_passphrase"` +} + +// toTLSConfiguration replaces libbeat's tlscommon.LoadTLSConfig. +func (t TLSConfig) toTLSConfiguration() *config.TLSConfiguration { + tlsCfg := config.NewTLSConfig().(*config.TLSConfiguration) + tlsCfg.InsecureSkipVerify = t.VerificationMode == "none" + + switch t.VerificationMode { + case "", "full", "none": + // supported, nothing to warn about + case "certificate", "strict": + log.Warnf("output.traceability.ssl.verification_mode %q is no longer supported; using 'full' semantics", t.VerificationMode) + default: + log.Warnf("output.traceability.ssl.verification_mode %q is not recognized; defaulting to 'full'", t.VerificationMode) + } + + if len(t.CipherSuites) > 0 { + tlsCfg.CipherSuites = config.NewCipherArray(t.CipherSuites) + } + + unsupported := map[string]bool{ + "supported_protocols": len(t.SupportedProtocols) > 0, + "certificate_authorities": len(t.CertificateAuthorities) > 0, + "curve_types": len(t.CurveTypes) > 0, + "ca_sha256": len(t.CASha256) > 0, + "renegotiation": t.Renegotiation != "", + "key_passphrase": t.KeyPassphrase != "", + } + for name, set := range unsupported { + if set { + log.Warnf("output.traceability.ssl.%s is no longer supported and will be ignored", name) + } + } + + return tlsCfg +} diff --git a/pkg/traceability/tlsconfig_test.go b/pkg/traceability/tlsconfig_test.go new file mode 100644 index 000000000..bbea9be65 --- /dev/null +++ b/pkg/traceability/tlsconfig_test.go @@ -0,0 +1,110 @@ +package traceability + +import ( + "strings" + "testing" + + "github.com/sirupsen/logrus" + "github.com/sirupsen/logrus/hooks/test" + "github.com/stretchr/testify/assert" + + "github.com/Axway/agent-sdk/pkg/util/log" +) + +func TestTLSConfigVerificationModeDefault(t *testing.T) { + tests := map[string]struct { + verificationMode string + wantInsecureSkipVerify bool + }{ + "unset defaults to verify (full)": {verificationMode: "", wantInsecureSkipVerify: false}, + "full verifies": {verificationMode: "full", wantInsecureSkipVerify: false}, + "none skips verification": {verificationMode: "none", wantInsecureSkipVerify: true}, + "certificate downgrades to full": {verificationMode: "certificate", wantInsecureSkipVerify: false}, + "strict downgrades to full": {verificationMode: "strict", wantInsecureSkipVerify: false}, + "unrecognized value defaults to full": {verificationMode: "bogus", wantInsecureSkipVerify: false}, + } + + for name, tc := range tests { + tc := tc + t.Run(name, func(t *testing.T) { + cfg := TLSConfig{VerificationMode: tc.verificationMode} + tlsCfg := cfg.toTLSConfiguration() + assert.Equal(t, tc.wantInsecureSkipVerify, tlsCfg.InsecureSkipVerify) + }) + } +} + +func TestTLSConfigCipherSuites(t *testing.T) { + tests := map[string]struct { + cipherSuites []string + wantLen int // 0 means "just assert non-empty", used for the default list + }{ + "explicit cipher suites are honored": {cipherSuites: []string{"ECDHE-RSA-AES-128-GCM-SHA256"}, wantLen: 1}, + "unset keeps agent-sdk's own default list rather than being nil'd out": {cipherSuites: nil}, + } + + for name, tc := range tests { + tc := tc + t.Run(name, func(t *testing.T) { + cfg := TLSConfig{CipherSuites: tc.cipherSuites} + tlsCfg := cfg.toTLSConfiguration() + if tc.wantLen > 0 { + assert.Len(t, tlsCfg.CipherSuites, tc.wantLen) + } else { + assert.NotEmpty(t, tlsCfg.CipherSuites) + } + }) + } +} + +func TestTLSConfigUnsupportedFieldsWarnNotFail(t *testing.T) { + tests := map[string]struct { + cfg TLSConfig + wantWarning string + }{ + "supported_protocols": { + cfg: TLSConfig{SupportedProtocols: []string{"TLSv1.2"}}, + wantWarning: "output.traceability.ssl.supported_protocols", + }, + "certificate_authorities": { + cfg: TLSConfig{CertificateAuthorities: []string{"/path/to/ca.pem"}}, + wantWarning: "output.traceability.ssl.certificate_authorities", + }, + "curve_types": { + cfg: TLSConfig{CurveTypes: []string{"P-256"}}, + wantWarning: "output.traceability.ssl.curve_types", + }, + "ca_sha256": { + cfg: TLSConfig{CASha256: []string{"deadbeef"}}, + wantWarning: "output.traceability.ssl.ca_sha256", + }, + "renegotiation": { + cfg: TLSConfig{Renegotiation: "freely"}, + wantWarning: "output.traceability.ssl.renegotiation", + }, + "key_passphrase": { + cfg: TLSConfig{KeyPassphrase: "secret"}, + wantWarning: "output.traceability.ssl.key_passphrase", + }, + } + + for name, tc := range tests { + tc := tc + t.Run(name, func(t *testing.T) { + originalHooks := log.Get().ReplaceHooks(make(logrus.LevelHooks)) + defer log.Get().ReplaceHooks(originalHooks) + hook := test.NewLocal(log.Get()) + + tlsCfg := tc.cfg.toTLSConfiguration() + assert.NotNil(t, tlsCfg) + + var found bool + for _, entry := range hook.AllEntries() { + if entry.Level == logrus.WarnLevel && strings.Contains(entry.Message, tc.wantWarning) { + found = true + } + } + assert.True(t, found, "expected a WARN log mentioning %q, got: %v", tc.wantWarning, hook.AllEntries()) + }) + } +} diff --git a/pkg/traceability/traceability.go b/pkg/traceability/traceability.go index 01381edd4..089de1a64 100644 --- a/pkg/traceability/traceability.go +++ b/pkg/traceability/traceability.go @@ -4,27 +4,17 @@ import ( "context" "fmt" "math/rand" - "net" "net/url" "os" "path" - "reflect" "sync" - "unsafe" "github.com/Axway/agent-sdk/pkg/agent" + "github.com/Axway/agent-sdk/pkg/event" "github.com/Axway/agent-sdk/pkg/jobs" "github.com/Axway/agent-sdk/pkg/traceability/sampling" "github.com/Axway/agent-sdk/pkg/util" "github.com/Axway/agent-sdk/pkg/util/log" - "github.com/elastic/beats/v7/libbeat/beat" - "github.com/elastic/beats/v7/libbeat/common" - "github.com/elastic/beats/v7/libbeat/common/transport" - "github.com/elastic/beats/v7/libbeat/common/transport/tlscommon" - "github.com/elastic/beats/v7/libbeat/outputs" - "github.com/elastic/beats/v7/libbeat/paths" - "github.com/elastic/beats/v7/libbeat/publisher" - "golang.org/x/net/proxy" hc "github.com/Axway/agent-sdk/pkg/util/healthcheck" ) @@ -34,13 +24,14 @@ const ( eventTypeStr = "event-type" ) -// OutputEventProcessor - P +// OutputEventProcessor - processes events before they are published type OutputEventProcessor interface { - Process(events []publisher.Event) []publisher.Event + Process(events []event.Event) []event.Event } var outputEventProcessor OutputEventProcessor var pathDataMutex sync.Mutex = sync.Mutex{} +var dataDirPath string const ( minWindowSize int = 1 @@ -78,16 +69,23 @@ func getClient() (*Client, error) { } } +// NetworkClient replaces libbeat's outputs.Client/outputs.NetworkClient. +type NetworkClient interface { + Connect() error + Close() error + Publish(ctx context.Context, batch event.Batch) error + String() string +} + // Client - struct type Client struct { sync.Mutex - transportClient outputs.Client + transportClient NetworkClient logger log.FieldLogger } func init() { clientMutex = &sync.Mutex{} - outputs.RegisterType(traceabilityStr, makeTraceabilityAgent) } // SetOutputEventProcessor - @@ -99,23 +97,29 @@ func SetOutputEventProcessor(eventProcessor OutputEventProcessor) { func GetDataDirPath() string { pathDataMutex.Lock() defer pathDataMutex.Unlock() - return paths.Paths.Data + return dataDirPath } -// SetDataDirPath - Sets the path of the data directory +// SetDataDirPath - Sets the path of the data directory. Must be called during +// agent init, before GetCacheDirPath/GetReportsDirPath are used, or those will +// create their directories under an empty/relative path. func SetDataDirPath(path string) { pathDataMutex.Lock() defer pathDataMutex.Unlock() - paths.Paths.Data = path + dataDirPath = path } // checkCreateDir func createDirIfNotExist(dirPath string) { _, err := os.Stat(dirPath) if os.IsNotExist(err) { - // Create the directory with the same permissions as the data dir - dataInfo, _ := os.Stat(GetDataDirPath()) - os.MkdirAll(dirPath, dataInfo.Mode().Perm()) + // Create the directory with the same permissions as the data dir, falling + // back to a sane default if the data dir doesn't exist yet either. + perm := os.FileMode(0755) + if dataInfo, err := os.Stat(GetDataDirPath()); err == nil { + perm = dataInfo.Mode().Perm() + } + os.MkdirAll(dirPath, perm) } } @@ -133,233 +137,108 @@ func GetReportsDirPath() string { return reportDir } -func makeTraceabilityAgent( - indexManager outputs.IndexManager, - beat beat.Info, - observer outputs.Observer, - libbeatCfg *common.Config, -) (outputs.Group, error) { +// NewClient replaces the libbeat outputs.RegisterType("traceability", makeTraceabilityAgent) +// factory-registration mechanism. +func NewClient(cfg *Config) ([]*Client, error) { logger := log.NewFieldLogger(). WithPackage("sdk.traceability"). - WithComponent("makeTraceabilityAgent") + WithComponent("NewClient") - var err error + traceCfg = cfg + outputConfig = cfg - logger.Trace("reading config") - traceCfg, err = readConfig(libbeatCfg, beat) - if err != nil { + if err := cfg.ValidateCfg(); err != nil { agent.UpdateStatusWithPrevious(agent.AgentFailed, agent.AgentRunning, err.Error()) - logger.WithError(err).Error("reading config") - return outputs.Fail(err) + logger.WithError(err).Error("validating config") + return nil, err } - defer func() { - if err != nil { - // skip hc register if err hit making agent - return - } - - if !agent.GetCentralConfig().GetUsageReportingConfig().IsOfflineMode() && util.IsNotTest() { - err := registerHealthCheckers(traceCfg) - if err != nil { - logger.WithError(err).Error("could not register healthcheck") - } - } - }() - validateProtocolPort() - logger = logger.WithField("config", traceCfg) - - if err := libbeatCfg.Merge(HostConfig{Hosts: traceCfg.Hosts, Protocol: traceCfg.Protocol}); err != nil { - agent.UpdateStatusWithPrevious(agent.AgentFailed, agent.AgentRunning, err.Error()) - logger.WithError(err).Error("merging host config") - return outputs.Fail(err) - } - - hosts, err := outputs.ReadHostList(libbeatCfg) - if err != nil { - agent.UpdateStatusWithPrevious(agent.AgentFailed, agent.AgentRunning, err.Error()) - logger.WithError(err).Error("reading hosts") - return outputs.Fail(err) - } - - logger = logger.WithField("hosts", hosts).WithField("config", traceCfg) + logger = logger.WithField("config", cfg).WithField("hosts", cfg.Hosts) logger.Tracef("initializing traceability client") - var transportGroup outputs.Group isSingleEntry := agent.GetCentralConfig().GetSingleURL() != "" - if IsTCPTransport() { - // For Single entry point register dialer factory for sni scheme and set the - // proxy url with sni scheme. When libbeat will register its dialer and sees - // proxy url with sni scheme, it will invoke the factory to construct the dialer - // The dialer will be invoked as proxy dialer in the libbeat dialer chain - // (proxy dialer, stat dialer, tls dialer). - if isSingleEntry { - // Register dialer factory with sni scheme for single entry point - proxy.RegisterDialerType("sni", ingestionSingleEntryDialer) - // If real proxy configured(not the sni proxy set here), validate the scheme - // since libbeats proxy dialer will not be invoked. - if traceCfg.Proxy.URL != "" { - proxCfg := &transport.ProxyConfig{ - URL: traceCfg.Proxy.URL, - LocalResolve: traceCfg.Proxy.LocalResolve, - } - err := proxCfg.Validate() - if err != nil { - logger.WithError(err).Error("validating proxy config") - outputs.Fail(err) - } - } - // Replace the proxy URL to sni by setting the environment variable - // Libbeat parses the yaml file and replaces the value from yaml - // with overridden environment variable. - // Set the sni host to the ingestion service host to allow the - // single entry dialer to receive the target address - os.Setenv("TRACEABILITY_PROXYURL", "sni://"+traceCfg.Hosts[0]) - } - - transportGroup, err = makeLogstashClient(indexManager, beat, observer, libbeatCfg) - } else { - transportGroup, err = makeHTTPClient(beat, observer, traceCfg, hosts, agent.GetUserAgent(), isSingleEntry) - } - + networkClients, err := makeHTTPClient(cfg, cfg.Hosts, agent.GetUserAgent(), isSingleEntry) if err != nil { + agent.UpdateStatusWithPrevious(agent.AgentFailed, agent.AgentRunning, err.Error()) logger.WithError(err).Error("creating traceability client") - return outputs.Fail(err) - } - - traceabilityGroup := outputs.Group{ - BatchSize: transportGroup.BatchSize, - Retry: transportGroup.Retry, + return nil, err } - clients := make([]outputs.Client, 0) - for _, client := range transportGroup.Clients { + clients := make([]*Client, 0, len(networkClients)) + for _, nc := range networkClients { outputClient := &Client{ - transportClient: client, + transportClient: nc, logger: logger.WithComponent("traceabilityClient").WithPackage("sdk.traceability"), } clients = append(clients, outputClient) addClient(outputClient) } - traceabilityGroup.Clients = clients - return traceabilityGroup, nil + + if !agent.GetCentralConfig().GetUsageReportingConfig().IsOfflineMode() && util.IsNotTest() { + if err := registerHealthCheckers(cfg); err != nil { + logger.WithError(err).Error("could not register healthcheck") + } + } + + return clients, nil } -// validateProtocolPort - validate the protocol matches the port +// validateProtocolPort - sets the protocol for single entry point hosts func validateProtocolPort() { isSingleEntry := agent.GetCentralConfig().GetSingleURL() != "" if isSingleEntry { // get the expected protocol for single entry host traceCfg.Protocol = agent.GetCentralConfig().GetTraceabilityProtocol() } - - // Validate that the port matches the - if len(traceCfg.Hosts) == 0 { - return - } - h, p := splitHostPort() - if p == tcpPort && IsHTTPTransport() { - traceCfg.Hosts[0] = fmt.Sprintf("%s:%s", h, defaultPort) - } else if p == defaultPort && IsTCPTransport() { - traceCfg.Hosts[0] = fmt.Sprintf("%s:%s", h, tcpPort) - } -} - -func makeLogstashClient(indexManager outputs.IndexManager, - beat beat.Info, - observer outputs.Observer, - libbeatCfg *common.Config, -) (outputs.Group, error) { - factory := outputs.FindFactory("logstash") - if factory == nil { - return outputs.Group{}, nil - } - group, err := factory(indexManager, beat, observer, libbeatCfg) - return group, err -} - -// Factory method for creating dialer for sni scheme -// Setup the single entry point dialer with single entry host mapping based -// on central config and traceability proxy url from original config that gets -// read by traceability output factory(makeTraceabilityAgent) -func ingestionSingleEntryDialer(proxyURL *url.URL, parentDialer proxy.Dialer) (proxy.Dialer, error) { - var traceProxyURL *url.URL - var err error - if traceCfg != nil && traceCfg.Proxy.URL != "" { - traceProxyURL, err = url.Parse(traceCfg.Proxy.URL) - if err != nil { - return nil, fmt.Errorf("proxy could not be parsed. %s", err.Error()) - } - } - var singleEntryHostMap map[string]string - if agent.GetCentralConfig() != nil { - cfgSingleURL := agent.GetCentralConfig().GetSingleURL() - if cfgSingleURL != "" { - // cfgSingleURL should not be empty as the factory method is registered based on that check - singleEntryURL, err := url.Parse(cfgSingleURL) - if err == nil && traceCfg != nil { - singleEntryHostMap = map[string]string{ - traceCfg.Hosts[0]: util.ParseAddr(singleEntryURL), - } - } - } - } - - dialer := util.NewDialer(traceProxyURL, singleEntryHostMap) - return dialer, nil } -func makeHTTPClient(beat beat.Info, observer outputs.Observer, traceCfg *Config, hosts []string, userAgent string, isSingleEntry bool) (outputs.Group, error) { - tls, err := tlscommon.LoadTLSConfig(traceCfg.TLS) - if err != nil { - agent.UpdateStatusWithPrevious(agent.AgentFailed, agent.AgentRunning, err.Error()) - return outputs.Fail(err) - } +// makeHTTPClient replaces libbeat's outputs.SuccessNet/outputs.NewFailoverClient/ +// outputs.WithBackoff wrapping. +func makeHTTPClient(cfg *Config, hosts []string, userAgent string, isSingleEntry bool) ([]NetworkClient, error) { + tlsCfg := cfg.TLS.toTLSConfiguration() - clients := make([]outputs.NetworkClient, len(hosts)) + clients := make([]NetworkClient, len(hosts)) for i, host := range hosts { - hostURL, err := common.MakeURL(traceCfg.Protocol, "/", host, 443) + hostURL, err := buildURL(cfg.Protocol, host) if err != nil { - return outputs.Fail(err) + return nil, err } - proxyURL, err := url.Parse(traceCfg.Proxy.URL) + proxyURL, err := url.Parse(cfg.Proxy.URL) if err != nil { - return outputs.Fail(err) + return nil, err } - var client outputs.NetworkClient - client, err = NewHTTPClient(HTTPClientSettings{ - BeatInfo: beat, + client, err := NewHTTPClient(HTTPClientSettings{ URL: hostURL, Proxy: proxyURL, - TLS: tls, - Timeout: traceCfg.Timeout, - CompressionLevel: traceCfg.CompressionLevel, - Observer: observer, + TLS: tlsCfg, + Timeout: cfg.Timeout, + CompressionLevel: cfg.CompressionLevel, UserAgent: userAgent, IsSingleEntry: isSingleEntry, }) - if err != nil { - return outputs.Fail(err) + return nil, err } - client = outputs.WithBackoff(client, traceCfg.Backoff.Init, traceCfg.Backoff.Max) - clients[i] = client + clients[i] = withBackoff(client, cfg.Backoff.Init, cfg.Backoff.Max) } - return outputs.SuccessNet(traceCfg.LoadBalance, traceCfg.BulkMaxSize, traceCfg.MaxRetries, clients) + if !cfg.LoadBalance { + return []NetworkClient{newFailoverClient(clients)}, nil + } + return clients, nil } // SetTransportClient - set the transport client -func (client *Client) SetTransportClient(outputClient outputs.Client) { +func (client *Client) SetTransportClient(outputClient NetworkClient) { client.Lock() defer client.Unlock() client.transportClient = outputClient } -// SetTransportClient - set the transport client -func (client *Client) getTransportClient() outputs.Client { +// getTransportClient - get the transport client +func (client *Client) getTransportClient() NetworkClient { client.Lock() defer client.Unlock() return client.transportClient @@ -377,12 +256,7 @@ func (client *Client) Connect() error { return nil } - networkClient := client.getTransportClient().(outputs.NetworkClient) - err := networkClient.Connect() - if err != nil { - return err - } - return nil + return client.getTransportClient().Connect() } // Close publish a single event to output. @@ -392,21 +266,17 @@ func (client *Client) Close() error { return nil } - err := client.getTransportClient().Close() - if err != nil { - return err - } - return nil + return client.getTransportClient().Close() } // Publish sends events to the clients sink. -func (client *Client) Publish(ctx context.Context, batch publisher.Batch) error { +func (client *Client) Publish(ctx context.Context, batch event.Batch) error { events := batch.Events() if len(events) == 0 { batch.ACK() return nil // nothing to do } - _, isMetric := events[0].Content.Meta["metric"] + _, isMetric := events[0].Meta["metric"] if agent.GetCentralConfig().GetUsageReportingConfig().IsOfflineMode() { if outputEventProcessor != nil && !isMetric { @@ -422,14 +292,14 @@ func (client *Client) Publish(ctx context.Context, batch publisher.Batch) error logger = logger.WithField(eventTypeStr, "transaction") if outputEventProcessor != nil { updatedEvents := outputEventProcessor.Process(events) - updateEvent(batch, updatedEvents) + batch.SetEvents(updatedEvents) } sampledEvents, err := sampling.FilterEvents(batch.Events()) if err != nil { logger.Error(err.Error()) } - updateEvent(batch, sampledEvents) + batch.SetEvents(sampledEvents) } events = batch.Events() @@ -456,17 +326,6 @@ func (client *Client) String() string { return traceabilityStr } -// updateEvent - updates the private field events in publisher.Batch -func updateEvent(batch publisher.Batch, events []publisher.Event) { - pointerVal := reflect.ValueOf(batch) - val := reflect.Indirect(pointerVal) - - member := val.FieldByName("events") - ptrToEvents := unsafe.Pointer(member.UnsafeAddr()) - realPtrToEvents := (*[]publisher.Event)(ptrToEvents) - *realPtrToEvents = events -} - func registerHealthCheckers(config *Config) error { hcJob := newTraceabilityHealthCheckJob() @@ -481,15 +340,3 @@ func registerHealthCheckers(config *Config) error { } return nil } - -func splitHostPort() (string, string) { - // Split the host and port from the URL - if len(traceCfg.Hosts) == 0 { - return "", fmt.Sprint(defaultPort) - } - host, port, err := net.SplitHostPort(traceCfg.Hosts[0]) - if err != nil { - return "", fmt.Sprint(defaultPort) - } - return host, port -} diff --git a/pkg/traceability/traceability_test.go b/pkg/traceability/traceability_test.go index 7e8cb1663..fe25f16a7 100644 --- a/pkg/traceability/traceability_test.go +++ b/pkg/traceability/traceability_test.go @@ -8,35 +8,22 @@ import ( "net/http" "net/http/httptest" "net/url" - "os" + "strings" "testing" "time" + "github.com/spf13/cobra" + "github.com/spf13/viper" + "github.com/stretchr/testify/assert" + "github.com/Axway/agent-sdk/pkg/agent" + "github.com/Axway/agent-sdk/pkg/cmd/properties" "github.com/Axway/agent-sdk/pkg/config" + "github.com/Axway/agent-sdk/pkg/event" + "github.com/Axway/agent-sdk/pkg/traceability/redaction" "github.com/Axway/agent-sdk/pkg/traceability/sampling" - "github.com/elastic/beats/v7/libbeat/beat" - "github.com/elastic/beats/v7/libbeat/common" - "github.com/elastic/beats/v7/libbeat/outputs" - "github.com/elastic/beats/v7/libbeat/publisher" - "github.com/stretchr/testify/assert" ) -var logstashClientCreateCalled = false - -func init() { - logstashFactory := func( - indexManager outputs.IndexManager, - beat beat.Info, - observer outputs.Observer, - cfg *common.Config, - ) (outputs.Group, error) { - logstashClientCreateCalled = true - return outputs.SuccessNet(false, 1, 1, nil) - } - outputs.RegisterType("logstash", logstashFactory) -} - func createCentralCfg(url, env string) *config.CentralConfiguration { cfg := config.NewCentralConfig(config.DiscoveryAgent).(*config.CentralConfiguration) cfg.URL = url @@ -54,15 +41,8 @@ func createCentralCfg(url, env string) *config.CentralConfiguration { return cfg } -func createTransport(config *Config) (outputs.Group, error) { - info := beat.Info{ - Beat: "test-beat", - IndexPrefix: "", - Version: "1.0", - } - // defcfg := DefaultConfig() - commonCfg, _ := common.NewConfigFrom(config) - return makeTraceabilityAgent(nil, info, nil, commonCfg) +func createTransport(cfg *Config) ([]*Client, error) { + return NewClient(cfg) } func createBatch(msgValue string) *MockBatch { @@ -73,18 +53,16 @@ func createBatch(msgValue string) *MockBatch { } } -func createEvent(msgValue string) []publisher.Event { - fieldsData := common.MapStr{ +func createEvent(msgValue string) []event.Event { + fieldsData := event.MapStr{ "message": msgValue, } - return []publisher.Event{ + return []event.Event{ { - Content: beat.Event{ - Timestamp: time.Now(), - Meta: common.MapStr{sampling.SampleKey: true}, - Private: nil, - Fields: fieldsData, - }, + Timestamp: time.Now(), + Meta: event.MapStr{sampling.SampleKey: true}, + Private: nil, + Fields: fieldsData, }, } } @@ -163,162 +141,201 @@ type MockBatch struct { acked bool retryCount int - events []publisher.Event + events []event.Event } -func (b *MockBatch) Events() []publisher.Event { return b.events } -func (b *MockBatch) ACK() { b.acked = true } -func (b *MockBatch) Drop() {} -func (b *MockBatch) Retry() {} -func (b *MockBatch) Cancelled() {} -func (b *MockBatch) RetryEvents(events []publisher.Event) { b.retryCount++ } -func (b *MockBatch) CancelledEvents(events []publisher.Event) {} +func (b *MockBatch) Events() []event.Event { return b.events } +func (b *MockBatch) SetEvents(events []event.Event) { b.events = events } +func (b *MockBatch) ACK() { b.acked = true } +func (b *MockBatch) Drop() {} +func (b *MockBatch) Retry() {} +func (b *MockBatch) Cancelled() {} +func (b *MockBatch) RetryEvents(events []event.Event) { b.retryCount++ } +func (b *MockBatch) CancelledEvents(events []event.Event) {} type testEventProcessor struct { msgValue string } -func (t *testEventProcessor) Process(events []publisher.Event) []publisher.Event { +func (t *testEventProcessor) Process(events []event.Event) []event.Event { return createEvent(t.msgValue) } -func TestCreateLogstashClient(t *testing.T) { - s := newMockHTTPServer() - defer s.Close() - - cfg := createCentralCfg(s.server.URL, "v7") - agent.Initialize(cfg) - - group, err := createTransport(nil) - - assert.NotNil(t, err) - assert.Contains(t, err.Error(), "config is nil") - assert.NotNil(t, group) - assert.Nil(t, group.Clients) - assert.False(t, logstashClientCreateCalled) - testConfig := DefaultConfig() - testConfig.Protocol = "tcp" +func TestParseConfig(t *testing.T) { + const testHost = "phoenix.datasearch.axway.com:443" + + // properties.Properties reads env var overrides through viper's AutomaticEnv, which pkg/cmd/root.go + // normally connects once at agent startup. Replicate that here since this test bypasses root.go. + viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) + viper.AutomaticEnv() + + agent.Initialize(createCentralCfg("http://localhost:8888", "v7")) + + tests := map[string]struct { + envVars map[string]string + assert func(t *testing.T, cfg *Config) + }{ + "defaults with no env vars set": { + envVars: map[string]string{}, + assert: func(t *testing.T, cfg *Config) { + assert.Equal(t, 3, cfg.CompressionLevel) + assert.Equal(t, 512, cfg.BulkMaxSize) + assert.Equal(t, "https", cfg.Protocol) + assert.Empty(t, cfg.Hosts) + }, + }, + "compression level out of bounds falls back to the default": { + envVars: map[string]string{ + "TRACEABILITY_COMPRESSIONLEVEL": "20", + }, + assert: func(t *testing.T, cfg *Config) { + assert.Equal(t, 3, cfg.CompressionLevel) + }, + }, + "valid full config round trip": { + envVars: map[string]string{ + "TRACEABILITY_HOST": testHost, + "TRACEABILITY_PROTOCOL": "https", + "TRACEABILITY_COMPRESSIONLEVEL": "5", + "TRACEABILITY_BULKMAXSIZE": "256", + "TRACEABILITY_MAXRETRIES": "5", + "TRACEABILITY_LOADBALANCE": "true", + "TRACEABILITY_SSL_VERIFICATIONMODE": "full", + "TRACEABILITY_SSL_CIPHERSUITES": "ECDHE-RSA-AES-128-GCM-SHA256", + }, + assert: func(t *testing.T, cfg *Config) { + assert.Equal(t, []string{testHost}, cfg.Hosts) + assert.Equal(t, "https", cfg.Protocol) + assert.Equal(t, 5, cfg.CompressionLevel) + assert.Equal(t, 256, cfg.BulkMaxSize) + assert.Equal(t, 5, cfg.MaxRetries) + assert.True(t, cfg.LoadBalance) + assert.Equal(t, "full", cfg.TLS.VerificationMode) + assert.Equal(t, []string{"ECDHE-RSA-AES-128-GCM-SHA256"}, cfg.TLS.CipherSuites) + }, + }, + "redaction show list matches mulesoft-agents' no-space-after-colon format": { + envVars: map[string]string{ + "TRACEABILITY_REDACTION_PATH_SHOW": `[{keyMatch:".*"}]`, + }, + assert: func(t *testing.T, cfg *Config) { + assert.Equal(t, []redaction.Show{{KeyMatch: ".*"}}, cfg.Redaction.Path.Allowed) + }, + }, + } - group, err = createTransport(testConfig) - assert.NotNil(t, err) - assert.Contains(t, err.Error(), "empty array accessing 'hosts'") - assert.NotNil(t, group) - assert.Nil(t, group.Clients) - assert.False(t, logstashClientCreateCalled) + for name, tc := range tests { + tc := tc + t.Run(name, func(t *testing.T) { + for k, v := range tc.envVars { + t.Setenv(k, v) + } - testConfig.Hosts = []string{ - "somehost", - "someotherhost", - } - group, err = createTransport(testConfig) - assert.Nil(t, err) - assert.NotNil(t, group) - assert.NotNil(t, group.Clients) - assert.True(t, logstashClientCreateCalled) + props := properties.NewProperties(&cobra.Command{}) + AddConfigProperties(props) - testConfig.Pipelining = 5 - testConfig.Hosts = []string{ - "somehost2", + cfg, err := ParseConfig(props) + assert.NoError(t, err) + assert.NotNil(t, cfg) + tc.assert(t, cfg) + }) } - group, err = createTransport(testConfig) - assert.Nil(t, err) - assert.NotNil(t, group) - assert.True(t, logstashClientCreateCalled) - traceabilityClient := group.Clients[0].(*Client) - assert.NotNil(t, traceabilityClient) - assert.False(t, IsHTTPTransport()) - assert.Equal(t, 3, GetMaxRetries()) } -func TestCreateLogstashClientWithSingleEntry(t *testing.T) { +func TestCreateHTTPClient(t *testing.T) { cfg := createCentralCfg("http://localhost:8888", "v7") - cfg.SingleURL = "https://ingestion.platform.axway.com" agent.Initialize(cfg) - logstashClientCreateCalled = false - testConfig := DefaultConfig() - testConfig.Protocol = "http" - testConfig.Hosts = []string{ - "somehost", - } - group, err := createTransport(testConfig) - assert.Nil(t, err) - assert.NotNil(t, group) - assert.NotNil(t, group.Clients) - assert.True(t, logstashClientCreateCalled) - assert.Equal(t, "tcp", traceCfg.Protocol) - transportProxy := os.Getenv("TRACEABILITY_PROXYURL") - assert.Equal(t, "sni://"+traceCfg.Hosts[0], transportProxy) - - testConfig.Proxy = ProxyConfig{ - URL: "http://localhost:9999", - LocalResolve: false, + tests := map[string]struct { + hosts []string + proxy ProxyConfig + wantErr bool + wantErrMsg string + wantClients int + }{ + "invalid port": { + hosts: []string{"somehost:invalidport"}, + wantErr: true, + wantErrMsg: "invalid port", + }, + "bad proxy URL": { + hosts: []string{"somehost"}, + proxy: ProxyConfig{URL: "bogus\\:bogus"}, + wantErr: true, + }, + "valid host and no proxy": { + hosts: []string{"somehost"}, + wantClients: 1, + }, } - testConfig.Hosts = []string{ - "somehost", + for name, tc := range tests { + tc := tc + t.Run(name, func(t *testing.T) { + testConfig := DefaultConfig() + testConfig.Hosts = tc.hosts + testConfig.Proxy = tc.proxy + + clients, err := createTransport(testConfig) + if tc.wantErr { + assert.NotNil(t, err) + if tc.wantErrMsg != "" { + assert.Contains(t, err.Error(), tc.wantErrMsg) + } + assert.Nil(t, clients) + return + } + assert.Nil(t, err) + assert.Equal(t, tc.wantClients, len(clients)) + assert.NotNil(t, clients[0]) + assert.True(t, IsHTTPTransport()) + assert.Equal(t, 3, GetMaxRetries()) + }) } - group, err = createTransport(testConfig) - assert.Nil(t, err) - assert.NotNil(t, group) - assert.NotNil(t, group.Clients) - assert.True(t, logstashClientCreateCalled) - assert.Equal(t, "tcp", traceCfg.Protocol) - assert.Equal(t, "http://localhost:9999", traceCfg.Proxy.URL) - transportProxy = os.Getenv("TRACEABILITY_PROXYURL") - assert.Equal(t, "sni://"+traceCfg.Hosts[0], transportProxy) } -func TestCreateHTTPClient(t *testing.T) { - logstashClientCreateCalled = false - cfg := createCentralCfg("http://localhost:8888", "v7") - agent.Initialize(cfg) - - testConfig := DefaultConfig() - - testConfig.Hosts = []string{ - "somehost:invalidport", +func TestValidateCfgRemovedProtocolPortHost(t *testing.T) { + tests := map[string]struct { + cfg *Config + wantErr error + }{ + "tcp protocol removed": { + cfg: &Config{Protocol: "tcp"}, + wantErr: ErrTCPProtocolRemoved, + }, + "lumberjack port 5044 removed": { + cfg: &Config{Protocol: "https", Hosts: []string{"phoenix.datasearch.axway.com:5044"}}, + wantErr: ErrPort5044Removed.FormatError("phoenix.datasearch.axway.com:5044"), + }, + "ingestion host removed": { + cfg: &Config{Protocol: "https", Hosts: []string{"ingestion.datasearch.axway.com:443"}}, + wantErr: ErrIngestionHostRemoved.FormatError("ingestion.datasearch.axway.com:443"), + }, + "ingestion-http host removed": { + cfg: &Config{Protocol: "https", Hosts: []string{"ingestion-http.datasearch.axway.com:443"}}, + wantErr: ErrIngestionHostRemoved.FormatError("ingestion-http.datasearch.axway.com:443"), + }, + "ingestion-lumberjack host removed": { + cfg: &Config{Protocol: "https", Hosts: []string{"ingestion-lumberjack.datasearch.axway.com:443"}}, + wantErr: ErrIngestionHostRemoved.FormatError("ingestion-lumberjack.datasearch.axway.com:443"), + }, + "valid phoenix https host passes": { + cfg: &Config{Protocol: "https", Hosts: []string{"phoenix.datasearch.axway.com:443"}}, + }, } - group, err := createTransport(testConfig) - assert.NotNil(t, err) - assert.Contains(t, err.Error(), "invalid port") - assert.NotNil(t, group) - assert.Nil(t, group.Clients) - assert.False(t, logstashClientCreateCalled) - - testConfig.Hosts = []string{ - "somehost", - } - testConfig.Proxy = ProxyConfig{ - URL: "bogus\\:bogus", + for name, tc := range tests { + tc := tc + t.Run(name, func(t *testing.T) { + err := tc.cfg.ValidateCfg() + if tc.wantErr == nil { + assert.Nil(t, err) + return + } + assert.NotNil(t, err) + assert.Equal(t, tc.wantErr.Error(), err.Error()) + }) } - - group, err = createTransport(testConfig) - assert.NotNil(t, err) - assert.NotNil(t, group) - assert.Nil(t, group.Clients) - assert.False(t, logstashClientCreateCalled) - - testConfig.Proxy = ProxyConfig{} - testConfig.CompressionLevel = 20 - group, err = createTransport(testConfig) - assert.NotNil(t, err) - assert.Contains(t, err.Error(), "requires value <= 9 accessing 'compression_level'") - assert.NotNil(t, group) - assert.Nil(t, group.Clients) - assert.False(t, logstashClientCreateCalled) - - testConfig.CompressionLevel = 0 - group, err = createTransport(testConfig) - assert.Nil(t, err) - assert.NotNil(t, group) - assert.Equal(t, 1, len(group.Clients)) - traceabilityClient := group.Clients[0].(*Client) - assert.NotNil(t, traceabilityClient) - assert.False(t, logstashClientCreateCalled) - assert.True(t, IsHTTPTransport()) - assert.Equal(t, 3, GetMaxRetries()) } func TestHTTPTransportWithJSONEncoding(t *testing.T) { @@ -337,10 +354,10 @@ func TestHTTPTransportWithJSONEncoding(t *testing.T) { testConfig.CompressionLevel = 0 testConfig.Hosts = []string{url.Hostname() + ":" + url.Port()} - group, err := createTransport(testConfig) + clients, err := createTransport(testConfig) assert.Nil(t, err) - assert.NotNil(t, group) - traceabilityClient := group.Clients[0].(*Client) + assert.Equal(t, 1, len(clients)) + traceabilityClient := clients[0] batch := createBatch("{\"f1\":\"test\"}") traceabilityClient.Connect() agent.StartAgentStatusUpdate() @@ -353,9 +370,9 @@ func TestHTTPTransportWithJSONEncoding(t *testing.T) { assert.NotEmpty(t, reqUA) assert.NotNil(t, publishedMessages) assert.Equal(t, 1, len(publishedMessages)) - event := publishedMessages[0] + msg := publishedMessages[0] assert.Nil(t, err) - assert.Equal(t, "test", event["f1"]) + assert.Equal(t, "test", msg["f1"]) assert.True(t, batch.acked) } @@ -376,9 +393,9 @@ func TestHTTPTransportWithOutputProcessor(t *testing.T) { eventProcessor := &testEventProcessor{msgValue: "{\"f1\":\"test\"}"} SetOutputEventProcessor(eventProcessor) - group, err := createTransport(testConfig) + clients, err := createTransport(testConfig) assert.Nil(t, err) - traceabilityClient := group.Clients[0].(*Client) + traceabilityClient := clients[0] batch := createBatch("{\"f0\":\"dummy\"}") traceabilityClient.Connect() @@ -390,9 +407,9 @@ func TestHTTPTransportWithOutputProcessor(t *testing.T) { publishedMessages := s.GetMessages() assert.NotNil(t, publishedMessages) assert.Equal(t, 1, len(publishedMessages)) - event := publishedMessages[0] - assert.Equal(t, "test", event["f1"]) - assert.Nil(t, event["f0"]) + msg := publishedMessages[0] + assert.Equal(t, "test", msg["f1"]) + assert.Nil(t, msg["f0"]) assert.True(t, batch.acked) SetOutputEventProcessor(nil) @@ -413,10 +430,10 @@ func TestHTTPTransportWithGzipEncoding(t *testing.T) { url.Hostname() + ":" + url.Port(), } - group, err := createTransport(testConfig) + clients, err := createTransport(testConfig) assert.Nil(t, err) - assert.NotNil(t, group) - traceabilityClient := group.Clients[0].(*Client) + assert.Equal(t, 1, len(clients)) + traceabilityClient := clients[0] batch := createBatch("{\"f1\":\"test\"}") traceabilityClient.Connect() @@ -428,10 +445,10 @@ func TestHTTPTransportWithGzipEncoding(t *testing.T) { assert.NotNil(t, publishedMessages) assert.Equal(t, 1, len(publishedMessages)) - event := publishedMessages[0] + msg := publishedMessages[0] assert.Nil(t, err) - assert.Equal(t, "test", event["f1"]) + assert.Equal(t, "test", msg["f1"]) assert.True(t, batch.acked) } @@ -450,9 +467,9 @@ func TestHTTPTransportRetries(t *testing.T) { url.Hostname() + ":" + url.Port(), } - group, err := createTransport(testConfig) + clients, err := createTransport(testConfig) assert.Nil(t, err) - traceabilityClient := group.Clients[0].(*Client) + traceabilityClient := clients[0] batch := createBatch("somemessage") s.responseStatus = 404 @@ -465,10 +482,10 @@ func TestHTTPTransportRetries(t *testing.T) { s.responseStatus = 500 batch = createBatch("somemessage") - group, err = createTransport(testConfig) + clients, err = createTransport(testConfig) assert.Nil(t, err) - traceabilityClient = group.Clients[0].(*Client) + traceabilityClient = clients[0] traceabilityClient.Connect() err = traceabilityClient.Publish(context.Background(), batch) traceabilityClient.Close() diff --git a/pkg/traceability/url.go b/pkg/traceability/url.go new file mode 100644 index 000000000..f56c20e22 --- /dev/null +++ b/pkg/traceability/url.go @@ -0,0 +1,23 @@ +package traceability + +import ( + "fmt" + "net" + "net/url" +) + +// buildURL replaces libbeat's common.MakeURL. +func buildURL(scheme, host string) (string, error) { + if scheme == "" { + scheme = "http" + } + if _, _, err := net.SplitHostPort(host); err != nil { + host += ":443" + } + + u, err := url.Parse(fmt.Sprintf("%s://%s/", scheme, host)) + if err != nil { + return "", err + } + return u.String(), nil +} diff --git a/pkg/transaction/eventgenerator.go b/pkg/transaction/eventgenerator.go index 840b8ad66..511ae9616 100644 --- a/pkg/transaction/eventgenerator.go +++ b/pkg/transaction/eventgenerator.go @@ -6,9 +6,6 @@ import ( "strings" "time" - "github.com/elastic/beats/v7/libbeat/beat" - "github.com/elastic/beats/v7/libbeat/common" - "github.com/Axway/agent-sdk/pkg/agent" "github.com/Axway/agent-sdk/pkg/agent/cache" "github.com/Axway/agent-sdk/pkg/apic" @@ -16,6 +13,7 @@ import ( catalog "github.com/Axway/agent-sdk/pkg/apic/apiserver/models/catalog/v1" management "github.com/Axway/agent-sdk/pkg/apic/apiserver/models/management/v1" "github.com/Axway/agent-sdk/pkg/cmd" + "github.com/Axway/agent-sdk/pkg/event" "github.com/Axway/agent-sdk/pkg/traceability" "github.com/Axway/agent-sdk/pkg/traceability/sampling" "github.com/Axway/agent-sdk/pkg/transaction/metric" @@ -28,9 +26,9 @@ import ( // EventGenerator - Create the events to be published to Condor type EventGenerator interface { - CreateEvents(summaryEvent LogEvent, detailEvents []LogEvent, eventTime time.Time, metaData common.MapStr, fields common.MapStr, privateData interface{}) (events []beat.Event, err error) + CreateEvents(summaryEvent LogEvent, detailEvents []LogEvent, eventTime time.Time, metaData event.MapStr, fields event.MapStr, privateData interface{}) (events []event.Event, err error) SetUseTrafficForAggregation(useTrafficForAggregation bool) - CreateFromEventReport(eventReport EventReport) (events []beat.Event, err error) + CreateFromEventReport(eventReport EventReport) (events []event.Event, err error) AddMetricDetailsFromEventReport(eventReport EventReport) error } @@ -62,7 +60,7 @@ func (e *Generator) SetUseTrafficForAggregation(useTrafficForAggregation bool) { } // CreateEvents - Creates new events to be sent to Amplify Observability -func (e *Generator) CreateEvents(summaryEvent LogEvent, detailEvents []LogEvent, eventTime time.Time, metaData common.MapStr, eventFields common.MapStr, privateData interface{}) ([]beat.Event, error) { +func (e *Generator) CreateEvents(summaryEvent LogEvent, detailEvents []LogEvent, eventTime time.Time, metaData event.MapStr, eventFields event.MapStr, privateData interface{}) ([]event.Event, error) { report, err := NewEventReportBuilder(). SetSummaryEvent(summaryEvent). SetDetailEvents(detailEvents). @@ -72,15 +70,15 @@ func (e *Generator) CreateEvents(summaryEvent LogEvent, detailEvents []LogEvent, SetPrivateData(privateData). Build() if err != nil { - return []beat.Event{}, err + return []event.Event{}, err } return e.CreateFromEventReport(report) } // CreateEvent - Creates a new event to be sent to Amplify Observability, expects sampling is handled by agent -func (e *Generator) CreateFromEventReport(eventReport EventReport) ([]beat.Event, error) { - events := make([]beat.Event, 0) +func (e *Generator) CreateFromEventReport(eventReport EventReport) ([]event.Event, error) { + events := make([]event.Event, 0) logger := e.logger // add logging fields from summary event @@ -221,8 +219,8 @@ func (e *Generator) trackMetrics(summaryEvent LogEvent, bytes int64) { } // CreateEvent - Creates a new event to be sent to Amplify Observability -func (e *Generator) createEvent(logEvent LogEvent, summaryProxy *Proxy, eventTime time.Time, metaData common.MapStr, eventFields common.MapStr, privateData interface{}) (beat.Event, error) { - event := beat.Event{} +func (e *Generator) createEvent(logEvent LogEvent, summaryProxy *Proxy, eventTime time.Time, metaData event.MapStr, eventFields event.MapStr, privateData interface{}) (event.Event, error) { + evt := event.Event{} e.logger. WithField("transactionID", logEvent.TransactionID). @@ -231,7 +229,7 @@ func (e *Generator) createEvent(logEvent LogEvent, summaryProxy *Proxy, eventTim cfg := agent.GetCentralConfig() if cfg == nil { - return event, fmt.Errorf("central config unavailable; cannot construct insights event for type %q", logEvent.Type) + return evt, fmt.Errorf("central config unavailable; cannot construct insights event for type %q", logEvent.Type) } orgID := metric.GetOrgGUID() @@ -240,10 +238,10 @@ func (e *Generator) createEvent(logEvent LogEvent, summaryProxy *Proxy, eventTim } envID := cfg.GetEnvironmentID() if orgID == "" { - return event, fmt.Errorf("required field \"org\" (tenantID) is empty for insights event type %q", logEvent.Type) + return evt, fmt.Errorf("required field \"org\" (tenantID) is empty for insights event type %q", logEvent.Type) } if envID == "" { - return event, fmt.Errorf("required field \"distribution.environment\" (environmentID) is empty for insights event type %q", logEvent.Type) + return evt, fmt.Errorf("required field \"distribution.environment\" (environmentID) is empty for insights event type %q", logEvent.Type) } e.logger. @@ -261,7 +259,7 @@ func (e *Generator) createEvent(logEvent LogEvent, summaryProxy *Proxy, eventTim insightsEvent, err := BuildTransactionV2Data(e.logger, logEvent, orgID, envID, summaryProxy, agent.GetCacheManager(), reporter) if err != nil { - return event, fmt.Errorf("failed to build insights event for type %q: %w", logEvent.Type, err) + return evt, fmt.Errorf("failed to build insights event for type %q: %w", logEvent.Type, err) } e.logger. @@ -272,15 +270,15 @@ func (e *Generator) createEvent(logEvent LogEvent, summaryProxy *Proxy, eventTim serialized, err := json.Marshal(insightsEvent) if err != nil { - return event, err + return evt, err } eventData, err := e.createEventData(serialized, eventFields) if err != nil { - return event, err + return evt, err } - return beat.Event{ + return event.Event{ Timestamp: eventTime, Meta: metaData, Private: privateData, @@ -298,14 +296,14 @@ func (e *Generator) getBytesSent(detailEvents []LogEvent) int { return 0 } -func (e *Generator) handleTransactionEvents(detailEvents []LogEvent, summaryProxy *Proxy, eventTime time.Time, metaData common.MapStr, eventFields common.MapStr, privateData interface{}) ([]beat.Event, error) { - events := make([]beat.Event, 0) - for _, event := range detailEvents { +func (e *Generator) handleTransactionEvents(detailEvents []LogEvent, summaryProxy *Proxy, eventTime time.Time, metaData event.MapStr, eventFields event.MapStr, privateData interface{}) ([]event.Event, error) { + events := make([]event.Event, 0) + for _, logEvent := range detailEvents { if metaData == nil { - metaData = common.MapStr{} + metaData = event.MapStr{} } - metaData.Put(sampling.SampleKey, true) - newEvent, err := e.createEvent(event, summaryProxy, eventTime, metaData, eventFields, privateData) + metaData[sampling.SampleKey] = true + newEvent, err := e.createEvent(logEvent, summaryProxy, eventTime, metaData, eventFields, privateData) if err != nil { return nil, err } @@ -480,7 +478,7 @@ func (e *Generator) healthcheck(name string) *hc.Status { return status } -func (e *Generator) createEventData(message []byte, eventFields common.MapStr) (eventData map[string]interface{}, err error) { +func (e *Generator) createEventData(message []byte, eventFields event.MapStr) (eventData map[string]interface{}, err error) { eventData = make(map[string]interface{}) // Copy event fields if specified if len(eventFields) > 0 { @@ -513,11 +511,11 @@ func (e *Generator) createEventFields() (fields map[string]string, err error) { return } -func SetSampleInMetadata(metadata common.MapStr) common.MapStr { +func SetSampleInMetadata(metadata event.MapStr) event.MapStr { if metadata == nil { - metadata = common.MapStr{} + metadata = event.MapStr{} } - metadata.Put(sampling.SampleKey, true) + metadata[sampling.SampleKey] = true return metadata } diff --git a/pkg/transaction/eventgenerator_test.go b/pkg/transaction/eventgenerator_test.go index 7218b8204..74e38f8a4 100644 --- a/pkg/transaction/eventgenerator_test.go +++ b/pkg/transaction/eventgenerator_test.go @@ -8,7 +8,6 @@ import ( "testing" "time" - "github.com/elastic/beats/v7/libbeat/common" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -17,6 +16,7 @@ import ( catalog "github.com/Axway/agent-sdk/pkg/apic/apiserver/models/catalog/v1" management "github.com/Axway/agent-sdk/pkg/apic/apiserver/models/management/v1" corecfg "github.com/Axway/agent-sdk/pkg/config" + "github.com/Axway/agent-sdk/pkg/event" "github.com/Axway/agent-sdk/pkg/traceability" "github.com/Axway/agent-sdk/pkg/traceability/sampling" "github.com/Axway/agent-sdk/pkg/transaction/models" @@ -93,7 +93,7 @@ func TestCreateEventWithValidTokenRequest(t *testing.T) { Status: "Pass", }, } - eventFields := make(common.MapStr) + eventFields := make(event.MapStr) eventFields["someKey.1"] = "someVal.1" eventFields["someKey.2"] = "someVal.2" eventFields["message"] = "existingMessage" diff --git a/pkg/transaction/eventreport.go b/pkg/transaction/eventreport.go index 62bec0579..28cdba7ee 100644 --- a/pkg/transaction/eventreport.go +++ b/pkg/transaction/eventreport.go @@ -6,9 +6,9 @@ import ( "time" "github.com/Axway/agent-sdk/pkg/agent" + "github.com/Axway/agent-sdk/pkg/event" "github.com/Axway/agent-sdk/pkg/transaction/models" transutil "github.com/Axway/agent-sdk/pkg/transaction/util" - "github.com/elastic/beats/v7/libbeat/common" ) type EventReport interface { @@ -16,8 +16,8 @@ type EventReport interface { GetDetailEvents() []LogEvent GetMetricsBatch() []interface{} GetEventTime() time.Time - GetMetadata() common.MapStr - GetFields() common.MapStr + GetMetadata() event.MapStr + GetFields() event.MapStr GetPrivateData() interface{} ShouldForceSample() bool ShouldHandleSampling() bool @@ -34,8 +34,8 @@ type eventReport struct { metricsBatch []interface{} metricsBatchLock sync.Mutex eventTime time.Time - metadata common.MapStr - fields common.MapStr + metadata event.MapStr + fields event.MapStr privateData interface{} skipSampling bool forceSample bool @@ -78,16 +78,16 @@ func (e *eventReport) GetEventTime() time.Time { return e.eventTime } -func (e *eventReport) GetMetadata() common.MapStr { +func (e *eventReport) GetMetadata() event.MapStr { if e.metadata == nil { - e.metadata = common.MapStr{} + e.metadata = event.MapStr{} } return e.metadata } -func (e *eventReport) GetFields() common.MapStr { +func (e *eventReport) GetFields() event.MapStr { if e.metadata == nil { - e.metadata = common.MapStr{} + e.metadata = event.MapStr{} } return e.fields } @@ -116,8 +116,8 @@ type EventReportBuilder interface { SetSummaryEvent(summaryEvent LogEvent) EventReportBuilder SetDetailEvents(detailEvents []LogEvent) EventReportBuilder SetEventTime(eventTime time.Time) EventReportBuilder - SetMetadata(metadata common.MapStr) EventReportBuilder - SetFields(fields common.MapStr) EventReportBuilder + SetMetadata(metadata event.MapStr) EventReportBuilder + SetFields(fields event.MapStr) EventReportBuilder SetPrivateData(privateData interface{}) EventReportBuilder SetSkipSampleHandling() EventReportBuilder SetForceSample() EventReportBuilder @@ -132,8 +132,8 @@ func NewEventReportBuilder() EventReportBuilder { metricsBatch: make([]interface{}, 0), metricsBatchLock: sync.Mutex{}, eventTime: time.Now(), - metadata: common.MapStr{}, - fields: common.MapStr{}, + metadata: event.MapStr{}, + fields: event.MapStr{}, privateData: nil, } } @@ -185,12 +185,12 @@ func (e *eventReport) SetEventTime(eventTime time.Time) EventReportBuilder { return e } -func (e *eventReport) SetMetadata(metadata common.MapStr) EventReportBuilder { +func (e *eventReport) SetMetadata(metadata event.MapStr) EventReportBuilder { e.metadata = metadata return e } -func (e *eventReport) SetFields(fields common.MapStr) EventReportBuilder { +func (e *eventReport) SetFields(fields event.MapStr) EventReportBuilder { e.fields = fields return e } diff --git a/pkg/transaction/metric/metricbatch.go b/pkg/transaction/metric/metricbatch.go index 2ea341ac9..ab65d3c94 100644 --- a/pkg/transaction/metric/metricbatch.go +++ b/pkg/transaction/metric/metricbatch.go @@ -4,8 +4,8 @@ import ( "context" "encoding/json" + "github.com/Axway/agent-sdk/pkg/event" "github.com/Axway/agent-sdk/pkg/traceability" - beatPub "github.com/elastic/beats/v7/libbeat/publisher" ) const cancelMsg = "event cancelled, counts added at next publish" @@ -18,17 +18,16 @@ type eventMetric struct { // EventBatch - creates a batch of MetricEvents to send to Condor type EventBatch struct { - beatPub.Batch - events []beatPub.Event + events []event.Event batchMetrics map[string]eventMetric collector *collector haveBatchLock bool } // AddEvent - adds an event to the batch -func (b *EventBatch) AddEvent(event beatPub.Event, registryKey string, counters map[string]*counter, group groupedMetrics) { - b.events = append(b.events, event) - eventID := event.Content.Meta[metricKey].(string) +func (b *EventBatch) AddEvent(evt event.Event, registryKey string, counters map[string]*counter, group groupedMetrics) { + b.events = append(b.events, evt) + eventID := evt.Meta[metricKey].(string) b.batchMetrics[eventID] = eventMetric{ registryKey: registryKey, counters: counters, @@ -36,9 +35,9 @@ func (b *EventBatch) AddEvent(event beatPub.Event, registryKey string, counters } } -// AddEvent - adds an event to the batch -func (b *EventBatch) AddEventWithoutHistogram(event beatPub.Event) { - b.events = append(b.events, event) +// AddEventWithoutHistogram - adds an event to the batch +func (b *EventBatch) AddEventWithoutHistogram(evt event.Event) { + b.events = append(b.events, evt) } // Publish - connects to the traceability clients and sends this batch of events @@ -86,17 +85,22 @@ func (b *EventBatch) batchUnlock() { } // Events - return the events in the batch -func (b *EventBatch) Events() []beatPub.Event { +func (b *EventBatch) Events() []event.Event { return b.events } +// SetEvents - replaces the events in the batch +func (b *EventBatch) SetEvents(events []event.Event) { + b.events = events +} + // ACK - all events have been acknowledgeded, cleanup the counters func (b *EventBatch) ACK() { b.ackEvents(b.events) b.batchUnlock() } -func (b *EventBatch) eventsNotAcked(events []beatPub.Event) { +func (b *EventBatch) eventsNotAcked(events []event.Event) { go b.logEvents(cancelMsg, events) b.batchUnlock() } @@ -117,30 +121,30 @@ func (b *EventBatch) Cancelled() { } // RetryEvents - certain events sent to retry -func (b *EventBatch) RetryEvents(events []beatPub.Event) { +func (b *EventBatch) RetryEvents(events []event.Event) { b.ackEvents(getEventsToAck(events, b.events)) b.eventsNotAcked(events) } // CancelledEvents - events have been cancelled -func (b *EventBatch) CancelledEvents(events []beatPub.Event) { +func (b *EventBatch) CancelledEvents(events []event.Event) { b.ackEvents(getEventsToAck(events, b.events)) b.eventsNotAcked(events) } -// Events - return the events in the batch -func (b *EventBatch) logEvents(status string, events []beatPub.Event) { - for _, event := range events { - metric := getMetricFromEvent(event) +// logEvents - logs the status of the given events +func (b *EventBatch) logEvents(status string, events []event.Event) { + for _, evt := range events { + metric := getMetricFromEvent(evt) if metric != nil { b.collector.logMetric(status, metric) } } } -func (b *EventBatch) ackEvents(events []beatPub.Event) { - for _, event := range events { - metric := getMetricFromEvent(event) +func (b *EventBatch) ackEvents(events []event.Event) { + for _, evt := range events { + metric := getMetricFromEvent(evt) if metric == nil { continue } @@ -163,8 +167,8 @@ func NewEventBatch(c *collector) *EventBatch { } } -func getEventsToAck(retryEvents []beatPub.Event, events []beatPub.Event) []beatPub.Event { - ackEvents := make([]beatPub.Event, 0) +func getEventsToAck(retryEvents []event.Event, events []event.Event) []event.Event { + ackEvents := make([]event.Event, 0) for _, e := range events { eID := "" if m := getMetricFromEvent(e); m != nil { @@ -191,8 +195,8 @@ func getEventsToAck(retryEvents []beatPub.Event, events []beatPub.Event) []beatP return ackEvents } -func getMetricFromEvent(event beatPub.Event) *centralMetric { - if data, found := event.Content.Fields[messageKey]; found { +func getMetricFromEvent(evt event.Event) *centralMetric { + if data, found := evt.Fields[messageKey]; found { v4Bytes := data.(string) v4Event := make(map[string]interface{}) err := json.Unmarshal([]byte(v4Bytes), &v4Event) diff --git a/pkg/transaction/metric/metricbatch_test.go b/pkg/transaction/metric/metricbatch_test.go new file mode 100644 index 000000000..fcd24ea89 --- /dev/null +++ b/pkg/transaction/metric/metricbatch_test.go @@ -0,0 +1,63 @@ +package metric + +import ( + "sync" + "testing" + "time" + + "github.com/stretchr/testify/assert" + + "github.com/Axway/agent-sdk/pkg/event" +) + +// compile-time proof that EventBatch satisfies the agent-sdk-owned event.Batch +// interface, replacing libbeat's publisher.Batch. +var _ event.Batch = (*EventBatch)(nil) + +func newTestEventBatch() (*EventBatch, event.Event) { + collector := &collector{batchLock: &sync.Mutex{}} + batch := NewEventBatch(collector) + evt := event.Event{Timestamp: time.Now(), Meta: event.MapStr{metricKey: "id-1"}, Fields: event.MapStr{"message": "m"}} + return batch, evt +} + +func TestEventBatchAddAndSetEvents(t *testing.T) { + batch, evt := newTestEventBatch() + + batch.AddEvent(evt, "", nil, groupedMetrics{}) + assert.Equal(t, []event.Event{evt}, batch.Events()) + + batch.SetEvents([]event.Event{}) + assert.Empty(t, batch.Events()) + + batch.AddEventWithoutHistogram(evt) + assert.Equal(t, []event.Event{evt}, batch.Events()) +} + +func TestEventBatchUnlocksAfterTerminalMethods(t *testing.T) { + tests := map[string]struct { + call func(b *EventBatch) + }{ + "ACK": {call: func(b *EventBatch) { b.ACK() }}, + "Retry": {call: func(b *EventBatch) { b.Retry() }}, + "Drop": {call: func(b *EventBatch) { b.Drop() }}, + "Cancelled": {call: func(b *EventBatch) { b.Cancelled() }}, + } + + for name, tc := range tests { + tc := tc + t.Run(name, func(t *testing.T) { + batch, evt := newTestEventBatch() + batch.AddEvent(evt, "", nil, groupedMetrics{}) + + batch.batchLock() + tc.call(batch) + assert.False(t, batch.haveBatchLock) + }) + } +} + +func TestCondorMetricEventCreateEventHasNoGuaranteedSendOrCache(t *testing.T) { + evt := event.Event{} + assert.Nil(t, evt.Private) +} diff --git a/pkg/transaction/metric/metricevent.go b/pkg/transaction/metric/metricevent.go index 23a95db92..0bf5a341e 100644 --- a/pkg/transaction/metric/metricevent.go +++ b/pkg/transaction/metric/metricevent.go @@ -5,11 +5,10 @@ import ( "time" "github.com/Axway/agent-sdk/pkg/agent" + "github.com/Axway/agent-sdk/pkg/event" "github.com/Axway/agent-sdk/pkg/traceability" "github.com/Axway/agent-sdk/pkg/traceability/sampling" "github.com/Axway/agent-sdk/pkg/util/log" - "github.com/elastic/beats/v7/libbeat/beat" - beatPub "github.com/elastic/beats/v7/libbeat/publisher" ) // CondorMetricEvent - the condor event format to send metric data @@ -30,20 +29,20 @@ func AddCondorMetricEventToBatch(metricEvent V4Event, batch *EventBatch, registr Timestamp: metricEvent.Data.GetStartTime(), ID: metricEvent.ID, } - event, err := cme.CreateEvent() + evt, err := cme.CreateEvent() if err != nil { return err } - batch.AddEvent(event, registryKey, counters, group) + batch.AddEvent(evt, registryKey, counters, group) return nil } -// CreateEvent - creates the beat event to add to the batch -func (c *CondorMetricEvent) CreateEvent() (beatPub.Event, error) { +// CreateEvent - creates the event to add to the batch +func (c *CondorMetricEvent) CreateEvent() (event.Event, error) { // Get the event token token, err := agent.GetCentralAuthToken() if err != nil { - return beatPub.Event{}, err + return event.Event{}, err } c.Fields["token"] = token c.Fields[traceability.FlowHeader] = metricFlow @@ -51,26 +50,23 @@ func (c *CondorMetricEvent) CreateEvent() (beatPub.Event, error) { // convert the CondorMetricEvent to json then to map[string]interface{} cmeJSON, err := json.Marshal(c) if err != nil { - return beatPub.Event{}, err + return event.Event{}, err } var fieldsData map[string]interface{} err = json.Unmarshal(cmeJSON, &fieldsData) if err != nil { - return beatPub.Event{}, err + return event.Event{}, err } - beatEnv := beatPub.Event{ - Content: beat.Event{ - Timestamp: c.Timestamp, - Meta: map[string]interface{}{ - metricKey: c.ID, - sampling.SampleKey: true, // All metric events should be sent - }, - Fields: fieldsData, + evt := event.Event{ + Timestamp: c.Timestamp, + Meta: event.MapStr{ + metricKey: c.ID, + sampling.SampleKey: true, // All metric events should be sent }, - Flags: beatPub.GuaranteedSend, + Fields: fieldsData, } - log.Tracef("Created Metric Event: %+v", beatEnv) - return beatEnv, nil + log.Tracef("Created Metric Event: %+v", evt) + return evt, nil } diff --git a/pkg/transaction/metric/metricscollector_bench_test.go b/pkg/transaction/metric/metricscollector_bench_test.go index 9fa1d2f76..82e1bc335 100644 --- a/pkg/transaction/metric/metricscollector_bench_test.go +++ b/pkg/transaction/metric/metricscollector_bench_test.go @@ -7,11 +7,10 @@ import ( "github.com/stretchr/testify/assert" - beatPub "github.com/elastic/beats/v7/libbeat/publisher" - "github.com/Axway/agent-sdk/pkg/agent" "github.com/Axway/agent-sdk/pkg/cmd" "github.com/Axway/agent-sdk/pkg/config" + "github.com/Axway/agent-sdk/pkg/event" "github.com/Axway/agent-sdk/pkg/traceability" "github.com/Axway/agent-sdk/pkg/transaction/models" "github.com/Axway/agent-sdk/pkg/util/healthcheck" @@ -67,8 +66,8 @@ func benchAppDetails() models.AppDetails { // into centralMetric (CustomUnits has a `json:"-"` tag), so getMetricFromEvent // cannot recover them. This raw decode is needed to verify custom unit data // survives the publish round trip. -func getRawEventData(event beatPub.Event) map[string]any { - data, found := event.Content.Fields[messageKey] +func getRawEventData(evt event.Event) map[string]any { + data, found := evt.Fields[messageKey] if !found { return nil } diff --git a/pkg/transaction/metric/metricscollector_test.go b/pkg/transaction/metric/metricscollector_test.go index 689cb2971..10efd3180 100644 --- a/pkg/transaction/metric/metricscollector_test.go +++ b/pkg/transaction/metric/metricscollector_test.go @@ -1140,7 +1140,7 @@ func TestMetricEventsReportedWithOwnGenerationStartTime(t *testing.T) { // each event carries its own generation's start time, not a single shared publish-cycle start time starts := map[int64]bool{} for _, event := range mock.capturedEvents { - raw, ok := event.Content.Fields[messageKey].(string) + raw, ok := event.Fields[messageKey].(string) if !ok { continue } diff --git a/pkg/transaction/metric/mockclient_test.go b/pkg/transaction/metric/mockclient_test.go index baa752716..a75da2603 100644 --- a/pkg/transaction/metric/mockclient_test.go +++ b/pkg/transaction/metric/mockclient_test.go @@ -4,24 +4,21 @@ import ( "context" "fmt" + "github.com/Axway/agent-sdk/pkg/event" "github.com/Axway/agent-sdk/pkg/traceability" "github.com/Axway/agent-sdk/pkg/util/log" - "github.com/elastic/beats/v7/libbeat/outputs" - beatPub "github.com/elastic/beats/v7/libbeat/publisher" ) type MockClient struct { - outputs.NetworkClient - retry int pubCount int eventsAcked int - capturedEvents []beatPub.Event + capturedEvents []event.Event } func (m *MockClient) Close() error { return nil } func (m *MockClient) Connect() error { return nil } -func (m *MockClient) Publish(_ context.Context, batch beatPub.Batch) error { +func (m *MockClient) Publish(_ context.Context, batch event.Batch) error { m.pubCount++ switch { case m.retry >= m.pubCount: @@ -39,7 +36,7 @@ func (m *MockClient) String() string { return "" } -func setupMockClient(retries int) outputs.Client { +func setupMockClient(retries int) traceability.NetworkClient { testClient := &MockClient{ pubCount: 0, retry: retries, diff --git a/pkg/util/log/log.go b/pkg/util/log/log.go index 8b3913faa..575180a94 100644 --- a/pkg/util/log/log.go +++ b/pkg/util/log/log.go @@ -3,15 +3,9 @@ package log import ( "os" - "github.com/elastic/beats/v7/libbeat/logp" "github.com/sirupsen/logrus" ) -const ( - debugSelector = "apic-agents" - traceSelector = "apic-agents-trace" -) - // Get returns the global logger func Get() *logrus.Logger { return log @@ -31,7 +25,6 @@ var networkTraceIgnoreHeaders = map[string]interface{}{ "Authorization": true, } -var isLogP bool var logHTTPTrace bool func init() { @@ -39,110 +32,54 @@ func init() { logHTTPTrace = networkTrace == "true" } -// SetIsLogP - -func SetIsLogP() { - isLogP = true -} - -// UnsetIsLogP - -func UnsetIsLogP() { - isLogP = false -} - // Trace - func Trace(args ...interface{}) { - if isLogP { - // forward trace logs to logp debug with the trace selector - if log.Level == logrus.TraceLevel { - logp.Debug(traceSelector, "%v", args...) - } - } else { - log.Trace(args...) - } + log.Trace(args...) } // Tracef - func Tracef(format string, args ...interface{}) { - if isLogP { - // forward trace logs to logp debug with the trace selector - if log.Level == logrus.TraceLevel { - logp.Debug(traceSelector, format, args...) - } - } else { - log.Tracef(format, args...) - } + log.Tracef(format, args...) } // Error - func Error(args ...interface{}) { - if isLogP { - logp.Err("%v", args...) - } else { - log.Error(args...) - } + log.Error(args...) } // Errorf - func Errorf(format string, args ...interface{}) { - if isLogP { - logp.Err(format, args...) - } else { - log.Errorf(format, args...) - } + log.Errorf(format, args...) } // Debug - func Debug(args ...interface{}) { - if isLogP { - logp.Debug(debugSelector, "%v", args...) - } else { - log.Debug(args...) - } + log.Debug(args...) } // Debugf - func Debugf(format string, args ...interface{}) { - if isLogP { - logp.Debug(debugSelector, format, args...) - } else { - log.Debugf(format, args...) - } + log.Debugf(format, args...) } // Info - func Info(args ...interface{}) { - if isLogP { - logp.Info("%v", args...) - } else { - log.Info(args...) - } + log.Info(args...) } // Infof - func Infof(format string, args ...interface{}) { - if isLogP { - logp.Info(format, args...) - } else { - log.Infof(format, args...) - } + log.Infof(format, args...) } // Warn - func Warn(args ...interface{}) { - if isLogP { - logp.Warn("%v", args...) - } else { - log.Warn(args...) - } + log.Warn(args...) } // Warnf - func Warnf(format string, args ...interface{}) { - if isLogP { - logp.Warn(format, args...) - } else { - log.Warnf(format, args...) - } + log.Warnf(format, args...) } // TraceRedacted Redacted log for traces diff --git a/pkg/util/log/logger.go b/pkg/util/log/logger.go index e53c3b1d7..5c00850c5 100644 --- a/pkg/util/log/logger.go +++ b/pkg/util/log/logger.go @@ -1,9 +1,6 @@ package log import ( - "fmt" - - "github.com/elastic/beats/v7/libbeat/logp" "github.com/sirupsen/logrus" ) @@ -52,309 +49,176 @@ type Redactor interface { TraceRedacted(fields []string, args ...interface{}) } -// NewFieldLogger returns a FieldLogger for standard logging, and logp logging. +// NewFieldLogger returns a FieldLogger for standard logging. func NewFieldLogger() FieldLogger { entry := logrus.NewEntry(log) - return &logger{ - entry: entry, - noLogP: false, - } + return &logger{entry: entry} } -// NewFieldLogger returns a FieldLogger for standard logging, and logp logging. +// NewMetricFieldLogger returns a FieldLogger for metric logging. func NewMetricFieldLogger() FieldLogger { entry := logrus.NewEntry(metric) - return &logger{ - entry: entry, - noLogP: true, - } + return &logger{entry: entry} } -// NewFieldLogger returns a FieldLogger for standard logging, and logp logging. +// NewUsageFieldLogger returns a FieldLogger for usage logging. func NewUsageFieldLogger() FieldLogger { entry := logrus.NewEntry(usage) - return &logger{ - entry: entry, - noLogP: true, - } + return &logger{entry: entry} } -// NewFieldLoggerEntry returns a FieldLogger for standard logging, and logp logging. -func NewFieldLoggerEntry(l *logrus.Logger, isLogP bool) FieldLogger { +// NewFieldLoggerEntry returns a FieldLogger wrapping the given logrus.Logger. +func NewFieldLoggerEntry(l *logrus.Logger) FieldLogger { entry := logrus.NewEntry(l) - return &logger{ - entry: entry, - noLogP: isLogP, - } + return &logger{entry: entry} } type logger struct { - entry *logrus.Entry - noLogP bool + entry *logrus.Entry } // WithComponent adds a field to the log message func (l *logger) WithComponent(value string) FieldLogger { - return &logger{entry: l.entry.WithField("component", value), noLogP: l.noLogP} + return &logger{entry: l.entry.WithField("component", value)} } // WithPackage adds a field to the log message func (l *logger) WithPackage(value string) FieldLogger { - return &logger{entry: l.entry.WithField("package", value), noLogP: l.noLogP} + return &logger{entry: l.entry.WithField("package", value)} } // WithField adds a field to the log message func (l *logger) WithField(key string, value interface{}) FieldLogger { - return &logger{entry: l.entry.WithField(key, value), noLogP: l.noLogP} + return &logger{entry: l.entry.WithField(key, value)} } // WithFields adds multiple fields to the log message func (l *logger) WithFields(fields logrus.Fields) FieldLogger { - return &logger{entry: l.entry.WithFields(fields), noLogP: l.noLogP} + return &logger{entry: l.entry.WithFields(fields)} } // WithError adds an error field to the message func (l *logger) WithError(err error) FieldLogger { - return &logger{entry: l.entry.WithError(err), noLogP: l.noLogP} + return &logger{entry: l.entry.WithError(err)} } // Debugf prints a formatted debug message func (l *logger) Debugf(format string, args ...interface{}) { - if l.isLogP() { - lgp := l.logpWithEntries() - lgp.Named(debugSelector).Debugf(format, args...) - return - } l.entry.Debugf(format, args...) } // Infof prints a formatted info message func (l *logger) Infof(format string, args ...interface{}) { - if l.isLogP() { - lgp := l.logpWithEntries() - lgp.Infof(format, args...) - return - } l.entry.Infof(format, args...) } // Printf formats a message func (l *logger) Printf(format string, args ...interface{}) { - if l.isLogP() { - lgp := l.logpWithEntries() - lgp.Infof(format, args...) - return - } l.entry.Printf(format, args...) } // Warnf prints a formatted warning message func (l *logger) Warnf(format string, args ...interface{}) { - if l.isLogP() { - lgp := l.logpWithEntries() - lgp.Warnf(format, args...) - return - } l.entry.Warnf(format, args...) } // Tracef prints a formatted trace message func (l *logger) Tracef(format string, args ...interface{}) { - if l.isLogP() && GetLevel() == logrus.TraceLevel { - lgp := l.logpWithEntries() - lgp.Named(traceSelector).Debugf(format, args...) - return - } l.entry.Tracef(format, args...) } // Errorf prints a formatted error message func (l *logger) Errorf(format string, args ...interface{}) { - if l.isLogP() { - lgp := l.logpWithEntries() - lgp.Errorw(format, args...) - return - } l.entry.Errorf(format, args...) } // Fatalf prints a formatted fatal message func (l *logger) Fatalf(format string, args ...interface{}) { - if l.isLogP() { - lgp := l.logpWithEntries() - lgp.Fatalw(format, args...) - return - } l.entry.Fatalf(format, args...) } // Panicf prints a formatted panic message func (l *logger) Panicf(format string, args ...interface{}) { - if l.isLogP() { - lgp := l.logpWithEntries() - lgp.Panicw(format, args...) - return - } l.entry.Panicf(format, args...) } // Debug prints a debug message func (l *logger) Debug(args ...interface{}) { - if l.isLogP() { - lgp := l.logpWithEntries() - lgp.Named(debugSelector).Debug(args...) - return - } l.entry.Debug(args...) } // Info prints an info message func (l *logger) Info(args ...interface{}) { - if l.isLogP() { - lgp := l.logpWithEntries() - lgp.Info(args...) - return - } l.entry.Info(args...) } // Print prints a message func (l *logger) Print(args ...interface{}) { - if l.isLogP() { - lgp := l.logpWithEntries() - lgp.Info(args...) - return - } l.entry.Print(args...) } // Trace prints a trace message func (l *logger) Trace(args ...interface{}) { - if l.isLogP() && GetLevel() == logrus.TraceLevel { - lgp := l.logpWithEntries() - lgp.Named(traceSelector).Debug(args...) - return - } l.entry.Trace(args...) } // Warn prints a warning message func (l *logger) Warn(args ...interface{}) { - if l.isLogP() { - lgp := l.logpWithEntries() - lgp.Warn(args...) - return - } l.entry.Warn(args...) } // Error prints an error message func (l *logger) Error(args ...interface{}) { - if l.isLogP() { - lgp := l.logpWithEntries() - lgp.Error(args...) - return - } l.entry.Error(args...) } // Fatal prints a fatal error message func (l *logger) Fatal(args ...interface{}) { - if l.isLogP() { - lgp := l.logpWithEntries() - lgp.Fatal(args...) - return - } l.entry.Fatal(args...) } // Panic prints a panic message func (l *logger) Panic(args ...interface{}) { - if l.isLogP() { - lgp := l.logpWithEntries() - lgp.Panic(args...) - return - } l.entry.Panic(args...) } // Debugln prints a debug line func (l *logger) Debugln(args ...interface{}) { - if l.isLogP() { - lgp := l.logpWithEntries() - lgp.Named(debugSelector).Debug(args...) - return - } l.entry.Debugln(args...) } // Infoln prints an info line func (l *logger) Infoln(args ...interface{}) { - if l.isLogP() { - lgp := l.logpWithEntries() - lgp.Info(args...) - return - } l.entry.Infoln(args...) } // Println prints a line func (l *logger) Println(args ...interface{}) { - if l.isLogP() { - lgp := l.logpWithEntries() - lgp.Info(args...) - return - } l.entry.Println(args...) } // Traceln prints a trace line func (l *logger) Traceln(args ...interface{}) { - if l.isLogP() && GetLevel() == logrus.TraceLevel { - lgp := l.logpWithEntries() - lgp.Named(traceSelector).Debug(args...) - return - } l.entry.Traceln(args...) } // Warnln prints a warn line func (l *logger) Warnln(args ...interface{}) { - if l.isLogP() { - lgp := l.logpWithEntries() - lgp.Warn(args...) - return - } l.entry.Warnln(args...) } // Errorln prints an error line func (l *logger) Errorln(args ...interface{}) { - if l.isLogP() { - lgp := l.logpWithEntries() - lgp.Error(args...) - return - } l.entry.Errorln(args...) } // Fatalln prints a fatal line func (l *logger) Fatalln(args ...interface{}) { - if l.isLogP() { - lgp := l.logpWithEntries() - lgp.Fatal(args...) - return - } l.entry.Fatalln(args...) } // Panicln prints a panic line func (l *logger) Panicln(args ...interface{}) { - if l.isLogP() { - lgp := l.logpWithEntries() - lgp.Panic(args...) - return - } l.entry.Panicln(args...) } @@ -377,22 +241,3 @@ func (l *logger) InfoRedacted(fields []string, args ...interface{}) { func (l *logger) DebugRedacted(fields []string, args ...interface{}) { l.Debug(ObscureArguments(fields, args...)) } - -func (l *logger) isLogP() bool { - if l.noLogP { - return false - } - return isLogP -} - -func (l *logger) logpWithEntries() *logp.Logger { - var entries []interface{} - for k, val := range l.entry.Data { - entries = append(entries, logp.String(k, fmt.Sprintf("%v", val))) - } - lgp := logp.L() - for _, entry := range entries { - lgp = lgp.With(entry) - } - return lgp -} diff --git a/pkg/util/log/loggerconfig.go b/pkg/util/log/loggerconfig.go index ca478a8ab..4296b01c7 100644 --- a/pkg/util/log/loggerconfig.go +++ b/pkg/util/log/loggerconfig.go @@ -111,7 +111,7 @@ func (b *LoggerConfig) Apply() error { logrus.SetOutput(writer) } - if !b.initialized || isLogP { + if !b.initialized { // Add the rotate file hook for log and logrus if b.output == File || b.output == Both { if b.path != "" {