diff --git a/go.mod b/go.mod index f6ca9fbf..71f17f4b 100644 --- a/go.mod +++ b/go.mod @@ -6,12 +6,11 @@ require ( github.com/BurntSushi/toml v0.3.1 github.com/DATA-DOG/go-sqlmock v1.4.1 github.com/RedHatInsights/insights-content-service v0.0.0-20201009081018-083923779f00 - github.com/RedHatInsights/insights-operator-utils v1.19.1 - github.com/RedHatInsights/insights-results-aggregator-data v1.1.0 + github.com/RedHatInsights/insights-operator-utils v1.21.0 + github.com/RedHatInsights/insights-results-aggregator-data v1.3.0 github.com/Shopify/sarama v1.27.1 github.com/deckarep/golang-set v1.7.1 github.com/dgrijalva/jwt-go v3.2.0+incompatible - github.com/fzipp/gocyclo v0.3.1 // indirect github.com/gchaincl/sqlhooks v1.3.0 github.com/google/uuid v1.1.2 github.com/gorilla/mux v1.8.0 diff --git a/go.sum b/go.sum index 3a14ca84..69a5724d 100644 --- a/go.sum +++ b/go.sum @@ -40,15 +40,16 @@ github.com/RedHatInsights/insights-operator-utils v1.6.2/go.mod h1:RW9Jq4LgqIkV3 github.com/RedHatInsights/insights-operator-utils v1.6.7/go.mod h1:ott1/rkxcyQtK6XdYj1Ur3XGSYRAHTplJiV5RKkij2o= github.com/RedHatInsights/insights-operator-utils v1.8.3/go.mod h1:L6alrkNSM+uBzlQdIihhGnwTpdw+bD8i8Fdh/OE9rdo= github.com/RedHatInsights/insights-operator-utils v1.12.0/go.mod h1:mN5jURLpSG+j7y3VPAUTPyHsTWSxrqSHSerSacDBgFU= -github.com/RedHatInsights/insights-operator-utils v1.19.1 h1:RaR56or9XHPPN/q/xkWdfXNxEkrb23BwLqvRngv9XMY= -github.com/RedHatInsights/insights-operator-utils v1.19.1/go.mod h1:mN5jURLpSG+j7y3VPAUTPyHsTWSxrqSHSerSacDBgFU= +github.com/RedHatInsights/insights-operator-utils v1.21.0 h1:P688UpKrbLJ5pbLj0j3qRanROPl/eKv4ieFm7UyUSB4= +github.com/RedHatInsights/insights-operator-utils v1.21.0/go.mod h1:B2hizFGwXCc8MT34QqVJ1A8ANTyGQZQWXQw/gSCEsaU= github.com/RedHatInsights/insights-results-aggregator v0.0.0-20200604090056-3534f6dd9c1c/go.mod h1:7Pc15NYXErx7BMJ4rF1Hacm+29G6atzjhwBpXNFMt+0= github.com/RedHatInsights/insights-results-aggregator-data v0.0.0-20200825113234-e84e924194bc/go.mod h1:DcDgoCCmBuUSKQOGrTi0BfFLdSjAp/KxIwyqKUd46sM= github.com/RedHatInsights/insights-results-aggregator-data v0.0.0-20201014142608-de97c4b07d5c/go.mod h1:x8IvreR2g24veCKVMXDPOR6a0D86QK9UCBfi5Xm5Gnc= github.com/RedHatInsights/insights-results-aggregator-data v0.0.0-20201109115720-126bd0348556/go.mod h1:+j10GLCbx42McRJE7uU+aVayf5Elwx4nKULFiPkki6U= github.com/RedHatInsights/insights-results-aggregator-data v1.0.1-0.20210614072933-b25730b1e023/go.mod h1:SDeBuNY8AIwkD4JB5/I54ArWG7qngP5/Ydn7xbu2iZo= -github.com/RedHatInsights/insights-results-aggregator-data v1.1.0 h1:Xl6e52pP1C8KDxBiotS4pBfVcjtdil+NgpmFUlik0Fk= -github.com/RedHatInsights/insights-results-aggregator-data v1.1.0/go.mod h1:rbiccYJ8whbpLLvNGMiaFzyMPs1A/2/Jh0P2U9DXF+4= +github.com/RedHatInsights/insights-results-aggregator-data v1.1.2/go.mod h1:rbiccYJ8whbpLLvNGMiaFzyMPs1A/2/Jh0P2U9DXF+4= +github.com/RedHatInsights/insights-results-aggregator-data v1.3.0 h1:H8GB/DHckuttLU/5I1MHUVRebRJfEYS0nyvxZZnhfKo= +github.com/RedHatInsights/insights-results-aggregator-data v1.3.0/go.mod h1:Ylo2cWFmraBzkwKLew54kZSsUTgeVvFJdIi/oRkdxtc= github.com/RedHatInsights/kafka-zerolog v0.0.0-20210304172207-928f026dc7ec h1:/msFfckx6EIj0rZncrMUfNixFvsLbOiRIe4J0AurhDo= github.com/RedHatInsights/kafka-zerolog v0.0.0-20210304172207-928f026dc7ec/go.mod h1:HJul5oCsCRNiRlh/ayJDGdW3PzGlid/5aaQwJBn7was= github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= @@ -56,7 +57,6 @@ github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWX github.com/Shopify/sarama v1.26.0/go.mod h1:y/CFFTO9eaMTNriwu/Q+W4eioLqiDMGkA1W+gmdfj8w= github.com/Shopify/sarama v1.27.1 h1:iUlzHymqWsITyttu6KxazcAz8WEj5FqcwFK/oEi7rE8= github.com/Shopify/sarama v1.27.1/go.mod h1:g5s5osgELxgM+Md9Qni9rzo7Rbt+vvFQI4bt/Mc93II= -github.com/Shopify/toxiproxy v2.1.4+incompatible h1:TKdv8HiTLgE5wdJuEML90aBgNWsokNbMijUGhmcoBJc= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= @@ -93,7 +93,6 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y= github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= -github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/bsm/sarama-cluster v2.1.10+incompatible/go.mod h1:r7ao+4tTNXvWm+VRpRJchr2kQhqxgmAp2iEX5W96gMM= @@ -157,19 +156,15 @@ github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4/go.mod h1:T9YF2M40nIgbVgp3rreNmTged+9HrbNTIQf1PsaIiTA= -github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/frankban/quicktest v1.4.1/go.mod h1:36zfPVQyHxymz4cH7wlDmVwDrJuljRB60qkgn7rorfQ= -github.com/frankban/quicktest v1.10.2 h1:19ARM85nVi4xH7xPXuc5eM/udya5ieh7b/Sv+d844Tk= github.com/frankban/quicktest v1.10.2/go.mod h1:K+q6oSqb0W0Ininfk863uOk1lMy69l/P6txr3mVT54s= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fzipp/gocyclo v0.0.0-20150627053110-6acd4345c835/go.mod h1:BjL/N0+C+j9uNX+1xcNuM9vdSIcXCZrQZUYbXOFbgN8= -github.com/fzipp/gocyclo v0.3.1 h1:A9UeX3HJSXTBzvHzhqoYVuE0eAhe+aM8XBCCwsPMZOc= -github.com/fzipp/gocyclo v0.3.1/go.mod h1:DJHO6AUmbdqj2ET4Z9iArSuwWgYDRryYt2wASxc7x3E= github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= github.com/gchaincl/sqlhooks v1.3.0 h1:yKPXxW9a5CjXaVf2HkQn6wn7TZARvbAOAelr3H8vK2Y= github.com/gchaincl/sqlhooks v1.3.0/go.mod h1:9BypXnereMT0+Ys8WGWHqzgkkOfHIhyeUCqXC24ra34= @@ -183,7 +178,6 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= -github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= @@ -195,7 +189,6 @@ github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-yaml/yaml v2.1.0+incompatible h1:RYi2hDdss1u4YE7GwixGzWwVo47T8UQwnTLB6vQiq+o= @@ -241,7 +234,6 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -255,7 +247,6 @@ github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= @@ -330,7 +321,6 @@ github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= @@ -339,7 +329,6 @@ github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q= github.com/juju/loggo v0.0.0-20180524022052-584905176618/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U= @@ -367,11 +356,9 @@ github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g= github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= @@ -438,9 +425,7 @@ github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7 github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= -github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4= github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= @@ -475,11 +460,9 @@ github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi github.com/pierrec/lz4 v2.2.6+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pierrec/lz4 v2.5.2+incompatible h1:WCjObylUIOlKy/+7Abdn34TLIkXiA4UWUMhxq9m9ZXI= github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= @@ -550,9 +533,7 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= @@ -585,7 +566,6 @@ github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3 github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= @@ -616,9 +596,7 @@ github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPU github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= github.com/verdverm/frisby v0.0.0-20170604211311-b16556248a9a h1:Mt+KWT4h97wIDQahX1eD3OLkmc/fGbLy7EndiE85kMQ= github.com/verdverm/frisby v0.0.0-20170604211311-b16556248a9a/go.mod h1:Z+jvFzFlZ6eHAKMfi8PZZphUtg4S0gc2EZYOL9UnWgA= -github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c h1:u40Z8hqBAAQyv+vATcGgV0YCnDjqSL7/q/JyPhhJSPk= github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= -github.com/xdg/stringprep v1.0.0 h1:d9X0esnoa3dFsV0FG35rAT0RIhYFlPq7MiP+DW89La0= github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= @@ -803,7 +781,6 @@ golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapK golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= @@ -852,7 +829,6 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= @@ -869,7 +845,6 @@ gopkg.in/jcmturner/aescts.v1 v1.0.1 h1:cVVZBK2b1zY26haWB4vbBiZrfFQnfbTVrE3xZq6hr gopkg.in/jcmturner/aescts.v1 v1.0.1/go.mod h1:nsR8qBOg+OucoIW+WMhB3GspUQXq9XorLnQb9XtvcOo= gopkg.in/jcmturner/dnsutils.v1 v1.0.1 h1:cIuC1OLRGZrld+16ZJvvZxVJeKPsvd5eUIvxfoN5hSM= gopkg.in/jcmturner/dnsutils.v1 v1.0.1/go.mod h1:m3v+5svpVOhtFAP/wSz+yzh4Mc0Fg7eRhxkJMWSIz9Q= -gopkg.in/jcmturner/goidentity.v3 v3.0.0 h1:1duIyWiTaYvVx3YX2CYtpJbUFd7/UuPYCfgXtQ3VTbI= gopkg.in/jcmturner/goidentity.v3 v3.0.0/go.mod h1:oG2kH0IvSYNIu80dVAyu/yoefjq1mNfM5bm88whjWx4= gopkg.in/jcmturner/gokrb5.v7 v7.2.3/go.mod h1:l8VISx+WGYp+Fp7KRbsiUuXTTOnxIc3Tuvyavf11/WM= gopkg.in/jcmturner/gokrb5.v7 v7.5.0 h1:a9tsXlIDD9SKxotJMK3niV7rPZAJeX2aD/0yg3qlIrg= diff --git a/openapi.json b/openapi.json index 6998417d..92ae739c 100644 --- a/openapi.json +++ b/openapi.json @@ -1181,6 +1181,81 @@ ] } }, + "/recommendations/organizations/{org_id}/users/{user_id}/list": { + "post": { + "summary": "Returns a list of recommendations and a number of clusters each one is hitting.", + "operationId": "getRecommendationsPost", + "description": "Recommendations will be retrieved based on the list of cluster IDs that is part of request body.", + "parameters": [ + { + "name": "org_id", + "in": "path", + "description": "Organization ID represented as positive integer", + "required": true, + "schema": { + "type": "integer", + "format": "int64", + "minimum": 0 + } + }, + { + "name": "user_id", + "in": "path", + "required": true, + "description": "Numeric ID of the user. An example: `42`", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "List of cluster IDs. An example: `34c3ecc5-624a-49a5-bab8-4fdc5e51a266.", + "required": true, + "content": { + "application/json": { + "schema": { + } + } + } + }, + "responses": { + "200": { + "description": "List of recommendations and a number of clusters each one is hitting", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "recommendations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "rule_id": { + "type": "string", + "description": "The rule ID in the | format.", + "example": "rule.module|ERROR_KEY" + }, + "impacted_clusters_cnt": { + "type": "int", + "description": "The number of clusters impacted by this rule.", + "example": 42 + } + } + } + }, + "status": { + "type": "string", + "example": "ok" + } + } + } + } + } + } + } + } + }, "/rules/users/{userId}/disabled": { "get": { "summary": "Returns a list of rules disabled from current account", diff --git a/server/endpoints.go b/server/endpoints.go index 392c9e1b..28b50f20 100644 --- a/server/endpoints.go +++ b/server/endpoints.go @@ -77,6 +77,9 @@ const ( // ListOfDisabledRulesSystemWide returns a list of rules disabled from current account ListOfDisabledRulesSystemWide = "rules/organizations/{org_id}/users/{user_id}/disabled_system_wide" + // RecommendationsListEndpoint receives a list of clusters in POST body and returns a list of all recommendations hitting for them + RecommendationsListEndpoint = "recommendations/organizations/{org_id}/users/{user_id}/list" + // Rating accepts a list of ratings in the request body and store them in the database for the given user Rating = "rules/organizations/{org_id}/users/{user_id}/rating" // MetricsEndpoint returns prometheus metrics @@ -121,12 +124,11 @@ func (server *HTTPServer) addEndpointsToRouter(router *mux.Router) { router.HandleFunc(apiPrefix+ListOfDisabledRulesFeedback, server.listOfReasons).Methods(http.MethodGet) router.HandleFunc(apiPrefix+Rating, server.setRuleRating).Methods(http.MethodPost) - // Endpoints to handle rules to be enabled, disabled, updated, and queried system-wide - router.HandleFunc(apiPrefix+EnableRuleSystemWide, server.enableRuleSystemWide).Methods(http.MethodPut, http.MethodOptions) - router.HandleFunc(apiPrefix+DisableRuleSystemWide, server.disableRuleSystemWide).Methods(http.MethodPut, http.MethodOptions) - router.HandleFunc(apiPrefix+UpdateRuleSystemWide, server.updateRuleSystemWide).Methods(http.MethodPost, http.MethodOptions) - router.HandleFunc(apiPrefix+ReadRuleSystemWide, server.readRuleSystemWide).Methods(http.MethodGet) - router.HandleFunc(apiPrefix+ListOfDisabledRulesSystemWide, server.listOfDisabledRulesSystemWide).Methods(http.MethodGet) + // Rule Enable/Disable/etc endpoints + server.addRuleEnableDisableEndpointsToRouter(router, apiPrefix) + + // Endpoints related to the Insights Advisor application + server.addInsightsAdvisorEndpointsToRouter(router, apiPrefix) // Prometheus metrics router.Handle(apiPrefix+MetricsEndpoint, promhttp.Handler()).Methods(http.MethodGet) @@ -137,3 +139,19 @@ func (server *HTTPServer) addEndpointsToRouter(router *mux.Router) { httputils.CreateOpenAPIHandler(server.Config.APISpecFile, server.Config.Debug, true), ).Methods(http.MethodGet) } + +// addRuleEnableDisableEndpointsToRouter method registers handlers for endpoints that +// allow for rules to be enabled, disabled, updated, and queried system-wide +func (server *HTTPServer) addRuleEnableDisableEndpointsToRouter(router *mux.Router, apiPrefix string) { + router.HandleFunc(apiPrefix+EnableRuleSystemWide, server.enableRuleSystemWide).Methods(http.MethodPut, http.MethodOptions) + router.HandleFunc(apiPrefix+DisableRuleSystemWide, server.disableRuleSystemWide).Methods(http.MethodPut, http.MethodOptions) + router.HandleFunc(apiPrefix+UpdateRuleSystemWide, server.updateRuleSystemWide).Methods(http.MethodPost, http.MethodOptions) + router.HandleFunc(apiPrefix+ReadRuleSystemWide, server.readRuleSystemWide).Methods(http.MethodGet) + router.HandleFunc(apiPrefix+ListOfDisabledRulesSystemWide, server.listOfDisabledRulesSystemWide).Methods(http.MethodGet) +} + +// addRuleEnableDisableEndpointsToRouter method registers handlers for endpoints that +// are related to the Insights Advisor application +func (server *HTTPServer) addInsightsAdvisorEndpointsToRouter(router *mux.Router, apiPrefix string) { + router.HandleFunc(apiPrefix+RecommendationsListEndpoint, server.getRecommendations).Methods(http.MethodPost, http.MethodOptions) +} diff --git a/server/reports.go b/server/reports.go index e5d4fcf6..4986a30a 100644 --- a/server/reports.go +++ b/server/reports.go @@ -36,6 +36,9 @@ const ( // OkStatusPayload is the text returned as body payload when an OK Status request is sent OkStatusPayload = "ok" + + // orgIDStr used in log messages + orgIDStr = "orgID" ) // validateClusterID function checks if the cluster ID is a valid UUID. @@ -200,7 +203,7 @@ func (server *HTTPServer) reportForListOfClusters(writer http.ResponseWriter, re // wrong state has been handled already return } - log.Info().Int("orgID", int(orgID)).Msg("reportForListOfClusters") + log.Info().Int(orgIDStr, int(orgID)).Msg("reportForListOfClusters") // try to read list of cluster IDs listOfClusters, successful := readClusterListFromPath(writer, request) @@ -224,7 +227,7 @@ func (server *HTTPServer) reportForListOfClustersPayload(writer http.ResponseWri // wrong state has been handled already return } - log.Info().Int("orgID", int(orgID)).Msg("reportForListOfClustersPayload") + log.Info().Int(orgIDStr, int(orgID)).Msg("reportForListOfClustersPayload") // try to read list of cluster IDs listOfClusters, successful := readClusterListFromBody(writer, request) @@ -236,3 +239,42 @@ func (server *HTTPServer) reportForListOfClustersPayload(writer http.ResponseWri // we were able to read the cluster IDs, let's process them processListOfClusters(server, writer, request, orgID, listOfClusters) } + +// getRecommendations retrieves all recommendations hitting for all clusters in the org +func (server *HTTPServer) getRecommendations(writer http.ResponseWriter, request *http.Request) { + // Extract user_id from URL + userID, ok := readUserID(writer, request) + if !ok { + // everything has been handled + return + } + log.Info().Str("userID", string(userID)).Msg("getRecommendations") + + // extract org_id from URL + orgID, ok := readOrgID(writer, request) + if !ok { + // everything has been handled + return + } + log.Info().Int(orgIDStr, int(orgID)).Msg("getRecommendations") + + var listOfClusters []types.ClusterName + err := json.NewDecoder(request.Body).Decode(&listOfClusters) + if err != nil { + handleServerError(writer, err) + return + } + log.Info().Msgf("getRecommendations list of clusters: %v", listOfClusters) + + recommendations, err := server.Storage.ReadRecommendationsForClusters(listOfClusters) + if err != nil { + log.Error().Err(err).Msg("Errors retrieving recommendations") + handleServerError(writer, err) + return + } + + err = responses.SendOK(writer, responses.BuildOkResponseWithData("recommendations", recommendations)) + if err != nil { + log.Error().Err(err).Msg(responseDataError) + } +} diff --git a/server/server_test.go b/server/server_test.go index 9a3509b4..56a38240 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -20,6 +20,7 @@ import ( "bytes" "context" "database/sql" + "encoding/json" "fmt" "io/ioutil" "net/http" @@ -1099,3 +1100,118 @@ func TestHTTPServer_ListOfDisabledRulesSystemWide(t *testing.T) { Body: `{"disabledRules":[],"status":"ok"}`, }) } + +func TestHTTPServer_RecommendationsListEndpoint_NoRecommendations(t *testing.T) { + mockStorage, closer := helpers.MustGetMockStorage(t, true) + defer closer() + + err := mockStorage.WriteRecommendationsForCluster( + testdata.OrgID, testdata.ClusterName, testdata.Report0Rules, + ) + helpers.FailOnError(t, err) + + clusterList := []types.ClusterName{testdata.GetRandomClusterID()} + reqBody, _ := json.Marshal(clusterList) + + helpers.AssertAPIRequest(t, mockStorage, nil, &helpers.APIRequest{ + Method: http.MethodPost, + Endpoint: server.RecommendationsListEndpoint, + EndpointArgs: []interface{}{testdata.OrgID, testdata.UserID}, + Body: reqBody, + }, &helpers.APIResponse{ + StatusCode: http.StatusOK, + Body: `{"recommendations":{},"status":"ok"}`, + }) +} + +func TestHTTPServer_RecommendationsListEndpoint_DifferentClusters(t *testing.T) { + mockStorage, closer := helpers.MustGetMockStorage(t, true) + defer closer() + + err := mockStorage.WriteRecommendationsForCluster( + testdata.OrgID, testdata.ClusterName, testdata.Report3Rules, + ) + helpers.FailOnError(t, err) + + clusterList := []types.ClusterName{testdata.GetRandomClusterID(), testdata.GetRandomClusterID()} + reqBody, _ := json.Marshal(clusterList) + + helpers.AssertAPIRequest(t, mockStorage, nil, &helpers.APIRequest{ + Method: http.MethodPost, + Endpoint: server.RecommendationsListEndpoint, + EndpointArgs: []interface{}{testdata.OrgID, testdata.UserID}, + Body: reqBody, + }, &helpers.APIResponse{ + StatusCode: http.StatusOK, + Body: `{"recommendations":{},"status":"ok"}`, + }) +} + +func TestHTTPServer_RecommendationsListEndpoint_3Recs1Cluster(t *testing.T) { + mockStorage, closer := helpers.MustGetMockStorage(t, true) + defer closer() + + err := mockStorage.WriteRecommendationsForCluster( + testdata.OrgID, testdata.ClusterName, testdata.Report3Rules, + ) + helpers.FailOnError(t, err) + + clusterList := []types.ClusterName{testdata.ClusterName} + reqBody, _ := json.Marshal(clusterList) + + respBody := `{"recommendations":{"%v":%v,"%v":%v,"%v":%v},"status":"ok"}` + respBody = fmt.Sprintf(respBody, + testdata.Rule1CompositeID, 1, + testdata.Rule2CompositeID, 1, + testdata.Rule3CompositeID, 1, + ) + + helpers.AssertAPIRequest(t, mockStorage, nil, &helpers.APIRequest{ + Method: http.MethodPost, + Endpoint: server.RecommendationsListEndpoint, + EndpointArgs: []interface{}{testdata.OrgID, testdata.UserID}, + Body: reqBody, + }, &helpers.APIResponse{ + StatusCode: http.StatusOK, + Body: respBody, + }) +} + +func TestHTTPServer_RecommendationsListEndpoint_3Recs2Clusters(t *testing.T) { + mockStorage, closer := helpers.MustGetMockStorage(t, true) + defer closer() + + clusterList := make([]types.ClusterName, 2) + for i := range clusterList { + clusterList[i] = testdata.GetRandomClusterID() + } + + err := mockStorage.WriteRecommendationsForCluster( + testdata.OrgID, clusterList[0], testdata.Report2Rules, + ) + helpers.FailOnError(t, err) + + err = mockStorage.WriteRecommendationsForCluster( + testdata.OrgID, clusterList[1], testdata.Report3Rules, + ) + helpers.FailOnError(t, err) + + reqBody, _ := json.Marshal(clusterList) + + respBody := `{"recommendations":{"%v":%v,"%v":%v,"%v":%v},"status":"ok"}` + respBody = fmt.Sprintf(respBody, + testdata.Rule1CompositeID, 2, + testdata.Rule2CompositeID, 2, + testdata.Rule3CompositeID, 1, + ) + + helpers.AssertAPIRequest(t, mockStorage, nil, &helpers.APIRequest{ + Method: http.MethodPost, + Endpoint: server.RecommendationsListEndpoint, + EndpointArgs: []interface{}{testdata.OrgID, testdata.UserID}, + Body: reqBody, + }, &helpers.APIResponse{ + StatusCode: http.StatusOK, + Body: respBody, + }) +} diff --git a/storage/noop_storage.go b/storage/noop_storage.go index 7c5ef738..f161610a 100644 --- a/storage/noop_storage.go +++ b/storage/noop_storage.go @@ -300,3 +300,10 @@ func (*NoopStorage) ListOfSystemWideDisabledRules( orgID types.OrgID, userID types.UserID) ([]utypes.SystemWideRuleDisable, error) { return nil, nil } + +// ReadRecommendationsForClusters reads all recommendations from recommendation table for given organization +func (*NoopStorage) ReadRecommendationsForClusters( + clusterList []types.ClusterName, +) (utypes.RecommendationImpactedClusters, error) { + return nil, nil +} diff --git a/storage/storage.go b/storage/storage.go index 6f6bf15b..f1a95acf 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -175,6 +175,7 @@ type Storage interface { ruleID types.RuleID, errorKey types.ErrorKey) (utypes.SystemWideRuleDisable, bool, error) ListOfSystemWideDisabledRules( orgID types.OrgID, userID types.UserID) ([]utypes.SystemWideRuleDisable, error) + ReadRecommendationsForClusters([]types.ClusterName) (utypes.RecommendationImpactedClusters, error) } // DBStorage is an implementation of Storage interface that use selected SQL like database @@ -928,6 +929,58 @@ func finishTransaction(tx *sql.Tx, err error) { } } +// ReadRecommendationsForClusters reads all recommendations from recommendation table for given organization +func (storage DBStorage) ReadRecommendationsForClusters( + clusterList []types.ClusterName, +) (utypes.RecommendationImpactedClusters, error) { + + recommendationsMap := make(utypes.RecommendationImpactedClusters, 0) + + // prepare arguments + args := argsWithClusterNames(clusterList) + + // construct the `in` clausule in SQL query statement + inClausule := constructInClausule(len(clusterList)) + + // disable "G202 (CWE-89): SQL string concatenation" + // #nosec G202 + query := ` + SELECT + rule_id, count(*) as impacted_clusters_c + FROM + recommendation + WHERE + cluster_id in (` + inClausule + `) + GROUP BY + rule_id + ` + rows, err := storage.connection.Query(query, args...) + if err != nil { + log.Error().Err(err).Msg("query to get recommendations") + return recommendationsMap, err + } + + for rows.Next() { + var ( + ruleID types.RuleID + impactedCnt utypes.ImpactedClustersCnt + ) + + err := rows.Scan( + &ruleID, + &impactedCnt, + ) + if err != nil { + log.Error().Err(err).Msg("read one recommendation") + return recommendationsMap, err + } + + recommendationsMap[ruleID] = impactedCnt + } + + return recommendationsMap, nil +} + // ReportsCount reads number of all records stored in database func (storage DBStorage) ReportsCount() (int, error) { count := -1 diff --git a/storage/storage_test.go b/storage/storage_test.go index 48c5e272..d7041a14 100644 --- a/storage/storage_test.go +++ b/storage/storage_test.go @@ -28,6 +28,7 @@ import ( "github.com/DATA-DOG/go-sqlmock" "github.com/RedHatInsights/insights-operator-utils/tests/helpers" + utypes "github.com/RedHatInsights/insights-operator-utils/types" "github.com/RedHatInsights/insights-results-aggregator-data/testdata" "github.com/Shopify/sarama" "github.com/rs/zerolog" @@ -1162,3 +1163,141 @@ func TestDBStorageInsertRecommendationsNoRuleHit(t *testing.T) { assert.Equal(t, 0, inserted) helpers.FailOnError(t, err) } + +// TestDBStorageReadRecommendationsForClusters checks that stored recommendations +// are retrieved correctly +func TestDBStorageReadRecommendationsForClusters(t *testing.T) { + mockStorage, closer := ira_helpers.MustGetMockStorage(t, true) + defer closer() + + err := mockStorage.WriteRecommendationsForCluster( + testdata.OrgID, testdata.ClusterName, testdata.Report3Rules, + ) + helpers.FailOnError(t, err) + + expectingImpactedC := utypes.ImpactedClustersCnt(1) + expect := utypes.RecommendationImpactedClusters{ + testdata.Rule1CompositeID: expectingImpactedC, + testdata.Rule2CompositeID: expectingImpactedC, + testdata.Rule3CompositeID: expectingImpactedC, + } + + res, err := mockStorage.ReadRecommendationsForClusters([]types.ClusterName{testdata.ClusterName}) + helpers.FailOnError(t, err) + + assert.Equal(t, expect, res) +} + +// TestDBStorageReadRecommendationsForClustersMoreClusters checks that stored recommendations +// for multiplpe clusters is calculated correctly +func TestDBStorageReadRecommendationsForClustersMoreClusters(t *testing.T) { + mockStorage, closer := ira_helpers.MustGetMockStorage(t, true) + defer closer() + + clusterList := make([]types.ClusterName, 3) + for i := range clusterList { + clusterList[i] = testdata.GetRandomClusterID() + } + + // cluster 0 has 0 rules == should not affect the results + err := mockStorage.WriteRecommendationsForCluster( + testdata.OrgID, clusterList[0], testdata.Report0Rules, + ) + helpers.FailOnError(t, err) + + // cluster 1 has rule1 and rule2 + err = mockStorage.WriteRecommendationsForCluster( + testdata.OrgID, clusterList[1], testdata.Report2Rules, + ) + helpers.FailOnError(t, err) + + // cluster 2 has rule1 and rule2 and rule3 + err = mockStorage.WriteRecommendationsForCluster( + testdata.OrgID, clusterList[2], testdata.Report3Rules, + ) + helpers.FailOnError(t, err) + + expect2Impacted := utypes.ImpactedClustersCnt(2) + expect := utypes.RecommendationImpactedClusters{ + testdata.Rule1CompositeID: expect2Impacted, + testdata.Rule2CompositeID: expect2Impacted, + testdata.Rule3CompositeID: utypes.ImpactedClustersCnt(1), + } + + res, err := mockStorage.ReadRecommendationsForClusters(clusterList) + helpers.FailOnError(t, err) + + assert.Equal(t, expect, res) +} + +// TestDBStorageReadRecommendationsForClustersNoRecommendations checks that when no recommendations +// are stored, it is an OK state +func TestDBStorageReadRecommendationsForClustersNoRecommendations(t *testing.T) { + mockStorage, closer := ira_helpers.MustGetMockStorage(t, true) + defer closer() + + err := mockStorage.WriteRecommendationsForCluster( + testdata.OrgID, testdata.ClusterName, testdata.ClusterReportEmpty, + ) + helpers.FailOnError(t, err) + + expect := utypes.RecommendationImpactedClusters{} + + res, err := mockStorage.ReadRecommendationsForClusters([]types.ClusterName{testdata.ClusterName}) + helpers.FailOnError(t, err) + + assert.Equal(t, expect, res) +} + +// TestDBStorageReadRecommendationsGetSelectedClusters loads several recommendations for the same org +// but "simulates" a situation where we only get a subset of them from the AMS API +func TestDBStorageReadRecommendationsGetSelectedClusters(t *testing.T) { + mockStorage, closer := ira_helpers.MustGetMockStorage(t, true) + defer closer() + + clusterList := make([]types.ClusterName, 3) + for i := range clusterList { + randomClusterID := testdata.GetRandomClusterID() + + clusterList[i] = randomClusterID + + err := mockStorage.WriteRecommendationsForCluster( + testdata.OrgID, randomClusterID, testdata.Report3Rules, + ) + helpers.FailOnError(t, err) + } + + // we only retrieve one cluster + res, err := mockStorage.ReadRecommendationsForClusters([]types.ClusterName{clusterList[0]}) + helpers.FailOnError(t, err) + + expect1Impacted := utypes.ImpactedClustersCnt(1) + expect := utypes.RecommendationImpactedClusters{ + testdata.Rule1CompositeID: expect1Impacted, + testdata.Rule2CompositeID: expect1Impacted, + testdata.Rule3CompositeID: expect1Impacted, + } + + assert.Equal(t, expect, res) +} + +// TestDBStorageReadRecommendationsForNonexistingClusters simulates getting a list of clusters where +// we have none in the DB +func TestDBStorageReadRecommendationsForNonexistingClusters(t *testing.T) { + mockStorage, closer := ira_helpers.MustGetMockStorage(t, true) + defer closer() + + err := mockStorage.WriteRecommendationsForCluster( + testdata.OrgID, testdata.ClusterName, testdata.Report3Rules, + ) + helpers.FailOnError(t, err) + + clusterList := make([]types.ClusterName, 3) + for i := range clusterList { + clusterList[i] = testdata.GetRandomClusterID() + } + res, err := mockStorage.ReadRecommendationsForClusters(clusterList) + helpers.FailOnError(t, err) + + assert.Equal(t, utypes.RecommendationImpactedClusters{}, res) +}