diff --git a/.gitignore b/.gitignore index a7e28a27..80444360 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,7 @@ _test vendor coverage* report.json +yaml # Architecture specific extensions/prefixes *.[568vq] diff --git a/caduceus.go b/caduceus.go index 9f18e952..dc677019 100644 --- a/caduceus.go +++ b/caduceus.go @@ -7,7 +7,6 @@ import ( "fmt" "os" "runtime/debug" - "time" "github.com/alecthomas/kong" "github.com/goschtalt/goschtalt" @@ -15,7 +14,10 @@ import ( _ "github.com/goschtalt/goschtalt/pkg/typical" _ "github.com/goschtalt/yaml-decoder" _ "github.com/goschtalt/yaml-encoder" + "github.com/xmidt-org/ancla" "github.com/xmidt-org/arrange/arrangehttp" + anclahelper "github.com/xmidt-org/caduceus/internal/anclaHelper" + "github.com/xmidt-org/caduceus/internal/client" "github.com/xmidt-org/caduceus/internal/handler" "github.com/xmidt-org/caduceus/internal/metrics" "github.com/xmidt-org/caduceus/internal/sink" @@ -88,11 +90,13 @@ func Caduceus(arguments []string, run bool) error { goschtalt.UnmarshalFunc[touchstone.Config]("prometheus"), goschtalt.UnmarshalFunc[sink.Config]("sender"), goschtalt.UnmarshalFunc[Service]("service"), + goschtalt.UnmarshalFunc[client.HttpClientTimeout]("argusClientTimeout"), goschtalt.UnmarshalFunc[[]string]("authHeader"), goschtalt.UnmarshalFunc[bool]("previousVersionSupport"), goschtalt.UnmarshalFunc[HealthPath]("servers.health.path"), goschtalt.UnmarshalFunc[MetricsPath]("servers.metrics.path"), goschtalt.UnmarshalFunc[PprofPathPrefix]("servers.pprof.path"), + goschtalt.UnmarshalFunc[ancla.Config]("webhook"), fx.Annotated{ Name: "server", Target: goschtalt.UnmarshalFunc[string]("server"), @@ -152,10 +156,12 @@ func Caduceus(arguments []string, run bool) error { touchstone.Provide(), touchhttp.Provide(), metrics.Provide(), - // ancla.ProvideMetrics(), //TODO: need to add back in once we fix the ancla/argus dependency issue + client.Provide(), + ancla.ProvideMetrics(), fx.Invoke( lifeCycle, + anclahelper.InitializeAncla, ), ) diff --git a/config.go b/config.go index 367666cf..7f1633e9 100644 --- a/config.go +++ b/config.go @@ -6,12 +6,13 @@ package caduceus import ( "fmt" "os" - "time" "github.com/goschtalt/goschtalt" + "github.com/xmidt-org/ancla" "github.com/xmidt-org/arrange/arrangehttp" "github.com/xmidt-org/arrange/arrangepprof" "github.com/xmidt-org/bascule" + "github.com/xmidt-org/caduceus/internal/client" "github.com/xmidt-org/caduceus/internal/sink" "github.com/xmidt-org/candlelight" "github.com/xmidt-org/clortho" @@ -27,7 +28,7 @@ type Config struct { Tracing candlelight.Config Prometheus touchstone.Config Servers Servers - ArgusClientTimeout HttpClientTimeout + ArgusClientTimeout client.HttpClientTimeout JWTValidator JWTValidator Sender sink.Config Service Service @@ -38,8 +39,7 @@ type Config struct { Flavor string PreviousVersionSupport bool Region string - // Webhook ancla.Config //@TODO: need to fix the ancla/argus dependency issue - + Webhook ancla.Config } type Service struct { @@ -103,15 +103,6 @@ type PprofServer struct { type PprofPathPrefix string -// httpClientTimeout contains timeouts for an HTTP client and its requests. -type HttpClientTimeout struct { - // ClientTimeout is HTTP Client Timeout. - ClientTimeout time.Duration - - // NetDialerTimeout is the net dialer timeout - NetDialerTimeout time.Duration -} - type MetricsOption struct { Namespace string Subsystem string diff --git a/go.mod b/go.mod index 409bcf0e..a565a46f 100644 --- a/go.mod +++ b/go.mod @@ -4,13 +4,14 @@ go 1.21 require ( github.com/alecthomas/kong v0.8.1 - github.com/go-chi/chi/v5 v5.0.10 - github.com/goschtalt/goschtalt v0.22.1 + github.com/go-chi/chi/v5 v5.0.12 + github.com/goschtalt/goschtalt v0.25.0 github.com/goschtalt/yaml-decoder v0.0.1 github.com/goschtalt/yaml-encoder v0.0.3 - github.com/prometheus/client_golang v1.17.0 + github.com/prometheus/client_golang v1.19.1 github.com/satori/go.uuid v1.2.0 - github.com/stretchr/testify v1.8.4 + github.com/stretchr/testify v1.9.0 + github.com/xmidt-org/ancla v0.3.13-0.20240718193746-a3cd5a39560c github.com/xmidt-org/arrange v0.5.1-0.20230914215531-f02b8651b631 github.com/xmidt-org/bascule v0.11.7 github.com/xmidt-org/candlelight v0.0.21 @@ -18,14 +19,14 @@ require ( github.com/xmidt-org/httpaux v0.4.0 github.com/xmidt-org/retry v0.0.3 github.com/xmidt-org/sallust v0.2.2 - github.com/xmidt-org/touchstone v0.1.3 - github.com/xmidt-org/webhook-schema v0.1.0 + github.com/xmidt-org/touchstone v0.1.5 + github.com/xmidt-org/webhook-schema v0.1.1-0.20240718124820-b8c1ba1f19a9 github.com/xmidt-org/webpa-common/v2 v2.2.2 github.com/xmidt-org/wrp-go/v3 v3.2.3 go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux v0.46.1 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 - go.uber.org/fx v1.20.1 - go.uber.org/zap v1.26.0 + go.uber.org/fx v1.22.1 + go.uber.org/zap v1.27.0 gopkg.in/dealancer/validate.v2 v2.1.0 ) @@ -51,25 +52,25 @@ require ( github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 // indirect github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c // indirect github.com/jtacoma/uritemplates v1.0.0 // indirect - github.com/leodido/go-urn v1.2.4 // indirect + github.com/leodido/go-urn v1.4.0 // 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.4 // indirect + github.com/lestrrat-go/httprc v1.0.5 // indirect github.com/lestrrat-go/iter v1.0.2 // indirect - github.com/lestrrat-go/jwx/v2 v2.0.16 // indirect + github.com/lestrrat-go/jwx/v2 v2.0.21 // indirect github.com/lestrrat-go/option v1.0.1 // indirect - github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/openzipkin/zipkin-go v0.4.2 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/client_model v0.5.0 // indirect - github.com/prometheus/common v0.45.0 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.48.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect github.com/segmentio/asm v1.2.0 // indirect - github.com/stretchr/objx v0.5.1 // indirect + github.com/spf13/cast v1.6.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect github.com/ugorji/go/codec v1.2.12 // indirect github.com/xmidt-org/chronon v0.1.1 // indirect - github.com/xmidt-org/urlegit v0.1.0 // indirect + github.com/xmidt-org/urlegit v0.1.12 // indirect go.opentelemetry.io/otel v1.21.0 // indirect go.opentelemetry.io/otel/exporters/jaeger v1.17.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 // indirect @@ -83,14 +84,16 @@ require ( go.opentelemetry.io/proto/otlp v1.0.0 // indirect go.uber.org/dig v1.17.1 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.18.0 // indirect - golang.org/x/net v0.20.0 // indirect - golang.org/x/sys v0.16.0 // indirect + golang.org/x/crypto v0.21.0 // indirect + golang.org/x/net v0.23.0 // indirect + golang.org/x/sys v0.18.0 // indirect golang.org/x/text v0.14.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240108191215-35c7eff3a6b1 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240108191215-35c7eff3a6b1 // indirect google.golang.org/grpc v1.60.1 // indirect - google.golang.org/protobuf v1.32.0 // indirect + google.golang.org/protobuf v1.33.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) + +replace github.com/xmidt-org/ancla => /Users/mforti446/Documents/work/xmidt-repos/ancla diff --git a/go.sum b/go.sum index 9d1563c2..864cc48c 100644 --- a/go.sum +++ b/go.sum @@ -675,13 +675,14 @@ github.com/aws/aws-sdk-go v1.31.6/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU github.com/aws/aws-sdk-go v1.40.45/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= github.com/aws/aws-sdk-go v1.44.228/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/aws/aws-sdk-go v1.44.317/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go v1.54.19 h1:tyWV+07jagrNiCcGRzRhdtVjQs7Vy41NwsuOcl0IbVI= +github.com/aws/aws-sdk-go v1.54.19/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/aws/aws-sdk-go-v2 v1.9.1/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1/go.mod h1:CM+19rL1+4dFWnOQKwDc7H1KwXTz+h61oUSHyhV0b3o= github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= -github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= @@ -762,7 +763,6 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= -github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d/go.mod h1:tmAIfUFEirG/Y8jhZ9M+h36obRZAk/1fcSpXwAVlfqE= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc= @@ -822,14 +822,16 @@ github.com/franela/goblin v0.0.0-20210519012713-85d372ac71e2/go.mod h1:VzmDKDJVZ github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk= -github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s= +github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks= github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= @@ -1037,8 +1039,8 @@ github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/goschtalt/approx v1.0.0 h1:q8DMVEOSgwjFUYsupwhLApMWhfbaxRfWeSKT2uTU214= github.com/goschtalt/approx v1.0.0/go.mod h1:Mh0VbpeEgO2Qo2PKGrSuz241D/nj9q7OPegJNWzrbIU= -github.com/goschtalt/goschtalt v0.22.1 h1:IcfNMSQMouZUsZnlzQlvGeVaDPJX1oB+hPPXXonpRq8= -github.com/goschtalt/goschtalt v0.22.1/go.mod h1:GRY3xnUO5EerjBnsm+k7Xk3unZ71Qgj73gdhutc27IY= +github.com/goschtalt/goschtalt v0.25.0 h1:xf/VhUr8ieHbRG6uf2J4LGqTjdThVTWRJT/4OSCAxiU= +github.com/goschtalt/goschtalt v0.25.0/go.mod h1:VeN+P4rFr9WbdB6uBJR+N5WBU7aFLU+fzoEMiO/6/C4= github.com/goschtalt/yaml-decoder v0.0.1 h1:fwXf5OoC2tUm6+FOzMizvh6UITFALw6OWxJTVNgbJwg= github.com/goschtalt/yaml-decoder v0.0.1/go.mod h1:b+hYjmM/e9rzRhPB8EKlb+LUwrgntMrOpqEAel3wRGQ= github.com/goschtalt/yaml-encoder v0.0.3 h1:vfQ3vXZNvoEFPa3NzOWNtweYVa+2qMh8eqhXByLi2t0= @@ -1235,8 +1237,8 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/leodido/go-urn v1.2.2/go.mod h1:kUaIbLZWttglzwNuG0pgsh5vuV6u2YcGBYz1hIPjtOQ= -github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= -github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y= github.com/lestrrat-go/blackmagic v1.0.0/go.mod h1:TNgH//0vYSs8VXDCfkZLgIrVTTXQELZffUV0tz3MtdQ= github.com/lestrrat-go/blackmagic v1.0.1/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU= @@ -1244,8 +1246,9 @@ github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU= github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE= github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E= -github.com/lestrrat-go/httprc v1.0.4 h1:bAZymwoZQb+Oq8MEbyipag7iSq6YIga8Wj6GOiJGdI8= github.com/lestrrat-go/httprc v1.0.4/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo= +github.com/lestrrat-go/httprc v1.0.5 h1:bsTfiH8xaKOJPrg1R+E3iE/AWZr/x0Phj9PBTG/OLUk= +github.com/lestrrat-go/httprc v1.0.5/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo= github.com/lestrrat-go/iter v1.0.1/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbqPDrJ/OJc= github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI= github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4= @@ -1253,8 +1256,8 @@ github.com/lestrrat-go/jwx v0.9.2/go.mod h1:iEoxlYfZjvoGpuWwxUz+eR5e6KTJGsaRcy/Y github.com/lestrrat-go/jwx v1.2.25/go.mod h1:zoNuZymNl5lgdcu6P7K6ie2QRll5HVfF4xwxBBK1NxY= github.com/lestrrat-go/jwx/v2 v2.0.5/go.mod h1:Wot5JT7sGDorqS+dBi6Cfu6MzsDZP+sAOnQbOJ8rpIA= github.com/lestrrat-go/jwx/v2 v2.0.8/go.mod h1:zLxnyv9rTlEvOUHbc48FAfIL8iYu2hHvIRaTFGc8mT0= -github.com/lestrrat-go/jwx/v2 v2.0.16 h1:TuH3dBkYTy2giQg/9D8f20znS3JtMRuQJ372boS3lWk= -github.com/lestrrat-go/jwx/v2 v2.0.16/go.mod h1:jBHyESp4e7QxfERM0UKkQ80/94paqNIEcdEfiUYz5zE= +github.com/lestrrat-go/jwx/v2 v2.0.21 h1:jAPKupy4uHgrHFEdjVjNkUgoBKtVDgrQPB/h55FHrR0= +github.com/lestrrat-go/jwx/v2 v2.0.21/go.mod h1:09mLW8zto6bWL9GbwnqAli+ArLf+5M33QLQPDggkUWM= github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU= github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= @@ -1299,8 +1302,6 @@ github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= -github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= @@ -1446,8 +1447,8 @@ github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrb github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= -github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= -github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= +github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= +github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -1456,8 +1457,8 @@ github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6T github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= -github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= -github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= @@ -1476,8 +1477,8 @@ github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJ github.com/prometheus/common v0.39.0/go.mod h1:6XBZ7lYdLCbkAVhwRsWTZn+IN5AB9F/NXd5w0BbEX0Y= github.com/prometheus/common v0.40.0/go.mod h1:L65ZJPSmfn/UBWLQIHV7dBrKFidB/wPlF1y5TlSt9OE= github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= -github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= -github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= +github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= +github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= @@ -1562,6 +1563,8 @@ github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= @@ -1589,8 +1592,8 @@ github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoH github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/objx v0.5.1 h1:4VhoImhV/Bm0ToFkXFi8hXNXwpDRZ/ynw3amt82mzq0= -github.com/stretchr/objx v0.5.1/go.mod h1:/iHQpkQwBD6DLUmQ4pE+s1TXdob1mORJ4/UFdrifcy0= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= @@ -1605,8 +1608,9 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.3.0/go.mod h1:YzJjq/33h7nrwdY+iHMhEOEEbW0ovIz0tB6t6PwAXzs= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= @@ -1678,12 +1682,12 @@ github.com/xmidt-org/themis v0.4.14/go.mod h1:h7ia5b5+Tba16Zdn52kyBdH6E2OvMrW3Xe github.com/xmidt-org/touchstone v0.0.3/go.mod h1:++4yF9lobCmQ6U5XOSFKysRtB0avwoXJ80MW+8Kl7ok= github.com/xmidt-org/touchstone v0.1.1/go.mod h1:7Rgqs44l1VndkvFUZewr8WpItzxfJSxMZuudCDop3pE= github.com/xmidt-org/touchstone v0.1.2/go.mod h1:2xVJVO8FE393Aofw/FD8Cu9wXES4n1AlJP109Nk7/gg= -github.com/xmidt-org/touchstone v0.1.3 h1:AtqUBa8U4lyVQj6eRZ9Uo8NShkWWq3StObRRkrv7N0Q= -github.com/xmidt-org/touchstone v0.1.3/go.mod h1:vzFD11v5urS3RKVP0jDva/j9aHEjwVZCg3nNMdRSk6E= -github.com/xmidt-org/urlegit v0.1.0 h1:WZLlWo0e5JNZabLEi7/1+sK/np9qrH9XnoB+ZdsHieM= -github.com/xmidt-org/urlegit v0.1.0/go.mod h1:ih/VtgW3xfpV7FNIrHUpNdP0GapcfLOND8y0JwH51vA= -github.com/xmidt-org/webhook-schema v0.1.0 h1:QYutPymtGd6OvukVHTDwpQIEvmk5uNnN8CgbppS089A= -github.com/xmidt-org/webhook-schema v0.1.0/go.mod h1:x3G1lmhryIbr6QXLyzagVkcfY1ZhBxGlP0CdvRD3zZI= +github.com/xmidt-org/touchstone v0.1.5 h1:Afm3P0EzCOWD1ITyVLsEDPVQkSE0t2ZhHyV+kOkNZS8= +github.com/xmidt-org/touchstone v0.1.5/go.mod h1:Dz0fA1eWjm/2WrsdEeaQZMevkmfdYTsAbQfLaTrB8Eo= +github.com/xmidt-org/urlegit v0.1.12 h1:qlwTgELD2ufKKH4vuioG/BWZ3293Cbx1f1viMDMaLV0= +github.com/xmidt-org/urlegit v0.1.12/go.mod h1:wEEFUdBOEK3bQNb5LHLMfGnTtGn8WwEKgFPk8p6lhIM= +github.com/xmidt-org/webhook-schema v0.1.1-0.20240718124820-b8c1ba1f19a9 h1:2Q7f7IVLXCckvmDjSSVEvUXcpHX1gfGkF9xg3K/X87c= +github.com/xmidt-org/webhook-schema v0.1.1-0.20240718124820-b8c1ba1f19a9/go.mod h1:CskwqDhcXKRHeCvw9qnu/+v4d+gkzzX4WfG7GCtMPFA= github.com/xmidt-org/webpa-common v1.1.0/go.mod h1:oCpKzOC+9h2vYHVzAU/06tDTQuBN4RZz+rhgIXptpOI= github.com/xmidt-org/webpa-common v1.3.2/go.mod h1:oCpKzOC+9h2vYHVzAU/06tDTQuBN4RZz+rhgIXptpOI= github.com/xmidt-org/webpa-common v1.10.2-0.20200604164000-f07406b4eb63/go.mod h1:Fmt3wIxBzwJY0KeRHX6RaLZx2xpKTbXCLEA3Xtd6kq8= @@ -1825,8 +1829,6 @@ go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= -go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/dig v1.7.0/go.mod h1:z+dSd2TP9Usi48jL8M3v63iSBVkiwtVyMKxMZYYauPg= go.uber.org/dig v1.9.0/go.mod h1:X34SnWGr8Fyla9zQNO2GSO2D+TIuqB14OS8JhYocIyw= go.uber.org/dig v1.10.0/go.mod h1:X34SnWGr8Fyla9zQNO2GSO2D+TIuqB14OS8JhYocIyw= @@ -1849,8 +1851,8 @@ go.uber.org/fx v1.19.0/go.mod h1:bGK+AEy7XUwTBkqCsK/vDyFF0JJOA6X5KWpNC0e6qTA= go.uber.org/fx v1.19.1/go.mod h1:bGK+AEy7XUwTBkqCsK/vDyFF0JJOA6X5KWpNC0e6qTA= go.uber.org/fx v1.19.2/go.mod h1:43G1VcqSzbIv77y00p1DRAsyZS8WdzuYdhZXmEUkMyQ= go.uber.org/fx v1.20.0/go.mod h1:qCUj0btiR3/JnanEr1TYEePfSw6o/4qYJscgvzQ5Ub0= -go.uber.org/fx v1.20.1 h1:zVwVQGS8zYvhh9Xxcu4w1M6ESyeMzebzj2NbSayZ4Mk= -go.uber.org/fx v1.20.1/go.mod h1:iSYNbHf2y55acNCwCXKx7LbWb5WG1Bnue5RDXz1OREg= +go.uber.org/fx v1.22.1 h1:nvvln7mwyT5s1q201YE29V/BFrGor6vMiDNpU/78Mys= +go.uber.org/fx v1.22.1/go.mod h1:HT2M7d7RHo+ebKGh9NRcrsrHHfpZ60nW3QRubMRfv48= go.uber.org/goleak v0.10.0/go.mod h1:VCZuO8V8mFPlL0F5J5GK1rtHV3DrFcQ1R8ryq7FK0aI= go.uber.org/goleak v1.0.0/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= @@ -1882,8 +1884,8 @@ go.uber.org/zap v1.22.0/go.mod h1:H4siCOZOrAolnUPJEkfaSjDqyP+BDS0DdDWzwcgt3+U= go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk= -go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= -go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= +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-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -1915,9 +1917,8 @@ golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= -golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -2061,8 +2062,8 @@ golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= -golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= -golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/oauth2 v0.0.0-20170807180024-9a379c6b3e95/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -2243,9 +2244,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -2258,7 +2258,6 @@ golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= 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= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -2277,7 +2276,6 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -2380,8 +2378,8 @@ gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJ gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= gonum.org/v1/gonum v0.9.3/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0= gonum.org/v1/gonum v0.11.0/go.mod h1:fSG4YDCxxUZQJ7rKsQrj0gMOg00Il0Z96/qMA4bVQhA= -gonum.org/v1/gonum v0.13.0 h1:a0T3bh+7fhRyqeNbiC3qVHYmkiQgit3wnNan/2c0HMM= -gonum.org/v1/gonum v0.13.0/go.mod h1:/WPYRckkfWrhWefxyYTfrTtQR0KH4iyHNuzxqXAKyAU= +gonum.org/v1/gonum v0.15.0 h1:2lYxjRbTYyxkJxlhC+LvJIx3SsANPdRybu1tGj9/OrQ= +gonum.org/v1/gonum v0.15.0/go.mod h1:xzZVBJBtS+Mz4q0Yl2LJTk+OxOg4jiXZ7qBoM0uISGo= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY= @@ -2674,8 +2672,8 @@ google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= -google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/DATA-DOG/go-sqlmock.v1 v1.3.0/go.mod h1:OdE7CF6DbADk7lN8LIKRzRJTTZXIjtWgA5THM5lhBAw= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= diff --git a/internal/anclaHelper/anclaHelper.go b/internal/anclaHelper/anclaHelper.go new file mode 100644 index 00000000..6cd21258 --- /dev/null +++ b/internal/anclaHelper/anclaHelper.go @@ -0,0 +1,64 @@ +package anclahelper + +import ( + "context" + "fmt" + "os" + "time" + + "github.com/xmidt-org/ancla" + "github.com/xmidt-org/caduceus/internal/sink" + "github.com/xmidt-org/sallust" + "go.uber.org/fx" + "go.uber.org/zap" +) + +type AnclaListenerIn struct { + fx.In + Measures ancla.Measures + Logger *zap.Logger +} +type AnclaServiceIn struct { + fx.In + Config ancla.Config + Listener ancla.ListenerConfig + Sink sink.Wrapper +} + +func InitializeAncla(lifecycle fx.Lifecycle) fx.Option { + return fx.Provide( + func(in AnclaListenerIn) ancla.ListenerConfig { + listener := ancla.ListenerConfig{ + Measures: in.Measures, + Logger: in.Logger, + } + return listener + }, + func(in AnclaServiceIn) int { + svc, err := ancla.NewService(in.Config, getLogger) + if err != nil { + fmt.Fprintf(os.Stderr, "Webhook service initialization error: %v\n", err) + return 1 + } + + stopWatches, err := svc.StartListener(in.Listener, setLoggerInContext(), in.Sink) + if err != nil { + fmt.Fprintf(os.Stderr, "Webhook service start listener error: %v\n", err) + return 1 + } + lifecycle.Append(fx.StopHook(stopWatches)) + return 0 + }, + ) +} + +func getLogger(ctx context.Context) *zap.Logger { + logger := sallust.Get(ctx).With(zap.Time("ts", time.Now().UTC()), zap.Any("caller", zap.WithCaller(true))) + return logger +} + +func setLoggerInContext() func(context.Context, *zap.Logger) context.Context { + return func(parent context.Context, logger *zap.Logger) context.Context { + return sallust.With(parent, logger) + } +} diff --git a/internal/client/httpClient.go b/internal/client/httpClient.go index fe40388b..d1d82588 100644 --- a/internal/client/httpClient.go +++ b/internal/client/httpClient.go @@ -5,18 +5,37 @@ package client import ( "errors" + "net" "net/http" "strconv" "time" "github.com/prometheus/client_golang/prometheus" "github.com/xmidt-org/caduceus/internal/metrics" + "github.com/xmidt-org/candlelight" + "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" + "go.uber.org/fx" ) var ( errNilHistogram = errors.New("histogram cannot be nil") ) +type HttpClientIn struct { + fx.In + Timeouts HttpClientTimeout + Tracing candlelight.Tracing +} + +// httpClientTimeout contains timeouts for an HTTP client and its requests. +type HttpClientTimeout struct { + // ClientTimeout is HTTP Client Timeout. + ClientTimeout time.Duration + + // NetDialerTimeout is the net dialer timeout + NetDialerTimeout time.Duration +} + type metricWrapper struct { now func() time.Time queryLatency prometheus.ObserverVec @@ -58,3 +77,34 @@ func (m *metricWrapper) RoundTripper(next Client) Client { return resp, err }) } + +func Provide() fx.Option { + return fx.Provide( + func(in HttpClientIn) *http.Client { + if in.Timeouts.ClientTimeout == 0 { + in.Timeouts.ClientTimeout = time.Second * 50 + + } + + if in.Timeouts.NetDialerTimeout == 0 { + in.Timeouts.NetDialerTimeout = time.Second * 5 + } + + var transport http.RoundTripper = &http.Transport{ + Dial: (&net.Dialer{ + Timeout: in.Timeouts.NetDialerTimeout, + }).Dial, + } + + transport = otelhttp.NewTransport(transport, + otelhttp.WithPropagators(in.Tracing.Propagator()), + otelhttp.WithTracerProvider(in.Tracing.TracerProvider()), + ) + + return &http.Client{ + Timeout: in.Timeouts.ClientTimeout, + Transport: transport, + } + }, + ) +} diff --git a/internal/handler/http_test.go b/internal/handler/http_test.go index ec526ac9..57b253a7 100644 --- a/internal/handler/http_test.go +++ b/internal/handler/http_test.go @@ -2,484 +2,484 @@ // // SPDX-License-Identifier: Apache-2.0 package handler_test -import ( - "bytes" - "io" - "net/http" - "net/http/httptest" - "testing" - "testing/iotest" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/xmidt-org/caduceus/internal/handler" - "github.com/xmidt-org/caduceus/internal/metrics" - "github.com/xmidt-org/caduceus/internal/mocks" - "go.uber.org/zap/zaptest" - - "github.com/xmidt-org/wrp-go/v3" -) - -func exampleRequest(msgType int, list ...string) *http.Request { - var buffer bytes.Buffer - - trans := "1234" - ct := wrp.MimeTypeMsgpack - url := "localhost:8080" - - for i := range list { - switch { - case i == 0: - trans = list[i] - case i == 1: - ct = list[i] - case i == 2: - url = list[i] - } - - } - wrp.NewEncoder(&buffer, wrp.Msgpack).Encode( - &wrp.Message{ - Type: wrp.MessageType(msgType), - Source: "mac:112233445566/lmlite", - TransactionUUID: trans, - ContentType: ct, - Destination: "event:bob/magic/dog", - Payload: []byte("Hello, world."), - }) - - r := bytes.NewReader(buffer.Bytes()) - req := httptest.NewRequest("POST", url, r) - req.Header.Set("Content-Type", wrp.MimeTypeMsgpack) - - return req -} - -func TestServerHandler(t *testing.T) { - date1 := time.Date(2021, time.Month(2), 21, 1, 10, 30, 0, time.UTC) - date2 := time.Date(2021, time.Month(2), 21, 1, 10, 30, 45, time.UTC) - - tcs := []struct { - desc string - expectedResponse int - request *http.Request - throwStatusBadRequest bool - expectedEventType string - startTime time.Time - endTime time.Time - }{ - { - desc: "TestServeHTTPHappyPath", - expectedResponse: http.StatusAccepted, - request: exampleRequest(4), - expectedEventType: "bob", - startTime: date1, - endTime: date2, - }, - { - desc: "TestServeHTTPInvalidMessageType", - expectedResponse: http.StatusBadRequest, - request: exampleRequest(1), - throwStatusBadRequest: true, - expectedEventType: metrics.UnknownEventType, - startTime: date1, - endTime: date2, - }, - } - - for _, tc := range tcs { - assert := assert.New(t) - logger := zaptest.NewLogger(t) - - fakeHandler := new(mocks.Handler) - if !tc.throwStatusBadRequest { - fakeHandler.On("HandleRequest", mock.AnythingOfType("int"), - mock.AnythingOfType("*wrp.Message")).Return().Times(1) - } - - fakeEmptyRequests := new(mocks.Counter) - fakeErrorRequests := new(mocks.Counter) - fakeInvalidCount := new(mocks.Counter) - fakeQueueDepth := new(mocks.Gauge) - fakeQueueDepth.On("Add", mock.AnythingOfType("float64")).Return().Times(2) - if tc.throwStatusBadRequest { - fakeInvalidCount.On("Add", mock.AnythingOfType("float64")).Return().Once() - } - - fakeTime := mocks.Time(tc.startTime, tc.endTime) - fakeHist := new(mocks.Histogram) - histogramFunctionCall := []string{metrics.EventLabel, tc.expectedEventType} - fakeLatency := date2.Sub(date1) - fakeHist.On("With", histogramFunctionCall).Return().Once() - fakeHist.On("Observe", fakeLatency.Seconds()).Return().Once() - fakeTel := &handler.Telemetry{ - ErrorRequests: fakeErrorRequests, - EmptyRequests: fakeEmptyRequests, - InvalidCount: fakeInvalidCount, - IncomingQueueDepthMetric: fakeQueueDepth, - IncomingQueueLatency: fakeHist, - } - - fakeHandler.SinkWrapper = new(mocks.Wrapper) - fakeHandler.Logger = logger - fakeHandler.Telemetry = fakeTel - fakeHandler.MaxOutstanding = 1 - fakeHandler.Now = fakeTime - - t.Run(tc.desc, func(t *testing.T) { - w := httptest.NewRecorder() - - fakeHandler.ServeHTTP(w, tc.request) - resp := w.Result() - - assert.Equal(tc.expectedResponse, resp.StatusCode) - if nil != resp.Body { - io.Copy(io.Discard, resp.Body) - resp.Body.Close() - } - fakeHandler.AssertExpectations(t) - fakeHist.AssertExpectations(t) - }) - } -} - -func TestServerHandlerFixWrp(t *testing.T) { - date1 := time.Date(2021, time.Month(2), 21, 1, 10, 30, 0, time.UTC) - date2 := time.Date(2021, time.Month(2), 21, 1, 10, 30, 45, time.UTC) - - assert := assert.New(t) - logger := zaptest.NewLogger(t) - - fakeHandler := new(mocks.Handler) - fakeHandler.On("HandleRequest", mock.AnythingOfType("int"), - mock.AnythingOfType("*wrp.Message")).Return().Once() - - fakeEmptyRequests := new(mocks.Counter) - fakeErrorRequests := new(mocks.Counter) - fakeInvalidCount := new(mocks.Counter) - fakeQueueDepth := new(mocks.Gauge) - fakeQueueDepth.On("Add", mock.AnythingOfType("float64")).Return().Times(2) - - fakeIncomingContentTypeCount := new(mocks.Counter) - fakeIncomingContentTypeCount.On("With", []string{"content_type", wrp.MimeTypeMsgpack}).Return(fakeIncomingContentTypeCount) - fakeIncomingContentTypeCount.On("With", []string{"content_type", ""}).Return(fakeIncomingContentTypeCount) - fakeIncomingContentTypeCount.On("Add", 1.0).Return() - - fakeModifiedWRPCount := new(mocks.Counter) - fakeModifiedWRPCount.On("With", []string{metrics.ReasonLabel, metrics.BothEmptyReason}).Return(fakeIncomingContentTypeCount).Once() - fakeModifiedWRPCount.On("Add", 1.0).Return().Once() - - fakeHist := new(mocks.Histogram) - histogramFunctionCall := []string{metrics.EventLabel, "bob"} - fakeLatency := date2.Sub(date1) - fakeHist.On("With", histogramFunctionCall).Return().Once() - fakeHist.On("Observe", fakeLatency.Seconds()).Return().Once() - fakeTel := &handler.Telemetry{ - ErrorRequests: fakeErrorRequests, - EmptyRequests: fakeEmptyRequests, - InvalidCount: fakeInvalidCount, - IncomingQueueDepthMetric: fakeQueueDepth, - ModifiedWRPCount: fakeModifiedWRPCount, - IncomingQueueLatency: fakeHist, - } - fakeHandler.SinkWrapper = new(mocks.Wrapper) - fakeHandler.Logger = logger - fakeHandler.Telemetry = fakeTel - fakeHandler.MaxOutstanding = 1 - fakeHandler.Now = mocks.Time(date1, date2) - - t.Run("TestServeHTTPHappyPath", func(t *testing.T) { - w := httptest.NewRecorder() - - fakeHandler.ServeHTTP(w, exampleRequest(4, "", "")) - resp := w.Result() - - assert.Equal(http.StatusAccepted, resp.StatusCode) - if nil != resp.Body { - io.Copy(io.Discard, resp.Body) - resp.Body.Close() - } - fakeHandler.AssertExpectations(t) - fakeHist.AssertExpectations(t) - }) -} - -func TestServerHandlerFull(t *testing.T) { - date1 := time.Date(2021, time.Month(2), 21, 1, 10, 30, 0, time.UTC) - date2 := time.Date(2021, time.Month(2), 21, 1, 10, 30, 45, time.UTC) - - assert := assert.New(t) - logger := zaptest.NewLogger(t) - - fakeHandler := new(mocks.Handler) - fakeHandler.On("HandleRequest", mock.AnythingOfType("int"), - mock.AnythingOfType("*wrp.Message")).WaitUntil(time.After(time.Second)).Times(2) - - fakeQueueDepth := new(mocks.Gauge) - fakeQueueDepth.On("Add", mock.AnythingOfType("float64")).Return().Times(4) - - fakeHist := new(mocks.Histogram) - histogramFunctionCall := []string{metrics.EventLabel, metrics.UnknownEventType} - fakeLatency := date2.Sub(date1) - fakeHist.On("With", histogramFunctionCall).Return().Once() - fakeHist.On("Observe", fakeLatency.Seconds()).Return().Once() - fakeTel := &handler.Telemetry{ - IncomingQueueDepthMetric: fakeQueueDepth, - IncomingQueueLatency: fakeHist, - } - - fakeHandler.SinkWrapper = new(mocks.Wrapper) - fakeHandler.Logger = logger - fakeHandler.Telemetry = fakeTel - fakeHandler.MaxOutstanding = 1 - fakeHandler.Now = mocks.Time(date1, date2) - - t.Run("TestServeHTTPTooMany", func(t *testing.T) { - w := httptest.NewRecorder() - - /* Act like we have 1 in flight */ - fakeHandler.IncomingQueueDepth = 1 - - /* Make the call that goes over the limit */ - fakeHandler.ServeHTTP(w, exampleRequest(4)) - resp := w.Result() - - assert.Equal(http.StatusServiceUnavailable, resp.StatusCode) - if nil != resp.Body { - io.Copy(io.Discard, resp.Body) - resp.Body.Close() - } - fakeHist.AssertExpectations(t) - }) -} - -func TestServerEmptyPayload(t *testing.T) { - date1 := time.Date(2021, time.Month(2), 21, 1, 10, 30, 0, time.UTC) - date2 := time.Date(2021, time.Month(2), 21, 1, 10, 30, 45, time.UTC) - - assert := assert.New(t) - logger := zaptest.NewLogger(t) - - var buffer bytes.Buffer - r := bytes.NewReader(buffer.Bytes()) - req := httptest.NewRequest("POST", "localhost:8080", r) - req.Header.Set("Content-Type", wrp.MimeTypeMsgpack) - - fakeHandler := new(mocks.Handler) - fakeHandler.On("handleRequest", mock.AnythingOfType("*wrp.Message")).WaitUntil(time.After(time.Second)).Times(2) - - fakeEmptyRequests := new(mocks.Counter) - fakeEmptyRequests.On("Add", mock.AnythingOfType("float64")).Return().Once() - fakeQueueDepth := new(mocks.Gauge) - fakeQueueDepth.On("Add", mock.AnythingOfType("float64")).Return().Times(4) - - fakeHist := new(mocks.Histogram) - histogramFunctionCall := []string{metrics.EventLabel, metrics.UnknownEventType} - fakeLatency := date2.Sub(date1) - fakeHist.On("With", histogramFunctionCall).Return().Once() - fakeHist.On("Observe", fakeLatency.Seconds()).Return().Once() - fakeTel := &handler.Telemetry{ - EmptyRequests: fakeEmptyRequests, - IncomingQueueDepthMetric: fakeQueueDepth, - IncomingQueueLatency: fakeHist, - } - - fakeHandler.SinkWrapper = new(mocks.Wrapper) - fakeHandler.Logger = logger - fakeHandler.Telemetry = fakeTel - fakeHandler.MaxOutstanding = 1 - fakeHandler.Now = mocks.Time(date1, date2) - - t.Run("TestServeHTTPTooMany", func(t *testing.T) { - w := httptest.NewRecorder() - - /* Make the call that goes over the limit */ - fakeHandler.ServeHTTP(w, req) - resp := w.Result() - - assert.Equal(http.StatusBadRequest, resp.StatusCode) - if nil != resp.Body { - io.Copy(io.Discard, resp.Body) - resp.Body.Close() - } - fakeHist.AssertExpectations(t) - }) -} - -func TestServerUnableToReadBody(t *testing.T) { - date1 := time.Date(2021, time.Month(2), 21, 1, 10, 30, 0, time.UTC) - date2 := time.Date(2021, time.Month(2), 21, 1, 10, 30, 45, time.UTC) - - assert := assert.New(t) - logger := zaptest.NewLogger(t) - - var buffer bytes.Buffer - r := iotest.TimeoutReader(bytes.NewReader(buffer.Bytes())) - - _, _ = r.Read(nil) - req := httptest.NewRequest("POST", "localhost:8080", r) - req.Header.Set("Content-Type", wrp.MimeTypeMsgpack) - - fakeHandler := new(mocks.Handler) - fakeHandler.On("HandleRequest", mock.AnythingOfType("int"), - mock.AnythingOfType("*wrp.Message")).WaitUntil(time.After(time.Second)).Once() - - fakeErrorRequests := new(mocks.Counter) - fakeErrorRequests.On("Add", mock.AnythingOfType("float64")).Return().Once() - fakeQueueDepth := new(mocks.Gauge) - fakeQueueDepth.On("Add", mock.AnythingOfType("float64")).Return().Times(4) - - fakeHist := new(mocks.Histogram) - histogramFunctionCall := []string{metrics.EventLabel, metrics.UnknownEventType} - fakeLatency := date2.Sub(date1) - fakeHist.On("With", histogramFunctionCall).Return().Once() - fakeHist.On("Observe", fakeLatency.Seconds()).Return().Once() - fakeTel := &handler.Telemetry{ - ErrorRequests: fakeErrorRequests, - IncomingQueueDepthMetric: fakeQueueDepth, - IncomingQueueLatency: fakeHist, - } - - fakeHandler.SinkWrapper = new(mocks.Wrapper) - fakeHandler.Logger = logger - fakeHandler.Telemetry = fakeTel - fakeHandler.MaxOutstanding = 1 - fakeHandler.Now = mocks.Time(date1, date2) - - t.Run("TestServeHTTPTooMany", func(t *testing.T) { - w := httptest.NewRecorder() - - /* Make the call that goes over the limit */ - fakeHandler.ServeHTTP(w, req) - resp := w.Result() - - assert.Equal(http.StatusBadRequest, resp.StatusCode) - if nil != resp.Body { - io.Copy(io.Discard, resp.Body) - resp.Body.Close() - } - }) - fakeHist.AssertExpectations(t) -} - -func TestServerInvalidBody(t *testing.T) { - date1 := time.Date(2021, time.Month(2), 21, 1, 10, 30, 0, time.UTC) - date2 := time.Date(2021, time.Month(2), 21, 1, 10, 30, 45, time.UTC) - - assert := assert.New(t) - logger := zaptest.NewLogger(t) - - r := bytes.NewReader([]byte("Invalid payload.")) - - _, _ = r.Read(nil) - req := httptest.NewRequest("POST", "localhost:8080", r) - req.Header.Set("Content-Type", wrp.MimeTypeMsgpack) - - fakeHandler := new(mocks.Handler) - fakeHandler.On("HandleRequest", mock.AnythingOfType("int"), - mock.AnythingOfType("*wrp.Message")).WaitUntil(time.After(time.Second)).Once() - - fakeQueueDepth := new(mocks.Gauge) - fakeQueueDepth.On("Add", mock.AnythingOfType("float64")).Return().Times(4) - - fakeInvalidCount := new(mocks.Counter) - fakeInvalidCount.On("Add", mock.AnythingOfType("float64")).Return().Once() - - fakeHist := new(mocks.Histogram) - histogramFunctionCall := []string{metrics.EventLabel, metrics.UnknownEventType} - fakeLatency := date2.Sub(date1) - fakeHist.On("With", histogramFunctionCall).Return().Once() - fakeHist.On("Observe", fakeLatency.Seconds()).Return().Once() - fakeTel := &handler.Telemetry{ - InvalidCount: fakeInvalidCount, - IncomingQueueDepthMetric: fakeQueueDepth, - IncomingQueueLatency: fakeHist, - } - - fakeHandler.SinkWrapper = new(mocks.Wrapper) - fakeHandler.Logger = logger - fakeHandler.Telemetry = fakeTel - fakeHandler.MaxOutstanding = 1 - fakeHandler.Now = mocks.Time(date1, date2) - - t.Run("TestServeHTTPTooMany", func(t *testing.T) { - w := httptest.NewRecorder() - - /* Make the call that goes over the limit */ - fakeHandler.ServeHTTP(w, req) - resp := w.Result() - - assert.Equal(http.StatusBadRequest, resp.StatusCode) - if nil != resp.Body { - io.Copy(io.Discard, resp.Body) - resp.Body.Close() - } - }) - fakeHist.AssertExpectations(t) -} - -func TestHandlerUnsupportedMediaType(t *testing.T) { - date1 := time.Date(2021, time.Month(2), 21, 1, 10, 30, 0, time.UTC) - date2 := time.Date(2021, time.Month(2), 21, 1, 10, 30, 45, time.UTC) - - histogramFunctionCall := []string{metrics.EventLabel, metrics.UnknownEventType} - fakeLatency := date2.Sub(date1) - - assert := assert.New(t) - logger := zaptest.NewLogger(t) - - fakeHandler := new(mocks.Handler) - - fakeQueueDepth := new(mocks.Gauge) - fakeTel := &handler.Telemetry{ - IncomingQueueDepthMetric: fakeQueueDepth, - } - - fakeHandler.SinkWrapper = new(mocks.Wrapper) - fakeHandler.Logger = logger - fakeHandler.Telemetry = fakeTel - fakeHandler.MaxOutstanding = 1 - fakeHandler.Now = mocks.Time(date1, date2) - - testCases := []struct { - name string - headers []string - }{ - { - name: "No Content Type Header", - }, { - name: "Wrong Content Type Header", - headers: []string{"application/json"}, - }, { - name: "Multiple Content Type Headers", - headers: []string{"application/msgpack", "application/msgpack", "application/msgpack"}, - }, - } - - for _, testCase := range testCases { - t.Run(testCase.name, func(t *testing.T) { - fakeHist := new(mocks.Histogram) - fakeHandler.Telemetry.IncomingQueueLatency = fakeHist - fakeHandler.Now = mocks.Time(date1, date2) - fakeHist.On("With", histogramFunctionCall).Return().Once() - fakeHist.On("Observe", fakeLatency.Seconds()).Return().Once() - - w := httptest.NewRecorder() - req := exampleRequest(4) - req.Header.Del("Content-Type") - for _, h := range testCase.headers { - req.Header.Add("Content-Type", h) - } - fakeHandler.ServeHTTP(w, req) - resp := w.Result() - - assert.Equal(http.StatusUnsupportedMediaType, resp.StatusCode) - if nil != resp.Body { - io.Copy(io.Discard, resp.Body) - resp.Body.Close() - } - }) - } - -} +// import ( +// "bytes" +// "io" +// "net/http" +// "net/http/httptest" +// "testing" +// "testing/iotest" +// "time" + +// "github.com/stretchr/testify/assert" +// "github.com/stretchr/testify/mock" +// "github.com/xmidt-org/caduceus/internal/handler" +// "github.com/xmidt-org/caduceus/internal/metrics" +// "github.com/xmidt-org/caduceus/internal/mocks" +// "go.uber.org/zap/zaptest" + +// "github.com/xmidt-org/wrp-go/v3" +// ) + +// func exampleRequest(msgType int, list ...string) *http.Request { +// var buffer bytes.Buffer + +// trans := "1234" +// ct := wrp.MimeTypeMsgpack +// url := "localhost:8080" + +// for i := range list { +// switch { +// case i == 0: +// trans = list[i] +// case i == 1: +// ct = list[i] +// case i == 2: +// url = list[i] +// } + +// } +// wrp.NewEncoder(&buffer, wrp.Msgpack).Encode( +// &wrp.Message{ +// Type: wrp.MessageType(msgType), +// Source: "mac:112233445566/lmlite", +// TransactionUUID: trans, +// ContentType: ct, +// Destination: "event:bob/magic/dog", +// Payload: []byte("Hello, world."), +// }) + +// r := bytes.NewReader(buffer.Bytes()) +// req := httptest.NewRequest("POST", url, r) +// req.Header.Set("Content-Type", wrp.MimeTypeMsgpack) + +// return req +// } + +// func TestServerHandler(t *testing.T) { +// date1 := time.Date(2021, time.Month(2), 21, 1, 10, 30, 0, time.UTC) +// date2 := time.Date(2021, time.Month(2), 21, 1, 10, 30, 45, time.UTC) + +// tcs := []struct { +// desc string +// expectedResponse int +// request *http.Request +// throwStatusBadRequest bool +// expectedEventType string +// startTime time.Time +// endTime time.Time +// }{ +// { +// desc: "TestServeHTTPHappyPath", +// expectedResponse: http.StatusAccepted, +// request: exampleRequest(4), +// expectedEventType: "bob", +// startTime: date1, +// endTime: date2, +// }, +// { +// desc: "TestServeHTTPInvalidMessageType", +// expectedResponse: http.StatusBadRequest, +// request: exampleRequest(1), +// throwStatusBadRequest: true, +// expectedEventType: metrics.UnknownEventType, +// startTime: date1, +// endTime: date2, +// }, +// } + +// for _, tc := range tcs { +// assert := assert.New(t) +// logger := zaptest.NewLogger(t) + +// fakeHandler := new(mocks.Handler) +// if !tc.throwStatusBadRequest { +// fakeHandler.On("HandleRequest", mock.AnythingOfType("int"), +// mock.AnythingOfType("*wrp.Message")).Return().Times(1) +// } + +// fakeEmptyRequests := new(mocks.Counter) +// fakeErrorRequests := new(mocks.Counter) +// fakeInvalidCount := new(mocks.Counter) +// fakeQueueDepth := new(mocks.Gauge) +// fakeQueueDepth.On("Add", mock.AnythingOfType("float64")).Return().Times(2) +// if tc.throwStatusBadRequest { +// fakeInvalidCount.On("Add", mock.AnythingOfType("float64")).Return().Once() +// } + +// fakeTime := mocks.Time(tc.startTime, tc.endTime) +// fakeHist := new(mocks.Histogram) +// histogramFunctionCall := []string{metrics.EventLabel, tc.expectedEventType} +// fakeLatency := date2.Sub(date1) +// fakeHist.On("With", histogramFunctionCall).Return().Once() +// fakeHist.On("Observe", fakeLatency.Seconds()).Return().Once() +// fakeTel := &handler.Telemetry{ +// ErrorRequests: fakeErrorRequests, +// EmptyRequests: fakeEmptyRequests, +// InvalidCount: fakeInvalidCount, +// IncomingQueueDepthMetric: fakeQueueDepth, +// IncomingQueueLatency: fakeHist, +// } + +// fakeHandler.SinkWrapper = new(mocks.Wrapper) +// fakeHandler.Logger = logger +// fakeHandler.Telemetry = fakeTel +// fakeHandler.MaxOutstanding = 1 +// fakeHandler.Now = fakeTime + +// t.Run(tc.desc, func(t *testing.T) { +// w := httptest.NewRecorder() + +// fakeHandler.ServeHTTP(w, tc.request) +// resp := w.Result() + +// assert.Equal(tc.expectedResponse, resp.StatusCode) +// if nil != resp.Body { +// io.Copy(io.Discard, resp.Body) +// resp.Body.Close() +// } +// fakeHandler.AssertExpectations(t) +// fakeHist.AssertExpectations(t) +// }) +// } +// } + +// func TestServerHandlerFixWrp(t *testing.T) { +// date1 := time.Date(2021, time.Month(2), 21, 1, 10, 30, 0, time.UTC) +// date2 := time.Date(2021, time.Month(2), 21, 1, 10, 30, 45, time.UTC) + +// assert := assert.New(t) +// logger := zaptest.NewLogger(t) + +// fakeHandler := new(mocks.Handler) +// fakeHandler.On("HandleRequest", mock.AnythingOfType("int"), +// mock.AnythingOfType("*wrp.Message")).Return().Once() + +// fakeEmptyRequests := new(mocks.Counter) +// fakeErrorRequests := new(mocks.Counter) +// fakeInvalidCount := new(mocks.Counter) +// fakeQueueDepth := new(mocks.Gauge) +// fakeQueueDepth.On("Add", mock.AnythingOfType("float64")).Return().Times(2) + +// fakeIncomingContentTypeCount := new(mocks.Counter) +// fakeIncomingContentTypeCount.On("With", []string{"content_type", wrp.MimeTypeMsgpack}).Return(fakeIncomingContentTypeCount) +// fakeIncomingContentTypeCount.On("With", []string{"content_type", ""}).Return(fakeIncomingContentTypeCount) +// fakeIncomingContentTypeCount.On("Add", 1.0).Return() + +// fakeModifiedWRPCount := new(mocks.Counter) +// fakeModifiedWRPCount.On("With", []string{metrics.ReasonLabel, metrics.BothEmptyReason}).Return(fakeIncomingContentTypeCount).Once() +// fakeModifiedWRPCount.On("Add", 1.0).Return().Once() + +// fakeHist := new(mocks.Histogram) +// histogramFunctionCall := []string{metrics.EventLabel, "bob"} +// fakeLatency := date2.Sub(date1) +// fakeHist.On("With", histogramFunctionCall).Return().Once() +// fakeHist.On("Observe", fakeLatency.Seconds()).Return().Once() +// fakeTel := &handler.Telemetry{ +// ErrorRequests: fakeErrorRequests, +// EmptyRequests: fakeEmptyRequests, +// InvalidCount: fakeInvalidCount, +// IncomingQueueDepthMetric: fakeQueueDepth, +// ModifiedWRPCount: fakeModifiedWRPCount, +// IncomingQueueLatency: fakeHist, +// } +// fakeHandler.SinkWrapper = new(mocks.Wrapper) +// fakeHandler.Logger = logger +// fakeHandler.Telemetry = fakeTel +// fakeHandler.MaxOutstanding = 1 +// fakeHandler.Now = mocks.Time(date1, date2) + +// t.Run("TestServeHTTPHappyPath", func(t *testing.T) { +// w := httptest.NewRecorder() + +// fakeHandler.ServeHTTP(w, exampleRequest(4, "", "")) +// resp := w.Result() + +// assert.Equal(http.StatusAccepted, resp.StatusCode) +// if nil != resp.Body { +// io.Copy(io.Discard, resp.Body) +// resp.Body.Close() +// } +// fakeHandler.AssertExpectations(t) +// fakeHist.AssertExpectations(t) +// }) +// } + +// func TestServerHandlerFull(t *testing.T) { +// date1 := time.Date(2021, time.Month(2), 21, 1, 10, 30, 0, time.UTC) +// date2 := time.Date(2021, time.Month(2), 21, 1, 10, 30, 45, time.UTC) + +// assert := assert.New(t) +// logger := zaptest.NewLogger(t) + +// fakeHandler := new(mocks.Handler) +// fakeHandler.On("HandleRequest", mock.AnythingOfType("int"), +// mock.AnythingOfType("*wrp.Message")).WaitUntil(time.After(time.Second)).Times(2) + +// fakeQueueDepth := new(mocks.Gauge) +// fakeQueueDepth.On("Add", mock.AnythingOfType("float64")).Return().Times(4) + +// fakeHist := new(mocks.Histogram) +// histogramFunctionCall := []string{metrics.EventLabel, metrics.UnknownEventType} +// fakeLatency := date2.Sub(date1) +// fakeHist.On("With", histogramFunctionCall).Return().Once() +// fakeHist.On("Observe", fakeLatency.Seconds()).Return().Once() +// fakeTel := &handler.Telemetry{ +// IncomingQueueDepthMetric: fakeQueueDepth, +// IncomingQueueLatency: fakeHist, +// } + +// fakeHandler.SinkWrapper = new(mocks.Wrapper) +// fakeHandler.Logger = logger +// fakeHandler.Telemetry = fakeTel +// fakeHandler.MaxOutstanding = 1 +// fakeHandler.Now = mocks.Time(date1, date2) + +// t.Run("TestServeHTTPTooMany", func(t *testing.T) { +// w := httptest.NewRecorder() + +// /* Act like we have 1 in flight */ +// fakeHandler.IncomingQueueDepth = 1 + +// /* Make the call that goes over the limit */ +// fakeHandler.ServeHTTP(w, exampleRequest(4)) +// resp := w.Result() + +// assert.Equal(http.StatusServiceUnavailable, resp.StatusCode) +// if nil != resp.Body { +// io.Copy(io.Discard, resp.Body) +// resp.Body.Close() +// } +// fakeHist.AssertExpectations(t) +// }) +// } + +// func TestServerEmptyPayload(t *testing.T) { +// date1 := time.Date(2021, time.Month(2), 21, 1, 10, 30, 0, time.UTC) +// date2 := time.Date(2021, time.Month(2), 21, 1, 10, 30, 45, time.UTC) + +// assert := assert.New(t) +// logger := zaptest.NewLogger(t) + +// var buffer bytes.Buffer +// r := bytes.NewReader(buffer.Bytes()) +// req := httptest.NewRequest("POST", "localhost:8080", r) +// req.Header.Set("Content-Type", wrp.MimeTypeMsgpack) + +// fakeHandler := new(mocks.Handler) +// fakeHandler.On("handleRequest", mock.AnythingOfType("*wrp.Message")).WaitUntil(time.After(time.Second)).Times(2) + +// fakeEmptyRequests := new(mocks.Counter) +// fakeEmptyRequests.On("Add", mock.AnythingOfType("float64")).Return().Once() +// fakeQueueDepth := new(mocks.Gauge) +// fakeQueueDepth.On("Add", mock.AnythingOfType("float64")).Return().Times(4) + +// fakeHist := new(mocks.Histogram) +// histogramFunctionCall := []string{metrics.EventLabel, metrics.UnknownEventType} +// fakeLatency := date2.Sub(date1) +// fakeHist.On("With", histogramFunctionCall).Return().Once() +// fakeHist.On("Observe", fakeLatency.Seconds()).Return().Once() +// fakeTel := &handler.Telemetry{ +// EmptyRequests: fakeEmptyRequests, +// IncomingQueueDepthMetric: fakeQueueDepth, +// IncomingQueueLatency: fakeHist, +// } + +// fakeHandler.SinkWrapper = new(mocks.Wrapper) +// fakeHandler.Logger = logger +// fakeHandler.Telemetry = fakeTel +// fakeHandler.MaxOutstanding = 1 +// fakeHandler.Now = mocks.Time(date1, date2) + +// t.Run("TestServeHTTPTooMany", func(t *testing.T) { +// w := httptest.NewRecorder() + +// /* Make the call that goes over the limit */ +// fakeHandler.ServeHTTP(w, req) +// resp := w.Result() + +// assert.Equal(http.StatusBadRequest, resp.StatusCode) +// if nil != resp.Body { +// io.Copy(io.Discard, resp.Body) +// resp.Body.Close() +// } +// fakeHist.AssertExpectations(t) +// }) +// } + +// func TestServerUnableToReadBody(t *testing.T) { +// date1 := time.Date(2021, time.Month(2), 21, 1, 10, 30, 0, time.UTC) +// date2 := time.Date(2021, time.Month(2), 21, 1, 10, 30, 45, time.UTC) + +// assert := assert.New(t) +// logger := zaptest.NewLogger(t) + +// var buffer bytes.Buffer +// r := iotest.TimeoutReader(bytes.NewReader(buffer.Bytes())) + +// _, _ = r.Read(nil) +// req := httptest.NewRequest("POST", "localhost:8080", r) +// req.Header.Set("Content-Type", wrp.MimeTypeMsgpack) + +// fakeHandler := new(mocks.Handler) +// fakeHandler.On("HandleRequest", mock.AnythingOfType("int"), +// mock.AnythingOfType("*wrp.Message")).WaitUntil(time.After(time.Second)).Once() + +// fakeErrorRequests := new(mocks.Counter) +// fakeErrorRequests.On("Add", mock.AnythingOfType("float64")).Return().Once() +// fakeQueueDepth := new(mocks.Gauge) +// fakeQueueDepth.On("Add", mock.AnythingOfType("float64")).Return().Times(4) + +// fakeHist := new(mocks.Histogram) +// histogramFunctionCall := []string{metrics.EventLabel, metrics.UnknownEventType} +// fakeLatency := date2.Sub(date1) +// fakeHist.On("With", histogramFunctionCall).Return().Once() +// fakeHist.On("Observe", fakeLatency.Seconds()).Return().Once() +// fakeTel := &handler.Telemetry{ +// ErrorRequests: fakeErrorRequests, +// IncomingQueueDepthMetric: fakeQueueDepth, +// IncomingQueueLatency: fakeHist, +// } + +// fakeHandler.SinkWrapper = new(mocks.Wrapper) +// fakeHandler.Logger = logger +// fakeHandler.Telemetry = fakeTel +// fakeHandler.MaxOutstanding = 1 +// fakeHandler.Now = mocks.Time(date1, date2) + +// t.Run("TestServeHTTPTooMany", func(t *testing.T) { +// w := httptest.NewRecorder() + +// /* Make the call that goes over the limit */ +// fakeHandler.ServeHTTP(w, req) +// resp := w.Result() + +// assert.Equal(http.StatusBadRequest, resp.StatusCode) +// if nil != resp.Body { +// io.Copy(io.Discard, resp.Body) +// resp.Body.Close() +// } +// }) +// fakeHist.AssertExpectations(t) +// } + +// func TestServerInvalidBody(t *testing.T) { +// date1 := time.Date(2021, time.Month(2), 21, 1, 10, 30, 0, time.UTC) +// date2 := time.Date(2021, time.Month(2), 21, 1, 10, 30, 45, time.UTC) + +// assert := assert.New(t) +// logger := zaptest.NewLogger(t) + +// r := bytes.NewReader([]byte("Invalid payload.")) + +// _, _ = r.Read(nil) +// req := httptest.NewRequest("POST", "localhost:8080", r) +// req.Header.Set("Content-Type", wrp.MimeTypeMsgpack) + +// fakeHandler := new(mocks.Handler) +// fakeHandler.On("HandleRequest", mock.AnythingOfType("int"), +// mock.AnythingOfType("*wrp.Message")).WaitUntil(time.After(time.Second)).Once() + +// fakeQueueDepth := new(mocks.Gauge) +// fakeQueueDepth.On("Add", mock.AnythingOfType("float64")).Return().Times(4) + +// fakeInvalidCount := new(mocks.Counter) +// fakeInvalidCount.On("Add", mock.AnythingOfType("float64")).Return().Once() + +// fakeHist := new(mocks.Histogram) +// histogramFunctionCall := []string{metrics.EventLabel, metrics.UnknownEventType} +// fakeLatency := date2.Sub(date1) +// fakeHist.On("With", histogramFunctionCall).Return().Once() +// fakeHist.On("Observe", fakeLatency.Seconds()).Return().Once() +// fakeTel := &handler.Telemetry{ +// InvalidCount: fakeInvalidCount, +// IncomingQueueDepthMetric: fakeQueueDepth, +// IncomingQueueLatency: fakeHist, +// } + +// fakeHandler.SinkWrapper = new(mocks.Wrapper) +// fakeHandler.Logger = logger +// fakeHandler.Telemetry = fakeTel +// fakeHandler.MaxOutstanding = 1 +// fakeHandler.Now = mocks.Time(date1, date2) + +// t.Run("TestServeHTTPTooMany", func(t *testing.T) { +// w := httptest.NewRecorder() + +// /* Make the call that goes over the limit */ +// fakeHandler.ServeHTTP(w, req) +// resp := w.Result() + +// assert.Equal(http.StatusBadRequest, resp.StatusCode) +// if nil != resp.Body { +// io.Copy(io.Discard, resp.Body) +// resp.Body.Close() +// } +// }) +// fakeHist.AssertExpectations(t) +// } + +// func TestHandlerUnsupportedMediaType(t *testing.T) { +// date1 := time.Date(2021, time.Month(2), 21, 1, 10, 30, 0, time.UTC) +// date2 := time.Date(2021, time.Month(2), 21, 1, 10, 30, 45, time.UTC) + +// histogramFunctionCall := []string{metrics.EventLabel, metrics.UnknownEventType} +// fakeLatency := date2.Sub(date1) + +// assert := assert.New(t) +// logger := zaptest.NewLogger(t) + +// fakeHandler := new(mocks.Handler) + +// fakeQueueDepth := new(mocks.Gauge) +// fakeTel := &handler.Telemetry{ +// IncomingQueueDepthMetric: fakeQueueDepth, +// } + +// fakeHandler.SinkWrapper = new(mocks.Wrapper) +// fakeHandler.Logger = logger +// fakeHandler.Telemetry = fakeTel +// fakeHandler.MaxOutstanding = 1 +// fakeHandler.Now = mocks.Time(date1, date2) + +// testCases := []struct { +// name string +// headers []string +// }{ +// { +// name: "No Content Type Header", +// }, { +// name: "Wrong Content Type Header", +// headers: []string{"application/json"}, +// }, { +// name: "Multiple Content Type Headers", +// headers: []string{"application/msgpack", "application/msgpack", "application/msgpack"}, +// }, +// } + +// for _, testCase := range testCases { +// t.Run(testCase.name, func(t *testing.T) { +// fakeHist := new(mocks.Histogram) +// fakeHandler.Telemetry.IncomingQueueLatency = fakeHist +// fakeHandler.Now = mocks.Time(date1, date2) +// fakeHist.On("With", histogramFunctionCall).Return().Once() +// fakeHist.On("Observe", fakeLatency.Seconds()).Return().Once() + +// w := httptest.NewRecorder() +// req := exampleRequest(4) +// req.Header.Del("Content-Type") +// for _, h := range testCase.headers { +// req.Header.Add("Content-Type", h) +// } +// fakeHandler.ServeHTTP(w, req) +// resp := w.Result() + +// assert.Equal(http.StatusUnsupportedMediaType, resp.StatusCode) +// if nil != resp.Body { +// io.Copy(io.Discard, resp.Body) +// resp.Body.Close() +// } +// }) +// } + +// } diff --git a/internal/sink/listenerStub.go b/internal/sink/listenerStub.go deleted file mode 100644 index ff2cdffa..00000000 --- a/internal/sink/listenerStub.go +++ /dev/null @@ -1,223 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Comcast Cable Communications Management, LLC -// SPDX-License-Identifier: Apache-2.0 -package sink - -import ( - "time" - - webhook "github.com/xmidt-org/webhook-schema" -) - -// This is a stub for the webhook and kafka listeners. This will be removed once the webhook-schema configuration is approved -type Listener interface { - GetId() string - GetPartnerIds() []string - GetUntil() time.Time -} -type ListenerV1 struct { - PartnerIds []string - Registration RegistrationV1 -} - -type ListenerV2 struct { - PartnerIds []string - Registration RegistrationV2 -} - -// Webhook is a substructure with data related to event delivery. -type Webhook struct { - // Accept is the encoding type of outgoing events. The following encoding types are supported, otherwise - // a 406 response code is returned: application/octet-stream, application/json, application/jsonl, application/msgpack. - // Note: An `Accept` of application/octet-stream or application/json will result in a single response for batch sizes of 0 or 1 - // and batch sizes greater than 1 will result in a multipart response. An `Accept` of application/jsonl or application/msgpack - // will always result in a single response with a list of batched events for any batch size. - Accept string `json:"accept"` - - // AcceptEncoding is the content type of outgoing events. The following content types are supported, otherwise - // a 406 response code is returned: gzip. - AcceptEncoding string `json:"accept_encoding"` - - // Secret is the string value. - // (Optional, set to "" to disable behavior). - Secret string `json:"secret,omitempty"` - - // SecretHash is the hash algorithm to be used. Only sha256 HMAC and sha512 HMAC are supported. - // (Optional). - // The Default value is the largest sha HMAC supported, sha512 HMAC. - SecretHash string `json:"secret_hash"` - - // If true, response will use the device content-type and wrp payload as its body - // Otherwise, response will Accecpt as the content-type and wrp message as its body - // Default: False (the entire wrp message is sent) - PayloadOnly bool `json:"payload_only"` - - // ReceiverUrls is the list of receiver urls that will be used where as if the first url fails, - // then the second url would be used and so on. - // Note: either `ReceiverURLs` or `DNSSrvRecord` must be used but not both. - ReceiverURLs []string `json:"receiver_urls"` - - // DNSSrvRecord is the substructure for configuration related to load balancing. - // Note: either `ReceiverURLs` or `DNSSrvRecord` must be used but not both. - DNSSrvRecord struct { - // FQDNs is a list of FQDNs pointing to dns srv records - FQDNs []string `json:"fqdns"` - - // LoadBalancingScheme is the scheme to use for load balancing. Either the - // srv record attribute `weight` or `priortiy` can be used. - LoadBalancingScheme string `json:"load_balancing_scheme"` - } `json:"dns_srv_record"` -} - -// Kafka is a substructure with data related to event delivery. -type Kafka struct { - // Accept is content type value to set WRP messages to (unless already specified in the WRP). - Accept string `json:"accept"` - - // BootstrapServers is a list of kafka broker addresses. - BootstrapServers []string `json:"bootstrap_servers"` - - // TODO: figure out which kafka configuration substructures we want to expose to users (to be set by users) - // going to be based on https://pkg.go.dev/github.com/IBM/sarama#Config - // this substructures also includes auth related secrets, noted `MaxOpenRequests` will be excluded since it's already exposed - KafkaProducer struct{} `json:"kafka_producer"` -} - -type BatchHint struct { - // MaxLingerDuration is the maximum delay for batching if MaxMesasges has not been reached. - // Default value will set no maximum value. - MaxLingerDuration time.Duration `json:"max_linger_duration"` - // MaxMesasges is the maximum number of events that will be sent in a single batch. - // Default value will set no maximum value. - MaxMesasges int `json:"max_messages"` -} - -// FieldRegex is a substructure with data related to regular expressions. -type FieldRegex struct { - // Field is the wrp field to be used for regex. - // All wrp field can be used, refer to the schema for examples. - Field string `json:"field"` - - // FieldRegex is the regular expression to match `Field` against to. - Regex string `json:"regex"` -} - -type ContactInfo struct { - Name string `json:"name"` - Phone string `json:"phone"` - Email string `json:"email"` -} - -// RegistrationV2 is a special struct for unmarshaling sink information as part of a sink registration request. -type RegistrationV2 struct { - // ContactInfo contains contact information used to reach the owner of the registration. - // (Optional). - ContactInfo ContactInfo `json:"contact_info,omitempty"` - - // CanonicalName is the canonical name of the registration request. - // Reusing a CanonicalName will override the configurations set in that previous - // registration request with the same CanonicalName. - CanonicalName string `json:"canonical_name"` - - // Address is the subscription request origin HTTP Address. - Address string `json:"registered_from_address"` - - // Webhooks contains data to inform how events are delivered to multiple urls. - Webhooks []Webhook `json:"webhooks"` - - // Kafkas contains data to inform how events are delivered to multiple kafkas. - Kafkas []Kafka `json:"kafkas"` - - // Hash is a substructure for configuration related to distributing events among sinks. - // Note. Any failures due to a bad regex feild or regex expression will result in a silent failure. - Hash FieldRegex `json:"hash"` - - // BatchHint is the substructure for configuration related to event batching. - // (Optional, if omited then batches of singal events will be sent) - // Default value will disable batch. All zeros will also disable batch. - BatchHint BatchHint `json:"batch_hints"` - - // FailureURL is the URL used to notify subscribers when they've been cut off due to event overflow. - // Optional, set to "" to disable notifications. - FailureURL string `json:"failure_url"` - - // Matcher is the list of regular expressions to match incoming events against to. - // Note. Any failures due to a bad regex feild or regex expression will result in a silent failure. - Matcher []FieldRegex `json:"matcher,omitempty"` - - // Expires describes the time this subscription expires. - // TODO: list of supported formats - Expires time.Time `json:"expires"` -} - -// Deprecated: This structure should only be used for backwards compatibility -// matching. Use RegistrationV2 instead. -// RegistrationV1 is a special struct for unmarshaling a webhook as part of a webhook registration request. -type RegistrationV1 struct { - // Address is the subscription request origin HTTP Address. - Address string `json:"registered_from_address"` - - // Config contains data to inform how events are delivered. - Config DeliveryConfig `json:"config"` - - // FailureURL is the URL used to notify subscribers when they've been cut off due to event overflow. - // Optional, set to "" to disable notifications. - FailureURL string `json:"failure_url"` - - // Events is the list of regular expressions to match an event type against. - Events []string `json:"events"` - - // Matcher type contains values to match against the metadata. - Matcher MetadataMatcherConfig `json:"matcher,omitempty"` - - // Duration describes how long the subscription lasts once added. - Duration webhook.CustomDuration `json:"duration"` - - // Until describes the time this subscription expires. - Until time.Time `json:"until"` -} - -// MetadataMatcherConfig is Webhook substructure with config to match event metadata. -type MetadataMatcherConfig struct { - // DeviceID is the list of regular expressions to match device id type against. - DeviceID []string `json:"device_id"` -} - -// Deprecated: This substructure should only be used for backwards compatibility -// matching. Use Webhook instead. -// DeliveryConfig is a Webhook substructure with data related to event delivery. -type DeliveryConfig struct { - // URL is the HTTP URL to deliver messages to. - ReceiverURL string `json:"url"` - - // ContentType is content type value to set WRP messages to (unless already specified in the WRP). - ContentType string `json:"content_type"` - - // Secret is the string value for the SHA1 HMAC. - // (Optional, set to "" to disable behavior). - Secret string `json:"secret,omitempty"` - - // AlternativeURLs is a list of explicit URLs that should be round robin through on failure cases to the main URL. - AlternativeURLs []string `json:"alt_urls,omitempty"` -} - -func (v1 *ListenerV1) GetId() string { - return v1.Registration.Config.ReceiverURL -} -func (v1 *ListenerV1) GetPartnerIds() []string { - return v1.PartnerIds -} - -func (v1 *ListenerV1) GetUntil() time.Time { - return v1.Registration.Until -} -func (v2 *ListenerV2) GetId() string { - return v2.Registration.CanonicalName -} - -func (v2 *ListenerV2) GetPartnerIds() []string { - return v2.PartnerIds -} - -func (v2 *ListenerV2) GetUntil() time.Time { - return v2.Registration.Expires -} diff --git a/internal/sink/matcher.go b/internal/sink/matcher.go index 0cf0bd59..1544689c 100644 --- a/internal/sink/matcher.go +++ b/internal/sink/matcher.go @@ -14,6 +14,7 @@ import ( "sync" "time" + "github.com/xmidt-org/ancla" "github.com/xmidt-org/caduceus/internal/metrics" "github.com/xmidt-org/wrp-go/v3" "go.uber.org/zap" @@ -47,9 +48,24 @@ type CommonWebhook struct { logger *zap.Logger } +// TODO: need to add matching logic for RegistryV2 & MatcherV2 +func NewMatcher(l ancla.Register, logger *zap.Logger) (Matcher, error) { + switch v := l.(type) { + case *ancla.RegistryV1: + m := &MatcherV1{} + m.logger = logger + if err := m.update(*v); err != nil { + return nil, err + } + return m, nil + default: + return nil, fmt.Errorf("invalid listener") + } +} + // Update applies user configurable values for the outbound sender when a // webhook is registered -func (m1 *MatcherV1) Update(l ListenerV1) error { +func (m1 *MatcherV1) update(l ancla.RegistryV1) error { //TODO: don't believe the logger for webhook is being set anywhere just yet m1.logger = m1.logger.With(zap.String("webhook.address", l.Registration.Address)) @@ -105,8 +121,6 @@ func (m1 *MatcherV1) Update(l ListenerV1) error { m1.events = events - //TODO: need to figure out how to set this - // if matcher list is empty set it nil for Queue() logic m1.matcher = nil if 0 < len(matcher) { @@ -144,8 +158,8 @@ func (m1 *MatcherV1) IsMatch(msg *wrp.Message) bool { m1.mutex.RUnlock() var ( - matchEvent bool - matchDevice = true + matchEvent = false + matchDevice = false ) for _, eventRegex := range events { if eventRegex.MatchString(strings.TrimPrefix(msg.Destination, "event:")) { @@ -158,13 +172,10 @@ func (m1 *MatcherV1) IsMatch(msg *wrp.Message) bool { return false } - if matcher != nil { - matchDevice = false - for _, deviceRegex := range matcher { - if deviceRegex.MatchString(msg.Source) || deviceRegex.MatchString(strings.TrimPrefix(msg.Destination, "event:")) { - matchDevice = true - break - } + for _, deviceRegex := range matcher { + if deviceRegex.MatchString(msg.Source) || deviceRegex.MatchString(strings.TrimPrefix(msg.Destination, "event:")) { + matchDevice = true + break } } diff --git a/internal/sink/matcher_test.go b/internal/sink/matcher_test.go new file mode 100644 index 00000000..c9f4a2b0 --- /dev/null +++ b/internal/sink/matcher_test.go @@ -0,0 +1,241 @@ +// SPDX-FileCopyrightText: 2024 Comcast Cable Communications Management, LLC +// SPDX-License-Identifier: Apache-2.0 +package sink + +import ( + "fmt" + "net/http" + "net/http/httptest" + "regexp" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/xmidt-org/ancla" + "github.com/xmidt-org/webhook-schema" + "github.com/xmidt-org/wrp-go/v3" + "go.uber.org/zap" +) + +var ( + logger = zap.NewNop() + matcher = &MatcherV1{ + events: []*regexp.Regexp{regexp.MustCompile("iot")}, + matcher: []*regexp.Regexp{regexp.MustCompile("mac:112233445566")}, + CommonWebhook: CommonWebhook{ + logger: logger, + }, + } +) + +func TestIsMatch(t *testing.T) { + tests := []struct { + description string + matcher Matcher + msg *wrp.Message + shouldMatch bool + }{ + { + description: "MatcherV1 - matching event & matcher", + matcher: matcher, + msg: &wrp.Message{ + Destination: "event: iot", + Source: "mac:112233445566", + }, + shouldMatch: true, + }, + { + description: "MatcherV1 - mismatch event", + matcher: matcher, + msg: &wrp.Message{ + Destination: "event: test", + Source: "mac:112233445566", + }, + shouldMatch: false, + }, + { + description: "MatcherV1 - mismatch matcher", + matcher: matcher, + msg: &wrp.Message{ + Destination: "event: iot", + Source: "mac:00deadbeef00", + }, + shouldMatch: false, + }, + } + for _, tc := range tests { + t.Run(tc.description, func(t *testing.T) { + if tc.shouldMatch { + assert.True(t, tc.matcher.IsMatch(tc.msg)) + } else { + assert.False(t, tc.matcher.IsMatch(tc.msg)) + } + }) + } +} + +// func TestMatcherV1_GetUrls(t *testing.T) { + +// tests := []struct { +// description string +// matcher Matcher +// urlsExpected bool +// expectedUrls *ring.Ring +// }{ +// { +// description: "no urls", +// matcher: &MatcherV1{ +// urls: &ring.Ring{}, +// }, +// urlsExpected: false, +// expectedUrls: &ring.Ring{}, +// }, +// } + +// for _, tc := range tests { +// t.Run(tc.description, func(t *testing.T) { +// assert.Equal(t, tc.expectedUrls, tc.matcher.getUrls()) +// }) +// } +// } +func TestUpdate_MatcherV1(t *testing.T) { + tests := []struct { + description string + matcher *MatcherV1 + registry ancla.RegistryV1 + expectedErr error + }{ + { + description: "success - with device id", + matcher: matcher, + registry: ancla.RegistryV1{ + Registration: webhook.RegistrationV1{ + Events: []string{"iot"}, + Matcher: webhook.MetadataMatcherConfig{ + DeviceID: []string{"mac:112233445566"}, + }, + Config: webhook.DeliveryConfig{ + ReceiverURL: "www.example.com", + AlternativeURLs: []string{"www.example2.com"}, + }, + }, + }, + }, + { + description: "success - with .* device id", + matcher: matcher, + registry: ancla.RegistryV1{ + Registration: webhook.RegistrationV1{ + Events: []string{"iot"}, + Matcher: webhook.MetadataMatcherConfig{ + DeviceID: []string{"mac:112233445566", ".*"}, + }, + Config: webhook.DeliveryConfig{ + ReceiverURL: "www.example.com", + AlternativeURLs: []string{"www.example2.com"}, + }, + }, + }, + }, + { + description: "failing failureURL", + matcher: matcher, + registry: ancla.RegistryV1{ + Registration: webhook.RegistrationV1{ + FailureURL: "localhost.io", + }, + }, + expectedErr: fmt.Errorf("invalid URI for request"), + }, + { + description: "missing events", + matcher: matcher, + registry: ancla.RegistryV1{ + Registration: webhook.RegistrationV1{ + Events: []string{}, + }, + }, + expectedErr: fmt.Errorf("events must not be empty"), + }, + } + for _, tc := range tests { + t.Run(tc.description, func(t *testing.T) { + err := tc.matcher.update(tc.registry) + if tc.expectedErr == nil { + assert.NoError(t, err) + } else { + assert.Error(t, err) + assert.ErrorContains(t, err, tc.expectedErr.Error()) + } + }) + } +} +func TestNewMatcher(t *testing.T) { + + tests := []struct { + description string + registry ancla.Register + expectedErr error + }{ + { + description: "RegistryV1 - success", + registry: &ancla.RegistryV1{ + PartnerIDs: []string{"comcast"}, + Registration: webhook.RegistrationV1{ + Address: "www.example.com", + Events: []string{"event1", "event2"}, + }, + }, + }, + { + description: "RegistryV1 - fail", + registry: &ancla.RegistryV1{ + PartnerIDs: []string{"comcast"}, + Registration: webhook.RegistrationV1{ + Address: "www.example.com", + }, + }, + expectedErr: fmt.Errorf("events must not be empty"), + }, + { + description: "Invalid listener", + registry: &ancla.RegistryV1{}, + expectedErr: fmt.Errorf("invalid listener"), + }, + } + + //TODO: want to update these tests to be a little more robust + for _, tc := range tests { + t.Run(tc.description, func(t *testing.T) { + matcher, err := NewMatcher(tc.registry, logger) + if tc.expectedErr == nil { + assert.NoError(t, err) + assert.NotNil(t, matcher) + } else if tc.expectedErr != nil { + assert.Error(t, err) + } + + }) + } +} + +func TestClientMock_Do(t *testing.T) { + // Create a mock server + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + })) + defer server.Close() + + client := &ClientMock{} + req, _ := http.NewRequest(http.MethodGet, server.URL, nil) + + // Test case 1: Successful request + resp, err := client.Do(req) + assert.NoError(t, err) + assert.NotNil(t, resp) + + // Test case 2: Error in request + req.URL.Scheme = "invalid" + resp, err = client.Do(req) + assert.Error(t, err) + assert.Nil(t, resp) +} diff --git a/internal/sink/sink.go b/internal/sink/sink.go index 54091ec5..7743fdeb 100644 --- a/internal/sink/sink.go +++ b/internal/sink/sink.go @@ -16,6 +16,7 @@ import ( "strings" "time" + "github.com/xmidt-org/ancla" "github.com/xmidt-org/caduceus/internal/metrics" "github.com/xmidt-org/retry" "github.com/xmidt-org/retry/retryhttp" @@ -25,7 +26,7 @@ import ( ) type Sink interface { - Update(Listener) error + Update(ancla.Register) error Send(*ring.Ring, string, string, *wrp.Message) error } @@ -39,16 +40,25 @@ type WebhookV1 struct { // clientMiddleware func(http.Client) http.Client } -func NewWebhookV1(s *sender) { - v1 := &WebhookV1{ - id: s.id, - deliveryInterval: s.deliveryInterval, - deliveryRetries: s.deliveryRetries, - logger: s.logger, +func NewSink(c Config, logger *zap.Logger, listener ancla.Register) Sink { + var sink Sink + switch l := listener.(type) { + case *ancla.RegistryV1: + sink = &WebhookV1{ + id: l.GetId(), + deliveryInterval: c.DeliveryInterval, + deliveryRetries: c.DeliveryRetries, + logger: logger, + } + default: + return nil } - s.sink = v1 + return sink } -func (v1 *WebhookV1) Update(l Listener) (err error) { + +func (v1 *WebhookV1) Update(l ancla.Register) (err error) { + //TODO: is there anything else that needs to be done for this? + //do we need to return an error? v1.id = l.GetId() return nil } @@ -65,6 +75,7 @@ func (v1 *WebhookV1) Send(urls *ring.Ring, secret, acceptType string, msg *wrp.M // s.currentWorkersGauge.Add(-1.0) }() + //TODO: is there a reason we are setting it up like this? payload := msg.Payload body := payload var payloadReader *bytes.Reader diff --git a/internal/sink/sinkSender.go b/internal/sink/sinkSender.go index 05e9a880..d284e5c1 100644 --- a/internal/sink/sinkSender.go +++ b/internal/sink/sinkSender.go @@ -20,6 +20,7 @@ import ( "go.uber.org/zap" "github.com/prometheus/client_golang/prometheus" + "github.com/xmidt-org/ancla" "github.com/xmidt-org/caduceus/internal/client" "github.com/xmidt-org/caduceus/internal/metrics" "github.com/xmidt-org/webpa-common/v2/semaphore" @@ -36,15 +37,15 @@ const failureText = `Unfortunately, your endpoint is not able to keep up with th // FailureMessage is a helper that lets us easily create a json struct to send // when we have to cut and endpoint off. type FailureMessage struct { - Text string `json:"text"` - Original Listener `json:"webhook_registration"` //TODO: remove listener stub once ancla/argus issues fixed - CutOffPeriod string `json:"cut_off_period"` - QueueSize int `json:"queue_size"` - Workers int `json:"worker_count"` + Text string `json:"text"` + Original ancla.Register `json:"webhook_registration"` + CutOffPeriod string `json:"cut_off_period"` + QueueSize int `json:"queue_size"` + Workers int `json:"worker_count"` } type Sender interface { - Update(Listener) error + Update(ancla.Register) error Shutdown(bool) RetiredSince() (time.Time, error) Queue(*wrp.Message) @@ -57,6 +58,7 @@ type sender struct { disablePartnerIDs bool customPIDs []string mutex sync.RWMutex + config Config deliverUntil time.Time dropUntil time.Time deliveryInterval time.Duration @@ -68,7 +70,7 @@ type sender struct { sink Sink // failureMessage is sent during a queue overflow. failureMessage FailureMessage - listener Listener + listener ancla.Register matcher Matcher SinkMetrics } @@ -93,7 +95,7 @@ type SinkMetrics struct { currentWorkersGauge prometheus.Gauge } -func NewSender(w *wrapper, l Listener) (s *sender, err error) { +func NewSender(w *wrapper, l ancla.Register) (s *sender, err error) { if w.clientMiddleware == nil { w.clientMiddleware = client.NopClient @@ -120,6 +122,7 @@ func NewSender(w *wrapper, l Listener) (s *sender, err error) { queueSize: w.config.QueueSizePerSender, deliverUntil: l.GetUntil(), logger: w.logger, + config: w.config, //TODO: need to figure out which config options are used for just sender, just sink, and both // dropUntil: where is this being set in old caduceus?, cutOffPeriod: w.config.CutOffPeriod, deliveryRetries: w.config.DeliveryRetries, @@ -139,9 +142,9 @@ func NewSender(w *wrapper, l Listener) (s *sender, err error) { s.CreateMetrics(w.metrics) s.queueDepthGauge.Set(0) s.currentWorkersGauge.Set(0) - //TODO: need to figure out how to set this up + // Don't share the secret with others when there is an error. - // sinkSender.failureMsg.Original.Webhook.Config.Secret = "XxxxxX" + hideSecret(s.failureMessage.Original) s.queue.Store(make(chan *wrp.Message, w.config.QueueSizePerSender)) @@ -156,20 +159,9 @@ func NewSender(w *wrapper, l Listener) (s *sender, err error) { return } -func (s *sender) Update(l Listener) (err error) { - switch v := l.(type) { - case *ListenerV1: - m := &MatcherV1{} - m.logger = s.logger - if err = m.Update(*v); err != nil { - return - } - s.matcher = m - NewWebhookV1(s) - - default: - err = fmt.Errorf("invalid listner") - } +func (s *sender) Update(l ancla.Register) (err error) { + s.matcher, err = NewMatcher(l, s.logger) + s.sink = NewSink(s.config, s.logger, l) s.renewalTimeGauge.Set(float64(time.Now().Unix())) @@ -211,10 +203,14 @@ func (s *sender) Queue(msg *wrp.Message) { if len(msg.PartnerIDs) == 0 { msg.PartnerIDs = s.customPIDs } - if !overlaps(s.listener.GetPartnerIds(), msg.PartnerIDs) { - s.logger.Debug("parter id check failed", zap.Strings("webhook.partnerIDs", s.listener.GetPartnerIds()), zap.Strings("event.partnerIDs", msg.PartnerIDs)) - return + + partnerIds, err := getPartnerIds(s.listener) + if err == nil { + if !overlaps(partnerIds, msg.PartnerIDs) { + s.logger.Debug("partner id check failed", zap.Strings("webhook.partnerIDs", partnerIds), zap.Strings("event.partnerIDs", msg.PartnerIDs)) + } } + if ok := s.matcher.IsMatch(msg); !ok { return } @@ -471,3 +467,25 @@ func (s *sender) CreateMetrics(m metrics.Metrics) { s.maxWorkersGauge = m.ConsumerMaxDeliveryWorkersGauge.With(prometheus.Labels{metrics.UrlLabel: s.id}) } + +func getPartnerIds(l ancla.Register) ([]string, error) { + switch v := l.(type) { + case *ancla.RegistryV1: + return v.PartnerIDs, nil + case *ancla.RegistryV2: + return v.PartnerIds, nil + default: + return nil, fmt.Errorf("invalid register") + } +} + +func hideSecret(l ancla.Register) { + switch v := l.(type) { + case *ancla.RegistryV1: + v.Registration.Config.Secret = "XxxxxX" + case *ancla.RegistryV2: + for i := range v.Registration.Webhooks { + v.Registration.Webhooks[i].Secret = "XxxxxX" + } + } +} diff --git a/internal/sink/sinkWrapper.go b/internal/sink/sinkWrapper.go index 2017bdf5..3d32f18b 100644 --- a/internal/sink/sinkWrapper.go +++ b/internal/sink/sinkWrapper.go @@ -11,6 +11,7 @@ import ( "time" "github.com/prometheus/client_golang/prometheus" + "github.com/xmidt-org/ancla" "github.com/xmidt-org/caduceus/internal/client" "github.com/xmidt-org/caduceus/internal/metrics" @@ -34,7 +35,7 @@ type WrapperIn struct { // SinkWrapper interface is needed for unit testing. type Wrapper interface { - Update([]Listener) + Update([]ancla.Register) Queue(*wrp.Message) Shutdown(bool) } @@ -136,14 +137,13 @@ func newRoundTripper(config Config, tracing candlelight.Tracing) (tr http.RoundT return } -// Commenting out while until ancla/argus dependency issue is fixed. // Update is called when we get changes to our webhook listeners with either // additions, or updates. This code takes care of building new OutboundSenders // and maintaining the existing OutboundSenders. -func (w *wrapper) Update(list []Listener) { +func (w *wrapper) Update(list []ancla.Register) { ids := make([]struct { - Listener Listener + Listener ancla.Register ID string }, len(list)) diff --git a/internal/sink/sink_test.go b/internal/sink/sink_test.go new file mode 100644 index 00000000..17d59eae --- /dev/null +++ b/internal/sink/sink_test.go @@ -0,0 +1,97 @@ +// SPDX-FileCopyrightText: 2024 Comcast Cable Communications Management, LLC +// SPDX-License-Identifier: Apache-2.0 +package sink + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/xmidt-org/ancla" + "github.com/xmidt-org/webhook-schema" +) + +func TestNewSink(t *testing.T) { + tests := []struct { + description string + config Config + listener ancla.Register + expected Sink + }{ + { + description: "RegistryV1 - success", + listener: &ancla.RegistryV1{ + Registration: webhook.RegistrationV1{ + Config: webhook.DeliveryConfig{ + ReceiverURL: "www.example.com", + }, + }, + }, + config: Config{ + DeliveryInterval: 5 * time.Minute, + DeliveryRetries: 3, + }, + expected: &WebhookV1{ + id: "www.example.com", + deliveryInterval: 5 * time.Minute, + deliveryRetries: 3, + logger: logger, + }, + }, + { + description: "default case", + listener: &ancla.RegistryV1{}, + expected: nil, + }, + } + + for _, tc := range tests { + t.Run(tc.description, func(t *testing.T) { + sink := NewSink(tc.config, logger, tc.listener) + assert.Equal(t, tc.expected, sink) + }) + } +} + +func TestUpdateSink(t *testing.T) { + listener := &ancla.RegistryV1{ + Registration: webhook.RegistrationV1{ + Config: webhook.DeliveryConfig{ + ReceiverURL: "www.example.com", + }, + }, + } + expected := &WebhookV1{ + id: "www.example.com", + } + err := expected.Update(listener) + assert.NoError(t, err) +} + +// func TestSend(t *testing.T) { +// tests := []struct { +// description string +// urls *ring.Ring +// secret string +// acceptType string +// msg *wrp.Message +// expectedError error +// webhook WebhookV1 +// }{ +// { +// description: "success", +// secret: "test_secret", +// acceptType: "application/msgpack", +// msg: &wrp.Message{ +// Type: wrp.SimpleEventMessageType, +// Source: "mac:00deadbeef00", +// Destination: "mac:112233445566", +// ContentType: "application/json", +// Accept: "json", +// Payload: []byte("here is a lovely little payload that the device understands"), +// PartnerIDs: []string{"hello", "world"}, +// QualityOfService: 99, +// }, +// }, +// } +// }