diff --git a/go.mod b/go.mod index 9e0d22a8f6e..d0e62735554 100644 --- a/go.mod +++ b/go.mod @@ -29,13 +29,7 @@ require ( github.com/andybalholm/brotli v1.1.1 // @grafana/partner-datasources github.com/apache/arrow-go/v18 v18.0.1-0.20241212180703-82be143d7c30 // @grafana/plugins-platform-backend github.com/armon/go-radix v1.0.0 // @grafana/grafana-app-platform-squad - github.com/aws/aws-sdk-go v1.55.6 // @grafana/aws-datasources - github.com/aws/aws-sdk-go-v2 v1.36.1 // @grafana/aws-datasources - github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.43.9 // @grafana/aws-datasources - github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.45.7 // @grafana/aws-datasources - github.com/aws/aws-sdk-go-v2/service/ec2 v1.200.0 // @grafana/aws-datasources - github.com/aws/aws-sdk-go-v2/service/oam v1.15.13 // @grafana/aws-datasources - github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi v1.25.13 // @grafana/aws-datasources + github.com/aws/aws-sdk-go v1.55.5 // @grafana/aws-datasources github.com/beevik/etree v1.4.1 // @grafana/grafana-backend-group github.com/benbjohnson/clock v1.3.5 // @grafana/alerting-backend github.com/blang/semver/v4 v4.0.0 // indirect; @grafana/grafana-developer-enablement-squad @@ -91,7 +85,7 @@ require ( github.com/grafana/grafana-api-golang-client v0.27.0 // @grafana/alerting-backend github.com/grafana/grafana-app-sdk v0.31.0 // @grafana/grafana-app-platform-squad github.com/grafana/grafana-app-sdk/logging v0.30.0 // @grafana/grafana-app-platform-squad - github.com/grafana/grafana-aws-sdk v0.33.0 // @grafana/aws-datasources + github.com/grafana/grafana-aws-sdk v0.31.5 // @grafana/aws-datasources github.com/grafana/grafana-azure-sdk-go/v2 v2.1.6 // @grafana/partner-datasources github.com/grafana/grafana-cloud-migration-snapshot v1.6.0 // @grafana/grafana-operator-experience-squad github.com/grafana/grafana-google-sdk-go v0.2.1 // @grafana/partner-datasources @@ -227,8 +221,6 @@ require ( github.com/grafana/grafana/pkg/storage/unified/resource v0.0.0-20250317130411-3f270d1de043 // @grafana/grafana-search-and-storage ) -require github.com/aws/smithy-go v1.22.2 // @grafana/aws-datasources - require ( cel.dev/expr v0.19.1 // indirect cloud.google.com/go v0.118.2 // indirect @@ -272,23 +264,25 @@ require ( github.com/armon/go-metrics v0.4.1 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/at-wat/mqtt-go v0.19.4 // indirect - github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 // indirect - github.com/aws/aws-sdk-go-v2/config v1.29.4 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.17.57 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.27 // indirect + github.com/aws/aws-sdk-go-v2 v1.30.3 // indirect + github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 // indirect + github.com/aws/aws-sdk-go-v2/config v1.27.27 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.17.27 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 // indirect github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.10 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.31 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.31 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 // indirect github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.17 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.12 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 // indirect github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15 // indirect github.com/aws/aws-sdk-go-v2/service/s3 v1.58.3 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.24.14 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.13 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.33.12 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 // indirect + github.com/aws/smithy-go v1.20.3 // indirect github.com/axiomhq/hyperloglog v0.0.0-20240507144631-af9851f82b27 // indirect github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/beorn7/perks v1.0.1 // indirect @@ -378,7 +372,7 @@ require ( github.com/grafana/jsonparser v0.0.0-20240425183733-ea80629e1a32 // indirect github.com/grafana/loki/pkg/push v0.0.0-20231124142027-e52380921608 // indirect github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect - github.com/grafana/sqlds/v4 v4.1.7 // indirect + github.com/grafana/sqlds/v4 v4.1.3 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // @grafana/grafana-search-and-storage github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a5340 // indirect github.com/hashicorp/consul/api v1.30.0 // indirect diff --git a/go.sum b/go.sum index 27e7aab477f..96df6362cc9 100644 --- a/go.sum +++ b/go.sum @@ -839,56 +839,46 @@ github.com/aws/aws-sdk-go v1.17.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN github.com/aws/aws-sdk-go v1.22.4/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/aws/aws-sdk-go v1.50.29/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= -github.com/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk= -github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= -github.com/aws/aws-sdk-go-v2 v1.36.1 h1:iTDl5U6oAhkNPba0e1t1hrwAo02ZMqbrGq4k5JBWM5E= -github.com/aws/aws-sdk-go-v2 v1.36.1/go.mod h1:5PMILGVKiW32oDzjj6RU52yrNrDPUHcbZQYr1sM7qmM= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 h1:lL7IfaFzngfx0ZwUGOZdsFFnQ5uLvR0hWqqhyE7Q9M8= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7/go.mod h1:QraP0UcVlQJsmHfioCrveWOC1nbiWUl3ej08h4mXWoc= -github.com/aws/aws-sdk-go-v2/config v1.29.4 h1:ObNqKsDYFGr2WxnoXKOhCvTlf3HhwtoGgc+KmZ4H5yg= -github.com/aws/aws-sdk-go-v2/config v1.29.4/go.mod h1:j2/AF7j/qxVmsNIChw1tWfsVKOayJoGRDjg1Tgq7NPk= -github.com/aws/aws-sdk-go-v2/credentials v1.17.57 h1:kFQDsbdBAR3GZsB8xA+51ptEnq9TIj3tS4MuP5b+TcQ= -github.com/aws/aws-sdk-go-v2/credentials v1.17.57/go.mod h1:2kerxPUUbTagAr/kkaHiqvj/bcYHzi2qiJS/ZinllU0= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.27 h1:7lOW8NUwE9UZekS1DYoiPdVAqZ6A+LheHWb+mHbNOq8= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.27/go.mod h1:w1BASFIPOPUae7AgaH4SbjNbfdkxuggLyGfNFTn8ITY= +github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= +github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= +github.com/aws/aws-sdk-go-v2 v1.30.3 h1:jUeBtG0Ih+ZIFH0F4UkmL9w3cSpaMv9tYYDbzILP8dY= +github.com/aws/aws-sdk-go-v2 v1.30.3/go.mod h1:nIQjQVp5sfpQcTc9mPSr1B0PaWK5ByX9MOoDadSN4lc= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 h1:tW1/Rkad38LA15X4UQtjXZXNKsCgkshC3EbmcUmghTg= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3/go.mod h1:UbnqO+zjqk3uIt9yCACHJ9IVNhyhOCnYk8yA19SAWrM= +github.com/aws/aws-sdk-go-v2/config v1.27.27 h1:HdqgGt1OAP0HkEDDShEl0oSYa9ZZBSOmKpdpsDMdO90= +github.com/aws/aws-sdk-go-v2/config v1.27.27/go.mod h1:MVYamCg76dFNINkZFu4n4RjDixhVr51HLj4ErWzrVwg= +github.com/aws/aws-sdk-go-v2/credentials v1.17.27 h1:2raNba6gr2IfA0eqqiP2XiQ0UVOpGPgDSi0I9iAP+UI= +github.com/aws/aws-sdk-go-v2/credentials v1.17.27/go.mod h1:gniiwbGahQByxan6YjQUMcW4Aov6bLC3m+evgcoN4r4= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 h1:KreluoV8FZDEtI6Co2xuNk/UqI9iwMrOx/87PBNIKqw= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11/go.mod h1:SeSUYBLsMYFoRvHE0Tjvn7kbxaUhl75CJi1sbfhMxkU= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.10 h1:zeN9UtUlA6FTx0vFSayxSX32HDw73Yb6Hh2izDSFxXY= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.10/go.mod h1:3HKuexPDcwLWPaqpW2UR/9n8N/u/3CKcGAzSs8p8u8g= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.31 h1:lWm9ucLSRFiI4dQQafLrEOmEDGry3Swrz0BIRdiHJqQ= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.31/go.mod h1:Huu6GG0YTfbPphQkDSo4dEGmQRTKb9k9G7RdtyQWxuI= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.31 h1:ACxDklUKKXb48+eg5ROZXi1vDgfMyfIA/WyvqHcHI0o= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.31/go.mod h1:yadnfsDwqXeVaohbGc/RaD287PuyRw2wugkh5ZL2J6k= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2 h1:Pg9URiobXy85kgFev3og2CuOZ8JZUBENF+dcgWBaYNk= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 h1:SoNJ4RlFEQEbtDcCEt+QG56MY4fm4W8rYirAmq+/DdU= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15/go.mod h1:U9ke74k1n2bf+RIgoX1SXFed1HLs51OgUSs+Ph0KJP8= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 h1:C6WHdGnTDIYETAm5iErQUiVNsclNx9qbJVPIt03B6bI= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15/go.mod h1:ZQLZqhcu+JhSrA9/NXRm8SkDvsycE+JkV3WGY41e+IM= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY= github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15 h1:Z5r7SycxmSllHYmaAZPpmN8GviDrSGhMS6bldqtXZPw= github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15/go.mod h1:CetW7bDE00QoGEmPUoZuRog07SGVAUVW6LFpNP0YfIg= -github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.43.9 h1:bfHEPSWRqKAUp9ugaYDo6bYmCwYGhpGlcSYbnjpZ4lQ= -github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.43.9/go.mod h1:w0Sa1DOIjqTBXmwYFk1r+i6Xtkeq21JGjUGe/NCqBHs= -github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.45.7 h1:DddWiL/XVT9GjMZqbYoIpJm5fFa08/CSk7fPN5neWVY= -github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.45.7/go.mod h1:zZeYjS1D+qvIOiDrCT89Rrm6vSn4m8DNhi0kb3wwzYM= -github.com/aws/aws-sdk-go-v2/service/ec2 v1.200.0 h1:3hH6o7Z2WeE1twvz44Aitn6Qz8DZN3Dh5IB4Eh2xq7s= -github.com/aws/aws-sdk-go-v2/service/ec2 v1.200.0/go.mod h1:I76S7jN0nfsYTBtuTgTsJtK2Q8yJVDgrLr5eLN64wMA= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2 h1:D4oz8/CzT9bAEYtVhSBmFj2dNOtaHOtMKc2vHBwYizA= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2/go.mod h1:Za3IHqTQ+yNcRHxu1OFucBh0ACZT4j4VQFF0BqpZcLY= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 h1:dT3MqvGhSoaIhRseqw2I0yH81l7wiR2vjs57O51EAm8= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3/go.mod h1:GlAeCkHwugxdHaueRr4nhPuY+WW+gR8UjlcqzPr1SPI= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.17 h1:YPYe6ZmvUfDDDELqEKtAd6bo8zxhkm+XEFEzQisqUIE= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.17/go.mod h1:oBtcnYua/CgzCWYN7NZ5j7PotFDaFSUjCYVTtfyn7vw= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.12 h1:O+8vD2rGjfihBewr5bT+QUfYUHIxCVgG61LHoT59shM= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.12/go.mod h1:usVdWJaosa66NMvmCrr08NcWDBRv4E6+YFG2pUdw1Lk= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 h1:HGErhhrxZlQ044RiM+WdoZxp0p+EGM62y3L6pwA4olE= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17/go.mod h1:RkZEx4l0EHYDJpWppMJ3nD9wZJAa8/0lq9aVC+r2UII= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15 h1:246A4lSTXWJw/rmlQI+TT2OcqeDMKBdyjEQrafMaQdA= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15/go.mod h1:haVfg3761/WF7YPuJOER2MP0k4UAXyHaLclKXB6usDg= -github.com/aws/aws-sdk-go-v2/service/oam v1.15.13 h1:4IrWw0hu8HEdo+6htGgzrjiiTJeyreyofj1SEZDb5qc= -github.com/aws/aws-sdk-go-v2/service/oam v1.15.13/go.mod h1:nUyUC5TvB8cwPPZjwCkJ6+NsT6TnJCEiLreXDdRQchU= -github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi v1.25.13 h1:wdMzCMpoSKRYp4vtciAxPzjJy7wSEQsl0pkvlAJQ+Xo= -github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi v1.25.13/go.mod h1:jhfb2oFQrEqsl6AqYkFlhz1kUys4AWXaFzfA1BCzYWY= github.com/aws/aws-sdk-go-v2/service/s3 v1.58.3 h1:hT8ZAZRIfqBqHbzKTII+CIiY8G2oC9OpLedkZ51DWl8= github.com/aws/aws-sdk-go-v2/service/s3 v1.58.3/go.mod h1:Lcxzg5rojyVPU/0eFwLtcyTaek/6Mtic5B1gJo7e/zE= -github.com/aws/aws-sdk-go-v2/service/sso v1.24.14 h1:c5WJ3iHz7rLIgArznb3JCSQT3uUMiz9DLZhIX+1G8ok= -github.com/aws/aws-sdk-go-v2/service/sso v1.24.14/go.mod h1:+JJQTxB6N4niArC14YNtxcQtwEqzS3o9Z32n7q33Rfs= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.13 h1:f1L/JtUkVODD+k1+IiSJUUv8A++2qVr+Xvb3xWXETMU= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.13/go.mod h1:tvqlFoja8/s0o+UruA1Nrezo/df0PzdunMDDurUfg6U= -github.com/aws/aws-sdk-go-v2/service/sts v1.33.12 h1:fqg6c1KVrc3SYWma/egWue5rKI4G2+M4wMQN2JosNAA= -github.com/aws/aws-sdk-go-v2/service/sts v1.33.12/go.mod h1:7Yn+p66q/jt38qMoVfNvjbm3D89mGBnkwDcijgtih8w= -github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ= -github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= +github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 h1:BXx0ZIxvrJdSgSvKTZ+yRBeSqqgPM89VPlulEcl37tM= +github.com/aws/aws-sdk-go-v2/service/sso v1.22.4/go.mod h1:ooyCOXjvJEsUw7x+ZDHeISPMhtwI3ZCB7ggFMcFfWLU= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 h1:yiwVzJW2ZxZTurVbYWA7QOrAaCYQR72t0wrSBfoesUE= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4/go.mod h1:0oxfLkpz3rQ/CHlx5hB7H69YUpFiI1tql6Q6Ne+1bCw= +github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 h1:ZsDKRLXGWHk8WdtyYMoGNO7bTudrvuKpDKgMVRlepGE= +github.com/aws/aws-sdk-go-v2/service/sts v1.30.3/go.mod h1:zwySh8fpFyXp9yOr/KVzxOl8SRqgf/IDw5aUt9UKFcQ= +github.com/aws/smithy-go v1.20.3 h1:ryHwveWzPV5BIof6fyDvor6V3iUL7nTfiTKXHiW05nE= +github.com/aws/smithy-go v1.20.3/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= github.com/axiomhq/hyperloglog v0.0.0-20191112132149-a4c4c47bc57f/go.mod h1:2stgcRjl6QmW+gU2h5E7BQXg4HU0gzxKWDuT5HviN9s= github.com/axiomhq/hyperloglog v0.0.0-20240507144631-af9851f82b27 h1:60m4tnanN1ctzIu4V3bfCNJ39BiOPSm1gHFlFjTkRE0= github.com/axiomhq/hyperloglog v0.0.0-20240507144631-af9851f82b27/go.mod h1:k08r+Yj1PRAmuayFiRK6MYuR5Ve4IuZtTfxErMIh0+c= @@ -1579,8 +1569,8 @@ github.com/grafana/grafana-app-sdk v0.31.0 h1:/mFCcx+YqG8cWAi9hePDJQxIdtXDClDIDR github.com/grafana/grafana-app-sdk v0.31.0/go.mod h1:Xw00NL7qpRLo5r3Gn48Bl1Xn2n4eUDI5pYf/wMufKWs= github.com/grafana/grafana-app-sdk/logging v0.30.0 h1:K/P/bm7Cp7Di4tqIJ3EQz2+842JozQGRaz62r95ApME= github.com/grafana/grafana-app-sdk/logging v0.30.0/go.mod h1:xy6ZyVXl50Z3DBDLybvBPphbykPhuVNed/VNmen9DQM= -github.com/grafana/grafana-aws-sdk v0.33.0 h1:mhuu9xyHvFgRJLXkahm9fHmS5PoNdzqIGGs+/u6NKNQ= -github.com/grafana/grafana-aws-sdk v0.33.0/go.mod h1:wjp8cLGt8NvrtZinUefObnn2BwpwXQ+aArT3S/RqW+4= +github.com/grafana/grafana-aws-sdk v0.31.5 h1:4HpMQx7n4Qqoi7Bgu8KHQ2QKT9fYYdHilX/Gh3FZKBE= +github.com/grafana/grafana-aws-sdk v0.31.5/go.mod h1:5p4Cjyr5ZiR6/RT2nFWkJ8XpIKgX4lAUmUMu70m2yCM= github.com/grafana/grafana-azure-sdk-go/v2 v2.1.6 h1:OfCkitCuomzZKW1WYHrG8MxKwtMhALb7jqoj+487eTg= github.com/grafana/grafana-azure-sdk-go/v2 v2.1.6/go.mod h1:V7y2BmsWxS3A9Ohebwn4OiSfJJqi//4JQydQ8fHTduo= github.com/grafana/grafana-cloud-migration-snapshot v1.6.0 h1:S4kHwr//AqhtL9xHBtz1gqVgZQeCRGTxjgsRBAkpjKY= @@ -1637,8 +1627,8 @@ github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc h1:GN2Lv3MGO7AS6PrR github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc/go.mod h1:+JKpmjMGhpgPL+rXZ5nsZieVzvarn86asRlBg4uNGnk= github.com/grafana/saml v0.4.15-0.20240917091248-ae3bbdad8a56 h1:SDGrP81Vcd102L3UJEryRd1eestRw73wt+b8vnVEFe0= github.com/grafana/saml v0.4.15-0.20240917091248-ae3bbdad8a56/go.mod h1:S4+611dxnKt8z/ulbvaJzcgSHsuhjVc1QHNTcr1R7Fw= -github.com/grafana/sqlds/v4 v4.1.7 h1:X5703emD4yZ7AF0cBbh4kwFS4smPL5yVNWRQFjUOOe8= -github.com/grafana/sqlds/v4 v4.1.7/go.mod h1:LnEai8vDHLPvJmggLqamDzMV6ldnzjZmRfMWoUQBKCE= +github.com/grafana/sqlds/v4 v4.1.3 h1:+Hy5Yz+tSbD5N3yuLM0VKTsWlVaCzM1S1m1QEBZL7fE= +github.com/grafana/sqlds/v4 v4.1.3/go.mod h1:Lx8IR939lIrCBpCKthv7AXs7E7bmNWPgt0gene/idT8= github.com/grafana/tempo v1.5.1-0.20241001135150-ed943d7a56b2 h1:XMreZ1SPjLpd9zhql5FXKFYwAcgBzS2E2MOPx4n+FyY= github.com/grafana/tempo v1.5.1-0.20241001135150-ed943d7a56b2/go.mod h1:UKONJhBCxmL+0ri27VMledCVzZIJqnl6Ah24A5vCRzs= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= diff --git a/go.work.sum b/go.work.sum index 91bbc83d04a..04236db57b6 100644 --- a/go.work.sum +++ b/go.work.sum @@ -848,7 +848,6 @@ github.com/elastic/go-windows v1.0.0/go.mod h1:TsU0Nrp7/y3+VwE82FoZF8gC/XFg/Elz6 github.com/elastic/go-windows v1.0.1 h1:AlYZOldA+UJ0/2nBuqWdo90GFCgG9xuyw9SYzGUtJm0= github.com/elastic/go-windows v1.0.1/go.mod h1:FoVvqWSun28vaDQPbj2Elfc0JahhPB7WQEGa3c814Ss= github.com/elazarl/goproxy v1.3.0/go.mod h1:X/5W/t+gzDyLfHW4DrMdpjqYjpXsURlBt9lpBDxZZZQ= -github.com/elazarl/goproxy v1.7.0/go.mod h1:X/5W/t+gzDyLfHW4DrMdpjqYjpXsURlBt9lpBDxZZZQ= github.com/elazarl/goproxy v1.7.1/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633 h1:H2pdYOb3KQ1/YsqVWoWNLQO+fusocsw354rqGTZtAgw= github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk= @@ -993,7 +992,6 @@ github.com/grafana/cog v0.0.23/go.mod h1:jrS9indvWuDs60RHEZpLaAkmZdgyoLKMOEUT0ji github.com/grafana/go-gelf/v2 v2.0.1 h1:BOChP0h/jLeD+7F9mL7tq10xVkDG15he3T1zHuQaWak= github.com/grafana/go-gelf/v2 v2.0.1/go.mod h1:lexHie0xzYGwCgiRGcvZ723bSNyNI8ZRD4s0CLobh90= github.com/grafana/grafana-plugin-sdk-go v0.263.0/go.mod h1:U43Cnrj/9DNYyvFcNdeUWNjMXTKNB0jcTcQGpWKd2gw= -github.com/grafana/grafana-plugin-sdk-go v0.266.0/go.mod h1:bxkXrBQ4QSmOncsWdIOcpgP+M6wajQNMAPXlbWrqAWY= github.com/grafana/grafana-plugin-sdk-go v0.267.0/go.mod h1:OuwS4c/JYgn0rr/w5zhJBpLo4gKm/vw15RsfpYAvK9Q= github.com/grafana/grafana/apps/advisor v0.0.0-20250123151950-b066a6313173/go.mod h1:goSDiy3jtC2cp8wjpPZdUHRENcoSUHae1/Px/MDfddA= github.com/grafana/grafana/apps/advisor v0.0.0-20250220154326-6e5de80ef295/go.mod h1:9I1dKV3Dqr0NPR9Af0WJGxOytp5/6W3JLiNChOz8r+c= @@ -1011,7 +1009,6 @@ github.com/grafana/tail v0.0.0-20230510142333-77b18831edf0/go.mod h1:7t5XR+2IA8P github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= -github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.2.0/go.mod h1:zrT2dxOAjNFPRGjTUe2Xmb4q4YdUwVvQFV6xiCSf+z0= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0/go.mod h1:qOchhhIlmRcqk/O9uCo/puJlyo07YINaIqdZfZG3Jkc= github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1/go.mod h1:5SN9VR2LTsRFsrEC6FHgRbTWrTHu6tqPeKxEQv15giM= github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= @@ -1146,7 +1143,6 @@ github.com/marstr/guid v1.1.0 h1:/M4H/1G4avsieL6BbUwCOBzulmoeKVP5ux/3mQNnbyI= github.com/matryer/moq v0.3.3 h1:pScMH9VyrdT4S93yiLpVyU8rCDqGQr24uOyBxmktG5Q= github.com/matryer/moq v0.3.3/go.mod h1:RJ75ZZZD71hejp39j4crZLsEDszGk6iH4v4YsWFKH4s= github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-shellwords v1.0.3 h1:K/VxK7SZ+cvuPgFSLKi5QPI9Vr/ipOf4C1gN+ntueUk= github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= github.com/mfridman/xflag v0.1.0 h1:TWZrZwG1QklFX5S4j1vxfF1sZbZeZSGofMwPMLAF29M= @@ -1166,7 +1162,6 @@ github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2Em github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc= github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= -github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= @@ -1674,7 +1669,6 @@ golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= -golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0 h1:OE9mWmgKkjJyEmDAAtGMPjXu+YNeGvK9VTSHY6+Qihc= gonum.org/v1/plot v0.14.0 h1:+LBDVFYwFe4LHhdP8coW6296MBEY4nQ+Y4vuUpJopcE= gonum.org/v1/plot v0.14.0/go.mod h1:MLdR9424SJed+5VqC6MsouEpig9pZX2VZ57H9ko2bXU= diff --git a/pkg/build/go.mod b/pkg/build/go.mod index ed6f08c804c..14352adba3e 100644 --- a/pkg/build/go.mod +++ b/pkg/build/go.mod @@ -10,7 +10,7 @@ replace github.com/docker/docker => github.com/moby/moby v27.5.1+incompatible require ( cloud.google.com/go/storage v1.50.0 // @grafana/grafana-backend-group github.com/Masterminds/semver/v3 v3.3.0 // @grafana/grafana-developer-enablement-squad - github.com/aws/aws-sdk-go v1.55.6 // @grafana/aws-datasources + github.com/aws/aws-sdk-go v1.55.5 // @grafana/aws-datasources github.com/docker/docker v27.5.1+incompatible // @grafana/grafana-developer-enablement-squad github.com/drone/drone-cli v1.8.0 // @grafana/grafana-developer-enablement-squad github.com/gogo/protobuf v1.3.2 // indirect; @grafana/alerting-backend diff --git a/pkg/build/go.sum b/pkg/build/go.sum index 55b8c06217d..1d55ac2a88b 100644 --- a/pkg/build/go.sum +++ b/pkg/build/go.sum @@ -53,8 +53,8 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= -github.com/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk= -github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= +github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= +github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= github.com/bmatcuk/doublestar v1.3.4 h1:gPypJ5xD31uhX6Tf54sDPUOBXTqKH4c9aPY66CyQrS0= github.com/bmatcuk/doublestar v1.3.4/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE= diff --git a/pkg/storage/unified/apistore/go.mod b/pkg/storage/unified/apistore/go.mod index 7955d73ee7d..a575dbab08b 100644 --- a/pkg/storage/unified/apistore/go.mod +++ b/pkg/storage/unified/apistore/go.mod @@ -76,26 +76,26 @@ require ( github.com/armon/go-metrics v0.4.1 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/at-wat/mqtt-go v0.19.4 // indirect - github.com/aws/aws-sdk-go v1.55.6 // indirect - github.com/aws/aws-sdk-go-v2 v1.36.1 // indirect - github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 // indirect - github.com/aws/aws-sdk-go-v2/config v1.29.4 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.17.57 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.27 // indirect + github.com/aws/aws-sdk-go v1.55.5 // indirect + github.com/aws/aws-sdk-go-v2 v1.30.3 // indirect + github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 // indirect + github.com/aws/aws-sdk-go-v2/config v1.27.27 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.17.27 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 // indirect github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.10 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.31 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.31 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 // indirect github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.17 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.12 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 // indirect github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15 // indirect github.com/aws/aws-sdk-go-v2/service/s3 v1.58.3 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.24.14 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.13 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.33.12 // indirect - github.com/aws/smithy-go v1.22.2 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 // indirect + github.com/aws/smithy-go v1.20.3 // indirect github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect @@ -210,7 +210,7 @@ require ( github.com/grafana/dskit v0.0.0-20241105154643-a6b453a88040 // indirect github.com/grafana/grafana-app-sdk v0.31.0 // indirect github.com/grafana/grafana-app-sdk/logging v0.30.0 // indirect - github.com/grafana/grafana-aws-sdk v0.33.0 // indirect + github.com/grafana/grafana-aws-sdk v0.31.5 // indirect github.com/grafana/grafana-azure-sdk-go/v2 v2.1.6 // indirect github.com/grafana/grafana-plugin-sdk-go v0.272.0 // indirect github.com/grafana/grafana/pkg/aggregator v0.0.0-20250220163425-b4c4b9abbdc8 // indirect @@ -219,7 +219,7 @@ require ( github.com/grafana/otel-profiling-go v0.5.1 // indirect github.com/grafana/pyroscope-go/godeltaprof v0.1.8 // indirect github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect - github.com/grafana/sqlds/v4 v4.1.7 // indirect + github.com/grafana/sqlds/v4 v4.1.3 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 // indirect github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.1 // indirect diff --git a/pkg/storage/unified/apistore/go.sum b/pkg/storage/unified/apistore/go.sum index bcc6edb5699..4d7afcb8d52 100644 --- a/pkg/storage/unified/apistore/go.sum +++ b/pkg/storage/unified/apistore/go.sum @@ -735,46 +735,46 @@ github.com/at-wat/mqtt-go v0.19.4 h1:R2cbCU7O5PHQ38unbe1Y51ncG3KsFEJV6QeipDoqdLQ github.com/at-wat/mqtt-go v0.19.4/go.mod h1:AsiWc9kqVOhqq7LzUeWT/AkKUBfx3Sw5cEe8lc06fqA= github.com/aws/aws-sdk-go v1.17.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= -github.com/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk= -github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= -github.com/aws/aws-sdk-go-v2 v1.36.1 h1:iTDl5U6oAhkNPba0e1t1hrwAo02ZMqbrGq4k5JBWM5E= -github.com/aws/aws-sdk-go-v2 v1.36.1/go.mod h1:5PMILGVKiW32oDzjj6RU52yrNrDPUHcbZQYr1sM7qmM= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 h1:lL7IfaFzngfx0ZwUGOZdsFFnQ5uLvR0hWqqhyE7Q9M8= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7/go.mod h1:QraP0UcVlQJsmHfioCrveWOC1nbiWUl3ej08h4mXWoc= -github.com/aws/aws-sdk-go-v2/config v1.29.4 h1:ObNqKsDYFGr2WxnoXKOhCvTlf3HhwtoGgc+KmZ4H5yg= -github.com/aws/aws-sdk-go-v2/config v1.29.4/go.mod h1:j2/AF7j/qxVmsNIChw1tWfsVKOayJoGRDjg1Tgq7NPk= -github.com/aws/aws-sdk-go-v2/credentials v1.17.57 h1:kFQDsbdBAR3GZsB8xA+51ptEnq9TIj3tS4MuP5b+TcQ= -github.com/aws/aws-sdk-go-v2/credentials v1.17.57/go.mod h1:2kerxPUUbTagAr/kkaHiqvj/bcYHzi2qiJS/ZinllU0= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.27 h1:7lOW8NUwE9UZekS1DYoiPdVAqZ6A+LheHWb+mHbNOq8= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.27/go.mod h1:w1BASFIPOPUae7AgaH4SbjNbfdkxuggLyGfNFTn8ITY= +github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= +github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= +github.com/aws/aws-sdk-go-v2 v1.30.3 h1:jUeBtG0Ih+ZIFH0F4UkmL9w3cSpaMv9tYYDbzILP8dY= +github.com/aws/aws-sdk-go-v2 v1.30.3/go.mod h1:nIQjQVp5sfpQcTc9mPSr1B0PaWK5ByX9MOoDadSN4lc= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 h1:tW1/Rkad38LA15X4UQtjXZXNKsCgkshC3EbmcUmghTg= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3/go.mod h1:UbnqO+zjqk3uIt9yCACHJ9IVNhyhOCnYk8yA19SAWrM= +github.com/aws/aws-sdk-go-v2/config v1.27.27 h1:HdqgGt1OAP0HkEDDShEl0oSYa9ZZBSOmKpdpsDMdO90= +github.com/aws/aws-sdk-go-v2/config v1.27.27/go.mod h1:MVYamCg76dFNINkZFu4n4RjDixhVr51HLj4ErWzrVwg= +github.com/aws/aws-sdk-go-v2/credentials v1.17.27 h1:2raNba6gr2IfA0eqqiP2XiQ0UVOpGPgDSi0I9iAP+UI= +github.com/aws/aws-sdk-go-v2/credentials v1.17.27/go.mod h1:gniiwbGahQByxan6YjQUMcW4Aov6bLC3m+evgcoN4r4= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 h1:KreluoV8FZDEtI6Co2xuNk/UqI9iwMrOx/87PBNIKqw= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11/go.mod h1:SeSUYBLsMYFoRvHE0Tjvn7kbxaUhl75CJi1sbfhMxkU= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.10 h1:zeN9UtUlA6FTx0vFSayxSX32HDw73Yb6Hh2izDSFxXY= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.10/go.mod h1:3HKuexPDcwLWPaqpW2UR/9n8N/u/3CKcGAzSs8p8u8g= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.31 h1:lWm9ucLSRFiI4dQQafLrEOmEDGry3Swrz0BIRdiHJqQ= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.31/go.mod h1:Huu6GG0YTfbPphQkDSo4dEGmQRTKb9k9G7RdtyQWxuI= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.31 h1:ACxDklUKKXb48+eg5ROZXi1vDgfMyfIA/WyvqHcHI0o= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.31/go.mod h1:yadnfsDwqXeVaohbGc/RaD287PuyRw2wugkh5ZL2J6k= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2 h1:Pg9URiobXy85kgFev3og2CuOZ8JZUBENF+dcgWBaYNk= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 h1:SoNJ4RlFEQEbtDcCEt+QG56MY4fm4W8rYirAmq+/DdU= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15/go.mod h1:U9ke74k1n2bf+RIgoX1SXFed1HLs51OgUSs+Ph0KJP8= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 h1:C6WHdGnTDIYETAm5iErQUiVNsclNx9qbJVPIt03B6bI= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15/go.mod h1:ZQLZqhcu+JhSrA9/NXRm8SkDvsycE+JkV3WGY41e+IM= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY= github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15 h1:Z5r7SycxmSllHYmaAZPpmN8GviDrSGhMS6bldqtXZPw= github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15/go.mod h1:CetW7bDE00QoGEmPUoZuRog07SGVAUVW6LFpNP0YfIg= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2 h1:D4oz8/CzT9bAEYtVhSBmFj2dNOtaHOtMKc2vHBwYizA= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2/go.mod h1:Za3IHqTQ+yNcRHxu1OFucBh0ACZT4j4VQFF0BqpZcLY= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 h1:dT3MqvGhSoaIhRseqw2I0yH81l7wiR2vjs57O51EAm8= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3/go.mod h1:GlAeCkHwugxdHaueRr4nhPuY+WW+gR8UjlcqzPr1SPI= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.17 h1:YPYe6ZmvUfDDDELqEKtAd6bo8zxhkm+XEFEzQisqUIE= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.17/go.mod h1:oBtcnYua/CgzCWYN7NZ5j7PotFDaFSUjCYVTtfyn7vw= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.12 h1:O+8vD2rGjfihBewr5bT+QUfYUHIxCVgG61LHoT59shM= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.12/go.mod h1:usVdWJaosa66NMvmCrr08NcWDBRv4E6+YFG2pUdw1Lk= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 h1:HGErhhrxZlQ044RiM+WdoZxp0p+EGM62y3L6pwA4olE= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17/go.mod h1:RkZEx4l0EHYDJpWppMJ3nD9wZJAa8/0lq9aVC+r2UII= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15 h1:246A4lSTXWJw/rmlQI+TT2OcqeDMKBdyjEQrafMaQdA= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15/go.mod h1:haVfg3761/WF7YPuJOER2MP0k4UAXyHaLclKXB6usDg= github.com/aws/aws-sdk-go-v2/service/s3 v1.58.3 h1:hT8ZAZRIfqBqHbzKTII+CIiY8G2oC9OpLedkZ51DWl8= github.com/aws/aws-sdk-go-v2/service/s3 v1.58.3/go.mod h1:Lcxzg5rojyVPU/0eFwLtcyTaek/6Mtic5B1gJo7e/zE= -github.com/aws/aws-sdk-go-v2/service/sso v1.24.14 h1:c5WJ3iHz7rLIgArznb3JCSQT3uUMiz9DLZhIX+1G8ok= -github.com/aws/aws-sdk-go-v2/service/sso v1.24.14/go.mod h1:+JJQTxB6N4niArC14YNtxcQtwEqzS3o9Z32n7q33Rfs= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.13 h1:f1L/JtUkVODD+k1+IiSJUUv8A++2qVr+Xvb3xWXETMU= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.13/go.mod h1:tvqlFoja8/s0o+UruA1Nrezo/df0PzdunMDDurUfg6U= -github.com/aws/aws-sdk-go-v2/service/sts v1.33.12 h1:fqg6c1KVrc3SYWma/egWue5rKI4G2+M4wMQN2JosNAA= -github.com/aws/aws-sdk-go-v2/service/sts v1.33.12/go.mod h1:7Yn+p66q/jt38qMoVfNvjbm3D89mGBnkwDcijgtih8w= -github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ= -github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= +github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 h1:BXx0ZIxvrJdSgSvKTZ+yRBeSqqgPM89VPlulEcl37tM= +github.com/aws/aws-sdk-go-v2/service/sso v1.22.4/go.mod h1:ooyCOXjvJEsUw7x+ZDHeISPMhtwI3ZCB7ggFMcFfWLU= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 h1:yiwVzJW2ZxZTurVbYWA7QOrAaCYQR72t0wrSBfoesUE= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4/go.mod h1:0oxfLkpz3rQ/CHlx5hB7H69YUpFiI1tql6Q6Ne+1bCw= +github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 h1:ZsDKRLXGWHk8WdtyYMoGNO7bTudrvuKpDKgMVRlepGE= +github.com/aws/aws-sdk-go-v2/service/sts v1.30.3/go.mod h1:zwySh8fpFyXp9yOr/KVzxOl8SRqgf/IDw5aUt9UKFcQ= +github.com/aws/smithy-go v1.20.3 h1:ryHwveWzPV5BIof6fyDvor6V3iUL7nTfiTKXHiW05nE= +github.com/aws/smithy-go v1.20.3/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= github.com/bboreham/go-loser v0.0.0-20230920113527-fcc2c21820a3 h1:6df1vn4bBlDDo4tARvBm7l6KA9iVMnE3NWizDeWSrps= @@ -1265,8 +1265,8 @@ github.com/grafana/grafana-app-sdk v0.31.0 h1:/mFCcx+YqG8cWAi9hePDJQxIdtXDClDIDR github.com/grafana/grafana-app-sdk v0.31.0/go.mod h1:Xw00NL7qpRLo5r3Gn48Bl1Xn2n4eUDI5pYf/wMufKWs= github.com/grafana/grafana-app-sdk/logging v0.30.0 h1:K/P/bm7Cp7Di4tqIJ3EQz2+842JozQGRaz62r95ApME= github.com/grafana/grafana-app-sdk/logging v0.30.0/go.mod h1:xy6ZyVXl50Z3DBDLybvBPphbykPhuVNed/VNmen9DQM= -github.com/grafana/grafana-aws-sdk v0.33.0 h1:mhuu9xyHvFgRJLXkahm9fHmS5PoNdzqIGGs+/u6NKNQ= -github.com/grafana/grafana-aws-sdk v0.33.0/go.mod h1:wjp8cLGt8NvrtZinUefObnn2BwpwXQ+aArT3S/RqW+4= +github.com/grafana/grafana-aws-sdk v0.31.5 h1:4HpMQx7n4Qqoi7Bgu8KHQ2QKT9fYYdHilX/Gh3FZKBE= +github.com/grafana/grafana-aws-sdk v0.31.5/go.mod h1:5p4Cjyr5ZiR6/RT2nFWkJ8XpIKgX4lAUmUMu70m2yCM= github.com/grafana/grafana-azure-sdk-go/v2 v2.1.6 h1:OfCkitCuomzZKW1WYHrG8MxKwtMhALb7jqoj+487eTg= github.com/grafana/grafana-azure-sdk-go/v2 v2.1.6/go.mod h1:V7y2BmsWxS3A9Ohebwn4OiSfJJqi//4JQydQ8fHTduo= github.com/grafana/grafana-plugin-sdk-go v0.272.0 h1:TmPIG+6e3lYGzkyfUfCHuaMaaiwDbkCacTZ7V/JaSeg= @@ -1285,8 +1285,8 @@ github.com/grafana/pyroscope-go/godeltaprof v0.1.8 h1:iwOtYXeeVSAeYefJNaxDytgjKt github.com/grafana/pyroscope-go/godeltaprof v0.1.8/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU= github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc h1:GN2Lv3MGO7AS6PrRoT6yV5+wkrOpcszoIsO4+4ds248= github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc/go.mod h1:+JKpmjMGhpgPL+rXZ5nsZieVzvarn86asRlBg4uNGnk= -github.com/grafana/sqlds/v4 v4.1.7 h1:X5703emD4yZ7AF0cBbh4kwFS4smPL5yVNWRQFjUOOe8= -github.com/grafana/sqlds/v4 v4.1.7/go.mod h1:LnEai8vDHLPvJmggLqamDzMV6ldnzjZmRfMWoUQBKCE= +github.com/grafana/sqlds/v4 v4.1.3 h1:+Hy5Yz+tSbD5N3yuLM0VKTsWlVaCzM1S1m1QEBZL7fE= +github.com/grafana/sqlds/v4 v4.1.3/go.mod h1:Lx8IR939lIrCBpCKthv7AXs7E7bmNWPgt0gene/idT8= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA= diff --git a/pkg/storage/unified/resource/go.mod b/pkg/storage/unified/resource/go.mod index 3877ed0daa3..689d9b2d97b 100644 --- a/pkg/storage/unified/resource/go.mod +++ b/pkg/storage/unified/resource/go.mod @@ -60,26 +60,26 @@ require ( github.com/VividCortex/mysqlerr v0.0.0-20170204212430-6c6b55f8796f // indirect github.com/apache/arrow-go/v18 v18.0.1-0.20241212180703-82be143d7c30 // indirect github.com/armon/go-metrics v0.4.1 // indirect - github.com/aws/aws-sdk-go v1.55.6 // indirect - github.com/aws/aws-sdk-go-v2 v1.36.1 // indirect - github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 // indirect - github.com/aws/aws-sdk-go-v2/config v1.29.4 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.17.57 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.27 // indirect + github.com/aws/aws-sdk-go v1.55.5 // indirect + github.com/aws/aws-sdk-go-v2 v1.30.3 // indirect + github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 // indirect + github.com/aws/aws-sdk-go-v2/config v1.27.27 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.17.27 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 // indirect github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.10 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.31 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.31 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 // indirect github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.17 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.12 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 // indirect github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15 // indirect github.com/aws/aws-sdk-go-v2/service/s3 v1.58.3 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.24.14 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.13 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.33.12 // indirect - github.com/aws/smithy-go v1.22.2 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 // indirect + github.com/aws/smithy-go v1.20.3 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/bluele/gcache v0.0.2 // indirect @@ -136,11 +136,11 @@ require ( github.com/grafana/dataplane/sdata v0.0.9 // indirect github.com/grafana/grafana-app-sdk v0.31.0 // indirect github.com/grafana/grafana-app-sdk/logging v0.30.0 // indirect - github.com/grafana/grafana-aws-sdk v0.33.0 // indirect + github.com/grafana/grafana-aws-sdk v0.31.5 // indirect github.com/grafana/grafana-azure-sdk-go/v2 v2.1.6 // indirect github.com/grafana/otel-profiling-go v0.5.1 // indirect github.com/grafana/pyroscope-go/godeltaprof v0.1.8 // indirect - github.com/grafana/sqlds/v4 v4.1.7 // indirect + github.com/grafana/sqlds/v4 v4.1.3 // indirect github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-hclog v1.6.3 // indirect diff --git a/pkg/storage/unified/resource/go.sum b/pkg/storage/unified/resource/go.sum index 0f72e759646..77e90d5ecf1 100644 --- a/pkg/storage/unified/resource/go.sum +++ b/pkg/storage/unified/resource/go.sum @@ -714,46 +714,46 @@ github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:W github.com/at-wat/mqtt-go v0.19.4 h1:R2cbCU7O5PHQ38unbe1Y51ncG3KsFEJV6QeipDoqdLQ= github.com/at-wat/mqtt-go v0.19.4/go.mod h1:AsiWc9kqVOhqq7LzUeWT/AkKUBfx3Sw5cEe8lc06fqA= github.com/aws/aws-sdk-go v1.17.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk= -github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= -github.com/aws/aws-sdk-go-v2 v1.36.1 h1:iTDl5U6oAhkNPba0e1t1hrwAo02ZMqbrGq4k5JBWM5E= -github.com/aws/aws-sdk-go-v2 v1.36.1/go.mod h1:5PMILGVKiW32oDzjj6RU52yrNrDPUHcbZQYr1sM7qmM= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 h1:lL7IfaFzngfx0ZwUGOZdsFFnQ5uLvR0hWqqhyE7Q9M8= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7/go.mod h1:QraP0UcVlQJsmHfioCrveWOC1nbiWUl3ej08h4mXWoc= -github.com/aws/aws-sdk-go-v2/config v1.29.4 h1:ObNqKsDYFGr2WxnoXKOhCvTlf3HhwtoGgc+KmZ4H5yg= -github.com/aws/aws-sdk-go-v2/config v1.29.4/go.mod h1:j2/AF7j/qxVmsNIChw1tWfsVKOayJoGRDjg1Tgq7NPk= -github.com/aws/aws-sdk-go-v2/credentials v1.17.57 h1:kFQDsbdBAR3GZsB8xA+51ptEnq9TIj3tS4MuP5b+TcQ= -github.com/aws/aws-sdk-go-v2/credentials v1.17.57/go.mod h1:2kerxPUUbTagAr/kkaHiqvj/bcYHzi2qiJS/ZinllU0= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.27 h1:7lOW8NUwE9UZekS1DYoiPdVAqZ6A+LheHWb+mHbNOq8= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.27/go.mod h1:w1BASFIPOPUae7AgaH4SbjNbfdkxuggLyGfNFTn8ITY= +github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= +github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= +github.com/aws/aws-sdk-go-v2 v1.30.3 h1:jUeBtG0Ih+ZIFH0F4UkmL9w3cSpaMv9tYYDbzILP8dY= +github.com/aws/aws-sdk-go-v2 v1.30.3/go.mod h1:nIQjQVp5sfpQcTc9mPSr1B0PaWK5ByX9MOoDadSN4lc= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 h1:tW1/Rkad38LA15X4UQtjXZXNKsCgkshC3EbmcUmghTg= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3/go.mod h1:UbnqO+zjqk3uIt9yCACHJ9IVNhyhOCnYk8yA19SAWrM= +github.com/aws/aws-sdk-go-v2/config v1.27.27 h1:HdqgGt1OAP0HkEDDShEl0oSYa9ZZBSOmKpdpsDMdO90= +github.com/aws/aws-sdk-go-v2/config v1.27.27/go.mod h1:MVYamCg76dFNINkZFu4n4RjDixhVr51HLj4ErWzrVwg= +github.com/aws/aws-sdk-go-v2/credentials v1.17.27 h1:2raNba6gr2IfA0eqqiP2XiQ0UVOpGPgDSi0I9iAP+UI= +github.com/aws/aws-sdk-go-v2/credentials v1.17.27/go.mod h1:gniiwbGahQByxan6YjQUMcW4Aov6bLC3m+evgcoN4r4= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 h1:KreluoV8FZDEtI6Co2xuNk/UqI9iwMrOx/87PBNIKqw= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11/go.mod h1:SeSUYBLsMYFoRvHE0Tjvn7kbxaUhl75CJi1sbfhMxkU= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.10 h1:zeN9UtUlA6FTx0vFSayxSX32HDw73Yb6Hh2izDSFxXY= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.10/go.mod h1:3HKuexPDcwLWPaqpW2UR/9n8N/u/3CKcGAzSs8p8u8g= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.31 h1:lWm9ucLSRFiI4dQQafLrEOmEDGry3Swrz0BIRdiHJqQ= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.31/go.mod h1:Huu6GG0YTfbPphQkDSo4dEGmQRTKb9k9G7RdtyQWxuI= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.31 h1:ACxDklUKKXb48+eg5ROZXi1vDgfMyfIA/WyvqHcHI0o= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.31/go.mod h1:yadnfsDwqXeVaohbGc/RaD287PuyRw2wugkh5ZL2J6k= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2 h1:Pg9URiobXy85kgFev3og2CuOZ8JZUBENF+dcgWBaYNk= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 h1:SoNJ4RlFEQEbtDcCEt+QG56MY4fm4W8rYirAmq+/DdU= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15/go.mod h1:U9ke74k1n2bf+RIgoX1SXFed1HLs51OgUSs+Ph0KJP8= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 h1:C6WHdGnTDIYETAm5iErQUiVNsclNx9qbJVPIt03B6bI= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15/go.mod h1:ZQLZqhcu+JhSrA9/NXRm8SkDvsycE+JkV3WGY41e+IM= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY= github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15 h1:Z5r7SycxmSllHYmaAZPpmN8GviDrSGhMS6bldqtXZPw= github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15/go.mod h1:CetW7bDE00QoGEmPUoZuRog07SGVAUVW6LFpNP0YfIg= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2 h1:D4oz8/CzT9bAEYtVhSBmFj2dNOtaHOtMKc2vHBwYizA= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2/go.mod h1:Za3IHqTQ+yNcRHxu1OFucBh0ACZT4j4VQFF0BqpZcLY= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 h1:dT3MqvGhSoaIhRseqw2I0yH81l7wiR2vjs57O51EAm8= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3/go.mod h1:GlAeCkHwugxdHaueRr4nhPuY+WW+gR8UjlcqzPr1SPI= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.17 h1:YPYe6ZmvUfDDDELqEKtAd6bo8zxhkm+XEFEzQisqUIE= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.17/go.mod h1:oBtcnYua/CgzCWYN7NZ5j7PotFDaFSUjCYVTtfyn7vw= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.12 h1:O+8vD2rGjfihBewr5bT+QUfYUHIxCVgG61LHoT59shM= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.12/go.mod h1:usVdWJaosa66NMvmCrr08NcWDBRv4E6+YFG2pUdw1Lk= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 h1:HGErhhrxZlQ044RiM+WdoZxp0p+EGM62y3L6pwA4olE= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17/go.mod h1:RkZEx4l0EHYDJpWppMJ3nD9wZJAa8/0lq9aVC+r2UII= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15 h1:246A4lSTXWJw/rmlQI+TT2OcqeDMKBdyjEQrafMaQdA= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15/go.mod h1:haVfg3761/WF7YPuJOER2MP0k4UAXyHaLclKXB6usDg= github.com/aws/aws-sdk-go-v2/service/s3 v1.58.3 h1:hT8ZAZRIfqBqHbzKTII+CIiY8G2oC9OpLedkZ51DWl8= github.com/aws/aws-sdk-go-v2/service/s3 v1.58.3/go.mod h1:Lcxzg5rojyVPU/0eFwLtcyTaek/6Mtic5B1gJo7e/zE= -github.com/aws/aws-sdk-go-v2/service/sso v1.24.14 h1:c5WJ3iHz7rLIgArznb3JCSQT3uUMiz9DLZhIX+1G8ok= -github.com/aws/aws-sdk-go-v2/service/sso v1.24.14/go.mod h1:+JJQTxB6N4niArC14YNtxcQtwEqzS3o9Z32n7q33Rfs= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.13 h1:f1L/JtUkVODD+k1+IiSJUUv8A++2qVr+Xvb3xWXETMU= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.13/go.mod h1:tvqlFoja8/s0o+UruA1Nrezo/df0PzdunMDDurUfg6U= -github.com/aws/aws-sdk-go-v2/service/sts v1.33.12 h1:fqg6c1KVrc3SYWma/egWue5rKI4G2+M4wMQN2JosNAA= -github.com/aws/aws-sdk-go-v2/service/sts v1.33.12/go.mod h1:7Yn+p66q/jt38qMoVfNvjbm3D89mGBnkwDcijgtih8w= -github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ= -github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= +github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 h1:BXx0ZIxvrJdSgSvKTZ+yRBeSqqgPM89VPlulEcl37tM= +github.com/aws/aws-sdk-go-v2/service/sso v1.22.4/go.mod h1:ooyCOXjvJEsUw7x+ZDHeISPMhtwI3ZCB7ggFMcFfWLU= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 h1:yiwVzJW2ZxZTurVbYWA7QOrAaCYQR72t0wrSBfoesUE= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4/go.mod h1:0oxfLkpz3rQ/CHlx5hB7H69YUpFiI1tql6Q6Ne+1bCw= +github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 h1:ZsDKRLXGWHk8WdtyYMoGNO7bTudrvuKpDKgMVRlepGE= +github.com/aws/aws-sdk-go-v2/service/sts v1.30.3/go.mod h1:zwySh8fpFyXp9yOr/KVzxOl8SRqgf/IDw5aUt9UKFcQ= +github.com/aws/smithy-go v1.20.3 h1:ryHwveWzPV5BIof6fyDvor6V3iUL7nTfiTKXHiW05nE= +github.com/aws/smithy-go v1.20.3/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -1160,8 +1160,8 @@ github.com/grafana/grafana-app-sdk v0.31.0 h1:/mFCcx+YqG8cWAi9hePDJQxIdtXDClDIDR github.com/grafana/grafana-app-sdk v0.31.0/go.mod h1:Xw00NL7qpRLo5r3Gn48Bl1Xn2n4eUDI5pYf/wMufKWs= github.com/grafana/grafana-app-sdk/logging v0.30.0 h1:K/P/bm7Cp7Di4tqIJ3EQz2+842JozQGRaz62r95ApME= github.com/grafana/grafana-app-sdk/logging v0.30.0/go.mod h1:xy6ZyVXl50Z3DBDLybvBPphbykPhuVNed/VNmen9DQM= -github.com/grafana/grafana-aws-sdk v0.33.0 h1:mhuu9xyHvFgRJLXkahm9fHmS5PoNdzqIGGs+/u6NKNQ= -github.com/grafana/grafana-aws-sdk v0.33.0/go.mod h1:wjp8cLGt8NvrtZinUefObnn2BwpwXQ+aArT3S/RqW+4= +github.com/grafana/grafana-aws-sdk v0.31.5 h1:4HpMQx7n4Qqoi7Bgu8KHQ2QKT9fYYdHilX/Gh3FZKBE= +github.com/grafana/grafana-aws-sdk v0.31.5/go.mod h1:5p4Cjyr5ZiR6/RT2nFWkJ8XpIKgX4lAUmUMu70m2yCM= github.com/grafana/grafana-azure-sdk-go/v2 v2.1.6 h1:OfCkitCuomzZKW1WYHrG8MxKwtMhALb7jqoj+487eTg= github.com/grafana/grafana-azure-sdk-go/v2 v2.1.6/go.mod h1:V7y2BmsWxS3A9Ohebwn4OiSfJJqi//4JQydQ8fHTduo= github.com/grafana/grafana-plugin-sdk-go v0.272.0 h1:TmPIG+6e3lYGzkyfUfCHuaMaaiwDbkCacTZ7V/JaSeg= @@ -1172,8 +1172,8 @@ github.com/grafana/otel-profiling-go v0.5.1 h1:stVPKAFZSa7eGiqbYuG25VcqYksR6iWvF github.com/grafana/otel-profiling-go v0.5.1/go.mod h1:ftN/t5A/4gQI19/8MoWurBEtC6gFw8Dns1sJZ9W4Tls= github.com/grafana/pyroscope-go/godeltaprof v0.1.8 h1:iwOtYXeeVSAeYefJNaxDytgjKtUuKQbJqgAIjlnicKg= github.com/grafana/pyroscope-go/godeltaprof v0.1.8/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU= -github.com/grafana/sqlds/v4 v4.1.7 h1:X5703emD4yZ7AF0cBbh4kwFS4smPL5yVNWRQFjUOOe8= -github.com/grafana/sqlds/v4 v4.1.7/go.mod h1:LnEai8vDHLPvJmggLqamDzMV6ldnzjZmRfMWoUQBKCE= +github.com/grafana/sqlds/v4 v4.1.3 h1:+Hy5Yz+tSbD5N3yuLM0VKTsWlVaCzM1S1m1QEBZL7fE= +github.com/grafana/sqlds/v4 v4.1.3/go.mod h1:Lx8IR939lIrCBpCKthv7AXs7E7bmNWPgt0gene/idT8= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.1 h1:KcFzXwzM/kGhIRHvc8jdixfIJjVzuUJdnv+5xsPutog= diff --git a/pkg/tsdb/cloudwatch/annotation_query.go b/pkg/tsdb/cloudwatch/annotation_query.go index be38f571726..2eb61676354 100644 --- a/pkg/tsdb/cloudwatch/annotation_query.go +++ b/pkg/tsdb/cloudwatch/annotation_query.go @@ -7,10 +7,8 @@ import ( "strconv" "time" - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/cloudwatch" - cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" - + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/cloudwatch" "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/data" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/kinds/dataquery" @@ -31,14 +29,14 @@ func (e *cloudWatchExecutor) executeAnnotationQuery(ctx context.Context, pluginC statistic = *model.Statistic } - var period int32 + var period int64 if model.Period != nil && *model.Period != "" { - p, err := strconv.ParseInt(*model.Period, 10, 32) + p, err := strconv.ParseInt(*model.Period, 10, 64) if err != nil { return nil, backend.DownstreamError(fmt.Errorf("query period must be an int")) } - period = int32(p) + period = p } prefixMatching := false @@ -71,11 +69,11 @@ func (e *cloudWatchExecutor) executeAnnotationQuery(ctx context.Context, pluginC if prefixMatching { params := &cloudwatch.DescribeAlarmsInput{ - MaxRecords: aws.Int32(100), + MaxRecords: aws.Int64(100), ActionPrefix: actionPrefix, AlarmNamePrefix: alarmNamePrefix, } - resp, err := cli.DescribeAlarms(ctx, params) + resp, err := cli.DescribeAlarms(params) if err != nil { result.Responses[query.RefID] = backend.ErrorResponseWithErrorSource(backend.DownstreamError(fmt.Errorf("%v: %w", "failed to call cloudwatch:DescribeAlarms", err))) return result, nil @@ -86,10 +84,10 @@ func (e *cloudWatchExecutor) executeAnnotationQuery(ctx context.Context, pluginC return result, backend.DownstreamError(errors.New("invalid annotations query")) } - var qd []cloudwatchtypes.Dimension + var qd []*cloudwatch.Dimension for k, v := range dimensions { for _, vvv := range v.ArrayOfString { - qd = append(qd, cloudwatchtypes.Dimension{ + qd = append(qd, &cloudwatch.Dimension{ Name: aws.String(k), Value: aws.String(vvv), }) @@ -99,10 +97,10 @@ func (e *cloudWatchExecutor) executeAnnotationQuery(ctx context.Context, pluginC Namespace: aws.String(model.Namespace), MetricName: aws.String(metricName), Dimensions: qd, - Statistic: cloudwatchtypes.Statistic(statistic), - Period: aws.Int32(period), + Statistic: aws.String(statistic), + Period: aws.Int64(period), } - resp, err := cli.DescribeAlarmsForMetric(ctx, params) + resp, err := cli.DescribeAlarmsForMetric(params) if err != nil { result.Responses[query.RefID] = backend.ErrorResponseWithErrorSource(backend.DownstreamError(fmt.Errorf("%v: %w", "failed to call cloudwatch:DescribeAlarmsForMetric", err))) return result, nil @@ -118,9 +116,9 @@ func (e *cloudWatchExecutor) executeAnnotationQuery(ctx context.Context, pluginC AlarmName: alarmName, StartDate: aws.Time(query.TimeRange.From), EndDate: aws.Time(query.TimeRange.To), - MaxRecords: aws.Int32(100), + MaxRecords: aws.Int64(100), } - resp, err := cli.DescribeAlarmHistory(ctx, params) + resp, err := cli.DescribeAlarmHistory(params) if err != nil { result.Responses[query.RefID] = backend.ErrorResponseWithErrorSource(backend.DownstreamError(fmt.Errorf("%v: %w", "failed to call cloudwatch:DescribeAlarmHistory", err))) return result, nil @@ -129,7 +127,7 @@ func (e *cloudWatchExecutor) executeAnnotationQuery(ctx context.Context, pluginC annotations = append(annotations, &annotationEvent{ Time: *history.Timestamp, Title: *history.AlarmName, - Tags: string(history.HistoryItemType), + Tags: *history.HistoryItemType, Text: *history.HistorySummary, }) } @@ -164,7 +162,7 @@ func transformAnnotationToTable(annotations []*annotationEvent, query backend.Da } func filterAlarms(alarms *cloudwatch.DescribeAlarmsOutput, namespace string, metricName string, - dimensions dataquery.Dimensions, statistic string, period int32) []*string { + dimensions dataquery.Dimensions, statistic string, period int64) []*string { alarmNames := make([]*string, 0) for _, alarm := range alarms.MetricAlarms { @@ -191,7 +189,7 @@ func filterAlarms(alarms *cloudwatch.DescribeAlarmsOutput, namespace string, met continue } - if string(alarm.Statistic) != statistic { + if *alarm.Statistic != statistic { continue } diff --git a/pkg/tsdb/cloudwatch/annotation_query_test.go b/pkg/tsdb/cloudwatch/annotation_query_test.go index 9dd77fb1f3b..6779c82a896 100644 --- a/pkg/tsdb/cloudwatch/annotation_query_test.go +++ b/pkg/tsdb/cloudwatch/annotation_query_test.go @@ -5,13 +5,12 @@ import ( "encoding/json" "testing" - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/cloudwatch" - + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/cloudwatch" + "github.com/aws/aws-sdk-go/service/cloudwatch/cloudwatchiface" "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend/log" - "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -23,7 +22,7 @@ func TestQuery_AnnotationQuery(t *testing.T) { }) var client fakeCWAnnotationsClient - NewCWClient = func(aws.Config) models.CWClient { + NewCWClient = func(sess *session.Session) cloudwatchiface.CloudWatchAPI { return &client } @@ -54,8 +53,8 @@ func TestQuery_AnnotationQuery(t *testing.T) { assert.Equal(t, &cloudwatch.DescribeAlarmsForMetricInput{ Namespace: aws.String("custom"), MetricName: aws.String("CPUUtilization"), - Statistic: "Average", - Period: aws.Int32(300), + Statistic: aws.String("Average"), + Period: aws.Int64(300), }, client.calls.describeAlarmsForMetric[0]) }) @@ -87,7 +86,7 @@ func TestQuery_AnnotationQuery(t *testing.T) { require.Len(t, client.calls.describeAlarms, 1) assert.Equal(t, &cloudwatch.DescribeAlarmsInput{ - MaxRecords: aws.Int32(100), + MaxRecords: aws.Int64(100), ActionPrefix: aws.String("some_action_prefix"), AlarmNamePrefix: aws.String("some_alarm_name_prefix"), }, client.calls.describeAlarms[0]) diff --git a/pkg/tsdb/cloudwatch/client_factory.go b/pkg/tsdb/cloudwatch/client_factory.go index 2a683e7772a..258640e7f80 100644 --- a/pkg/tsdb/cloudwatch/client_factory.go +++ b/pkg/tsdb/cloudwatch/client_factory.go @@ -1,53 +1,64 @@ package cloudwatch import ( - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/cloudwatch" - "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" - "github.com/aws/aws-sdk-go-v2/service/ec2" - "github.com/aws/aws-sdk-go-v2/service/oam" - "github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi" + "github.com/aws/aws-sdk-go/aws/client" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/cloudwatch" + "github.com/aws/aws-sdk-go/service/cloudwatch/cloudwatchiface" + "github.com/aws/aws-sdk-go/service/cloudwatchlogs" + "github.com/aws/aws-sdk-go/service/cloudwatchlogs/cloudwatchlogsiface" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go/service/oam" + "github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi" + "github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi/resourcegroupstaggingapiiface" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" ) -// NewCWClient is a CloudWatch metrics api factory. +// NewMetricsAPI is a CloudWatch metrics api factory. // // Stubbable by tests. -var NewCWClient = func(cfg aws.Config) models.CWClient { - return cloudwatch.NewFromConfig(cfg) +var NewMetricsAPI = func(sess *session.Session) models.CloudWatchMetricsAPIProvider { + return cloudwatch.New(sess) } // NewLogsAPI is a CloudWatch logs api factory. // // Stubbable by tests. -var NewLogsAPI = func(cfg aws.Config) models.CloudWatchLogsAPIProvider { - return cloudwatchlogs.NewFromConfig(cfg) +var NewLogsAPI = func(sess *session.Session) models.CloudWatchLogsAPIProvider { + return cloudwatchlogs.New(sess) } -// NewOAMAPI is a CloudWatch OAM API factory +// NewOAMAPI is a CloudWatch OAM api factory. // // Stubbable by tests. -var NewOAMAPI = func(cfg aws.Config) models.OAMAPIProvider { - return oam.NewFromConfig(cfg) +var NewOAMAPI = func(sess *session.Session) models.OAMAPIProvider { + return oam.New(sess) } -// NewEC2API is a CloudWatch EC2 API factory +// NewCWClient is a CloudWatch client factory. // -// Stubbable by tests -var NewEC2API = func(cfg aws.Config) models.EC2APIProvider { - return ec2.NewFromConfig(cfg) +// Stubbable by tests. +var NewCWClient = func(sess *session.Session) cloudwatchiface.CloudWatchAPI { + return cloudwatch.New(sess) } // NewCWLogsClient is a CloudWatch logs client factory. // // Stubbable by tests. -var NewCWLogsClient = func(cfg aws.Config) models.CWLogsClient { - return cloudwatchlogs.NewFromConfig(cfg) +var NewCWLogsClient = func(sess *session.Session) cloudwatchlogsiface.CloudWatchLogsAPI { + return cloudwatchlogs.New(sess) } -// NewRGTAClient is a ResourceGroupsTaggingAPI Client factory. +// NewEC2Client is a client factory. // // Stubbable by tests. -var NewRGTAClient = func(cfg aws.Config) resourcegroupstaggingapi.GetResourcesAPIClient { - return resourcegroupstaggingapi.NewFromConfig(cfg) +var NewEC2Client = func(provider client.ConfigProvider) models.EC2APIProvider { + return ec2.New(provider) +} + +// RGTA client factory. +// +// Stubbable by tests. +var newRGTAClient = func(provider client.ConfigProvider) resourcegroupstaggingapiiface.ResourceGroupsTaggingAPIAPI { + return resourcegroupstaggingapi.New(provider) } diff --git a/pkg/tsdb/cloudwatch/clients/metrics.go b/pkg/tsdb/cloudwatch/clients/metrics.go index d3f46606f8e..b3648492a59 100644 --- a/pkg/tsdb/cloudwatch/clients/metrics.go +++ b/pkg/tsdb/cloudwatch/clients/metrics.go @@ -3,42 +3,41 @@ package clients import ( "context" - "github.com/aws/aws-sdk-go-v2/service/cloudwatch" - + "github.com/aws/aws-sdk-go/aws/awsutil" + "github.com/aws/aws-sdk-go/service/cloudwatch" + "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources" + "github.com/grafana/grafana/pkg/tsdb/cloudwatch/utils" ) -type MetricsClient struct { - cloudwatch.ListMetricsAPIClient - +// this client wraps the CloudWatch API and handles pagination and the composition of the MetricResponse DTO +type metricsClient struct { + models.CloudWatchMetricsAPIProvider listMetricsPageLimit int } -func NewMetricsClient(client cloudwatch.ListMetricsAPIClient, listMetricsPageLimit int) *MetricsClient { - return &MetricsClient{ - ListMetricsAPIClient: client, - listMetricsPageLimit: listMetricsPageLimit, - } +func NewMetricsClient(api models.CloudWatchMetricsAPIProvider, pageLimit int) *metricsClient { + return &metricsClient{CloudWatchMetricsAPIProvider: api, listMetricsPageLimit: pageLimit} } -func (mc *MetricsClient) ListMetricsWithPageLimit(ctx context.Context, params *cloudwatch.ListMetricsInput) ([]resources.MetricResponse, error) { - var response []resources.MetricResponse - paginator := cloudwatch.NewListMetricsPaginator(mc.ListMetricsAPIClient, params) - includeAccount := params.IncludeLinkedAccounts != nil && *params.IncludeLinkedAccounts - pages := 0 - for paginator.HasMorePages() && pages < mc.listMetricsPageLimit { - pages += 1 - page, err := paginator.NextPage(ctx) - if err != nil { - return response, err - } - for i, metric := range page.Metrics { - resp := resources.MetricResponse{Metric: metric} - if includeAccount && len(page.OwningAccounts) >= i { - resp.AccountId = &page.OwningAccounts[i] +func (l *metricsClient) ListMetricsWithPageLimit(ctx context.Context, params *cloudwatch.ListMetricsInput) ([]resources.MetricResponse, error) { + var cloudWatchMetrics []resources.MetricResponse + pageNum := 0 + err := l.ListMetricsPagesWithContext(ctx, params, func(page *cloudwatch.ListMetricsOutput, lastPage bool) bool { + pageNum++ + utils.QueriesTotalCounter.WithLabelValues(utils.ListMetricsLabel).Inc() + metrics, err := awsutil.ValuesAtPath(page, "Metrics") + if err == nil { + for idx, metric := range metrics { + metric := resources.MetricResponse{Metric: metric.(*cloudwatch.Metric)} + if len(page.OwningAccounts) >= idx && params.IncludeLinkedAccounts != nil && *params.IncludeLinkedAccounts { + metric.AccountId = page.OwningAccounts[idx] + } + cloudWatchMetrics = append(cloudWatchMetrics, metric) } - response = append(response, resp) } - } - return response, nil + return !lastPage && pageNum < l.listMetricsPageLimit + }) + + return cloudWatchMetrics, err } diff --git a/pkg/tsdb/cloudwatch/clients/metrics_test.go b/pkg/tsdb/cloudwatch/clients/metrics_test.go index 7f221cc8247..644aecf85e4 100644 --- a/pkg/tsdb/cloudwatch/clients/metrics_test.go +++ b/pkg/tsdb/cloudwatch/clients/metrics_test.go @@ -4,19 +4,16 @@ import ( "context" "testing" - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/cloudwatch" - cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" - + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/cloudwatch" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/mocks" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestMetricsClient(t *testing.T) { - metrics := []cloudwatchtypes.Metric{ + metrics := []*cloudwatch.Metric{ {MetricName: aws.String("Test_MetricName1")}, {MetricName: aws.String("Test_MetricName2")}, {MetricName: aws.String("Test_MetricName3")}, @@ -53,25 +50,25 @@ func TestMetricsClient(t *testing.T) { }) t.Run("Should return account id in case IncludeLinkedAccounts is set to true", func(t *testing.T) { - fakeApi := &mocks.FakeMetricsAPI{Metrics: []cloudwatchtypes.Metric{ + fakeApi := &mocks.FakeMetricsAPI{Metrics: []*cloudwatch.Metric{ {MetricName: aws.String("Test_MetricName1")}, {MetricName: aws.String("Test_MetricName2")}, {MetricName: aws.String("Test_MetricName3")}, - }, OwningAccounts: []string{"1234567890", "1234567890", "1234567895"}} + }, OwningAccounts: []*string{aws.String("1234567890"), aws.String("1234567890"), aws.String("1234567895")}} client := NewMetricsClient(fakeApi, 100) response, err := client.ListMetricsWithPageLimit(ctx, &cloudwatch.ListMetricsInput{IncludeLinkedAccounts: aws.Bool(true)}) require.NoError(t, err) expected := []resources.MetricResponse{ - {Metric: cloudwatchtypes.Metric{MetricName: aws.String("Test_MetricName1")}, AccountId: stringPtr("1234567890")}, - {Metric: cloudwatchtypes.Metric{MetricName: aws.String("Test_MetricName2")}, AccountId: stringPtr("1234567890")}, - {Metric: cloudwatchtypes.Metric{MetricName: aws.String("Test_MetricName3")}, AccountId: stringPtr("1234567895")}, + {Metric: &cloudwatch.Metric{MetricName: aws.String("Test_MetricName1")}, AccountId: stringPtr("1234567890")}, + {Metric: &cloudwatch.Metric{MetricName: aws.String("Test_MetricName2")}, AccountId: stringPtr("1234567890")}, + {Metric: &cloudwatch.Metric{MetricName: aws.String("Test_MetricName3")}, AccountId: stringPtr("1234567895")}, } assert.Equal(t, expected, response) }) t.Run("Should not return account id in case IncludeLinkedAccounts is set to false", func(t *testing.T) { - fakeApi := &mocks.FakeMetricsAPI{Metrics: []cloudwatchtypes.Metric{{MetricName: aws.String("Test_MetricName1")}}, OwningAccounts: []string{"1234567890"}} + fakeApi := &mocks.FakeMetricsAPI{Metrics: []*cloudwatch.Metric{{MetricName: aws.String("Test_MetricName1")}}, OwningAccounts: []*string{aws.String("1234567890")}} client := NewMetricsClient(fakeApi, 100) response, err := client.ListMetricsWithPageLimit(ctx, &cloudwatch.ListMetricsInput{IncludeLinkedAccounts: aws.Bool(false)}) diff --git a/pkg/tsdb/cloudwatch/cloudwatch.go b/pkg/tsdb/cloudwatch/cloudwatch.go index 88b96836ff9..74ae89e7748 100644 --- a/pkg/tsdb/cloudwatch/cloudwatch.go +++ b/pkg/tsdb/cloudwatch/cloudwatch.go @@ -3,17 +3,18 @@ package cloudwatch import ( "context" "encoding/json" + "errors" "fmt" - "slices" + "net/http" "time" - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/cloudwatch" - "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" - cloudwatchlogstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" - "github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi" - - "github.com/grafana/grafana-aws-sdk/pkg/awsauth" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/cloudwatch" + "github.com/aws/aws-sdk-go/service/cloudwatch/cloudwatchiface" + "github.com/aws/aws-sdk-go/service/cloudwatchlogs" + "github.com/aws/aws-sdk-go/service/cloudwatchlogs/cloudwatchlogsiface" + "github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi/resourcegroupstaggingapiiface" "github.com/grafana/grafana-aws-sdk/pkg/awsds" "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend/datasource" @@ -36,13 +37,6 @@ const ( // headerFromAlert is used by datasources to identify alert queries headerFromAlert = "FromAlert" - - defaultRegion = "default" - logsQueryMode = "Logs" - // QueryTypes - annotationQuery = "annotationQuery" - logAction = "logAction" - timeSeriesQuery = "timeSeriesQuery" ) type DataQueryJson struct { @@ -51,39 +45,21 @@ type DataQueryJson struct { } type DataSource struct { - Settings models.CloudWatchSettings - ProxyOpts *proxy.Options - AWSConfigProvider awsauth.ConfigProvider - + Settings models.CloudWatchSettings + HTTPClient *http.Client + sessions SessionCache tagValueCache *cache.Cache + ProxyOpts *proxy.Options } -func (ds *DataSource) newAWSConfig(ctx context.Context, region string) (aws.Config, error) { - if region == defaultRegion { - if len(ds.Settings.Region) == 0 { - return aws.Config{}, models.ErrMissingRegion - } - region = ds.Settings.Region - } - authSettings := awsauth.Settings{ - CredentialsProfile: ds.Settings.Profile, - LegacyAuthType: ds.Settings.AuthType, - AssumeRoleARN: ds.Settings.AssumeRoleARN, - ExternalID: ds.Settings.GrafanaSettings.ExternalID, - Endpoint: ds.Settings.Endpoint, - Region: region, - AccessKey: ds.Settings.AccessKey, - SecretKey: ds.Settings.SecretKey, - } - if ds.Settings.GrafanaSettings.SecureSocksDSProxyEnabled && ds.Settings.SecureSocksProxyEnabled { - authSettings.ProxyOptions = ds.ProxyOpts - } - cfg, err := ds.AWSConfigProvider.GetConfig(ctx, authSettings) - if err != nil { - return aws.Config{}, err - } - return cfg, nil -} +const ( + defaultRegion = "default" + logsQueryMode = "Logs" + // QueryTypes + annotationQuery = "annotationQuery" + logAction = "logAction" + timeSeriesQuery = "timeSeriesQuery" +) func ProvideService(httpClientProvider *httpclient.Provider) *CloudWatchService { logger := backend.NewLoggerWith("logger", "tsdb.cloudwatch") @@ -104,7 +80,7 @@ type CloudWatchService struct { } type SessionCache interface { - CredentialsProviderV2(ctx context.Context, cfg awsds.GetSessionConfig) (aws.CredentialsProvider, error) + GetSessionWithAuthSettings(c awsds.GetSessionConfig, as awsds.AuthSettings) (*session.Session, error) } func newExecutor(im instancemgmt.InstanceManager, logger log.Logger) *cloudWatchExecutor { @@ -129,12 +105,18 @@ func NewInstanceSettings(httpClientProvider *httpclient.Provider) datasource.Ins return nil, err } + httpClient, err := httpClientProvider.New(opts) + if err != nil { + return nil, fmt.Errorf("error creating http client: %w", err) + } + return DataSource{ Settings: instanceSettings, + HTTPClient: httpClient, tagValueCache: cache.New(tagValueCacheExpiration, tagValueCacheExpiration*5), + sessions: awsds.NewSessionCache(), // this is used to build a custom dialer when secure socks proxy is enabled - ProxyOpts: opts.ProxyOptions, - AWSConfigProvider: awsauth.NewConfigProvider(), + ProxyOpts: opts.ProxyOptions, }, nil } } @@ -163,31 +145,30 @@ func instrumentContext(ctx context.Context, endpoint string, pCtx backend.Plugin } func (e *cloudWatchExecutor) getRequestContext(ctx context.Context, pluginCtx backend.PluginContext, region string) (models.RequestContext, error) { + r := region instance, err := e.getInstance(ctx, pluginCtx) - if err != nil { - return models.RequestContext{}, err - } - if region == defaultRegion { - region = instance.Settings.Region + if err != nil { + return models.RequestContext{}, err + } + r = instance.Settings.Region } - cfg, err := instance.newAWSConfig(ctx, defaultRegion) + ec2Client, err := e.getEC2Client(ctx, pluginCtx, defaultRegion) if err != nil { return models.RequestContext{}, err } - ec2client := NewEC2API(cfg) - cfg, err = instance.newAWSConfig(ctx, region) + sess, err := instance.newSession(r) if err != nil { return models.RequestContext{}, err } return models.RequestContext{ - OAMAPIProvider: NewOAMAPI(cfg), - MetricsClientProvider: clients.NewMetricsClient(NewCWClient(cfg), instance.Settings.GrafanaSettings.ListMetricsPageLimit), - LogsAPIProvider: NewLogsAPI(cfg), - EC2APIProvider: ec2client, + OAMAPIProvider: NewOAMAPI(sess), + MetricsClientProvider: clients.NewMetricsClient(NewMetricsAPI(sess), instance.Settings.GrafanaSettings.ListMetricsPageLimit), + LogsAPIProvider: NewLogsAPI(sess), + EC2APIProvider: ec2Client, Settings: instance.Settings, Logger: e.logger.FromContext(ctx), }, nil @@ -291,32 +272,86 @@ func (e *cloudWatchExecutor) checkHealthMetrics(ctx context.Context, pluginCtx b return err } - cfg, err := instance.newAWSConfig(ctx, defaultRegion) + session, err := instance.newSession(defaultRegion) if err != nil { return err } - metricClient := clients.NewMetricsClient(NewCWClient(cfg), instance.Settings.GrafanaSettings.ListMetricsPageLimit) + metricClient := clients.NewMetricsClient(NewMetricsAPI(session), instance.Settings.GrafanaSettings.ListMetricsPageLimit) _, err = metricClient.ListMetricsWithPageLimit(ctx, params) return err } func (e *cloudWatchExecutor) checkHealthLogs(ctx context.Context, pluginCtx backend.PluginContext) error { - cfg, err := e.getAWSConfig(ctx, pluginCtx, defaultRegion) + session, err := e.newSessionFromContext(ctx, pluginCtx, defaultRegion) if err != nil { return err } - logsClient := NewLogsAPI(cfg) - _, err = logsClient.DescribeLogGroups(ctx, &cloudwatchlogs.DescribeLogGroupsInput{Limit: aws.Int32(1)}) + logsClient := NewLogsAPI(session) + _, err = logsClient.DescribeLogGroupsWithContext(ctx, &cloudwatchlogs.DescribeLogGroupsInput{Limit: aws.Int64(1)}) return err } -func (e *cloudWatchExecutor) getAWSConfig(ctx context.Context, pluginCtx backend.PluginContext, region string) (aws.Config, error) { +func (ds *DataSource) newSession(region string) (*session.Session, error) { + if region == defaultRegion { + if len(ds.Settings.Region) == 0 { + return nil, models.ErrMissingRegion + } + region = ds.Settings.Region + } + sess, err := ds.sessions.GetSessionWithAuthSettings(awsds.GetSessionConfig{ + // https://github.com/grafana/grafana/issues/46365 + // HTTPClient: instance.HTTPClient, + Settings: awsds.AWSDatasourceSettings{ + Profile: ds.Settings.Profile, + Region: region, + AuthType: ds.Settings.AuthType, + AssumeRoleARN: ds.Settings.AssumeRoleARN, + ExternalID: ds.Settings.ExternalID, + Endpoint: ds.Settings.Endpoint, + DefaultRegion: ds.Settings.Region, + AccessKey: ds.Settings.AccessKey, + SecretKey: ds.Settings.SecretKey, + }, + UserAgentName: aws.String("Cloudwatch")}, + ds.Settings.GrafanaSettings) + if err != nil { + return nil, err + } + + // work around until https://github.com/grafana/grafana/issues/39089 is implemented + if ds.Settings.GrafanaSettings.SecureSocksDSProxyEnabled && ds.Settings.SecureSocksProxyEnabled { + // only update the transport to try to avoid the issue mentioned here https://github.com/grafana/grafana/issues/46365 + // also, 'sess' is cached and reused, so the first time it might have the transport not set, the following uses it will + if sess.Config.HTTPClient.Transport == nil { + // following go standard library logic (https://pkg.go.dev/net/http#Client), if no Transport is provided, + // then we use http.DefaultTransport + defTransport, ok := http.DefaultTransport.(*http.Transport) + if !ok { + // this should not happen but validating just in case + return nil, errors.New("default http client transport is not of type http.Transport") + } + sess.Config.HTTPClient.Transport = defTransport.Clone() + } + err = proxy.New(ds.ProxyOpts).ConfigureSecureSocksHTTPProxy(sess.Config.HTTPClient.Transport.(*http.Transport)) + if err != nil { + return nil, fmt.Errorf("error configuring Secure Socks proxy for Transport: %w", err) + } + } else if sess.Config.HTTPClient != nil { + // Workaround for https://github.com/grafana/grafana/issues/91356 - PDC transport set above + // stays on the cached session after PDC is disabled + sess.Config.HTTPClient.Transport = nil + } + return sess, nil +} + +func (e *cloudWatchExecutor) newSessionFromContext(ctx context.Context, pluginCtx backend.PluginContext, region string) (*session.Session, error) { instance, err := e.getInstance(ctx, pluginCtx) if err != nil { - return aws.Config{}, err + return nil, err } - return instance.newAWSConfig(ctx, region) + + return instance.newSession(region) } func (e *cloudWatchExecutor) getInstance(ctx context.Context, pluginCtx backend.PluginContext) (*DataSource, error) { @@ -329,51 +364,44 @@ func (e *cloudWatchExecutor) getInstance(ctx context.Context, pluginCtx backend. return &instance, nil } -func (e *cloudWatchExecutor) getCWClient(ctx context.Context, pluginCtx backend.PluginContext, region string) (models.CWClient, error) { - cfg, err := e.getAWSConfig(ctx, pluginCtx, region) +func (e *cloudWatchExecutor) getCWClient(ctx context.Context, pluginCtx backend.PluginContext, region string) (cloudwatchiface.CloudWatchAPI, error) { + sess, err := e.newSessionFromContext(ctx, pluginCtx, region) if err != nil { return nil, err } - return NewCWClient(cfg), nil + return NewCWClient(sess), nil } -func (e *cloudWatchExecutor) getCWLogsClient(ctx context.Context, pluginCtx backend.PluginContext, region string) (models.CWLogsClient, error) { - cfg, err := e.getAWSConfig(ctx, pluginCtx, region) +func (e *cloudWatchExecutor) getCWLogsClient(ctx context.Context, pluginCtx backend.PluginContext, region string) (cloudwatchlogsiface.CloudWatchLogsAPI, error) { + sess, err := e.newSessionFromContext(ctx, pluginCtx, region) if err != nil { return nil, err } - logsClient := NewCWLogsClient(cfg) + logsClient := NewCWLogsClient(sess) return logsClient, nil } func (e *cloudWatchExecutor) getEC2Client(ctx context.Context, pluginCtx backend.PluginContext, region string) (models.EC2APIProvider, error) { - cfg, err := e.getAWSConfig(ctx, pluginCtx, region) + sess, err := e.newSessionFromContext(ctx, pluginCtx, region) if err != nil { return nil, err } - return NewEC2API(cfg), nil + return NewEC2Client(sess), nil } -func (e *cloudWatchExecutor) getRGTAClient(ctx context.Context, pluginCtx backend.PluginContext, region string) (resourcegroupstaggingapi.GetResourcesAPIClient, +func (e *cloudWatchExecutor) getRGTAClient(ctx context.Context, pluginCtx backend.PluginContext, region string) (resourcegroupstaggingapiiface.ResourceGroupsTaggingAPIAPI, error) { - cfg, err := e.getAWSConfig(ctx, pluginCtx, region) + sess, err := e.newSessionFromContext(ctx, pluginCtx, region) if err != nil { return nil, err } - return NewRGTAClient(cfg), nil + return newRGTAClient(sess), nil } -var terminatedStates = []cloudwatchlogstypes.QueryStatus{ - cloudwatchlogstypes.QueryStatusComplete, - cloudwatchlogstypes.QueryStatusCancelled, - cloudwatchlogstypes.QueryStatusFailed, - cloudwatchlogstypes.QueryStatusTimeout, -} - -func isTerminated(queryStatus cloudwatchlogstypes.QueryStatus) bool { - return slices.Contains(terminatedStates, queryStatus) +func isTerminated(queryStatus string) bool { + return queryStatus == "Complete" || queryStatus == "Cancelled" || queryStatus == "Failed" || queryStatus == "Timeout" } diff --git a/pkg/tsdb/cloudwatch/cloudwatch_integration_test.go b/pkg/tsdb/cloudwatch/cloudwatch_integration_test.go index 7b0cfcf75b9..e9ce1632e21 100644 --- a/pkg/tsdb/cloudwatch/cloudwatch_integration_test.go +++ b/pkg/tsdb/cloudwatch/cloudwatch_integration_test.go @@ -6,12 +6,12 @@ import ( "net/http" "testing" - "github.com/aws/aws-sdk-go-v2/aws" - cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" - "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" - cloudwatchlogstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" - "github.com/aws/aws-sdk-go-v2/service/ec2" - + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/client" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/cloudwatch" + "github.com/aws/aws-sdk-go/service/cloudwatchlogs" + "github.com/aws/aws-sdk-go/service/ec2" "github.com/grafana/grafana-aws-sdk/pkg/awsds" "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend/datasource" @@ -27,46 +27,46 @@ import ( func Test_CloudWatch_CallResource_Integration_Test(t *testing.T) { sender := &mockedCallResourceResponseSenderForOauth{} - origNewCWClient := NewCWClient + origNewMetricsAPI := NewMetricsAPI origNewOAMAPI := NewOAMAPI origNewLogsAPI := NewLogsAPI - origNewEC2API := NewEC2API - NewOAMAPI = func(aws.Config) models.OAMAPIProvider { return nil } + origNewEC2Client := NewEC2Client + NewOAMAPI = func(sess *session.Session) models.OAMAPIProvider { return nil } var logApi mocks.LogsAPI - NewLogsAPI = func(aws.Config) models.CloudWatchLogsAPIProvider { + NewLogsAPI = func(sess *session.Session) models.CloudWatchLogsAPIProvider { return &logApi } ec2Mock := &mocks.EC2Mock{} - ec2Mock.On("DescribeRegions", mock.Anything, mock.Anything).Return(&ec2.DescribeRegionsOutput{}, nil) - NewEC2API = func(aws.Config) models.EC2APIProvider { + ec2Mock.On("DescribeRegionsWithContext", mock.Anything, mock.Anything).Return(&ec2.DescribeRegionsOutput{}, nil) + NewEC2Client = func(provider client.ConfigProvider) models.EC2APIProvider { return ec2Mock } t.Cleanup(func() { NewOAMAPI = origNewOAMAPI - NewCWClient = origNewCWClient + NewMetricsAPI = origNewMetricsAPI NewLogsAPI = origNewLogsAPI - NewEC2API = origNewEC2API + NewEC2Client = origNewEC2Client }) var api mocks.FakeMetricsAPI - NewCWClient = func(aws.Config) models.CWClient { + NewMetricsAPI = func(sess *session.Session) models.CloudWatchMetricsAPIProvider { return &api } t.Run("Should handle dimension value request and return values from the api", func(t *testing.T) { - im := testInstanceManager(100, false) - api = mocks.FakeMetricsAPI{Metrics: []cloudwatchtypes.Metric{ - {MetricName: aws.String("Test_MetricName1"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1"), Value: aws.String("Value1")}, {Name: aws.String("Test_DimensionName2"), Value: aws.String("Value2")}}}, - {MetricName: aws.String("Test_MetricName2"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1"), Value: aws.String("Value3")}}}, - {MetricName: aws.String("Test_MetricName3"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName2"), Value: aws.String("Value1")}}}, - {MetricName: aws.String("Test_MetricName10"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName4"), Value: aws.String("Value2")}, {Name: aws.String("Test_DimensionName5")}}}, - {MetricName: aws.String("Test_MetricName4"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName2"), Value: aws.String("Value3")}}}, - {MetricName: aws.String("Test_MetricName5"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1"), Value: aws.String("Value4")}}}, - {MetricName: aws.String("Test_MetricName6"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1"), Value: aws.String("Value6")}}}, - {MetricName: aws.String("Test_MetricName7"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName4"), Value: aws.String("Value7")}}}, - {MetricName: aws.String("Test_MetricName8"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName4"), Value: aws.String("Value1")}}}, - {MetricName: aws.String("Test_MetricName9"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1"), Value: aws.String("Value2")}}}, + im := testInstanceManager(100) + api = mocks.FakeMetricsAPI{Metrics: []*cloudwatch.Metric{ + {MetricName: aws.String("Test_MetricName1"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1"), Value: aws.String("Value1")}, {Name: aws.String("Test_DimensionName2"), Value: aws.String("Value2")}}}, + {MetricName: aws.String("Test_MetricName2"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1"), Value: aws.String("Value3")}}}, + {MetricName: aws.String("Test_MetricName3"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName2"), Value: aws.String("Value1")}}}, + {MetricName: aws.String("Test_MetricName10"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName4"), Value: aws.String("Value2")}, {Name: aws.String("Test_DimensionName5")}}}, + {MetricName: aws.String("Test_MetricName4"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName2"), Value: aws.String("Value3")}}}, + {MetricName: aws.String("Test_MetricName5"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1"), Value: aws.String("Value4")}}}, + {MetricName: aws.String("Test_MetricName6"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1"), Value: aws.String("Value6")}}}, + {MetricName: aws.String("Test_MetricName7"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName4"), Value: aws.String("Value7")}}}, + {MetricName: aws.String("Test_MetricName8"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName4"), Value: aws.String("Value1")}}}, + {MetricName: aws.String("Test_MetricName9"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1"), Value: aws.String("Value2")}}}, }, MetricsPerPage: 100} executor := newExecutor(im, log.NewNullLogger()) @@ -91,18 +91,18 @@ func Test_CloudWatch_CallResource_Integration_Test(t *testing.T) { }) t.Run("Should handle dimension key filter query and return keys from the api", func(t *testing.T) { - im := testInstanceManager(3, false) - api = mocks.FakeMetricsAPI{Metrics: []cloudwatchtypes.Metric{ - {MetricName: aws.String("Test_MetricName1"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1")}, {Name: aws.String("Test_DimensionName2")}}}, - {MetricName: aws.String("Test_MetricName2"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1")}}}, - {MetricName: aws.String("Test_MetricName3"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName2")}}}, - {MetricName: aws.String("Test_MetricName10"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName4")}, {Name: aws.String("Test_DimensionName5")}}}, - {MetricName: aws.String("Test_MetricName4"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName2")}}}, - {MetricName: aws.String("Test_MetricName5"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1")}}}, - {MetricName: aws.String("Test_MetricName6"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1")}}}, - {MetricName: aws.String("Test_MetricName7"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName4")}}}, - {MetricName: aws.String("Test_MetricName8"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName4")}}}, - {MetricName: aws.String("Test_MetricName9"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1")}}}, + im := testInstanceManager(3) + api = mocks.FakeMetricsAPI{Metrics: []*cloudwatch.Metric{ + {MetricName: aws.String("Test_MetricName1"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1")}, {Name: aws.String("Test_DimensionName2")}}}, + {MetricName: aws.String("Test_MetricName2"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1")}}}, + {MetricName: aws.String("Test_MetricName3"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName2")}}}, + {MetricName: aws.String("Test_MetricName10"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName4")}, {Name: aws.String("Test_DimensionName5")}}}, + {MetricName: aws.String("Test_MetricName4"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName2")}}}, + {MetricName: aws.String("Test_MetricName5"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1")}}}, + {MetricName: aws.String("Test_MetricName6"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1")}}}, + {MetricName: aws.String("Test_MetricName7"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName4")}}}, + {MetricName: aws.String("Test_MetricName8"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName4")}}}, + {MetricName: aws.String("Test_MetricName9"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1")}}}, }, MetricsPerPage: 2} executor := newExecutor(im, log.NewNullLogger()) @@ -177,18 +177,18 @@ func Test_CloudWatch_CallResource_Integration_Test(t *testing.T) { }) t.Run("Should handle custom namespace metrics query and return metrics from api", func(t *testing.T) { - im := testInstanceManager(3, false) - api = mocks.FakeMetricsAPI{Metrics: []cloudwatchtypes.Metric{ - {MetricName: aws.String("Test_MetricName1"), Namespace: aws.String("AWS/EC2"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1")}, {Name: aws.String("Test_DimensionName2")}}}, - {MetricName: aws.String("Test_MetricName2"), Namespace: aws.String("AWS/EC2"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1")}}}, - {MetricName: aws.String("Test_MetricName3"), Namespace: aws.String("AWS/ECS"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName2")}}}, - {MetricName: aws.String("Test_MetricName10"), Namespace: aws.String("AWS/ECS"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName4")}, {Name: aws.String("Test_DimensionName5")}}}, - {MetricName: aws.String("Test_MetricName4"), Namespace: aws.String("AWS/ECS"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName2")}}}, - {MetricName: aws.String("Test_MetricName5"), Namespace: aws.String("AWS/Redshift"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1")}}}, - {MetricName: aws.String("Test_MetricName6"), Namespace: aws.String("AWS/Redshift"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1")}}}, - {MetricName: aws.String("Test_MetricName7"), Namespace: aws.String("AWS/EC2"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName4")}}}, - {MetricName: aws.String("Test_MetricName8"), Namespace: aws.String("AWS/EC2"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName4")}}}, - {MetricName: aws.String("Test_MetricName9"), Namespace: aws.String("AWS/EC2"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1")}}}, + im := testInstanceManager(3) + api = mocks.FakeMetricsAPI{Metrics: []*cloudwatch.Metric{ + {MetricName: aws.String("Test_MetricName1"), Namespace: aws.String("AWS/EC2"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1")}, {Name: aws.String("Test_DimensionName2")}}}, + {MetricName: aws.String("Test_MetricName2"), Namespace: aws.String("AWS/EC2"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1")}}}, + {MetricName: aws.String("Test_MetricName3"), Namespace: aws.String("AWS/ECS"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName2")}}}, + {MetricName: aws.String("Test_MetricName10"), Namespace: aws.String("AWS/ECS"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName4")}, {Name: aws.String("Test_DimensionName5")}}}, + {MetricName: aws.String("Test_MetricName4"), Namespace: aws.String("AWS/ECS"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName2")}}}, + {MetricName: aws.String("Test_MetricName5"), Namespace: aws.String("AWS/Redshift"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1")}}}, + {MetricName: aws.String("Test_MetricName6"), Namespace: aws.String("AWS/Redshift"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1")}}}, + {MetricName: aws.String("Test_MetricName7"), Namespace: aws.String("AWS/EC2"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName4")}}}, + {MetricName: aws.String("Test_MetricName8"), Namespace: aws.String("AWS/EC2"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName4")}}}, + {MetricName: aws.String("Test_MetricName9"), Namespace: aws.String("AWS/EC2"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1")}}}, }, MetricsPerPage: 2} executor := newExecutor(im, log.NewNullLogger()) @@ -215,15 +215,15 @@ func Test_CloudWatch_CallResource_Integration_Test(t *testing.T) { t.Run("Should handle log group fields request", func(t *testing.T) { im := defaultTestInstanceManager() logApi = mocks.LogsAPI{} - logApi.On("GetLogGroupFields", mock.Anything).Return(&cloudwatchlogs.GetLogGroupFieldsOutput{ - LogGroupFields: []cloudwatchlogstypes.LogGroupField{ + logApi.On("GetLogGroupFieldsWithContext", mock.Anything).Return(&cloudwatchlogs.GetLogGroupFieldsOutput{ + LogGroupFields: []*cloudwatchlogs.LogGroupField{ { Name: aws.String("field1"), - Percent: 50, + Percent: aws.Int64(50), }, { Name: aws.String("field2"), - Percent: 50, + Percent: aws.Int64(50), }, }, }, nil) diff --git a/pkg/tsdb/cloudwatch/cloudwatch_test.go b/pkg/tsdb/cloudwatch/cloudwatch_test.go index e7a2c163bb9..5e36adcda4c 100644 --- a/pkg/tsdb/cloudwatch/cloudwatch_test.go +++ b/pkg/tsdb/cloudwatch/cloudwatch_test.go @@ -6,11 +6,11 @@ import ( "testing" "time" - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/cloudwatch" - "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" - cloudwatchlogstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" - + "github.com/aws/aws-sdk-go/aws" + awsclient "github.com/aws/aws-sdk-go/aws/client" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/cloudwatch" + "github.com/aws/aws-sdk-go/service/cloudwatchlogs" "github.com/google/go-cmp/cmp" "github.com/grafana/grafana-aws-sdk/pkg/awsds" "github.com/grafana/grafana-plugin-sdk-go/backend" @@ -114,21 +114,21 @@ func TestNewInstanceSettings(t *testing.T) { } func Test_CheckHealth(t *testing.T) { - origNewCWClient := NewCWClient + origNewMetricsAPI := NewMetricsAPI origNewCWLogsClient := NewCWLogsClient origNewLogsAPI := NewLogsAPI t.Cleanup(func() { - NewCWClient = origNewCWClient + NewMetricsAPI = origNewMetricsAPI NewCWLogsClient = origNewCWLogsClient NewLogsAPI = origNewLogsAPI }) var client fakeCheckHealthClient - NewCWClient = func(aws.Config) models.CWClient { + NewMetricsAPI = func(sess *session.Session) models.CloudWatchMetricsAPIProvider { return client } - NewLogsAPI = func(aws.Config) models.CloudWatchLogsAPIProvider { + NewLogsAPI = func(sess *session.Session) models.CloudWatchLogsAPIProvider { return client } im := defaultTestInstanceManager() @@ -138,7 +138,8 @@ func Test_CheckHealth(t *testing.T) { executor := newExecutor(im, log.NewNullLogger()) resp, err := executor.CheckHealth(context.Background(), &backend.CheckHealthRequest{ - PluginContext: backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}}}) + PluginContext: backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}}, + }) assert.NoError(t, err) assert.Equal(t, &backend.CheckHealthResult{ @@ -149,7 +150,7 @@ func Test_CheckHealth(t *testing.T) { t.Run("successfully queries metrics, fails during logs query", func(t *testing.T) { client = fakeCheckHealthClient{ - describeLogGroupsFunction: func(context.Context, *cloudwatchlogs.DescribeLogGroupsInput, ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.DescribeLogGroupsOutput, error) { + describeLogGroups: func(input *cloudwatchlogs.DescribeLogGroupsInput) (*cloudwatchlogs.DescribeLogGroupsOutput, error) { return nil, fmt.Errorf("some logs query error") }} @@ -168,8 +169,8 @@ func Test_CheckHealth(t *testing.T) { t.Run("successfully queries logs, fails during metrics query", func(t *testing.T) { client = fakeCheckHealthClient{ - listMetricsFunction: func(context.Context, *cloudwatch.ListMetricsInput, ...func(*cloudwatch.Options)) (*cloudwatch.ListMetricsOutput, error) { - return nil, fmt.Errorf("some list metrics error") + listMetricsPages: func(input *cloudwatch.ListMetricsInput, fn func(*cloudwatch.ListMetricsOutput, bool) bool) error { + return fmt.Errorf("some list metrics error") }} executor := newExecutor(im, log.NewNullLogger()) @@ -187,7 +188,14 @@ func Test_CheckHealth(t *testing.T) { t.Run("fail to get clients", func(t *testing.T) { client = fakeCheckHealthClient{} - im := testInstanceManagerWithSettings(models.CloudWatchSettings{AWSDatasourceSettings: awsds.AWSDatasourceSettings{Region: "us-east-1"}}, true) + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{ + Settings: models.CloudWatchSettings{AWSDatasourceSettings: awsds.AWSDatasourceSettings{Region: "us-east-1"}}, + sessions: &fakeSessionCache{getSessionWithAuthSettings: func(c awsds.GetSessionConfig, a awsds.AuthSettings) (*session.Session, error) { + return nil, fmt.Errorf("some sessions error") + }}, + }, nil + }) executor := newExecutor(im, log.NewNullLogger()) @@ -198,14 +206,12 @@ func Test_CheckHealth(t *testing.T) { assert.NoError(t, err) assert.Equal(t, &backend.CheckHealthResult{ Status: backend.HealthStatusError, - Message: "1. CloudWatch metrics query failed: LoadDefaultConfig failed\n2. CloudWatch logs query failed: LoadDefaultConfig failed", + Message: "1. CloudWatch metrics query failed: some sessions error\n2. CloudWatch logs query failed: some sessions error", }, resp) }) } -func TestGetAWSConfig_passes_authSettings(t *testing.T) { - // TODO: update this for the new auth structure, or remove it - t.Skip() +func TestNewSession_passes_authSettings(t *testing.T) { ctxDuration := 15 * time.Minute expectedSettings := awsds.AuthSettings{ AllowedAuthProviders: []string{"foo", "bar", "baz"}, @@ -215,7 +221,7 @@ func TestGetAWSConfig_passes_authSettings(t *testing.T) { ListMetricsPageLimit: 50, SecureSocksDSProxyEnabled: true, } - im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + im := datasource.NewInstanceManager((func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { return DataSource{ Settings: models.CloudWatchSettings{ AWSDatasourceSettings: awsds.AWSDatasourceSettings{ @@ -223,33 +229,39 @@ func TestGetAWSConfig_passes_authSettings(t *testing.T) { }, GrafanaSettings: expectedSettings, }, + sessions: &fakeSessionCache{getSessionWithAuthSettings: func(c awsds.GetSessionConfig, a awsds.AuthSettings) (*session.Session, error) { + assert.Equal(t, expectedSettings, a) + return &session.Session{ + Config: &aws.Config{}, + }, nil + }}, }, nil - }) + })) executor := newExecutor(im, log.NewNullLogger()) - _, err := executor.getAWSConfig(context.Background(), + _, err := executor.newSessionFromContext(context.Background(), backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}}, "us-east-1") require.NoError(t, err) } func TestQuery_ResourceRequest_DescribeLogGroups_with_CrossAccountQuerying(t *testing.T) { sender := &mockedCallResourceResponseSenderForOauth{} - origNewMetricsAPI := NewCWClient + origNewMetricsAPI := NewMetricsAPI origNewOAMAPI := NewOAMAPI origNewLogsAPI := NewLogsAPI - origNewEC2API := NewEC2API - NewCWClient = func(aws.Config) models.CWClient { return nil } - NewOAMAPI = func(aws.Config) models.OAMAPIProvider { return nil } - NewEC2API = func(aws.Config) models.EC2APIProvider { return nil } + origNewEC2Client := NewEC2Client + NewMetricsAPI = func(sess *session.Session) models.CloudWatchMetricsAPIProvider { return nil } + NewOAMAPI = func(sess *session.Session) models.OAMAPIProvider { return nil } + NewEC2Client = func(provider awsclient.ConfigProvider) models.EC2APIProvider { return nil } t.Cleanup(func() { NewOAMAPI = origNewOAMAPI - NewCWClient = origNewMetricsAPI + NewMetricsAPI = origNewMetricsAPI NewLogsAPI = origNewLogsAPI - NewEC2API = origNewEC2API + NewEC2Client = origNewEC2Client }) var logsApi mocks.LogsAPI - NewLogsAPI = func(aws.Config) models.CloudWatchLogsAPIProvider { + NewLogsAPI = func(sess *session.Session) models.CloudWatchLogsAPIProvider { return &logsApi } @@ -257,8 +269,8 @@ func TestQuery_ResourceRequest_DescribeLogGroups_with_CrossAccountQuerying(t *te t.Run("maps log group api response to resource response of log-groups", func(t *testing.T) { logsApi = mocks.LogsAPI{} - logsApi.On("DescribeLogGroups", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{ - LogGroups: []cloudwatchlogstypes.LogGroup{ + logsApi.On("DescribeLogGroupsWithContext", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{ + LogGroups: []*cloudwatchlogs.LogGroup{ {Arn: aws.String("arn:aws:logs:us-east-1:111:log-group:group_a"), LogGroupName: aws.String("group_a")}, }, }, nil) @@ -285,11 +297,11 @@ func TestQuery_ResourceRequest_DescribeLogGroups_with_CrossAccountQuerying(t *te } ]`, string(sender.Response.Body)) - logsApi.AssertCalled(t, "DescribeLogGroups", + logsApi.AssertCalled(t, "DescribeLogGroupsWithContext", &cloudwatchlogs.DescribeLogGroupsInput{ - AccountIdentifiers: []string{"some-account-id"}, + AccountIdentifiers: []*string{utils.Pointer("some-account-id")}, IncludeLinkedAccounts: utils.Pointer(true), - Limit: aws.Int32(50), + Limit: utils.Pointer(int64(50)), LogGroupNamePrefix: utils.Pointer("some-pattern"), }) }) diff --git a/pkg/tsdb/cloudwatch/get_dimension_values_for_wildcards.go b/pkg/tsdb/cloudwatch/get_dimension_values_for_wildcards.go index ddc281c196f..2919304d84a 100644 --- a/pkg/tsdb/cloudwatch/get_dimension_values_for_wildcards.go +++ b/pkg/tsdb/cloudwatch/get_dimension_values_for_wildcards.go @@ -9,7 +9,6 @@ import ( "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/services" - "github.com/patrickmn/go-cache" ) @@ -30,7 +29,7 @@ func shouldSkipFetchingWildcards(ctx context.Context, q *models.CloudWatchQuery) func (e *cloudWatchExecutor) getDimensionValuesForWildcards( ctx context.Context, region string, - client models.CWClient, + client models.CloudWatchMetricsAPIProvider, origQueries []*models.CloudWatchQuery, tagValueCache *cache.Cache, listMetricsPageLimit int, diff --git a/pkg/tsdb/cloudwatch/get_dimension_values_for_wildcards_test.go b/pkg/tsdb/cloudwatch/get_dimension_values_for_wildcards_test.go index 2d8058f2b76..5549bc3fc5b 100644 --- a/pkg/tsdb/cloudwatch/get_dimension_values_for_wildcards_test.go +++ b/pkg/tsdb/cloudwatch/get_dimension_values_for_wildcards_test.go @@ -4,8 +4,7 @@ import ( "context" "testing" - cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" - + "github.com/aws/aws-sdk-go/service/cloudwatch" "github.com/grafana/grafana-plugin-sdk-go/backend/log" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/kinds/dataquery" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/mocks" @@ -30,10 +29,10 @@ func TestGetDimensionValuesForWildcards(t *testing.T) { query.Dimensions = map[string][]string{"Test_DimensionName": {"*"}} query.MetricQueryType = models.MetricQueryTypeSearch query.MatchExact = false - api := &mocks.MetricsAPI{Metrics: []cloudwatchtypes.Metric{ - {MetricName: utils.Pointer("Test_MetricName"), Dimensions: []cloudwatchtypes.Dimension{{Name: utils.Pointer("Test_DimensionName"), Value: utils.Pointer("Value")}}}, + api := &mocks.MetricsAPI{Metrics: []*cloudwatch.Metric{ + {MetricName: utils.Pointer("Test_MetricName"), Dimensions: []*cloudwatch.Dimension{{Name: utils.Pointer("Test_DimensionName"), Value: utils.Pointer("Value")}}}, }} - api.On("ListMetrics").Return(nil) + api.On("ListMetricsPagesWithContext").Return(nil) _, err := executor.getDimensionValuesForWildcards(ctx, "us-east-1", api, []*models.CloudWatchQuery{query}, tagValueCache, 50, noSkip) assert.Nil(t, err) // make sure the original query wasn't altered @@ -53,8 +52,8 @@ func TestGetDimensionValuesForWildcards(t *testing.T) { query.Dimensions = map[string][]string{"Test_DimensionName2": {"*"}} query.MetricQueryType = models.MetricQueryTypeSearch query.MatchExact = false - api := &mocks.MetricsAPI{Metrics: []cloudwatchtypes.Metric{}} - api.On("ListMetrics").Return(nil) + api := &mocks.MetricsAPI{Metrics: []*cloudwatch.Metric{}} + api.On("ListMetricsPagesWithContext").Return(nil) queries, err := executor.getDimensionValuesForWildcards(ctx, "us-east-1", api, []*models.CloudWatchQuery{query}, tagValueCache, 50, noSkip) assert.Nil(t, err) assert.Len(t, queries, 1) @@ -62,10 +61,10 @@ func TestGetDimensionValuesForWildcards(t *testing.T) { assert.Equal(t, map[string][]string{"Test_DimensionName2": {}}, queries[0].Dimensions) // Confirm that it calls the api again if the last call did not return any values - api.Metrics = []cloudwatchtypes.Metric{ - {MetricName: utils.Pointer("Test_MetricName"), Dimensions: []cloudwatchtypes.Dimension{{Name: utils.Pointer("Test_DimensionName2"), Value: utils.Pointer("Value")}}}, + api.Metrics = []*cloudwatch.Metric{ + {MetricName: utils.Pointer("Test_MetricName"), Dimensions: []*cloudwatch.Dimension{{Name: utils.Pointer("Test_DimensionName2"), Value: utils.Pointer("Value")}}}, } - api.On("ListMetrics").Return(nil) + api.On("ListMetricsPagesWithContext").Return(nil) queries, err = executor.getDimensionValuesForWildcards(ctx, "us-east-1", api, []*models.CloudWatchQuery{query}, tagValueCache, 50, noSkip) assert.Nil(t, err) assert.Len(t, queries, 1) @@ -133,13 +132,13 @@ func TestGetDimensionValuesForWildcards(t *testing.T) { query.Dimensions = map[string][]string{"Test_DimensionName1": {"*"}} query.MetricQueryType = models.MetricQueryTypeSearch query.MatchExact = false - api := &mocks.MetricsAPI{Metrics: []cloudwatchtypes.Metric{ - {MetricName: utils.Pointer("Test_MetricName1"), Dimensions: []cloudwatchtypes.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Value1")}, {Name: utils.Pointer("Test_DimensionName2"), Value: utils.Pointer("Value2")}}}, - {MetricName: utils.Pointer("Test_MetricName2"), Dimensions: []cloudwatchtypes.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Value3")}}}, - {MetricName: utils.Pointer("Test_MetricName3"), Dimensions: []cloudwatchtypes.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Value4")}}}, - {MetricName: utils.Pointer("Test_MetricName4"), Dimensions: []cloudwatchtypes.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Value2")}}}, + api := &mocks.MetricsAPI{Metrics: []*cloudwatch.Metric{ + {MetricName: utils.Pointer("Test_MetricName1"), Dimensions: []*cloudwatch.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Value1")}, {Name: utils.Pointer("Test_DimensionName2"), Value: utils.Pointer("Value2")}}}, + {MetricName: utils.Pointer("Test_MetricName2"), Dimensions: []*cloudwatch.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Value3")}}}, + {MetricName: utils.Pointer("Test_MetricName3"), Dimensions: []*cloudwatch.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Value4")}}}, + {MetricName: utils.Pointer("Test_MetricName4"), Dimensions: []*cloudwatch.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Value2")}}}, }} - api.On("ListMetrics").Return(nil) + api.On("ListMetricsPagesWithContext").Return(nil) queries, err := executor.getDimensionValuesForWildcards(ctx, "us-east-1", api, []*models.CloudWatchQuery{query}, cache.New(0, 0), 50, shouldSkipFetchingWildcards) assert.Nil(t, err) assert.Len(t, queries, 1) @@ -168,13 +167,13 @@ func TestGetDimensionValuesForWildcards(t *testing.T) { } query.MetricQueryType = models.MetricQueryTypeQuery - api := &mocks.MetricsAPI{Metrics: []cloudwatchtypes.Metric{ - {MetricName: utils.Pointer("Test_MetricName"), Dimensions: []cloudwatchtypes.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Dimension1Value1")}, {Name: utils.Pointer("Test_DimensionName2"), Value: utils.Pointer("Dimension2Value1")}}}, - {MetricName: utils.Pointer("Test_MetricName"), Dimensions: []cloudwatchtypes.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Dimension1Value2")}, {Name: utils.Pointer("Test_DimensionName2"), Value: utils.Pointer("Dimension2Value2")}}}, - {MetricName: utils.Pointer("Test_MetricName"), Dimensions: []cloudwatchtypes.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Dimension1Value3")}, {Name: utils.Pointer("Test_DimensionName2"), Value: utils.Pointer("Dimension2Value3")}}}, - {MetricName: utils.Pointer("Test_MetricName"), Dimensions: []cloudwatchtypes.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Dimension1Value4")}, {Name: utils.Pointer("Test_DimensionName2"), Value: utils.Pointer("Dimension2Value4")}}}, + api := &mocks.MetricsAPI{Metrics: []*cloudwatch.Metric{ + {MetricName: utils.Pointer("Test_MetricName"), Dimensions: []*cloudwatch.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Dimension1Value1")}, {Name: utils.Pointer("Test_DimensionName2"), Value: utils.Pointer("Dimension2Value1")}}}, + {MetricName: utils.Pointer("Test_MetricName"), Dimensions: []*cloudwatch.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Dimension1Value2")}, {Name: utils.Pointer("Test_DimensionName2"), Value: utils.Pointer("Dimension2Value2")}}}, + {MetricName: utils.Pointer("Test_MetricName"), Dimensions: []*cloudwatch.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Dimension1Value3")}, {Name: utils.Pointer("Test_DimensionName2"), Value: utils.Pointer("Dimension2Value3")}}}, + {MetricName: utils.Pointer("Test_MetricName"), Dimensions: []*cloudwatch.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Dimension1Value4")}, {Name: utils.Pointer("Test_DimensionName2"), Value: utils.Pointer("Dimension2Value4")}}}, }} - api.On("ListMetrics").Return(nil) + api.On("ListMetricsPagesWithContext").Return(nil) queries, err := executor.getDimensionValuesForWildcards(ctx, "us-east-1", api, []*models.CloudWatchQuery{query}, cache.New(0, 0), 50, noSkip) assert.Nil(t, err) assert.Len(t, queries, 1) diff --git a/pkg/tsdb/cloudwatch/get_metric_data_executor.go b/pkg/tsdb/cloudwatch/get_metric_data_executor.go index 99395e03452..6ad996ab6e1 100644 --- a/pkg/tsdb/cloudwatch/get_metric_data_executor.go +++ b/pkg/tsdb/cloudwatch/get_metric_data_executor.go @@ -4,16 +4,15 @@ import ( "context" "time" - "github.com/aws/aws-sdk-go-v2/aws" - - "github.com/aws/aws-sdk-go-v2/service/cloudwatch" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/cloudwatch" + "github.com/aws/aws-sdk-go/service/cloudwatch/cloudwatchiface" "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/features" - "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/utils" ) -func (e *cloudWatchExecutor) executeRequest(ctx context.Context, client models.CWClient, +func (e *cloudWatchExecutor) executeRequest(ctx context.Context, client cloudwatchiface.CloudWatchAPI, metricDataInput *cloudwatch.GetMetricDataInput) ([]*cloudwatch.GetMetricDataOutput, error) { mdo := make([]*cloudwatch.GetMetricDataOutput, 0) @@ -27,7 +26,7 @@ func (e *cloudWatchExecutor) executeRequest(ctx context.Context, client models.C *metricDataInput.EndTime = metricDataInput.EndTime.Truncate(time.Minute).Add(time.Minute) } - resp, err := client.GetMetricData(ctx, metricDataInput) + resp, err := client.GetMetricDataWithContext(ctx, metricDataInput) if err != nil { return mdo, backend.DownstreamError(err) } diff --git a/pkg/tsdb/cloudwatch/get_metric_data_executor_test.go b/pkg/tsdb/cloudwatch/get_metric_data_executor_test.go index b940fb0f4de..3485daff5da 100644 --- a/pkg/tsdb/cloudwatch/get_metric_data_executor_test.go +++ b/pkg/tsdb/cloudwatch/get_metric_data_executor_test.go @@ -5,10 +5,8 @@ import ( "testing" "time" - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/cloudwatch" - cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" - + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/cloudwatch" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/features" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/mocks" "github.com/stretchr/testify/assert" @@ -20,37 +18,37 @@ func TestGetMetricDataExecutorTestRequest(t *testing.T) { t.Run("Should round up end time if cloudWatchRoundUpEndTime is enabled", func(t *testing.T) { executor := &cloudWatchExecutor{} queryEndTime, _ := time.Parse("2006-01-02T15:04:05Z07:00", "2024-05-01T01:45:04Z") - inputs := &cloudwatch.GetMetricDataInput{EndTime: &queryEndTime, MetricDataQueries: []cloudwatchtypes.MetricDataQuery{}} + inputs := &cloudwatch.GetMetricDataInput{EndTime: &queryEndTime, MetricDataQueries: []*cloudwatch.MetricDataQuery{}} mockMetricClient := &mocks.MetricsAPI{} - mockMetricClient.On("GetMetricData", mock.Anything, mock.Anything, mock.Anything).Return( + mockMetricClient.On("GetMetricDataWithContext", mock.Anything, mock.Anything, mock.Anything).Return( &cloudwatch.GetMetricDataOutput{ - MetricDataResults: []cloudwatchtypes.MetricDataResult{{Values: []float64{}}}, + MetricDataResults: []*cloudwatch.MetricDataResult{{Values: []*float64{}}}, }, nil).Once() _, err := executor.executeRequest(contextWithFeaturesEnabled(features.FlagCloudWatchRoundUpEndTime), mockMetricClient, inputs) require.NoError(t, err) expectedTime, _ := time.Parse("2006-01-02T15:04:05Z07:00", "2024-05-01T01:46:00Z") - expectedInput := &cloudwatch.GetMetricDataInput{EndTime: &expectedTime, MetricDataQueries: []cloudwatchtypes.MetricDataQuery{}} - mockMetricClient.AssertCalled(t, "GetMetricData", mock.Anything, expectedInput, mock.Anything) + expectedInput := &cloudwatch.GetMetricDataInput{EndTime: &expectedTime, MetricDataQueries: []*cloudwatch.MetricDataQuery{}} + mockMetricClient.AssertCalled(t, "GetMetricDataWithContext", mock.Anything, expectedInput, mock.Anything) }) } func TestGetMetricDataExecutorTestResponse(t *testing.T) { executor := &cloudWatchExecutor{} - inputs := &cloudwatch.GetMetricDataInput{EndTime: aws.Time(time.Now()), MetricDataQueries: []cloudwatchtypes.MetricDataQuery{}} + inputs := &cloudwatch.GetMetricDataInput{EndTime: aws.Time(time.Now()), MetricDataQueries: []*cloudwatch.MetricDataQuery{}} mockMetricClient := &mocks.MetricsAPI{} - mockMetricClient.On("GetMetricData", mock.Anything, mock.Anything, mock.Anything).Return( + mockMetricClient.On("GetMetricDataWithContext", mock.Anything, mock.Anything, mock.Anything).Return( &cloudwatch.GetMetricDataOutput{ - MetricDataResults: []cloudwatchtypes.MetricDataResult{{Values: []float64{12.3, 23.5}}}, + MetricDataResults: []*cloudwatch.MetricDataResult{{Values: []*float64{aws.Float64(12.3), aws.Float64(23.5)}}}, NextToken: aws.String("next"), }, nil).Once() - mockMetricClient.On("GetMetricData", mock.Anything, mock.Anything, mock.Anything).Return( + mockMetricClient.On("GetMetricDataWithContext", mock.Anything, mock.Anything, mock.Anything).Return( &cloudwatch.GetMetricDataOutput{ - MetricDataResults: []cloudwatchtypes.MetricDataResult{{Values: []float64{100}}}, + MetricDataResults: []*cloudwatch.MetricDataResult{{Values: []*float64{aws.Float64(100)}}}, }, nil).Once() res, err := executor.executeRequest(context.Background(), mockMetricClient, inputs) require.NoError(t, err) require.Len(t, res, 2) require.Len(t, res[0].MetricDataResults[0].Values, 2) - assert.Equal(t, 23.5, res[0].MetricDataResults[0].Values[1]) - assert.Equal(t, 100.0, res[1].MetricDataResults[0].Values[0]) + assert.Equal(t, 23.5, *res[0].MetricDataResults[0].Values[1]) + assert.Equal(t, 100.0, *res[1].MetricDataResults[0].Values[0]) } diff --git a/pkg/tsdb/cloudwatch/log_actions.go b/pkg/tsdb/cloudwatch/log_actions.go index fdc197183af..80572895974 100644 --- a/pkg/tsdb/cloudwatch/log_actions.go +++ b/pkg/tsdb/cloudwatch/log_actions.go @@ -10,12 +10,11 @@ import ( "strings" "time" - "github.com/aws/smithy-go" - - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" - cloudwatchlogstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" - + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/aws/request" + "github.com/aws/aws-sdk-go/service/cloudwatchlogs" + "github.com/aws/aws-sdk-go/service/cloudwatchlogs/cloudwatchlogsiface" "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/data" "golang.org/x/sync/errgroup" @@ -26,8 +25,10 @@ import ( ) const ( - defaultEventLimit = int32(10) - defaultLogGroupLimit = int32(50) + limitExceededException = "LimitExceededException" + throttlingException = "ThrottlingException" + defaultEventLimit = int64(10) + defaultLogGroupLimit = int64(50) logIdentifierInternal = "__log__grafana_internal__" logStreamIdentifierInternal = "__logstream__grafana_internal__" ) @@ -42,6 +43,45 @@ func (e *AWSError) Error() string { return fmt.Sprintf("CloudWatch error: %s: %s", e.Code, e.Message) } +// StartQueryInputWithLanguage copies the StartQueryInput struct from aws-sdk-go@v1.55.5 +// (https://github.com/aws/aws-sdk-go/blob/7112c0a0c2d01713a9db2d57f0e5722225baf5b5/service/cloudwatchlogs/api.go#L19541) +// to add support for the new QueryLanguage parameter, which is unlikely to be backported +// since v1 of the aws-sdk-go is in maintenance mode. We've removed the comments for +// clarity. +type StartQueryInputWithLanguage struct { + _ struct{} `type:"structure"` + + EndTime *int64 `locationName:"endTime" type:"long" required:"true"` + Limit *int64 `locationName:"limit" min:"1" type:"integer"` + LogGroupIdentifiers []*string `locationName:"logGroupIdentifiers" type:"list"` + LogGroupName *string `locationName:"logGroupName" min:"1" type:"string"` + LogGroupNames []*string `locationName:"logGroupNames" type:"list"` + QueryString *string `locationName:"queryString" type:"string" required:"true"` + // QueryLanguage is the only change here from the original code. + QueryLanguage *string `locationName:"queryLanguage" type:"string"` + StartTime *int64 `locationName:"startTime" type:"long" required:"true"` +} +type WithQueryLanguageFunc func(language *dataquery.LogsQueryLanguage) func(*request.Request) + +// WithQueryLanguage assigns the function to a variable in order to mock it in log_actions_test.go +var WithQueryLanguage WithQueryLanguageFunc = withQueryLanguage + +func withQueryLanguage(language *dataquery.LogsQueryLanguage) func(request *request.Request) { + return func(request *request.Request) { + sqi := request.Params.(*cloudwatchlogs.StartQueryInput) + request.Params = &StartQueryInputWithLanguage{ + EndTime: sqi.EndTime, + Limit: sqi.Limit, + LogGroupIdentifiers: sqi.LogGroupIdentifiers, + LogGroupName: sqi.LogGroupName, + LogGroupNames: sqi.LogGroupNames, + QueryString: sqi.QueryString, + QueryLanguage: (*string)(language), + StartTime: sqi.StartTime, + } + } +} + func (e *cloudWatchExecutor) executeLogActions(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) { resp := backend.NewQueryDataResponse() @@ -128,35 +168,37 @@ func (e *cloudWatchExecutor) executeLogAction(ctx context.Context, logsQuery mod return data, nil } -func (e *cloudWatchExecutor) handleGetLogEvents(ctx context.Context, logsClient models.CWLogsClient, +func (e *cloudWatchExecutor) handleGetLogEvents(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI, logsQuery models.LogsQuery) (*data.Frame, error) { limit := defaultEventLimit if logsQuery.Limit != nil && *logsQuery.Limit > 0 { limit = *logsQuery.Limit } + + queryRequest := &cloudwatchlogs.GetLogEventsInput{ + Limit: aws.Int64(limit), + StartFromHead: aws.Bool(logsQuery.StartFromHead), + } + if logsQuery.LogGroupName == "" { return nil, backend.DownstreamError(fmt.Errorf("Error: Parameter 'logGroupName' is required")) } + queryRequest.SetLogGroupName(logsQuery.LogGroupName) + if logsQuery.LogStreamName == "" { return nil, backend.DownstreamError(fmt.Errorf("Error: Parameter 'logStreamName' is required")) } - - queryRequest := &cloudwatchlogs.GetLogEventsInput{ - Limit: aws.Int32(limit), - StartFromHead: aws.Bool(logsQuery.StartFromHead), - LogGroupName: &logsQuery.LogGroupName, - LogStreamName: &logsQuery.LogStreamName, - } + queryRequest.SetLogStreamName(logsQuery.LogStreamName) if logsQuery.StartTime != nil && *logsQuery.StartTime != 0 { - queryRequest.StartTime = logsQuery.StartTime + queryRequest.SetStartTime(*logsQuery.StartTime) } if logsQuery.EndTime != nil && *logsQuery.EndTime != 0 { - queryRequest.EndTime = logsQuery.EndTime + queryRequest.SetEndTime(*logsQuery.EndTime) } - logEvents, err := logsClient.GetLogEvents(ctx, queryRequest) + logEvents, err := logsClient.GetLogEventsWithContext(ctx, queryRequest) if err != nil { return nil, backend.DownstreamError(err) } @@ -181,7 +223,7 @@ func (e *cloudWatchExecutor) handleGetLogEvents(ctx context.Context, logsClient return data.NewFrame("logEvents", timestampField, messageField), nil } -func (e *cloudWatchExecutor) executeStartQuery(ctx context.Context, logsClient models.CWLogsClient, +func (e *cloudWatchExecutor) executeStartQuery(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI, logsQuery models.LogsQuery, timeRange backend.TimeRange) (*cloudwatchlogs.StartQueryOutput, error) { startTime := timeRange.From endTime := timeRange.To @@ -225,34 +267,34 @@ func (e *cloudWatchExecutor) executeStartQuery(ctx context.Context, logsClient m // due to a bug in the startQuery api, we remove * from the arn, otherwise it throws an error logGroupIdentifiers = append(logGroupIdentifiers, strings.TrimSuffix(arn, "*")) } - startQueryInput.LogGroupIdentifiers = logGroupIdentifiers + startQueryInput.LogGroupIdentifiers = aws.StringSlice(logGroupIdentifiers) } else { // even though log group names are being phased out, we still need to support them for backwards compatibility and alert queries - startQueryInput.LogGroupNames = logsQuery.LogGroupNames + startQueryInput.LogGroupNames = aws.StringSlice(logsQuery.LogGroupNames) } } if logsQuery.Limit != nil { - startQueryInput.Limit = aws.Int32(*logsQuery.Limit) - } - if logsQuery.QueryLanguage != nil { - startQueryInput.QueryLanguage = cloudwatchlogstypes.QueryLanguage(*logsQuery.QueryLanguage) + startQueryInput.Limit = aws.Int64(*logsQuery.Limit) } e.logger.FromContext(ctx).Debug("Calling startquery with context with input", "input", startQueryInput) - resp, err := logsClient.StartQuery(ctx, startQueryInput) + resp, err := logsClient.StartQueryWithContext(ctx, startQueryInput, WithQueryLanguage(logsQuery.QueryLanguage)) if err != nil { - if errors.Is(err, &cloudwatchlogstypes.LimitExceededException{}) { - e.logger.FromContext(ctx).Debug("ExecuteStartQuery limit exceeded", "err", err) - } else if errors.Is(err, &cloudwatchlogstypes.ThrottlingException{}) { - e.logger.FromContext(ctx).Debug("ExecuteStartQuery rate exceeded", "err", err) + var awsErr awserr.Error + if errors.As(err, &awsErr) && awsErr.Code() == "LimitExceededException" { + e.logger.FromContext(ctx).Debug("ExecuteStartQuery limit exceeded", "err", awsErr) + err = &AWSError{Code: limitExceededException, Message: err.Error()} + } else if errors.As(err, &awsErr) && awsErr.Code() == "ThrottlingException" { + e.logger.FromContext(ctx).Debug("ExecuteStartQuery rate exceeded", "err", awsErr) + err = &AWSError{Code: throttlingException, Message: err.Error()} } err = backend.DownstreamError(err) } return resp, err } -func (e *cloudWatchExecutor) handleStartQuery(ctx context.Context, logsClient models.CWLogsClient, +func (e *cloudWatchExecutor) handleStartQuery(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI, logsQuery models.LogsQuery, timeRange backend.TimeRange, refID string) (*data.Frame, error) { startQueryResponse, err := e.executeStartQuery(ctx, logsClient, logsQuery, timeRange) if err != nil { @@ -276,19 +318,20 @@ func (e *cloudWatchExecutor) handleStartQuery(ctx context.Context, logsClient mo return dataFrame, nil } -func (e *cloudWatchExecutor) executeStopQuery(ctx context.Context, logsClient models.CWLogsClient, +func (e *cloudWatchExecutor) executeStopQuery(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI, logsQuery models.LogsQuery) (*cloudwatchlogs.StopQueryOutput, error) { queryInput := &cloudwatchlogs.StopQueryInput{ QueryId: aws.String(logsQuery.QueryId), } - response, err := logsClient.StopQuery(ctx, queryInput) + response, err := logsClient.StopQueryWithContext(ctx, queryInput) if err != nil { // If the query has already stopped by the time CloudWatch receives the stop query request, // an "InvalidParameterException" error is returned. For our purposes though the query has been // stopped, so we ignore the error. - if errors.Is(err, &cloudwatchlogstypes.InvalidParameterException{}) { - response = &cloudwatchlogs.StopQueryOutput{Success: false} + var awsErr awserr.Error + if errors.As(err, &awsErr) && awsErr.Code() == "InvalidParameterException" { + response = &cloudwatchlogs.StopQueryOutput{Success: aws.Bool(false)} err = nil } else { err = backend.DownstreamError(err) @@ -298,35 +341,35 @@ func (e *cloudWatchExecutor) executeStopQuery(ctx context.Context, logsClient mo return response, err } -func (e *cloudWatchExecutor) handleStopQuery(ctx context.Context, logsClient models.CWLogsClient, +func (e *cloudWatchExecutor) handleStopQuery(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI, logsQuery models.LogsQuery) (*data.Frame, error) { response, err := e.executeStopQuery(ctx, logsClient, logsQuery) if err != nil { return nil, err } - dataFrame := data.NewFrame("StopQueryResponse", data.NewField("success", nil, []bool{response.Success})) + dataFrame := data.NewFrame("StopQueryResponse", data.NewField("success", nil, []bool{*response.Success})) return dataFrame, nil } -func (e *cloudWatchExecutor) executeGetQueryResults(ctx context.Context, logsClient models.CWLogsClient, +func (e *cloudWatchExecutor) executeGetQueryResults(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI, logsQuery models.LogsQuery) (*cloudwatchlogs.GetQueryResultsOutput, error) { queryInput := &cloudwatchlogs.GetQueryResultsInput{ QueryId: aws.String(logsQuery.QueryId), } - getQueryResultsResponse, err := logsClient.GetQueryResults(ctx, queryInput) + getQueryResultsResponse, err := logsClient.GetQueryResultsWithContext(ctx, queryInput) if err != nil { - var awsErr smithy.APIError + var awsErr awserr.Error if errors.As(err, &awsErr) { - err = &AWSError{Code: awsErr.ErrorCode(), Message: awsErr.ErrorMessage()} + err = &AWSError{Code: awsErr.Code(), Message: err.Error()} } err = backend.DownstreamError(err) } return getQueryResultsResponse, err } -func (e *cloudWatchExecutor) handleGetQueryResults(ctx context.Context, logsClient models.CWLogsClient, +func (e *cloudWatchExecutor) handleGetQueryResults(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI, logsQuery models.LogsQuery, refID string) (*data.Frame, error) { getQueryResultsOutput, err := e.executeGetQueryResults(ctx, logsClient, logsQuery) if err != nil { diff --git a/pkg/tsdb/cloudwatch/log_actions_test.go b/pkg/tsdb/cloudwatch/log_actions_test.go index 0cddf9f10b1..ae06b447820 100644 --- a/pkg/tsdb/cloudwatch/log_actions_test.go +++ b/pkg/tsdb/cloudwatch/log_actions_test.go @@ -6,15 +6,19 @@ import ( "testing" "time" - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" - cloudwatchlogstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" - + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/request" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/cloudwatchlogs" + "github.com/aws/aws-sdk-go/service/cloudwatchlogs/cloudwatchlogsiface" "github.com/grafana/grafana-aws-sdk/pkg/awsds" "github.com/grafana/grafana-plugin-sdk-go/backend" + "github.com/grafana/grafana-plugin-sdk-go/backend/datasource" + "github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt" "github.com/grafana/grafana-plugin-sdk-go/backend/log" "github.com/grafana/grafana-plugin-sdk-go/data" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/features" + "github.com/grafana/grafana/pkg/tsdb/cloudwatch/kinds/dataquery" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/mocks" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/utils" @@ -32,7 +36,7 @@ func TestQuery_handleGetLogEvents_passes_nil_start_and_end_times_to_GetLogEvents var cli fakeCWLogsClient - NewCWLogsClient = func(cfg aws.Config) models.CWLogsClient { + NewCWLogsClient = func(sess *session.Session) cloudwatchlogsiface.CloudWatchLogsAPI { return &cli } const refID = "A" @@ -53,7 +57,7 @@ func TestQuery_handleGetLogEvents_passes_nil_start_and_end_times_to_GetLogEvents expectedInput: []*cloudwatchlogs.GetLogEventsInput{ { EndTime: aws.Int64(1), - Limit: aws.Int32(10), + Limit: aws.Int64(10), LogGroupName: aws.String("foo"), LogStreamName: aws.String("bar"), StartFromHead: aws.Bool(false), @@ -72,7 +76,7 @@ func TestQuery_handleGetLogEvents_passes_nil_start_and_end_times_to_GetLogEvents expectedInput: []*cloudwatchlogs.GetLogEventsInput{ { StartTime: aws.Int64(1), - Limit: aws.Int32(10), + Limit: aws.Int64(10), LogGroupName: aws.String("foo"), LogStreamName: aws.String("bar"), StartFromHead: aws.Bool(true), @@ -84,7 +88,11 @@ func TestQuery_handleGetLogEvents_passes_nil_start_and_end_times_to_GetLogEvents for name, test := range testCases { t.Run(name, func(t *testing.T) { cli = fakeCWLogsClient{} - im := defaultTestInstanceManager() + + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil + }) + executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ PluginContext: backend.PluginContext{ @@ -100,8 +108,8 @@ func TestQuery_handleGetLogEvents_passes_nil_start_and_end_times_to_GetLogEvents }) require.NoError(t, err) - require.Len(t, cli.calls.getEvents, 1) - assert.Equal(t, test.expectedInput, cli.calls.getEvents) + require.Len(t, cli.calls.getEventsWithContext, 1) + assert.Equal(t, test.expectedInput, cli.calls.getEventsWithContext) }) } } @@ -112,15 +120,17 @@ func TestQuery_GetLogEvents_returns_response_from_GetLogEvents_to_data_frame_fie NewCWLogsClient = origNewCWLogsClient }) var cli *mocks.MockLogEvents - NewCWLogsClient = func(cfg aws.Config) models.CWLogsClient { + NewCWLogsClient = func(sess *session.Session) cloudwatchlogsiface.CloudWatchLogsAPI { return cli } - im := defaultTestInstanceManager() + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil + }) executor := newExecutor(im, log.NewNullLogger()) cli = &mocks.MockLogEvents{} - cli.On("GetLogEvents", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.GetLogEventsOutput{ - Events: []cloudwatchlogstypes.OutputLogEvent{{ + cli.On("GetLogEventsWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.GetLogEventsOutput{ + Events: []*cloudwatchlogs.OutputLogEvent{{ Message: utils.Pointer("some message"), Timestamp: utils.Pointer(int64(15)), }}}, nil) @@ -164,7 +174,7 @@ func TestQuery_StartQuery(t *testing.T) { var cli fakeCWLogsClient - NewCWLogsClient = func(cfg aws.Config) models.CWLogsClient { + NewCWLogsClient = func(sess *session.Session) cloudwatchlogsiface.CloudWatchLogsAPI { return &cli } @@ -173,18 +183,18 @@ func TestQuery_StartQuery(t *testing.T) { cli = fakeCWLogsClient{ logGroupFields: cloudwatchlogs.GetLogGroupFieldsOutput{ - LogGroupFields: []cloudwatchlogstypes.LogGroupField{ + LogGroupFields: []*cloudwatchlogs.LogGroupField{ { Name: aws.String("field_a"), - Percent: 100, + Percent: aws.Int64(100), }, { Name: aws.String("field_b"), - Percent: 30, + Percent: aws.Int64(30), }, { Name: aws.String("field_c"), - Percent: 55, + Percent: aws.Int64(55), }, }, }, @@ -195,11 +205,13 @@ func TestQuery_StartQuery(t *testing.T) { To: time.Unix(1584700643, 0), } - im := testInstanceManagerWithSettings(models.CloudWatchSettings{ - AWSDatasourceSettings: awsds.AWSDatasourceSettings{ - Region: "us-east-2", - }, - }, false) + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{ + AWSDatasourceSettings: awsds.AWSDatasourceSettings{ + Region: "us-east-2", + }, + }, sessions: &fakeSessionCache{}}, nil + }) executor := newExecutor(im, log.NewNullLogger()) resp, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ @@ -229,18 +241,18 @@ func TestQuery_StartQuery(t *testing.T) { const refID = "A" cli = fakeCWLogsClient{ logGroupFields: cloudwatchlogs.GetLogGroupFieldsOutput{ - LogGroupFields: []cloudwatchlogstypes.LogGroupField{ + LogGroupFields: []*cloudwatchlogs.LogGroupField{ { Name: aws.String("field_a"), - Percent: 100, + Percent: aws.Int64(100), }, { Name: aws.String("field_b"), - Percent: 30, + Percent: aws.Int64(30), }, { Name: aws.String("field_c"), - Percent: 55, + Percent: aws.Int64(55), }, }, }, @@ -251,11 +263,13 @@ func TestQuery_StartQuery(t *testing.T) { To: time.Unix(1584873443000, 0), } - im := testInstanceManagerWithSettings(models.CloudWatchSettings{ - AWSDatasourceSettings: awsds.AWSDatasourceSettings{ - Region: "us-east-2", - }, - }, false) + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{ + AWSDatasourceSettings: awsds.AWSDatasourceSettings{ + Region: "us-east-2", + }, + }, sessions: &fakeSessionCache{}}, nil + }) executor := newExecutor(im, log.NewNullLogger()) resp, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ @@ -297,6 +311,26 @@ func TestQuery_StartQuery(t *testing.T) { }) } +type withQueryLanguageMock struct { + capturedLanguage *dataquery.LogsQueryLanguage + mockWithQueryLanguage func(language *dataquery.LogsQueryLanguage) func(request *request.Request) +} + +func newWithQueryLanguageMock() *withQueryLanguageMock { + mock := &withQueryLanguageMock{ + capturedLanguage: new(dataquery.LogsQueryLanguage), + } + + mock.mockWithQueryLanguage = func(language *dataquery.LogsQueryLanguage) func(request *request.Request) { + *mock.capturedLanguage = *language + return func(req *request.Request) { + + } + } + + return mock +} + func Test_executeStartQuery(t *testing.T) { origNewCWLogsClient := NewCWLogsClient t.Cleanup(func() { @@ -305,15 +339,15 @@ func Test_executeStartQuery(t *testing.T) { var cli fakeCWLogsClient - NewCWLogsClient = func(cfg aws.Config) models.CWLogsClient { + NewCWLogsClient = func(sess *session.Session) cloudwatchlogsiface.CloudWatchLogsAPI { return &cli } - t.Run("successfully parses information from JSON to StartQuery for language", func(t *testing.T) { + t.Run("successfully parses information from JSON to StartQueryWithContext for language", func(t *testing.T) { testCases := map[string]struct { queries []backend.DataQuery expectedOutput []*cloudwatchlogs.StartQueryInput - queryLanguage cloudwatchlogstypes.QueryLanguage + queryLanguage dataquery.LogsQueryLanguage }{ "not defined": { queries: []backend.DataQuery{ @@ -332,12 +366,11 @@ func Test_executeStartQuery(t *testing.T) { expectedOutput: []*cloudwatchlogs.StartQueryInput{{ StartTime: aws.Int64(0), EndTime: aws.Int64(1), - Limit: aws.Int32(12), + Limit: aws.Int64(12), QueryString: aws.String("fields @timestamp,ltrim(@log) as __log__grafana_internal__,ltrim(@logStream) as __logstream__grafana_internal__|fields @message"), - LogGroupNames: []string{"some name", "another name"}, - QueryLanguage: cloudwatchlogstypes.QueryLanguageCwli, + LogGroupNames: []*string{aws.String("some name"), aws.String("another name")}, }}, - queryLanguage: cloudwatchlogstypes.QueryLanguageCwli, + queryLanguage: dataquery.LogsQueryLanguageCWLI, }, "CWLI": { queries: []backend.DataQuery{{ @@ -356,13 +389,12 @@ func Test_executeStartQuery(t *testing.T) { { StartTime: aws.Int64(0), EndTime: aws.Int64(1), - Limit: aws.Int32(12), + Limit: aws.Int64(12), QueryString: aws.String("fields @timestamp,ltrim(@log) as __log__grafana_internal__,ltrim(@logStream) as __logstream__grafana_internal__|fields @message"), - LogGroupNames: []string{"some name", "another name"}, - QueryLanguage: cloudwatchlogstypes.QueryLanguageCwli, + LogGroupNames: []*string{aws.String("some name"), aws.String("another name")}, }, }, - queryLanguage: cloudwatchlogstypes.QueryLanguageCwli, + queryLanguage: dataquery.LogsQueryLanguageCWLI, }, "PPL": { queries: []backend.DataQuery{{ @@ -381,13 +413,12 @@ func Test_executeStartQuery(t *testing.T) { { StartTime: aws.Int64(0), EndTime: aws.Int64(1), - Limit: aws.Int32(12), + Limit: aws.Int64(12), QueryString: aws.String("source logs | fields @message"), - LogGroupNames: []string{"some name", "another name"}, - QueryLanguage: cloudwatchlogstypes.QueryLanguagePpl, + LogGroupNames: []*string{aws.String("some name"), aws.String("another name")}, }, }, - queryLanguage: cloudwatchlogstypes.QueryLanguagePpl, + queryLanguage: dataquery.LogsQueryLanguagePPL, }, "SQL": { queries: []backend.DataQuery{ @@ -408,35 +439,46 @@ func Test_executeStartQuery(t *testing.T) { { StartTime: aws.Int64(0), EndTime: aws.Int64(1), - Limit: aws.Int32(12), + Limit: aws.Int64(12), QueryString: aws.String("SELECT * FROM logs"), LogGroupNames: nil, - QueryLanguage: cloudwatchlogstypes.QueryLanguageSql, }, }, - queryLanguage: cloudwatchlogstypes.QueryLanguageSql, + queryLanguage: dataquery.LogsQueryLanguageSQL, }, } for name, test := range testCases { t.Run(name, func(t *testing.T) { cli = fakeCWLogsClient{} - im := defaultTestInstanceManager() + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil + }) executor := newExecutor(im, log.NewNullLogger()) + languageMock := newWithQueryLanguageMock() + originalWithQueryLanguage := WithQueryLanguage + WithQueryLanguage = languageMock.mockWithQueryLanguage + defer func() { + WithQueryLanguage = originalWithQueryLanguage + }() + _, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ PluginContext: backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}}, Queries: test.queries, }) assert.NoError(t, err) - assert.Equal(t, test.expectedOutput, cli.calls.startQuery) + assert.Equal(t, test.expectedOutput, cli.calls.startQueryWithContext) + assert.Equal(t, &test.queryLanguage, languageMock.capturedLanguage) }) } }) t.Run("does not populate StartQueryInput.limit when no limit provided", func(t *testing.T) { cli = fakeCWLogsClient{} - im := defaultTestInstanceManager() + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil + }) executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ @@ -454,13 +496,15 @@ func Test_executeStartQuery(t *testing.T) { }) assert.NoError(t, err) - require.Len(t, cli.calls.startQuery, 1) - assert.Nil(t, cli.calls.startQuery[0].Limit) + require.Len(t, cli.calls.startQueryWithContext, 1) + assert.Nil(t, cli.calls.startQueryWithContext[0].Limit) }) t.Run("attaches logGroupIdentifiers if the crossAccount feature is enabled", func(t *testing.T) { cli = fakeCWLogsClient{} - im := defaultTestInstanceManager() + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil + }) executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(contextWithFeaturesEnabled(features.FlagCloudWatchCrossAccountQuerying), &backend.QueryDataRequest{ @@ -486,17 +530,18 @@ func Test_executeStartQuery(t *testing.T) { { StartTime: aws.Int64(0), EndTime: aws.Int64(1), - Limit: aws.Int32(12), + Limit: aws.Int64(12), QueryString: aws.String("fields @timestamp,ltrim(@log) as __log__grafana_internal__,ltrim(@logStream) as __logstream__grafana_internal__|fields @message"), - LogGroupIdentifiers: []string{"fakeARN"}, - QueryLanguage: cloudwatchlogstypes.QueryLanguageCwli, + LogGroupIdentifiers: []*string{aws.String("fakeARN")}, }, - }, cli.calls.startQuery) + }, cli.calls.startQueryWithContext) }) t.Run("attaches logGroupIdentifiers if the crossAccount feature is enabled and strips out trailing *", func(t *testing.T) { cli = fakeCWLogsClient{} - im := defaultTestInstanceManager() + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil + }) executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(contextWithFeaturesEnabled(features.FlagCloudWatchCrossAccountQuerying), &backend.QueryDataRequest{ @@ -521,17 +566,18 @@ func Test_executeStartQuery(t *testing.T) { { StartTime: aws.Int64(0), EndTime: aws.Int64(1), - Limit: aws.Int32(12), + Limit: aws.Int64(12), QueryString: aws.String("fields @timestamp,ltrim(@log) as __log__grafana_internal__,ltrim(@logStream) as __logstream__grafana_internal__|fields @message"), - LogGroupIdentifiers: []string{"*fake**ARN"}, - QueryLanguage: cloudwatchlogstypes.QueryLanguageCwli, + LogGroupIdentifiers: []*string{aws.String("*fake**ARN")}, }, - }, cli.calls.startQuery) + }, cli.calls.startQueryWithContext) }) t.Run("uses LogGroupNames if the cross account feature flag is not enabled, and log group names is present", func(t *testing.T) { cli = fakeCWLogsClient{} - im := defaultTestInstanceManager() + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil + }) executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ PluginContext: backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}}, @@ -555,17 +601,18 @@ func Test_executeStartQuery(t *testing.T) { { StartTime: aws.Int64(0), EndTime: aws.Int64(1), - Limit: aws.Int32(12), + Limit: aws.Int64(12), QueryString: aws.String("fields @timestamp,ltrim(@log) as __log__grafana_internal__,ltrim(@logStream) as __logstream__grafana_internal__|fields @message"), - LogGroupNames: []string{"/log-group-name"}, - QueryLanguage: cloudwatchlogstypes.QueryLanguageCwli, + LogGroupNames: []*string{aws.String("/log-group-name")}, }, - }, cli.calls.startQuery) + }, cli.calls.startQueryWithContext) }) t.Run("ignores logGroups if feature flag is disabled even if logGroupNames is not present", func(t *testing.T) { cli = fakeCWLogsClient{} - im := defaultTestInstanceManager() + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil + }) executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ PluginContext: backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}}, @@ -588,17 +635,18 @@ func Test_executeStartQuery(t *testing.T) { { StartTime: aws.Int64(0), EndTime: aws.Int64(1), - Limit: aws.Int32(12), + Limit: aws.Int64(12), QueryString: aws.String("fields @timestamp,ltrim(@log) as __log__grafana_internal__,ltrim(@logStream) as __logstream__grafana_internal__|fields @message"), - LogGroupNames: nil, - QueryLanguage: cloudwatchlogstypes.QueryLanguageCwli, + LogGroupNames: []*string{}, }, - }, cli.calls.startQuery) + }, cli.calls.startQueryWithContext) }) t.Run("it always uses logGroups when feature flag is enabled and ignores log group names", func(t *testing.T) { cli = fakeCWLogsClient{} - im := defaultTestInstanceManager() + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil + }) executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(contextWithFeaturesEnabled(features.FlagCloudWatchCrossAccountQuerying), &backend.QueryDataRequest{ PluginContext: backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}}, @@ -622,12 +670,11 @@ func Test_executeStartQuery(t *testing.T) { { StartTime: aws.Int64(0), EndTime: aws.Int64(1), - Limit: aws.Int32(12), + Limit: aws.Int64(12), QueryString: aws.String("fields @timestamp,ltrim(@log) as __log__grafana_internal__,ltrim(@logStream) as __logstream__grafana_internal__|fields @message"), - LogGroupIdentifiers: []string{"*fake**ARN"}, - QueryLanguage: cloudwatchlogstypes.QueryLanguageCwli, + LogGroupIdentifiers: []*string{aws.String("*fake**ARN")}, }, - }, cli.calls.startQuery) + }, cli.calls.startQueryWithContext) }) } @@ -639,30 +686,32 @@ func TestQuery_StopQuery(t *testing.T) { var cli fakeCWLogsClient - NewCWLogsClient = func(aws.Config) models.CWLogsClient { + NewCWLogsClient = func(sess *session.Session) cloudwatchlogsiface.CloudWatchLogsAPI { return &cli } cli = fakeCWLogsClient{ logGroupFields: cloudwatchlogs.GetLogGroupFieldsOutput{ - LogGroupFields: []cloudwatchlogstypes.LogGroupField{ + LogGroupFields: []*cloudwatchlogs.LogGroupField{ { Name: aws.String("field_a"), - Percent: 100, + Percent: aws.Int64(100), }, { Name: aws.String("field_b"), - Percent: 30, + Percent: aws.Int64(30), }, { Name: aws.String("field_c"), - Percent: 55, + Percent: aws.Int64(55), }, }, }, } - im := defaultTestInstanceManager() + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil + }) timeRange := backend.TimeRange{ From: time.Unix(1584873443, 0), @@ -709,14 +758,14 @@ func TestQuery_GetQueryResults(t *testing.T) { var cli fakeCWLogsClient - NewCWLogsClient = func(aws.Config) models.CWLogsClient { + NewCWLogsClient = func(sess *session.Session) cloudwatchlogsiface.CloudWatchLogsAPI { return &cli } const refID = "A" cli = fakeCWLogsClient{ queryResults: cloudwatchlogs.GetQueryResultsOutput{ - Results: [][]cloudwatchlogstypes.ResultField{ + Results: [][]*cloudwatchlogs.ResultField{ { { Field: aws.String("@timestamp"), @@ -746,16 +795,18 @@ func TestQuery_GetQueryResults(t *testing.T) { }, }, }, - Statistics: &cloudwatchlogstypes.QueryStatistics{ - BytesScanned: 512, - RecordsMatched: 256, - RecordsScanned: 1024, + Statistics: &cloudwatchlogs.QueryStatistics{ + BytesScanned: aws.Float64(512), + RecordsMatched: aws.Float64(256), + RecordsScanned: aws.Float64(1024), }, - Status: "Complete", + Status: aws.String("Complete"), }, } - im := defaultTestInstanceManager() + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil + }) executor := newExecutor(im, log.NewNullLogger()) resp, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ diff --git a/pkg/tsdb/cloudwatch/log_query.go b/pkg/tsdb/cloudwatch/log_query.go index e4f8b0e4dc3..eab124843f6 100644 --- a/pkg/tsdb/cloudwatch/log_query.go +++ b/pkg/tsdb/cloudwatch/log_query.go @@ -7,9 +7,7 @@ import ( "strconv" "time" - "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" - cloudwatchlogstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" - + "github.com/aws/aws-sdk-go/service/cloudwatchlogs" "github.com/grafana/grafana-plugin-sdk-go/data" ) @@ -20,7 +18,7 @@ func logsResultsToDataframes(response *cloudwatchlogs.GetQueryResultsOutput, gro return nil, fmt.Errorf("response is nil, cannot convert log results to data frames") } - nonEmptyRows := make([][]cloudwatchlogstypes.ResultField, 0) + nonEmptyRows := make([][]*cloudwatchlogs.ResultField, 0) for _, row := range response.Results { // Sometimes CloudWatch can send empty rows if len(row) == 0 { @@ -117,20 +115,26 @@ func logsResultsToDataframes(response *cloudwatchlogs.GetQueryResultsOutput, gro queryStats := make([]data.QueryStat, 0) if response.Statistics != nil { - queryStats = append(queryStats, data.QueryStat{ - FieldConfig: data.FieldConfig{DisplayName: "Bytes scanned"}, - Value: response.Statistics.BytesScanned, - }) + if response.Statistics.BytesScanned != nil { + queryStats = append(queryStats, data.QueryStat{ + FieldConfig: data.FieldConfig{DisplayName: "Bytes scanned"}, + Value: *response.Statistics.BytesScanned, + }) + } - queryStats = append(queryStats, data.QueryStat{ - FieldConfig: data.FieldConfig{DisplayName: "Records scanned"}, - Value: response.Statistics.RecordsScanned, - }) + if response.Statistics.RecordsScanned != nil { + queryStats = append(queryStats, data.QueryStat{ + FieldConfig: data.FieldConfig{DisplayName: "Records scanned"}, + Value: *response.Statistics.RecordsScanned, + }) + } - queryStats = append(queryStats, data.QueryStat{ - FieldConfig: data.FieldConfig{DisplayName: "Records matched"}, - Value: response.Statistics.RecordsMatched, - }) + if response.Statistics.RecordsMatched != nil { + queryStats = append(queryStats, data.QueryStat{ + FieldConfig: data.FieldConfig{DisplayName: "Records matched"}, + Value: *response.Statistics.RecordsMatched, + }) + } } frame := data.NewFrame("CloudWatchLogsResponse", newFields...) @@ -143,8 +147,10 @@ func logsResultsToDataframes(response *cloudwatchlogs.GetQueryResultsOutput, gro frame.Meta.Stats = queryStats } - frame.Meta.Custom = map[string]any{ - "Status": string(response.Status), + if response.Status != nil { + frame.Meta.Custom = map[string]any{ + "Status": *response.Status, + } } // Results aren't guaranteed to come ordered by time (ascending), so we need to sort @@ -152,7 +158,7 @@ func logsResultsToDataframes(response *cloudwatchlogs.GetQueryResultsOutput, gro return frame, nil } -func changeToStringField(lengthOfValues int, rows [][]cloudwatchlogstypes.ResultField, logEventField string) []*string { +func changeToStringField(lengthOfValues int, rows [][]*cloudwatchlogs.ResultField, logEventField string) []*string { fieldValuesAsStrings := make([]*string, lengthOfValues) for i, resultFields := range rows { for _, field := range resultFields { diff --git a/pkg/tsdb/cloudwatch/log_query_test.go b/pkg/tsdb/cloudwatch/log_query_test.go index 9e86b4e752c..a112451915e 100644 --- a/pkg/tsdb/cloudwatch/log_query_test.go +++ b/pkg/tsdb/cloudwatch/log_query_test.go @@ -5,10 +5,8 @@ import ( "testing" "time" - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" - cloudwatchlogstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" - + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/cloudwatchlogs" "github.com/grafana/grafana-plugin-sdk-go/data" "github.com/stretchr/testify/assert" @@ -21,63 +19,63 @@ import ( func TestLogsResultsToDataframes(t *testing.T) { fakeCloudwatchResponse := &cloudwatchlogs.GetQueryResultsOutput{ - Results: [][]cloudwatchlogstypes.ResultField{ + Results: [][]*cloudwatchlogs.ResultField{ { - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("@ptr"), Value: aws.String("fake ptr"), }, - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("@timestamp"), Value: aws.String("2020-03-02 15:04:05.000"), }, - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("line"), Value: aws.String("test message 1"), }, - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("@logStream"), Value: aws.String("fakelogstream"), }, - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("@log"), Value: aws.String("fakelog"), }, - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String(logStreamIdentifierInternal), Value: aws.String("fakelogstream"), }, - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String(logIdentifierInternal), Value: aws.String("fakelog"), }, }, { - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("@ptr"), Value: aws.String("fake ptr"), }, - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("@timestamp"), Value: aws.String("2020-03-02 16:04:05.000"), }, - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("line"), Value: aws.String("test message 2"), }, - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("@logStream"), Value: aws.String("fakelogstream"), }, - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("@log"), Value: aws.String("fakelog"), }, - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String(logStreamIdentifierInternal), Value: aws.String("fakelogstream"), }, - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String(logIdentifierInternal), Value: aws.String("fakelog"), }, @@ -86,47 +84,47 @@ func TestLogsResultsToDataframes(t *testing.T) { {}, // or rows with only timestamp { - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("@timestamp"), Value: aws.String("2020-03-02 17:04:05.000"), }, }, { - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("@ptr"), Value: aws.String("fake ptr"), }, - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("@timestamp"), Value: aws.String("2020-03-02 17:04:05.000"), }, - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("line"), Value: aws.String("test message 3"), }, - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("@logStream"), Value: aws.String("fakelogstream"), }, - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("@log"), Value: aws.String("fakelog"), }, - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String(logStreamIdentifierInternal), Value: aws.String("fakelogstream"), }, - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String(logIdentifierInternal), Value: aws.String("fakelog"), }, }, }, - Status: "ok", - Statistics: &cloudwatchlogstypes.QueryStatistics{ - BytesScanned: 2000, - RecordsMatched: 3, - RecordsScanned: 5000, + Status: aws.String("ok"), + Statistics: &cloudwatchlogs.QueryStatistics{ + BytesScanned: aws.Float64(2000), + RecordsMatched: aws.Float64(3), + RecordsScanned: aws.Float64(5000), }, } @@ -226,33 +224,33 @@ func TestLogsResultsToDataframes(t *testing.T) { func TestLogsResultsToDataframes_MixedTypes_NumericValuesMixedWithStringFallBackToStringValues(t *testing.T) { dataframes, err := logsResultsToDataframes(&cloudwatchlogs.GetQueryResultsOutput{ - Results: [][]cloudwatchlogstypes.ResultField{ + Results: [][]*cloudwatchlogs.ResultField{ { - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("numberOrString"), Value: aws.String("-1.234"), }, }, { - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("numberOrString"), Value: aws.String("1"), }, }, { - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("numberOrString"), Value: aws.String("not a number"), }, }, { - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("numberOrString"), Value: aws.String("2.000"), }, }, }, - Status: "ok", + Status: aws.String("ok"), }, []string{}) require.NoError(t, err) @@ -286,27 +284,27 @@ func TestLogsResultsToDataframes_With_Millisecond_Timestamps(t *testing.T) { ingestionTimeField := int64(1732790372916) dataframes, err := logsResultsToDataframes(&cloudwatchlogs.GetQueryResultsOutput{ - Results: [][]cloudwatchlogstypes.ResultField{ + Results: [][]*cloudwatchlogs.ResultField{ { - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("@timestamp"), Value: aws.String(fmt.Sprintf("%d", timestampField)), }, - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("@ingestionTime"), Value: aws.String(fmt.Sprintf("%d", ingestionTimeField)), }, - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("stringTimeField"), Value: aws.String(stringTimeField), }, - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("message"), Value: aws.String("log message"), }, }, }, - Status: "ok", + Status: aws.String("ok"), }, []string{}) require.NoError(t, err) @@ -350,23 +348,23 @@ func TestLogsResultsToDataframes_With_Int_Grouping_Field(t *testing.T) { timestampField := int64(1732749534876) dataframes, err := logsResultsToDataframes(&cloudwatchlogs.GetQueryResultsOutput{ - Results: [][]cloudwatchlogstypes.ResultField{ + Results: [][]*cloudwatchlogs.ResultField{ { - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("@timestamp"), Value: aws.String(fmt.Sprintf("%d", timestampField)), }, - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("numberField"), Value: aws.String("8"), }, - cloudwatchlogstypes.ResultField{ + &cloudwatchlogs.ResultField{ Field: aws.String("groupingNumber"), Value: aws.String("100"), }, }, }, - Status: "ok", + Status: aws.String("ok"), }, []string{"groupingNumber"}) require.NoError(t, err) diff --git a/pkg/tsdb/cloudwatch/log_sync_query.go b/pkg/tsdb/cloudwatch/log_sync_query.go index a2ee420bf4e..fb3f099a9b4 100644 --- a/pkg/tsdb/cloudwatch/log_sync_query.go +++ b/pkg/tsdb/cloudwatch/log_sync_query.go @@ -7,8 +7,8 @@ import ( "fmt" "time" - "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" - + "github.com/aws/aws-sdk-go/service/cloudwatchlogs" + "github.com/aws/aws-sdk-go/service/cloudwatchlogs/cloudwatchlogsiface" "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/data" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/kinds/dataquery" @@ -86,7 +86,7 @@ var executeSyncLogQuery = func(ctx context.Context, e *cloudWatchExecutor, req * return resp, nil } -func (e *cloudWatchExecutor) syncQuery(ctx context.Context, logsClient models.CWLogsClient, +func (e *cloudWatchExecutor) syncQuery(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI, queryContext backend.DataQuery, logsQuery models.LogsQuery, logsTimeout time.Duration) (*cloudwatchlogs.GetQueryResultsOutput, error) { startQueryOutput, err := e.executeStartQuery(ctx, logsClient, logsQuery, queryContext.TimeRange) if err != nil { @@ -117,7 +117,7 @@ func (e *cloudWatchExecutor) syncQuery(ctx context.Context, logsClient models.CW if err != nil { return nil, err } - if isTerminated(res.Status) { + if isTerminated(*res.Status) { return res, err } if time.Duration(attemptCount)*time.Second >= logsTimeout { diff --git a/pkg/tsdb/cloudwatch/log_sync_query_test.go b/pkg/tsdb/cloudwatch/log_sync_query_test.go index eeb228dac8b..cb1f8d0cab3 100644 --- a/pkg/tsdb/cloudwatch/log_sync_query_test.go +++ b/pkg/tsdb/cloudwatch/log_sync_query_test.go @@ -7,12 +7,14 @@ import ( "testing" "time" - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" - cloudwatchlogstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" - + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/cloudwatchlogs" + "github.com/aws/aws-sdk-go/service/cloudwatchlogs/cloudwatchlogsiface" "github.com/grafana/grafana-aws-sdk/pkg/awsds" "github.com/grafana/grafana-plugin-sdk-go/backend" + "github.com/grafana/grafana-plugin-sdk-go/backend/datasource" + "github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt" "github.com/grafana/grafana-plugin-sdk-go/backend/log" "github.com/grafana/grafana-plugin-sdk-go/data" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" @@ -29,13 +31,16 @@ func Test_executeSyncLogQuery(t *testing.T) { }) var cli fakeCWLogsClient - NewCWLogsClient = func(aws.Config) models.CWLogsClient { + NewCWLogsClient = func(sess *session.Session) cloudwatchlogsiface.CloudWatchLogsAPI { return &cli } t.Run("getCWLogsClient is called with region from input JSON", func(t *testing.T) { - cli = fakeCWLogsClient{queryResults: cloudwatchlogs.GetQueryResultsOutput{Status: "Complete"}} - im := defaultTestInstanceManager() + cli = fakeCWLogsClient{queryResults: cloudwatchlogs.GetQueryResultsOutput{Status: aws.String("Complete")}} + sess := fakeSessionCache{} + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{}, sessions: &sess}, nil + }) executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ @@ -53,12 +58,15 @@ func Test_executeSyncLogQuery(t *testing.T) { }) assert.NoError(t, err) - //assert.Equal(t, []string{"some region"}, sess.calledRegions) + assert.Equal(t, []string{"some region"}, sess.calledRegions) }) t.Run("getCWLogsClient is called with region from instance manager when region is default", func(t *testing.T) { - cli = fakeCWLogsClient{queryResults: cloudwatchlogs.GetQueryResultsOutput{Status: "Complete"}} - im := testInstanceManagerWithSettings(models.CloudWatchSettings{AWSDatasourceSettings: awsds.AWSDatasourceSettings{Region: "instance manager's region"}}, false) + cli = fakeCWLogsClient{queryResults: cloudwatchlogs.GetQueryResultsOutput{Status: aws.String("Complete")}} + sess := fakeSessionCache{} + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{AWSDatasourceSettings: awsds.AWSDatasourceSettings{Region: "instance manager's region"}}, sessions: &sess}, nil + }) executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ @@ -76,7 +84,7 @@ func Test_executeSyncLogQuery(t *testing.T) { }) assert.NoError(t, err) - //assert.Equal(t, []string{"instance manager's region"}, sess.calledRegions) + assert.Equal(t, []string{"instance manager's region"}, sess.calledRegions) }) t.Run("with header", func(t *testing.T) { @@ -111,8 +119,10 @@ func Test_executeSyncLogQuery(t *testing.T) { for _, tc := range testcases { t.Run(tc.name, func(t *testing.T) { syncCalled = false - cli = fakeCWLogsClient{queryResults: cloudwatchlogs.GetQueryResultsOutput{Status: "Complete"}} - im := testInstanceManagerWithSettings(models.CloudWatchSettings{AWSDatasourceSettings: awsds.AWSDatasourceSettings{Region: "instance manager's region"}}, false) + cli = fakeCWLogsClient{queryResults: cloudwatchlogs.GetQueryResultsOutput{Status: aws.String("Complete")}} + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{AWSDatasourceSettings: awsds.AWSDatasourceSettings{Region: "instance manager's region"}}, sessions: &fakeSessionCache{}}, nil + }) executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ @@ -151,8 +161,10 @@ func Test_executeSyncLogQuery(t *testing.T) { executeSyncLogQuery = origExecuteSyncLogQuery }) - cli = fakeCWLogsClient{queryResults: cloudwatchlogs.GetQueryResultsOutput{Status: "Complete"}} - im := testInstanceManagerWithSettings(models.CloudWatchSettings{AWSDatasourceSettings: awsds.AWSDatasourceSettings{Region: "instance manager's region"}}, false) + cli = fakeCWLogsClient{queryResults: cloudwatchlogs.GetQueryResultsOutput{Status: aws.String("Complete")}} + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{AWSDatasourceSettings: awsds.AWSDatasourceSettings{Region: "instance manager's region"}}, sessions: &fakeSessionCache{}}, nil + }) executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ @@ -180,17 +192,19 @@ func Test_executeSyncLogQuery_handles_RefId_from_input_queries(t *testing.T) { }) var cli *mockLogsSyncClient - NewCWLogsClient = func(aws.Config) models.CWLogsClient { + NewCWLogsClient = func(sess *session.Session) cloudwatchlogsiface.CloudWatchLogsAPI { return cli } t.Run("when a query refId is not provided, 'A' is assigned by default", func(t *testing.T) { cli = &mockLogsSyncClient{} - cli.On("StartQuery", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.StartQueryOutput{ + cli.On("StartQueryWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.StartQueryOutput{ QueryId: aws.String("abcd-efgh-ijkl-mnop"), }, nil) - cli.On("GetQueryResults", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.GetQueryResultsOutput{Status: "Complete"}, nil) - im := defaultTestInstanceManager() + cli.On("GetQueryResultsWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.GetQueryResultsOutput{Status: aws.String("Complete")}, nil) + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil + }) executor := newExecutor(im, log.NewNullLogger()) res, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ @@ -213,11 +227,13 @@ func Test_executeSyncLogQuery_handles_RefId_from_input_queries(t *testing.T) { t.Run("when a query refId is provided, it is returned in the response", func(t *testing.T) { cli = &mockLogsSyncClient{} - cli.On("StartQuery", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.StartQueryOutput{ + cli.On("StartQueryWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.StartQueryOutput{ QueryId: aws.String("abcd-efgh-ijkl-mnop"), }, nil) - cli.On("GetQueryResults", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.GetQueryResultsOutput{Status: "Complete"}, nil) - im := defaultTestInstanceManager() + cli.On("GetQueryResultsWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.GetQueryResultsOutput{Status: aws.String("Complete")}, nil) + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil + }) executor := newExecutor(im, log.NewNullLogger()) res, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ @@ -253,38 +269,40 @@ func Test_executeSyncLogQuery_handles_RefId_from_input_queries(t *testing.T) { // when each query has a different response from AWS API calls, the RefIds are correctly reassigned to the associated response. cli = &mockLogsSyncClient{} // mock.MatchedBy makes sure that the QueryId below will only be returned when the input expression = "query string for A" - cli.On("StartQuery", mock.Anything, mock.MatchedBy(func(input *cloudwatchlogs.StartQueryInput) bool { + cli.On("StartQueryWithContext", mock.Anything, mock.MatchedBy(func(input *cloudwatchlogs.StartQueryInput) bool { return *input.QueryString == "fields @timestamp,ltrim(@log) as __log__grafana_internal__,ltrim(@logStream) as __logstream__grafana_internal__|query string for A" }), mock.Anything).Return(&cloudwatchlogs.StartQueryOutput{ QueryId: aws.String("queryId for A"), }, nil) // mock.MatchedBy makes sure that the QueryId below will only be returned when the input expression = "query string for B" - cli.On("StartQuery", mock.Anything, mock.MatchedBy(func(input *cloudwatchlogs.StartQueryInput) bool { + cli.On("StartQueryWithContext", mock.Anything, mock.MatchedBy(func(input *cloudwatchlogs.StartQueryInput) bool { return *input.QueryString == "fields @timestamp,ltrim(@log) as __log__grafana_internal__,ltrim(@logStream) as __logstream__grafana_internal__|query string for B" }), mock.Anything).Return(&cloudwatchlogs.StartQueryOutput{ QueryId: aws.String("queryId for B"), }, nil) - cli.On("GetQueryResults", mock.Anything, mock.MatchedBy(func(input *cloudwatchlogs.GetQueryResultsInput) bool { + cli.On("GetQueryResultsWithContext", mock.Anything, mock.MatchedBy(func(input *cloudwatchlogs.GetQueryResultsInput) bool { return *input.QueryId == "queryId for A" }), mock.Anything).Return(&cloudwatchlogs.GetQueryResultsOutput{ // this result will only be returned when the argument is QueryId = "queryId for A" - Results: [][]cloudwatchlogstypes.ResultField{{{ + Results: [][]*cloudwatchlogs.ResultField{{{ Field: utils.Pointer("@log"), Value: utils.Pointer("A result"), }}}, - Status: "Complete"}, nil) - cli.On("GetQueryResults", mock.Anything, mock.MatchedBy(func(input *cloudwatchlogs.GetQueryResultsInput) bool { + Status: aws.String("Complete")}, nil) + cli.On("GetQueryResultsWithContext", mock.Anything, mock.MatchedBy(func(input *cloudwatchlogs.GetQueryResultsInput) bool { return *input.QueryId == "queryId for B" }), mock.Anything).Return(&cloudwatchlogs.GetQueryResultsOutput{ // this result will only be returned when the argument is QueryId = "queryId for B" - Results: [][]cloudwatchlogstypes.ResultField{{{ + Results: [][]*cloudwatchlogs.ResultField{{{ Field: utils.Pointer("@log"), Value: utils.Pointer("B result"), }}}, - Status: "Complete"}, nil) + Status: aws.String("Complete")}, nil) - im := defaultTestInstanceManager() + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil + }) executor := newExecutor(im, log.NewNullLogger()) res, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ @@ -310,24 +328,26 @@ func Test_executeSyncLogQuery_handles_RefId_from_input_queries(t *testing.T) { }, }) - expectedLogFieldFromFirstCall := data.NewField("@log", nil, []*string{utils.Pointer("A result")}) // verifies the response from GetQueryResults matches the input RefId A + expectedLogFieldFromFirstCall := data.NewField("@log", nil, []*string{utils.Pointer("A result")}) // verifies the response from GetQueryResultsWithContext matches the input RefId A assert.NoError(t, err) respA, ok := res.Responses["A"] require.True(t, ok) assert.Equal(t, []*data.Field{expectedLogFieldFromFirstCall}, respA.Frames[0].Fields) - expectedLogFieldFromSecondCall := data.NewField("@log", nil, []*string{utils.Pointer("B result")}) // verifies the response from GetQueryResults matches the input RefId B + expectedLogFieldFromSecondCall := data.NewField("@log", nil, []*string{utils.Pointer("B result")}) // verifies the response from GetQueryResultsWithContext matches the input RefId B respB, ok := res.Responses["B"] require.True(t, ok) assert.Equal(t, []*data.Field{expectedLogFieldFromSecondCall}, respB.Frames[0].Fields) }) t.Run("when logsTimeout setting is defined, the polling period will be set to that variable", func(t *testing.T) { cli = &mockLogsSyncClient{} - cli.On("StartQuery", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.StartQueryOutput{ + cli.On("StartQueryWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.StartQueryOutput{ QueryId: aws.String("abcd-efgh-ijkl-mnop"), }, nil) - cli.On("GetQueryResults", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.GetQueryResultsOutput{Status: "Running"}, nil) - im := testInstanceManagerWithSettings(models.CloudWatchSettings{LogsTimeout: models.Duration{Duration: time.Millisecond}}, false) + cli.On("GetQueryResultsWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.GetQueryResultsOutput{Status: aws.String("Running")}, nil) + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{LogsTimeout: models.Duration{Duration: time.Millisecond}}, sessions: &fakeSessionCache{}}, nil + }) executor := newExecutor(im, log.NewNullLogger()) @@ -346,19 +366,21 @@ func Test_executeSyncLogQuery_handles_RefId_from_input_queries(t *testing.T) { }, }) assert.Error(t, err) - cli.AssertNumberOfCalls(t, "GetQueryResults", 1) + cli.AssertNumberOfCalls(t, "GetQueryResultsWithContext", 1) }) t.Run("when getQueryResults returns aws error is returned, it keeps the context", func(t *testing.T) { cli = &mockLogsSyncClient{} - cli.On("StartQuery", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.StartQueryOutput{ + cli.On("StartQueryWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.StartQueryOutput{ QueryId: aws.String("abcd-efgh-ijkl-mnop"), }, nil) - cli.On("GetQueryResults", mock.Anything, mock.Anything, mock.Anything).Return( - &cloudwatchlogs.GetQueryResultsOutput{Status: "Complete"}, - &fakeSmithyError{code: "foo", message: "bar"}, + cli.On("GetQueryResultsWithContext", mock.Anything, mock.Anything, mock.Anything).Return( + &cloudwatchlogs.GetQueryResultsOutput{Status: aws.String("Complete")}, + &fakeAWSError{code: "foo", message: "bar"}, ) - im := defaultTestInstanceManager() + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil + }) executor := newExecutor(im, log.NewNullLogger()) res, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ diff --git a/pkg/tsdb/cloudwatch/metric_data_input_builder.go b/pkg/tsdb/cloudwatch/metric_data_input_builder.go index b659127c658..2bbf4da5df4 100644 --- a/pkg/tsdb/cloudwatch/metric_data_input_builder.go +++ b/pkg/tsdb/cloudwatch/metric_data_input_builder.go @@ -4,9 +4,8 @@ import ( "context" "time" - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/cloudwatch" - cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/cloudwatch" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" ) @@ -16,13 +15,13 @@ func (e *cloudWatchExecutor) buildMetricDataInput(ctx context.Context, startTime metricDataInput := &cloudwatch.GetMetricDataInput{ StartTime: aws.Time(startTime), EndTime: aws.Time(endTime), - ScanBy: cloudwatchtypes.ScanByTimestampAscending, + ScanBy: aws.String("TimestampAscending"), } shouldSetLabelOptions := len(queries) > 0 && len(queries[0].TimezoneUTCOffset) > 0 if shouldSetLabelOptions { - metricDataInput.LabelOptions = &cloudwatchtypes.LabelOptions{ + metricDataInput.LabelOptions = &cloudwatch.LabelOptions{ Timezone: aws.String(queries[0].TimezoneUTCOffset), } } diff --git a/pkg/tsdb/cloudwatch/metric_data_input_builder_test.go b/pkg/tsdb/cloudwatch/metric_data_input_builder_test.go index efb7e1d5a82..5b8ba2beb53 100644 --- a/pkg/tsdb/cloudwatch/metric_data_input_builder_test.go +++ b/pkg/tsdb/cloudwatch/metric_data_input_builder_test.go @@ -5,9 +5,8 @@ import ( "testing" "time" - "github.com/aws/aws-sdk-go-v2/aws" - cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" - + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/cloudwatch" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -21,9 +20,9 @@ func TestMetricDataInputBuilder(t *testing.T) { tests := []struct { name string timezoneUTCOffset string - expectedLabelOptions *cloudwatchtypes.LabelOptions + expectedLabelOptions *cloudwatch.LabelOptions }{ - {name: "when timezoneUTCOffset is provided", timezoneUTCOffset: "+1234", expectedLabelOptions: &cloudwatchtypes.LabelOptions{Timezone: aws.String("+1234")}}, + {name: "when timezoneUTCOffset is provided", timezoneUTCOffset: "+1234", expectedLabelOptions: &cloudwatch.LabelOptions{Timezone: aws.String("+1234")}}, {name: "when timezoneUTCOffset is not provided", timezoneUTCOffset: "", expectedLabelOptions: nil}, } diff --git a/pkg/tsdb/cloudwatch/metric_data_query_builder.go b/pkg/tsdb/cloudwatch/metric_data_query_builder.go index 2edb4f35be8..fbed54f5950 100644 --- a/pkg/tsdb/cloudwatch/metric_data_query_builder.go +++ b/pkg/tsdb/cloudwatch/metric_data_query_builder.go @@ -4,10 +4,11 @@ import ( "context" "fmt" "sort" + "strconv" "strings" - "github.com/aws/aws-sdk-go-v2/aws" - cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/cloudwatch" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/features" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" @@ -15,8 +16,8 @@ import ( const keySeparator = "|&|" -func (e *cloudWatchExecutor) buildMetricDataQuery(ctx context.Context, query *models.CloudWatchQuery) (cloudwatchtypes.MetricDataQuery, error) { - mdq := cloudwatchtypes.MetricDataQuery{ +func (e *cloudWatchExecutor) buildMetricDataQuery(ctx context.Context, query *models.CloudWatchQuery) (*cloudwatch.MetricDataQuery, error) { + mdq := &cloudwatch.MetricDataQuery{ Id: aws.String(query.Id), ReturnData: aws.Bool(query.ReturnData), } @@ -27,10 +28,10 @@ func (e *cloudWatchExecutor) buildMetricDataQuery(ctx context.Context, query *mo switch query.GetGetMetricDataAPIMode() { case models.GMDApiModeMathExpression: - mdq.Period = aws.Int32(int32(query.Period)) + mdq.Period = aws.Int64(int64(query.Period)) mdq.Expression = aws.String(query.Expression) case models.GMDApiModeSQLExpression: - mdq.Period = aws.Int32(int32(query.Period)) + mdq.Period = aws.Int64(int64(query.Period)) mdq.Expression = aws.String(query.SqlExpression) case models.GMDApiModeInferredSearchExpression: mdq.Expression = aws.String(buildSearchExpression(query, query.Statistic)) @@ -38,17 +39,17 @@ func (e *cloudWatchExecutor) buildMetricDataQuery(ctx context.Context, query *mo mdq.Label = aws.String(buildSearchExpressionLabel(query)) } case models.GMDApiModeMetricStat: - mdq.MetricStat = &cloudwatchtypes.MetricStat{ - Metric: &cloudwatchtypes.Metric{ + mdq.MetricStat = &cloudwatch.MetricStat{ + Metric: &cloudwatch.Metric{ Namespace: aws.String(query.Namespace), MetricName: aws.String(query.MetricName), - Dimensions: make([]cloudwatchtypes.Dimension, 0), + Dimensions: make([]*cloudwatch.Dimension, 0), }, - Period: aws.Int32(int32(query.Period)), + Period: aws.Int64(int64(query.Period)), } for key, values := range query.Dimensions { mdq.MetricStat.Metric.Dimensions = append(mdq.MetricStat.Metric.Dimensions, - cloudwatchtypes.Dimension{ + &cloudwatch.Dimension{ Name: aws.String(key), Value: aws.String(values[0]), }) @@ -120,14 +121,14 @@ func buildSearchExpression(query *models.CloudWatchQuery, stat string) string { } schema = fmt.Sprintf("{%s}", schema) schemaSearchTermAndAccount := strings.TrimSpace(strings.Join([]string{schema, searchTerm, account}, " ")) - return fmt.Sprintf("REMOVE_EMPTY(SEARCH('%s', '%s', %d))", schemaSearchTermAndAccount, stat, query.Period) + return fmt.Sprintf("REMOVE_EMPTY(SEARCH('%s', '%s', %s))", schemaSearchTermAndAccount, stat, strconv.Itoa(query.Period)) } sort.Strings(dimensionNamesWithoutKnownValues) searchTerm = appendSearch(searchTerm, join(dimensionNamesWithoutKnownValues, " ", `"`, `"`)) namespace := fmt.Sprintf("Namespace=%q", query.Namespace) namespaceSearchTermAndAccount := strings.TrimSpace(strings.Join([]string{namespace, searchTerm, account}, " ")) - return fmt.Sprintf(`REMOVE_EMPTY(SEARCH('%s', '%s', %d))`, namespaceSearchTermAndAccount, stat, query.Period) + return fmt.Sprintf(`REMOVE_EMPTY(SEARCH('%s', '%s', %s))`, namespaceSearchTermAndAccount, stat, strconv.Itoa(query.Period)) } func buildSearchExpressionLabel(query *models.CloudWatchQuery) string { diff --git a/pkg/tsdb/cloudwatch/metric_data_query_builder_test.go b/pkg/tsdb/cloudwatch/metric_data_query_builder_test.go index e6e74b8acf5..907516f0d80 100644 --- a/pkg/tsdb/cloudwatch/metric_data_query_builder_test.go +++ b/pkg/tsdb/cloudwatch/metric_data_query_builder_test.go @@ -4,8 +4,7 @@ import ( "context" "testing" - "github.com/aws/aws-sdk-go-v2/aws" - + "github.com/aws/aws-sdk-go/aws" "github.com/grafana/grafana-plugin-sdk-go/backend/log" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/features" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" @@ -89,7 +88,7 @@ func TestMetricDataQueryBuilder(t *testing.T) { mdq, err := executor.buildMetricDataQuery(context.Background(), query) require.NoError(t, err) require.Nil(t, mdq.MetricStat) - assert.Equal(t, int32(300), *mdq.Period) + assert.Equal(t, int64(300), *mdq.Period) assert.Equal(t, `SUM([a,b])`, *mdq.Expression) }) diff --git a/pkg/tsdb/cloudwatch/metric_find_query.go b/pkg/tsdb/cloudwatch/metric_find_query.go index ce618787859..77b41f1669a 100644 --- a/pkg/tsdb/cloudwatch/metric_find_query.go +++ b/pkg/tsdb/cloudwatch/metric_find_query.go @@ -12,14 +12,10 @@ import ( "strings" "time" - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/ec2" - ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" - - "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" - "github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi" - resourcegroupstaggingapitypes "github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi/types" - + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/cloudwatchlogs" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi" "github.com/grafana/grafana-plugin-sdk-go/backend" ) @@ -47,7 +43,7 @@ func (e *cloudWatchExecutor) handleGetEbsVolumeIds(ctx context.Context, pluginCt region := parameters.Get("region") instanceId := parameters.Get("instanceId") - instanceIds := parseMultiSelectValue(instanceId) + instanceIds := aws.StringSlice(parseMultiSelectValue(instanceId)) instances, err := e.ec2DescribeInstances(ctx, pluginCtx, region, nil, instanceIds) if err != nil { return nil, err @@ -76,16 +72,16 @@ func (e *cloudWatchExecutor) handleGetEc2InstanceAttribute(ctx context.Context, return nil, fmt.Errorf("error unmarshaling filter: %v", err) } - var filters []ec2types.Filter + var filters []*ec2.Filter for k, v := range filterMap { if vv, ok := v.([]any); ok { - var values []string + var values []*string for _, vvv := range vv { if vvvv, ok := vvv.(string); ok { - values = append(values, vvvv) + values = append(values, &vvvv) } } - filters = append(filters, ec2types.Filter{ + filters = append(filters, &ec2.Filter{ Name: aws.String(k), Values: values, }) @@ -124,7 +120,7 @@ func (e *cloudWatchExecutor) handleGetEc2InstanceAttribute(ctx context.Context, return result, nil } -func getInstanceAttributeValue(attributeName string, instance ec2types.Instance) (value string, found bool, err error) { +func getInstanceAttributeValue(attributeName string, instance *ec2.Instance) (value string, found bool, err error) { tags := make(map[string]string) for _, tag := range instance.Tags { tags[*tag.Key] = *tag.Value @@ -156,12 +152,7 @@ func getInstanceAttributeValue(attributeName string, instance ec2types.Instance) if v.Kind() == reflect.Ptr && v.IsNil() { return "", false, nil } - if v.Kind() == reflect.String { - if v.String() == "" { - return "", false, nil - } - data = v.String() - } else if attr, ok := v.Interface().(*string); ok { + if attr, ok := v.Interface().(*string); ok { data = *attr } else if attr, ok := v.Interface().(*time.Time); ok { data = attr.String() @@ -188,23 +179,24 @@ func (e *cloudWatchExecutor) handleGetResourceArns(ctx context.Context, pluginCt return nil, fmt.Errorf("error unmarshaling filter: %v", err) } - var filters []resourcegroupstaggingapitypes.TagFilter + var filters []*resourcegroupstaggingapi.TagFilter for k, v := range tagsMap { if vv, ok := v.([]any); ok { - var values []string + var values []*string for _, vvv := range vv { if vvvv, ok := vvv.(string); ok { - values = append(values, vvvv) + values = append(values, &vvvv) } } - filters = append(filters, resourcegroupstaggingapitypes.TagFilter{ + filters = append(filters, &resourcegroupstaggingapi.TagFilter{ Key: aws.String(k), Values: values, }) } } - resourceTypes := []string{resourceType} + var resourceTypes []*string + resourceTypes = append(resourceTypes, &resourceType) resources, err := e.resourceGroupsGetResources(ctx, pluginCtx, region, filters, resourceTypes) if err != nil { @@ -220,7 +212,7 @@ func (e *cloudWatchExecutor) handleGetResourceArns(ctx context.Context, pluginCt return result, nil } -func (e *cloudWatchExecutor) ec2DescribeInstances(ctx context.Context, pluginCtx backend.PluginContext, region string, filters []ec2types.Filter, instanceIds []string) (*ec2.DescribeInstancesOutput, error) { +func (e *cloudWatchExecutor) ec2DescribeInstances(ctx context.Context, pluginCtx backend.PluginContext, region string, filters []*ec2.Filter, instanceIds []*string) (*ec2.DescribeInstancesOutput, error) { params := &ec2.DescribeInstancesInput{ Filters: filters, InstanceIds: instanceIds, @@ -231,20 +223,19 @@ func (e *cloudWatchExecutor) ec2DescribeInstances(ctx context.Context, pluginCtx return nil, err } - resp := &ec2.DescribeInstancesOutput{} - pager := ec2.NewDescribeInstancesPaginator(client, params) - for pager.HasMorePages() { - page, err := pager.NextPage(ctx) - if err != nil { - return resp, fmt.Errorf("describe instances pager failed: %w", err) - } + var resp ec2.DescribeInstancesOutput + if err := client.DescribeInstancesPagesWithContext(ctx, params, func(page *ec2.DescribeInstancesOutput, lastPage bool) bool { resp.Reservations = append(resp.Reservations, page.Reservations...) + return !lastPage + }); err != nil { + return nil, fmt.Errorf("failed to call ec2:DescribeInstances, %w", err) } - return resp, nil + + return &resp, nil } -func (e *cloudWatchExecutor) resourceGroupsGetResources(ctx context.Context, pluginCtx backend.PluginContext, region string, filters []resourcegroupstaggingapitypes.TagFilter, - resourceTypes []string) (*resourcegroupstaggingapi.GetResourcesOutput, error) { +func (e *cloudWatchExecutor) resourceGroupsGetResources(ctx context.Context, pluginCtx backend.PluginContext, region string, filters []*resourcegroupstaggingapi.TagFilter, + resourceTypes []*string) (*resourcegroupstaggingapi.GetResourcesOutput, error) { params := &resourcegroupstaggingapi.GetResourcesInput{ ResourceTypeFilters: resourceTypes, TagFilters: filters, @@ -256,13 +247,12 @@ func (e *cloudWatchExecutor) resourceGroupsGetResources(ctx context.Context, plu } var resp resourcegroupstaggingapi.GetResourcesOutput - paginator := resourcegroupstaggingapi.NewGetResourcesPaginator(client, params) - for paginator.HasMorePages() { - page, err := paginator.NextPage(ctx) - if err != nil { - return nil, fmt.Errorf("get resource groups paginator failed: %w", err) - } - resp.ResourceTagMappingList = append(resp.ResourceTagMappingList, page.ResourceTagMappingList...) + if err := client.GetResourcesPagesWithContext(ctx, params, + func(page *resourcegroupstaggingapi.GetResourcesOutput, lastPage bool) bool { + resp.ResourceTagMappingList = append(resp.ResourceTagMappingList, page.ResourceTagMappingList...) + return !lastPage + }); err != nil { + return nil, fmt.Errorf("failed to call tag:GetResources, %w", err) } return &resp, nil @@ -280,17 +270,17 @@ func (e *cloudWatchExecutor) handleGetLogGroups(ctx context.Context, pluginCtx b } logGroupLimit := defaultLogGroupLimit - intLimit, err := strconv.ParseInt(limit, 10, 32) + intLimit, err := strconv.ParseInt(limit, 10, 64) if err == nil && intLimit > 0 { - logGroupLimit = int32(intLimit) + logGroupLimit = intLimit } - input := &cloudwatchlogs.DescribeLogGroupsInput{Limit: aws.Int32(logGroupLimit)} + input := &cloudwatchlogs.DescribeLogGroupsInput{Limit: aws.Int64(logGroupLimit)} if len(logGroupNamePrefix) > 0 { input.LogGroupNamePrefix = aws.String(logGroupNamePrefix) } var response *cloudwatchlogs.DescribeLogGroupsOutput - response, err = logsClient.DescribeLogGroups(ctx, input) + response, err = logsClient.DescribeLogGroupsWithContext(ctx, input) if err != nil || response == nil { return nil, err } diff --git a/pkg/tsdb/cloudwatch/metric_find_query_test.go b/pkg/tsdb/cloudwatch/metric_find_query_test.go index 7a4598896ab..85329bd5760 100644 --- a/pkg/tsdb/cloudwatch/metric_find_query_test.go +++ b/pkg/tsdb/cloudwatch/metric_find_query_test.go @@ -6,12 +6,14 @@ import ( "net/url" "testing" - "github.com/aws/aws-sdk-go-v2/aws" - ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" - "github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi" - resourcegroupstaggingapitypes "github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi/types" - + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/client" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi" + "github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi/resourcegroupstaggingapiiface" "github.com/grafana/grafana-plugin-sdk-go/backend" + "github.com/grafana/grafana-plugin-sdk-go/backend/datasource" + "github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt" "github.com/grafana/grafana-plugin-sdk-go/backend/log" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" "github.com/stretchr/testify/assert" @@ -19,26 +21,26 @@ import ( ) func TestQuery_InstanceAttributes(t *testing.T) { - origNewEC2API := NewEC2API + origNewEC2Client := NewEC2Client t.Cleanup(func() { - NewEC2API = origNewEC2API + NewEC2Client = origNewEC2Client }) var cli oldEC2Client - NewEC2API = func(aws.Config) models.EC2APIProvider { + NewEC2Client = func(client.ConfigProvider) models.EC2APIProvider { return cli } t.Run("Get instance ID", func(t *testing.T) { const instanceID = "i-12345678" cli = oldEC2Client{ - reservations: []ec2types.Reservation{ + reservations: []*ec2.Reservation{ { - Instances: []ec2types.Instance{ + Instances: []*ec2.Instance{ { InstanceId: aws.String(instanceID), - Tags: []ec2types.Tag{ + Tags: []*ec2.Tag{ { Key: aws.String("Environment"), Value: aws.String("production"), @@ -50,7 +52,9 @@ func TestQuery_InstanceAttributes(t *testing.T) { }, } - im := defaultTestInstanceManager() + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil + }) filterMap := map[string][]string{ "tag:Environment": {"production"}, @@ -78,17 +82,17 @@ func TestQuery_InstanceAttributes(t *testing.T) { }) t.Run("Get different types", func(t *testing.T) { - var expectedInt int32 = 3 + var expectedInt int64 = 3 var expectedBool = true var expectedArn = "arn" cli = oldEC2Client{ - reservations: []ec2types.Reservation{ + reservations: []*ec2.Reservation{ { - Instances: []ec2types.Instance{ + Instances: []*ec2.Instance{ { AmiLaunchIndex: &expectedInt, EbsOptimized: &expectedBool, - IamInstanceProfile: &ec2types.IamInstanceProfile{ + IamInstanceProfile: &ec2.IamInstanceProfile{ Arn: &expectedArn, }, }, @@ -97,7 +101,9 @@ func TestQuery_InstanceAttributes(t *testing.T) { }, } - im := defaultTestInstanceManager() + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil + }) executor := newExecutor(im, log.NewNullLogger()) @@ -157,52 +163,52 @@ func TestQuery_InstanceAttributes(t *testing.T) { } func TestQuery_EBSVolumeIDs(t *testing.T) { - origNewEC2API := NewEC2API + origNewEC2Client := NewEC2Client t.Cleanup(func() { - NewEC2API = origNewEC2API + NewEC2Client = origNewEC2Client }) var cli oldEC2Client - NewEC2API = func(aws.Config) models.EC2APIProvider { + NewEC2Client = func(client.ConfigProvider) models.EC2APIProvider { return cli } t.Run("", func(t *testing.T) { cli = oldEC2Client{ - reservations: []ec2types.Reservation{ + reservations: []*ec2.Reservation{ { - Instances: []ec2types.Instance{ + Instances: []*ec2.Instance{ { InstanceId: aws.String("i-1"), - BlockDeviceMappings: []ec2types.InstanceBlockDeviceMapping{ - {Ebs: &ec2types.EbsInstanceBlockDevice{VolumeId: aws.String("vol-1-1")}}, - {Ebs: &ec2types.EbsInstanceBlockDevice{VolumeId: aws.String("vol-1-2")}}, + BlockDeviceMappings: []*ec2.InstanceBlockDeviceMapping{ + {Ebs: &ec2.EbsInstanceBlockDevice{VolumeId: aws.String("vol-1-1")}}, + {Ebs: &ec2.EbsInstanceBlockDevice{VolumeId: aws.String("vol-1-2")}}, }, }, { InstanceId: aws.String("i-2"), - BlockDeviceMappings: []ec2types.InstanceBlockDeviceMapping{ - {Ebs: &ec2types.EbsInstanceBlockDevice{VolumeId: aws.String("vol-2-1")}}, - {Ebs: &ec2types.EbsInstanceBlockDevice{VolumeId: aws.String("vol-2-2")}}, + BlockDeviceMappings: []*ec2.InstanceBlockDeviceMapping{ + {Ebs: &ec2.EbsInstanceBlockDevice{VolumeId: aws.String("vol-2-1")}}, + {Ebs: &ec2.EbsInstanceBlockDevice{VolumeId: aws.String("vol-2-2")}}, }, }, }, }, { - Instances: []ec2types.Instance{ + Instances: []*ec2.Instance{ { InstanceId: aws.String("i-3"), - BlockDeviceMappings: []ec2types.InstanceBlockDeviceMapping{ - {Ebs: &ec2types.EbsInstanceBlockDevice{VolumeId: aws.String("vol-3-1")}}, - {Ebs: &ec2types.EbsInstanceBlockDevice{VolumeId: aws.String("vol-3-2")}}, + BlockDeviceMappings: []*ec2.InstanceBlockDeviceMapping{ + {Ebs: &ec2.EbsInstanceBlockDevice{VolumeId: aws.String("vol-3-1")}}, + {Ebs: &ec2.EbsInstanceBlockDevice{VolumeId: aws.String("vol-3-2")}}, }, }, { InstanceId: aws.String("i-4"), - BlockDeviceMappings: []ec2types.InstanceBlockDeviceMapping{ - {Ebs: &ec2types.EbsInstanceBlockDevice{VolumeId: aws.String("vol-4-1")}}, - {Ebs: &ec2types.EbsInstanceBlockDevice{VolumeId: aws.String("vol-4-2")}}, + BlockDeviceMappings: []*ec2.InstanceBlockDeviceMapping{ + {Ebs: &ec2.EbsInstanceBlockDevice{VolumeId: aws.String("vol-4-1")}}, + {Ebs: &ec2.EbsInstanceBlockDevice{VolumeId: aws.String("vol-4-2")}}, }, }, }, @@ -210,7 +216,9 @@ func TestQuery_EBSVolumeIDs(t *testing.T) { }, } - im := defaultTestInstanceManager() + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil + }) executor := newExecutor(im, log.NewNullLogger()) resp, err := executor.handleGetEbsVolumeIds( @@ -234,23 +242,23 @@ func TestQuery_EBSVolumeIDs(t *testing.T) { } func TestQuery_ResourceARNs(t *testing.T) { - origNewRGTAClient := NewRGTAClient + origNewRGTAClient := newRGTAClient t.Cleanup(func() { - NewRGTAClient = origNewRGTAClient + newRGTAClient = origNewRGTAClient }) var cli fakeRGTAClient - NewRGTAClient = func(aws.Config) resourcegroupstaggingapi.GetResourcesAPIClient { + newRGTAClient = func(client.ConfigProvider) resourcegroupstaggingapiiface.ResourceGroupsTaggingAPIAPI { return cli } t.Run("", func(t *testing.T) { cli = fakeRGTAClient{ - tagMapping: []resourcegroupstaggingapitypes.ResourceTagMapping{ + tagMapping: []*resourcegroupstaggingapi.ResourceTagMapping{ { ResourceARN: aws.String("arn:aws:ec2:us-east-1:123456789012:instance/i-12345678901234567"), - Tags: []resourcegroupstaggingapitypes.Tag{ + Tags: []*resourcegroupstaggingapi.Tag{ { Key: aws.String("Environment"), Value: aws.String("production"), @@ -259,7 +267,7 @@ func TestQuery_ResourceARNs(t *testing.T) { }, { ResourceARN: aws.String("arn:aws:ec2:us-east-1:123456789012:instance/i-76543210987654321"), - Tags: []resourcegroupstaggingapitypes.Tag{ + Tags: []*resourcegroupstaggingapi.Tag{ { Key: aws.String("Environment"), Value: aws.String("production"), @@ -269,7 +277,9 @@ func TestQuery_ResourceARNs(t *testing.T) { }, } - im := defaultTestInstanceManager() + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil + }) tagMap := map[string][]string{ "Environment": {"production"}, diff --git a/pkg/tsdb/cloudwatch/mocks/cloudwatch_metric_api.go b/pkg/tsdb/cloudwatch/mocks/cloudwatch_metric_api.go index 86040e165a2..cdb1aeca82f 100644 --- a/pkg/tsdb/cloudwatch/mocks/cloudwatch_metric_api.go +++ b/pkg/tsdb/cloudwatch/mocks/cloudwatch_metric_api.go @@ -1,65 +1,71 @@ package mocks import ( - "context" - - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/cloudwatch" - cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" - - "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" - + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/request" + "github.com/aws/aws-sdk-go/service/cloudwatch" + "github.com/aws/aws-sdk-go/service/cloudwatch/cloudwatchiface" "github.com/stretchr/testify/mock" ) type FakeMetricsAPI struct { - models.CWClient - - Metrics []cloudwatchtypes.Metric - OwningAccounts []string + Metrics []*cloudwatch.Metric + OwningAccounts []*string MetricsPerPage int - - cursor int } -func (c *FakeMetricsAPI) ListMetrics(_ context.Context, _ *cloudwatch.ListMetricsInput, _ ...func(*cloudwatch.Options)) (*cloudwatch.ListMetricsOutput, error) { +func (c *FakeMetricsAPI) ListMetricsPagesWithContext(ctx aws.Context, input *cloudwatch.ListMetricsInput, fn func(*cloudwatch.ListMetricsOutput, bool) bool, opts ...request.Option) error { if c.MetricsPerPage == 0 { c.MetricsPerPage = 1000 } - var metrics []cloudwatchtypes.Metric - nextToken := aws.String("yes") - if c.cursor < len(c.Metrics) { - end := c.cursor + c.MetricsPerPage - if end > len(c.Metrics) { - end = len(c.Metrics) - nextToken = nil - } - metrics = c.Metrics[c.cursor:end] - } - c.cursor += c.MetricsPerPage + chunks := chunkSlice(c.Metrics, c.MetricsPerPage) - return &cloudwatch.ListMetricsOutput{ - Metrics: metrics, - OwningAccounts: c.OwningAccounts, - NextToken: nextToken, - }, nil + for i, metrics := range chunks { + response := fn(&cloudwatch.ListMetricsOutput{ + Metrics: metrics, + OwningAccounts: c.OwningAccounts, + }, i+1 == len(chunks)) + if !response { + break + } + } + return nil +} + +func chunkSlice(slice []*cloudwatch.Metric, chunkSize int) [][]*cloudwatch.Metric { + var chunks [][]*cloudwatch.Metric + for { + if len(slice) == 0 { + break + } + if len(slice) < chunkSize { + chunkSize = len(slice) + } + + chunks = append(chunks, slice[0:chunkSize]) + slice = slice[chunkSize:] + } + + return chunks } type MetricsAPI struct { + cloudwatchiface.CloudWatchAPI mock.Mock - models.CWClient - Metrics []cloudwatchtypes.Metric + Metrics []*cloudwatch.Metric } -func (m *MetricsAPI) GetMetricData(ctx context.Context, input *cloudwatch.GetMetricDataInput, optFns ...func(*cloudwatch.Options)) (*cloudwatch.GetMetricDataOutput, error) { - args := m.Called(ctx, input, optFns) +func (m *MetricsAPI) GetMetricDataWithContext(ctx aws.Context, input *cloudwatch.GetMetricDataInput, opts ...request.Option) (*cloudwatch.GetMetricDataOutput, error) { + args := m.Called(ctx, input, opts) return args.Get(0).(*cloudwatch.GetMetricDataOutput), args.Error(1) } -func (m *MetricsAPI) ListMetrics(_ context.Context, _ *cloudwatch.ListMetricsInput, _ ...func(*cloudwatch.Options)) (*cloudwatch.ListMetricsOutput, error) { - return &cloudwatch.ListMetricsOutput{ +func (m *MetricsAPI) ListMetricsPagesWithContext(ctx aws.Context, input *cloudwatch.ListMetricsInput, fn func(*cloudwatch.ListMetricsOutput, bool) bool, opts ...request.Option) error { + fn(&cloudwatch.ListMetricsOutput{ Metrics: m.Metrics, - }, m.Called().Error(0) + }, true) + + return m.Called().Error(0) } diff --git a/pkg/tsdb/cloudwatch/mocks/logs.go b/pkg/tsdb/cloudwatch/mocks/logs.go index 7843fdaacd1..e0b16bf436d 100644 --- a/pkg/tsdb/cloudwatch/mocks/logs.go +++ b/pkg/tsdb/cloudwatch/mocks/logs.go @@ -3,7 +3,10 @@ package mocks import ( "context" - "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/request" + "github.com/aws/aws-sdk-go/service/cloudwatchlogs" + "github.com/aws/aws-sdk-go/service/cloudwatchlogs/cloudwatchlogsiface" "github.com/stretchr/testify/mock" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources" @@ -13,13 +16,13 @@ type LogsAPI struct { mock.Mock } -func (l *LogsAPI) DescribeLogGroups(_ context.Context, input *cloudwatchlogs.DescribeLogGroupsInput, _ ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.DescribeLogGroupsOutput, error) { +func (l *LogsAPI) DescribeLogGroupsWithContext(ctx context.Context, input *cloudwatchlogs.DescribeLogGroupsInput, option ...request.Option) (*cloudwatchlogs.DescribeLogGroupsOutput, error) { args := l.Called(input) return args.Get(0).(*cloudwatchlogs.DescribeLogGroupsOutput), args.Error(1) } -func (l *LogsAPI) GetLogGroupFields(_ context.Context, input *cloudwatchlogs.GetLogGroupFieldsInput, _ ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.GetLogGroupFieldsOutput, error) { +func (l *LogsAPI) GetLogGroupFieldsWithContext(ctx context.Context, input *cloudwatchlogs.GetLogGroupFieldsInput, option ...request.Option) (*cloudwatchlogs.GetLogGroupFieldsOutput, error) { args := l.Called(input) return args.Get(0).(*cloudwatchlogs.GetLogGroupFieldsOutput), args.Error(1) @@ -29,40 +32,26 @@ type LogsService struct { mock.Mock } -func (l *LogsService) GetLogGroups(_ context.Context, request resources.LogGroupsRequest) ([]resources.ResourceResponse[resources.LogGroup], error) { +func (l *LogsService) GetLogGroupsWithContext(ctx context.Context, request resources.LogGroupsRequest) ([]resources.ResourceResponse[resources.LogGroup], error) { args := l.Called(request) return args.Get(0).([]resources.ResourceResponse[resources.LogGroup]), args.Error(1) } -func (l *LogsService) GetLogGroupFields(_ context.Context, request resources.LogGroupFieldsRequest) ([]resources.ResourceResponse[resources.LogGroupField], error) { +func (l *LogsService) GetLogGroupFieldsWithContext(ctx context.Context, request resources.LogGroupFieldsRequest, option ...request.Option) ([]resources.ResourceResponse[resources.LogGroupField], error) { args := l.Called(request) return args.Get(0).([]resources.ResourceResponse[resources.LogGroupField]), args.Error(1) } type MockLogEvents struct { + cloudwatchlogsiface.CloudWatchLogsAPI + mock.Mock } -func (m *MockLogEvents) StartQuery(context.Context, *cloudwatchlogs.StartQueryInput, ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.StartQueryOutput, error) { - return nil, nil -} - -func (m *MockLogEvents) StopQuery(context.Context, *cloudwatchlogs.StopQueryInput, ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.StopQueryOutput, error) { - return nil, nil -} - -func (m *MockLogEvents) GetQueryResults(context.Context, *cloudwatchlogs.GetQueryResultsInput, ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.GetQueryResultsOutput, error) { - return nil, nil -} - -func (m *MockLogEvents) DescribeLogGroups(context.Context, *cloudwatchlogs.DescribeLogGroupsInput, ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.DescribeLogGroupsOutput, error) { - return nil, nil -} - -func (m *MockLogEvents) GetLogEvents(ctx context.Context, input *cloudwatchlogs.GetLogEventsInput, optFns ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.GetLogEventsOutput, error) { - args := m.Called(ctx, input, optFns) +func (m *MockLogEvents) GetLogEventsWithContext(ctx aws.Context, input *cloudwatchlogs.GetLogEventsInput, option ...request.Option) (*cloudwatchlogs.GetLogEventsOutput, error) { + args := m.Called(ctx, input, option) return args.Get(0).(*cloudwatchlogs.GetLogEventsOutput), args.Error(1) } diff --git a/pkg/tsdb/cloudwatch/mocks/metrics_client.go b/pkg/tsdb/cloudwatch/mocks/metrics_client.go index adf23796945..44ab1fbcb5b 100644 --- a/pkg/tsdb/cloudwatch/mocks/metrics_client.go +++ b/pkg/tsdb/cloudwatch/mocks/metrics_client.go @@ -3,8 +3,7 @@ package mocks import ( "context" - "github.com/aws/aws-sdk-go-v2/service/cloudwatch" - + "github.com/aws/aws-sdk-go/service/cloudwatch" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources" "github.com/stretchr/testify/mock" ) @@ -13,7 +12,7 @@ type FakeMetricsClient struct { mock.Mock } -func (m *FakeMetricsClient) ListMetricsWithPageLimit(_ context.Context, params *cloudwatch.ListMetricsInput) ([]resources.MetricResponse, error) { +func (m *FakeMetricsClient) ListMetricsWithPageLimit(ctx context.Context, params *cloudwatch.ListMetricsInput) ([]resources.MetricResponse, error) { args := m.Called(params) return args.Get(0).([]resources.MetricResponse), args.Error(1) } diff --git a/pkg/tsdb/cloudwatch/mocks/oam_client.go b/pkg/tsdb/cloudwatch/mocks/oam_client.go index d16253e3252..932ca1579b4 100644 --- a/pkg/tsdb/cloudwatch/mocks/oam_client.go +++ b/pkg/tsdb/cloudwatch/mocks/oam_client.go @@ -3,7 +3,8 @@ package mocks import ( "context" - "github.com/aws/aws-sdk-go-v2/service/oam" + "github.com/aws/aws-sdk-go/aws/request" + "github.com/aws/aws-sdk-go/service/oam" "github.com/stretchr/testify/mock" ) @@ -11,12 +12,12 @@ type FakeOAMClient struct { mock.Mock } -func (o *FakeOAMClient) ListSinks(_ context.Context, input *oam.ListSinksInput, _ ...func(*oam.Options)) (*oam.ListSinksOutput, error) { +func (o *FakeOAMClient) ListSinksWithContext(ctx context.Context, input *oam.ListSinksInput, opts ...request.Option) (*oam.ListSinksOutput, error) { args := o.Called(input) return args.Get(0).(*oam.ListSinksOutput), args.Error(1) } -func (o *FakeOAMClient) ListAttachedLinks(_ context.Context, input *oam.ListAttachedLinksInput, _ ...func(*oam.Options)) (*oam.ListAttachedLinksOutput, error) { +func (o *FakeOAMClient) ListAttachedLinksWithContext(ctx context.Context, input *oam.ListAttachedLinksInput, opts ...request.Option) (*oam.ListAttachedLinksOutput, error) { args := o.Called(input) return args.Get(0).(*oam.ListAttachedLinksOutput), args.Error(1) } diff --git a/pkg/tsdb/cloudwatch/mocks/regions.go b/pkg/tsdb/cloudwatch/mocks/regions.go index a7717481516..1d0ff115ea7 100644 --- a/pkg/tsdb/cloudwatch/mocks/regions.go +++ b/pkg/tsdb/cloudwatch/mocks/regions.go @@ -3,7 +3,9 @@ package mocks import ( "context" - "github.com/aws/aws-sdk-go-v2/service/ec2" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/request" + "github.com/aws/aws-sdk-go/service/ec2" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources" "github.com/stretchr/testify/mock" ) @@ -12,7 +14,7 @@ type RegionsService struct { mock.Mock } -func (r *RegionsService) GetRegions(_ context.Context) (in []resources.ResourceResponse[resources.Region], e error) { +func (r *RegionsService) GetRegions(ctx context.Context) (in []resources.ResourceResponse[resources.Region], e error) { args := r.Called() return args.Get(0).(([]resources.ResourceResponse[resources.Region])), args.Error(1) } @@ -21,12 +23,12 @@ type EC2Mock struct { mock.Mock } -func (e *EC2Mock) DescribeRegions(_ context.Context, _ *ec2.DescribeRegionsInput, _ ...func(*ec2.Options)) (*ec2.DescribeRegionsOutput, error) { +func (e *EC2Mock) DescribeRegionsWithContext(ctx aws.Context, in *ec2.DescribeRegionsInput, opts ...request.Option) (*ec2.DescribeRegionsOutput, error) { args := e.Called() return args.Get(0).(*ec2.DescribeRegionsOutput), args.Error(1) } -func (e *EC2Mock) DescribeInstances(_ context.Context, in *ec2.DescribeInstancesInput, _ ...func(*ec2.Options)) (*ec2.DescribeInstancesOutput, error) { - args := e.Called(in) - return nil, args.Error(0) +func (e *EC2Mock) DescribeInstancesPagesWithContext(ctx aws.Context, in *ec2.DescribeInstancesInput, fn func(*ec2.DescribeInstancesOutput, bool) bool, opts ...request.Option) error { + args := e.Called(in, fn) + return args.Error(0) } diff --git a/pkg/tsdb/cloudwatch/models/api.go b/pkg/tsdb/cloudwatch/models/api.go index 35d60b7febc..c54c156b123 100644 --- a/pkg/tsdb/cloudwatch/models/api.go +++ b/pkg/tsdb/cloudwatch/models/api.go @@ -4,11 +4,11 @@ import ( "context" "net/url" - "github.com/aws/aws-sdk-go-v2/service/cloudwatch" - "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" - "github.com/aws/aws-sdk-go-v2/service/ec2" - "github.com/aws/aws-sdk-go-v2/service/oam" - + "github.com/aws/aws-sdk-go/aws/request" + "github.com/aws/aws-sdk-go/service/cloudwatch" + "github.com/aws/aws-sdk-go/service/cloudwatchlogs" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go/service/oam" "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend/log" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources" @@ -19,13 +19,12 @@ type RequestContextFactoryFunc func(ctx context.Context, pluginCtx backend.Plugi type RouteHandlerFunc func(ctx context.Context, pluginCtx backend.PluginContext, reqContextFactory RequestContextFactoryFunc, parameters url.Values) ([]byte, *HttpError) type RequestContext struct { - MetricsClientProvider MetricsClientProvider - ListMetricsAPIProvider cloudwatch.ListMetricsAPIClient - LogsAPIProvider CloudWatchLogsAPIProvider - OAMAPIProvider OAMAPIProvider - EC2APIProvider EC2APIProvider - Settings CloudWatchSettings - Logger log.Logger + MetricsClientProvider MetricsClientProvider + LogsAPIProvider CloudWatchLogsAPIProvider + OAMAPIProvider OAMAPIProvider + EC2APIProvider EC2APIProvider + Settings CloudWatchSettings + Logger log.Logger } // Services @@ -36,8 +35,8 @@ type ListMetricsProvider interface { } type LogGroupsProvider interface { - GetLogGroups(ctx context.Context, request resources.LogGroupsRequest) ([]resources.ResourceResponse[resources.LogGroup], error) - GetLogGroupFields(ctx context.Context, request resources.LogGroupFieldsRequest) ([]resources.ResourceResponse[resources.LogGroupField], error) + GetLogGroupsWithContext(ctx context.Context, request resources.LogGroupsRequest) ([]resources.ResourceResponse[resources.LogGroup], error) + GetLogGroupFieldsWithContext(ctx context.Context, request resources.LogGroupFieldsRequest, option ...request.Option) ([]resources.ResourceResponse[resources.LogGroupField], error) } type AccountsProvider interface { @@ -55,42 +54,20 @@ type MetricsClientProvider interface { // APIs - instead of using the API defined in the services within the aws-sdk-go directly, specify a subset of the API with methods that are actually used in a service or a client type CloudWatchMetricsAPIProvider interface { - ListMetrics(ctx context.Context, in *cloudwatch.ListMetricsInput, optFns ...func(*cloudwatch.Options)) error + ListMetricsPagesWithContext(ctx context.Context, in *cloudwatch.ListMetricsInput, fn func(*cloudwatch.ListMetricsOutput, bool) bool, opts ...request.Option) error } type CloudWatchLogsAPIProvider interface { - cloudwatchlogs.DescribeLogGroupsAPIClient - GetLogGroupFields(ctx context.Context, in *cloudwatchlogs.GetLogGroupFieldsInput, optFns ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.GetLogGroupFieldsOutput, error) + DescribeLogGroupsWithContext(ctx context.Context, in *cloudwatchlogs.DescribeLogGroupsInput, opts ...request.Option) (*cloudwatchlogs.DescribeLogGroupsOutput, error) + GetLogGroupFieldsWithContext(ctx context.Context, in *cloudwatchlogs.GetLogGroupFieldsInput, option ...request.Option) (*cloudwatchlogs.GetLogGroupFieldsOutput, error) } type OAMAPIProvider interface { - ListSinks(ctx context.Context, in *oam.ListSinksInput, optFns ...func(options *oam.Options)) (*oam.ListSinksOutput, error) - ListAttachedLinks(ctx context.Context, in *oam.ListAttachedLinksInput, optFns ...func(options *oam.Options)) (*oam.ListAttachedLinksOutput, error) + ListSinksWithContext(ctx context.Context, in *oam.ListSinksInput, opts ...request.Option) (*oam.ListSinksOutput, error) + ListAttachedLinksWithContext(ctx context.Context, in *oam.ListAttachedLinksInput, opts ...request.Option) (*oam.ListAttachedLinksOutput, error) } type EC2APIProvider interface { - DescribeRegions(ctx context.Context, in *ec2.DescribeRegionsInput, optFns ...func(*ec2.Options)) (*ec2.DescribeRegionsOutput, error) - ec2.DescribeInstancesAPIClient -} - -type CWLogsClient interface { - StartQuery(context.Context, *cloudwatchlogs.StartQueryInput, ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.StartQueryOutput, error) - StopQuery(context.Context, *cloudwatchlogs.StopQueryInput, ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.StopQueryOutput, error) - GetQueryResults(context.Context, *cloudwatchlogs.GetQueryResultsInput, ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.GetQueryResultsOutput, error) - - cloudwatchlogs.GetLogEventsAPIClient - cloudwatchlogs.DescribeLogGroupsAPIClient -} - -type CWClient interface { - AlarmsAPI - cloudwatch.GetMetricDataAPIClient - cloudwatch.ListMetricsAPIClient -} - -type AlarmsAPI interface { - cloudwatch.DescribeAlarmsAPIClient - cloudwatch.DescribeAlarmHistoryAPIClient - - DescribeAlarmsForMetric(context.Context, *cloudwatch.DescribeAlarmsForMetricInput, ...func(*cloudwatch.Options)) (*cloudwatch.DescribeAlarmsForMetricOutput, error) + DescribeRegionsWithContext(ctx context.Context, in *ec2.DescribeRegionsInput, opts ...request.Option) (*ec2.DescribeRegionsOutput, error) + DescribeInstancesPagesWithContext(ctx context.Context, in *ec2.DescribeInstancesInput, fn func(*ec2.DescribeInstancesOutput, bool) bool, opts ...request.Option) error } diff --git a/pkg/tsdb/cloudwatch/models/cloudwatch_query.go b/pkg/tsdb/cloudwatch/models/cloudwatch_query.go index 301b2d59569..6907257c55e 100644 --- a/pkg/tsdb/cloudwatch/models/cloudwatch_query.go +++ b/pkg/tsdb/cloudwatch/models/cloudwatch_query.go @@ -11,8 +11,7 @@ import ( "strings" "time" - "github.com/aws/aws-sdk-go-v2/service/cloudwatch" - + "github.com/aws/aws-sdk-go/aws/endpoints" "github.com/google/uuid" "github.com/grafana/grafana-plugin-sdk-go/backend" @@ -202,11 +201,7 @@ func (q *CloudWatchQuery) BuildDeepLink(startTime time.Time, endTime time.Time) return "", fmt.Errorf("could not marshal link: %w", err) } - endpoint, err := getEndpoint(q.Region) - if err != nil { - return "", err - } - url, err := url.Parse(fmt.Sprintf(`https://%s/cloudwatch/deeplink.js`, endpoint)) + url, err := url.Parse(fmt.Sprintf(`https://%s/cloudwatch/deeplink.js`, getEndpoint(q.Region))) if err != nil { return "", fmt.Errorf("unable to parse CloudWatch console deep link") } @@ -508,18 +503,14 @@ func parseDimensions(dimensions dataquery.Dimensions) (map[string][]string, erro return parsedDimensions, nil } -func getEndpoint(region string) (string, error) { - resolver := cloudwatch.NewDefaultEndpointResolver() - endpoint, err := resolver.ResolveEndpoint(region, cloudwatch.EndpointResolverOptions{}) - if err != nil { - return "", fmt.Errorf("resolve endpoint failed: %w", err) +func getEndpoint(region string) string { + partition, _ := endpoints.PartitionForRegion(endpoints.DefaultPartitions(), region) + url := defaultConsoleURL + if partition.ID() == endpoints.AwsUsGovPartitionID { + url = usGovConsoleURL } - consoleURL := defaultConsoleURL - switch endpoint.PartitionID { - case "aws-us-gov": - consoleURL = usGovConsoleURL - case "aws-cn": - consoleURL = chinaConsoleURL + if partition.ID() == endpoints.AwsCnPartitionID { + url = chinaConsoleURL } - return fmt.Sprintf("%s.%s", region, consoleURL), nil + return fmt.Sprintf("%s.%s", region, url) } diff --git a/pkg/tsdb/cloudwatch/models/cloudwatch_query_test.go b/pkg/tsdb/cloudwatch/models/cloudwatch_query_test.go index d38fdd9c659..fe438fb2afd 100644 --- a/pkg/tsdb/cloudwatch/models/cloudwatch_query_test.go +++ b/pkg/tsdb/cloudwatch/models/cloudwatch_query_test.go @@ -7,15 +7,13 @@ import ( "testing" "time" - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend/log" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/kinds/dataquery" - "github.com/grafana/grafana/pkg/tsdb/cloudwatch/utils" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/grafana/grafana/pkg/tsdb/cloudwatch/utils" ) var logger = log.NewNullLogger() @@ -934,6 +932,7 @@ func Test_migrateAliasToDynamicLabel_single_query_preserves_old_alias_and_create for name, tc := range testCases { t.Run(name, func(t *testing.T) { average := "Average" + false := false queryToMigrate := metricsDataQuery{ CloudWatchMetricsQuery: dataquery.CloudWatchMetricsQuery{ @@ -946,7 +945,7 @@ func Test_migrateAliasToDynamicLabel_single_query_preserves_old_alias_and_create }, Statistic: &average, Period: utils.Pointer("600"), - Hide: aws.Bool(false), + Hide: &false, }, } @@ -1306,8 +1305,7 @@ func TestGetEndpoint(t *testing.T) { } for _, ts := range testcases { t.Run(fmt.Sprintf("should create correct endpoint for %s", ts), func(t *testing.T) { - actual, err := getEndpoint(ts.region) - assert.NoError(t, err) + actual := getEndpoint(ts.region) assert.Equal(t, ts.expectedEndpoint, actual) }) } diff --git a/pkg/tsdb/cloudwatch/models/logs_query.go b/pkg/tsdb/cloudwatch/models/logs_query.go index 49b9e1187b1..f9ea0c3e0ff 100644 --- a/pkg/tsdb/cloudwatch/models/logs_query.go +++ b/pkg/tsdb/cloudwatch/models/logs_query.go @@ -8,7 +8,7 @@ type LogsQuery struct { dataquery.CloudWatchLogsQuery StartTime *int64 EndTime *int64 - Limit *int32 + Limit *int64 LogGroupName string LogStreamName string QueryId string diff --git a/pkg/tsdb/cloudwatch/models/query_row_response.go b/pkg/tsdb/cloudwatch/models/query_row_response.go index 8362faf89df..b99913b0a91 100644 --- a/pkg/tsdb/cloudwatch/models/query_row_response.go +++ b/pkg/tsdb/cloudwatch/models/query_row_response.go @@ -1,32 +1,32 @@ package models import ( - cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" + "github.com/aws/aws-sdk-go/service/cloudwatch" ) -// QueryRowResponse represents the GetMetricData response for a query row in the query editor. +// queryRowResponse represents the GetMetricData response for a query row in the query editor. type QueryRowResponse struct { - partialDataSet map[string]*cloudwatchtypes.MetricDataResult + partialDataSet map[string]*cloudwatch.MetricDataResult ErrorCodes map[string]bool HasArithmeticError bool ArithmeticErrorMessage string HasPermissionError bool PermissionErrorMessage string - Metrics []*cloudwatchtypes.MetricDataResult - StatusCode cloudwatchtypes.StatusCode + Metrics []*cloudwatch.MetricDataResult + StatusCode string } func NewQueryRowResponse(errors map[string]bool) QueryRowResponse { return QueryRowResponse{ - partialDataSet: make(map[string]*cloudwatchtypes.MetricDataResult), + partialDataSet: make(map[string]*cloudwatch.MetricDataResult), ErrorCodes: errors, HasArithmeticError: false, ArithmeticErrorMessage: "", - Metrics: []*cloudwatchtypes.MetricDataResult{}, + Metrics: []*cloudwatch.MetricDataResult{}, } } -func (q *QueryRowResponse) AddMetricDataResult(mdr *cloudwatchtypes.MetricDataResult) { +func (q *QueryRowResponse) AddMetricDataResult(mdr *cloudwatch.MetricDataResult) { if mdr.Label == nil { return } @@ -34,16 +34,16 @@ func (q *QueryRowResponse) AddMetricDataResult(mdr *cloudwatchtypes.MetricDataRe if partialData, ok := q.partialDataSet[*mdr.Label]; ok { partialData.Timestamps = append(partialData.Timestamps, mdr.Timestamps...) partialData.Values = append(partialData.Values, mdr.Values...) - q.StatusCode = mdr.StatusCode - if mdr.StatusCode != cloudwatchtypes.StatusCodePartialData { + q.StatusCode = *mdr.StatusCode + if *mdr.StatusCode != "PartialData" { delete(q.partialDataSet, *mdr.Label) } return } q.Metrics = append(q.Metrics, mdr) - q.StatusCode = mdr.StatusCode - if mdr.StatusCode == cloudwatchtypes.StatusCodePartialData { + q.StatusCode = *mdr.StatusCode + if *mdr.StatusCode == "PartialData" { q.partialDataSet[*mdr.Label] = mdr } } diff --git a/pkg/tsdb/cloudwatch/models/resources/log_groups_resource_request.go b/pkg/tsdb/cloudwatch/models/resources/log_groups_resource_request.go index 45474dbfa41..80843c9d74e 100644 --- a/pkg/tsdb/cloudwatch/models/resources/log_groups_resource_request.go +++ b/pkg/tsdb/cloudwatch/models/resources/log_groups_resource_request.go @@ -6,11 +6,11 @@ import ( "strconv" ) -const defaultLogGroupLimit = int32(50) +const defaultLogGroupLimit = int64(50) type LogGroupsRequest struct { ResourceRequest - Limit int32 + Limit int64 LogGroupNamePrefix, LogGroupNamePattern *string ListAllLogGroups bool } @@ -45,11 +45,11 @@ func setIfNotEmptyString(paramValue string) *string { return ¶mValue } -func getLimit(limit string) int32 { +func getLimit(limit string) int64 { logGroupLimit := defaultLogGroupLimit intLimit, err := strconv.ParseInt(limit, 10, 64) if err == nil && intLimit > 0 { - logGroupLimit = int32(intLimit) + logGroupLimit = intLimit } return logGroupLimit } diff --git a/pkg/tsdb/cloudwatch/models/resources/types.go b/pkg/tsdb/cloudwatch/models/resources/types.go index f5de61f7f27..baeab0c04b5 100644 --- a/pkg/tsdb/cloudwatch/models/resources/types.go +++ b/pkg/tsdb/cloudwatch/models/resources/types.go @@ -1,8 +1,6 @@ package resources -import ( - cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" -) +import "github.com/aws/aws-sdk-go/service/cloudwatch" type Dimension struct { Name string @@ -15,7 +13,7 @@ type ResourceResponse[T any] struct { } type MetricResponse struct { - Metric cloudwatchtypes.Metric + *cloudwatch.Metric AccountId *string `json:"accountId,omitempty"` } diff --git a/pkg/tsdb/cloudwatch/response_parser.go b/pkg/tsdb/cloudwatch/response_parser.go index 2d7db092e48..85b69a00a8b 100644 --- a/pkg/tsdb/cloudwatch/response_parser.go +++ b/pkg/tsdb/cloudwatch/response_parser.go @@ -8,8 +8,7 @@ import ( "strings" "time" - "github.com/aws/aws-sdk-go-v2/service/cloudwatch" - + "github.com/aws/aws-sdk-go/service/cloudwatch" "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/data" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/features" @@ -89,7 +88,7 @@ func aggregateResponse(getMetricDataOutputs []*cloudwatch.GetMetricDataOutput) m } } - response.AddMetricDataResult(&r) + response.AddMetricDataResult(r) responseByID[id] = response } } @@ -229,9 +228,16 @@ func buildDataFrames(ctx context.Context, aggregatedResponse models.QueryRowResp } else { labels = getLabels(label, query, false) } + timestamps := []*time.Time{} + points := []*float64{} + for j, t := range metric.Timestamps { + val := metric.Values[j] + timestamps = append(timestamps, t) + points = append(points, val) + } - timeField := data.NewField(data.TimeSeriesTimeFieldName, nil, metric.Timestamps) - valueField := data.NewField(data.TimeSeriesValueFieldName, labels, metric.Values) + timeField := data.NewField(data.TimeSeriesTimeFieldName, nil, timestamps) + valueField := data.NewField(data.TimeSeriesValueFieldName, labels, points) // CloudWatch appends the dimensions to the returned label if the query label is not dynamic, so static labels need to be set if hasStaticLabel { diff --git a/pkg/tsdb/cloudwatch/response_parser_test.go b/pkg/tsdb/cloudwatch/response_parser_test.go index 3e46cd6c861..d1c8229fa3e 100644 --- a/pkg/tsdb/cloudwatch/response_parser_test.go +++ b/pkg/tsdb/cloudwatch/response_parser_test.go @@ -7,10 +7,8 @@ import ( "testing" "time" - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/cloudwatch" - cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" - + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/cloudwatch" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/features" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" "github.com/stretchr/testify/assert" @@ -43,7 +41,7 @@ func TestCloudWatchResponseParser(t *testing.T) { assert.Len(t, aggregatedResponse[idA].Metrics[0].Values, 10) }) t.Run("should have statuscode 'Complete'", func(t *testing.T) { - assert.Equal(t, cloudwatchtypes.StatusCodeComplete, aggregatedResponse[idA].StatusCode) + assert.Equal(t, "Complete", aggregatedResponse[idA].StatusCode) }) t.Run("should have exceeded request limit", func(t *testing.T) { assert.True(t, aggregatedResponse[idA].ErrorCodes["MaxMetricsExceeded"]) @@ -65,7 +63,7 @@ func TestCloudWatchResponseParser(t *testing.T) { aggregatedResponse := aggregateResponse(getMetricDataOutputs) idB := "b" t.Run("should have statuscode is 'PartialData'", func(t *testing.T) { - assert.Equal(t, cloudwatchtypes.StatusCodePartialData, aggregatedResponse[idB].StatusCode) + assert.Equal(t, "PartialData", aggregatedResponse[idB].StatusCode) }) t.Run("should have an arithmetic error and an error message", func(t *testing.T) { assert.True(t, aggregatedResponse[idB].HasArithmeticError) @@ -87,7 +85,7 @@ func TestCloudWatchResponseParser(t *testing.T) { assert.Len(t, aggregatedResponse[idA].Metrics[0].Values, 6) }) t.Run("should have statuscode 'Complete'", func(t *testing.T) { - assert.Equal(t, cloudwatchtypes.StatusCodeComplete, aggregatedResponse[idA].StatusCode) + assert.Equal(t, "Complete", aggregatedResponse[idA].StatusCode) }) }) @@ -155,36 +153,36 @@ func Test_buildDataFrames_parse_label_to_name_and_labels(t *testing.T) { t.Run("using multi filter", func(t *testing.T) { timestamp := time.Unix(0, 0) response := &models.QueryRowResponse{ - Metrics: []*cloudwatchtypes.MetricDataResult{ + Metrics: []*cloudwatch.MetricDataResult{ { Id: aws.String("id1"), Label: aws.String("lb1|&|lb1"), - Timestamps: []time.Time{ - timestamp, - timestamp.Add(time.Minute), - timestamp.Add(3 * time.Minute), + Timestamps: []*time.Time{ + aws.Time(timestamp), + aws.Time(timestamp.Add(time.Minute)), + aws.Time(timestamp.Add(3 * time.Minute)), }, - Values: []float64{ - 10, - 20, - 30, + Values: []*float64{ + aws.Float64(10), + aws.Float64(20), + aws.Float64(30), }, - StatusCode: cloudwatchtypes.StatusCodeComplete, + StatusCode: aws.String("Complete"), }, { Id: aws.String("id2"), Label: aws.String("lb2|&|lb2"), - Timestamps: []time.Time{ - timestamp, - timestamp.Add(time.Minute), - timestamp.Add(3 * time.Minute), + Timestamps: []*time.Time{ + aws.Time(timestamp), + aws.Time(timestamp.Add(time.Minute)), + aws.Time(timestamp.Add(3 * time.Minute)), }, - Values: []float64{ - 10, - 20, - 30, + Values: []*float64{ + aws.Float64(10), + aws.Float64(20), + aws.Float64(30), }, - StatusCode: cloudwatchtypes.StatusCodeComplete, + StatusCode: aws.String("Complete"), }, }, } @@ -225,36 +223,36 @@ func Test_buildDataFrames_parse_label_to_name_and_labels(t *testing.T) { t.Run("using multiple wildcard filters", func(t *testing.T) { timestamp := time.Unix(0, 0) response := &models.QueryRowResponse{ - Metrics: []*cloudwatchtypes.MetricDataResult{ + Metrics: []*cloudwatch.MetricDataResult{ { Id: aws.String("lb3"), Label: aws.String("some label lb3|&|inst1|&|balancer 1"), - Timestamps: []time.Time{ - timestamp, - timestamp.Add(time.Minute), - timestamp.Add(3 * time.Minute), + Timestamps: []*time.Time{ + aws.Time(timestamp), + aws.Time(timestamp.Add(time.Minute)), + aws.Time(timestamp.Add(3 * time.Minute)), }, - Values: []float64{ - 10, - 20, - 30, + Values: []*float64{ + aws.Float64(10), + aws.Float64(20), + aws.Float64(30), }, - StatusCode: cloudwatchtypes.StatusCodeComplete, + StatusCode: aws.String("Complete"), }, { Id: aws.String("lb4"), Label: aws.String("some label lb4|&|inst2|&|balancer 2"), - Timestamps: []time.Time{ - timestamp, - timestamp.Add(time.Minute), - timestamp.Add(3 * time.Minute), + Timestamps: []*time.Time{ + aws.Time(timestamp), + aws.Time(timestamp.Add(time.Minute)), + aws.Time(timestamp.Add(3 * time.Minute)), }, - Values: []float64{ - 10, - 20, - 30, + Values: []*float64{ + aws.Float64(10), + aws.Float64(20), + aws.Float64(30), }, - StatusCode: cloudwatchtypes.StatusCodeComplete, + StatusCode: aws.String("Complete"), }, }, } @@ -296,17 +294,17 @@ func Test_buildDataFrames_parse_label_to_name_and_labels(t *testing.T) { timestamp := time.Unix(0, 0) // When there are no results, CloudWatch sets the label values to -- response := &models.QueryRowResponse{ - Metrics: []*cloudwatchtypes.MetricDataResult{ + Metrics: []*cloudwatch.MetricDataResult{ { Id: aws.String("lb3"), Label: aws.String("some label|&|--"), - Timestamps: []time.Time{ - timestamp, - timestamp.Add(time.Minute), - timestamp.Add(3 * time.Minute), + Timestamps: []*time.Time{ + aws.Time(timestamp), + aws.Time(timestamp.Add(time.Minute)), + aws.Time(timestamp.Add(3 * time.Minute)), }, - Values: []float64{}, - StatusCode: cloudwatchtypes.StatusCodeComplete, + Values: []*float64{}, + StatusCode: aws.String("Complete"), }, }, } @@ -339,17 +337,17 @@ func Test_buildDataFrames_parse_label_to_name_and_labels(t *testing.T) { timestamp := time.Unix(0, 0) // When there are no results, CloudWatch sets the label values to -- response := &models.QueryRowResponse{ - Metrics: []*cloudwatchtypes.MetricDataResult{ + Metrics: []*cloudwatch.MetricDataResult{ { Id: aws.String("lb3"), Label: aws.String("some label|&|--"), - Timestamps: []time.Time{ - timestamp, - timestamp.Add(time.Minute), - timestamp.Add(3 * time.Minute), + Timestamps: []*time.Time{ + aws.Time(timestamp), + aws.Time(timestamp.Add(time.Minute)), + aws.Time(timestamp.Add(3 * time.Minute)), }, - Values: []float64{}, - StatusCode: cloudwatchtypes.StatusCodeComplete, + Values: []*float64{}, + StatusCode: aws.String("Complete"), }, }, } @@ -389,15 +387,15 @@ func Test_buildDataFrames_parse_label_to_name_and_labels(t *testing.T) { t.Run("when not using multi-value dimension filters on a `MetricSearch` query", func(t *testing.T) { timestamp := time.Unix(0, 0) response := &models.QueryRowResponse{ - Metrics: []*cloudwatchtypes.MetricDataResult{ + Metrics: []*cloudwatch.MetricDataResult{ { Id: aws.String("lb3"), Label: aws.String("some label"), - Timestamps: []time.Time{ - timestamp, + Timestamps: []*time.Time{ + aws.Time(timestamp), }, - Values: []float64{23}, - StatusCode: cloudwatchtypes.StatusCodeComplete, + Values: []*float64{aws.Float64(23)}, + StatusCode: aws.String("Complete"), }, }, } @@ -431,15 +429,15 @@ func Test_buildDataFrames_parse_label_to_name_and_labels(t *testing.T) { t.Run("when non-static label set on a `MetricSearch` query", func(t *testing.T) { timestamp := time.Unix(0, 0) response := &models.QueryRowResponse{ - Metrics: []*cloudwatchtypes.MetricDataResult{ + Metrics: []*cloudwatch.MetricDataResult{ { Id: aws.String("lb3"), Label: aws.String("some label|&|res"), - Timestamps: []time.Time{ - timestamp, + Timestamps: []*time.Time{ + aws.Time(timestamp), }, - Values: []float64{23}, - StatusCode: cloudwatchtypes.StatusCodeComplete, + Values: []*float64{aws.Float64(23)}, + StatusCode: aws.String("Complete"), }, }, } @@ -474,15 +472,15 @@ func Test_buildDataFrames_parse_label_to_name_and_labels(t *testing.T) { t.Run("when static label set on a `MetricSearch` query", func(t *testing.T) { timestamp := time.Unix(0, 0) response := &models.QueryRowResponse{ - Metrics: []*cloudwatchtypes.MetricDataResult{ + Metrics: []*cloudwatch.MetricDataResult{ { Id: aws.String("lb3"), Label: aws.String("some label|&|res"), - Timestamps: []time.Time{ - timestamp, + Timestamps: []*time.Time{ + aws.Time(timestamp), }, - Values: []float64{23}, - StatusCode: cloudwatchtypes.StatusCodeComplete, + Values: []*float64{aws.Float64(23)}, + StatusCode: aws.String("Complete"), }, }, } @@ -517,15 +515,15 @@ func Test_buildDataFrames_parse_label_to_name_and_labels(t *testing.T) { t.Run("when code editor used for `MetricSearch` query add fallback label", func(t *testing.T) { timestamp := time.Unix(0, 0) response := &models.QueryRowResponse{ - Metrics: []*cloudwatchtypes.MetricDataResult{ + Metrics: []*cloudwatch.MetricDataResult{ { Id: aws.String("lb3"), Label: aws.String("some label"), - Timestamps: []time.Time{ - timestamp, + Timestamps: []*time.Time{ + aws.Time(timestamp), }, - Values: []float64{23}, - StatusCode: cloudwatchtypes.StatusCodeComplete, + Values: []*float64{aws.Float64(23)}, + StatusCode: aws.String("Complete"), }, }, } @@ -555,24 +553,24 @@ func Test_buildDataFrames_parse_label_to_name_and_labels(t *testing.T) { t.Run("when `MetricQuery` query has no label set and `GROUP BY` clause has multiple fields", func(t *testing.T) { timestamp := time.Unix(0, 0) response := &models.QueryRowResponse{ - Metrics: []*cloudwatchtypes.MetricDataResult{ + Metrics: []*cloudwatch.MetricDataResult{ { Id: aws.String("query1"), Label: aws.String("EC2 vCPU"), - Timestamps: []time.Time{ - timestamp, + Timestamps: []*time.Time{ + aws.Time(timestamp), }, - Values: []float64{23}, - StatusCode: cloudwatchtypes.StatusCodeComplete, + Values: []*float64{aws.Float64(23)}, + StatusCode: aws.String("Complete"), }, { Id: aws.String("query2"), Label: aws.String("Elastic Loading Balancing ApplicationLoadBalancersPerRegion"), - Timestamps: []time.Time{ - timestamp, + Timestamps: []*time.Time{ + aws.Time(timestamp), }, - Values: []float64{23}, - StatusCode: cloudwatchtypes.StatusCodeComplete, + Values: []*float64{aws.Float64(23)}, + StatusCode: aws.String("Complete"), }, }, } @@ -603,15 +601,15 @@ func Test_buildDataFrames_parse_label_to_name_and_labels(t *testing.T) { t.Run("when `MetricQuery` query has no `GROUP BY` clause", func(t *testing.T) { timestamp := time.Unix(0, 0) response := &models.QueryRowResponse{ - Metrics: []*cloudwatchtypes.MetricDataResult{ + Metrics: []*cloudwatch.MetricDataResult{ { Id: aws.String("query1"), Label: aws.String("cloudwatch-default-label"), - Timestamps: []time.Time{ - timestamp, + Timestamps: []*time.Time{ + aws.Time(timestamp), }, - Values: []float64{23}, - StatusCode: cloudwatchtypes.StatusCodeComplete, + Values: []*float64{aws.Float64(23)}, + StatusCode: aws.String("Complete"), }, }, } @@ -637,15 +635,15 @@ func Test_buildDataFrames_parse_label_to_name_and_labels(t *testing.T) { t.Run("ignore dimensions for raw mode query", func(t *testing.T) { timestamp := time.Unix(0, 0) response := &models.QueryRowResponse{ - Metrics: []*cloudwatchtypes.MetricDataResult{ + Metrics: []*cloudwatch.MetricDataResult{ { Id: aws.String("lb3"), Label: aws.String("some label"), - Timestamps: []time.Time{ - timestamp, + Timestamps: []*time.Time{ + aws.Time(timestamp), }, - Values: []float64{23}, - StatusCode: cloudwatchtypes.StatusCodeComplete, + Values: []*float64{aws.Float64(23)}, + StatusCode: aws.String("Complete"), }, }, } @@ -677,21 +675,21 @@ func Test_buildDataFrames_parse_label_to_name_and_labels(t *testing.T) { t.Run("Parse cloudwatch response", func(t *testing.T) { timestamp := time.Unix(0, 0) response := &models.QueryRowResponse{ - Metrics: []*cloudwatchtypes.MetricDataResult{ + Metrics: []*cloudwatch.MetricDataResult{ { Id: aws.String("id1"), Label: aws.String("some label"), - Timestamps: []time.Time{ - timestamp, - timestamp.Add(time.Minute), - timestamp.Add(3 * time.Minute), + Timestamps: []*time.Time{ + aws.Time(timestamp), + aws.Time(timestamp.Add(time.Minute)), + aws.Time(timestamp.Add(3 * time.Minute)), }, - Values: []float64{ - 10, - 20, - 30, + Values: []*float64{ + aws.Float64(10), + aws.Float64(20), + aws.Float64(30), }, - StatusCode: cloudwatchtypes.StatusCodeComplete, + StatusCode: aws.String("Complete"), }, }, } @@ -719,9 +717,9 @@ func Test_buildDataFrames_parse_label_to_name_and_labels(t *testing.T) { assert.Equal(t, "some label", frame.Name) assert.Equal(t, "Time", frame.Fields[0].Name) assert.Equal(t, "lb", frame.Fields[1].Labels["LoadBalancer"]) - assert.Equal(t, 10.0, frame.Fields[1].At(0).(float64)) - assert.Equal(t, 20.0, frame.Fields[1].At(1).(float64)) - assert.Equal(t, 30.0, frame.Fields[1].At(2).(float64)) + assert.Equal(t, 10.0, *frame.Fields[1].At(0).(*float64)) + assert.Equal(t, 20.0, *frame.Fields[1].At(1).(*float64)) + assert.Equal(t, 30.0, *frame.Fields[1].At(2).(*float64)) assert.Equal(t, "Value", frame.Fields[1].Name) assert.Equal(t, "", frame.Fields[1].Config.DisplayName) }) diff --git a/pkg/tsdb/cloudwatch/routes/log_group_fields.go b/pkg/tsdb/cloudwatch/routes/log_group_fields.go index 418e234cbbe..455c2860368 100644 --- a/pkg/tsdb/cloudwatch/routes/log_group_fields.go +++ b/pkg/tsdb/cloudwatch/routes/log_group_fields.go @@ -22,7 +22,7 @@ func LogGroupFieldsHandler(ctx context.Context, pluginCtx backend.PluginContext, return nil, models.NewHttpError("newLogGroupsService error", http.StatusInternalServerError, err) } - logGroupFields, err := service.GetLogGroupFields(ctx, request) + logGroupFields, err := service.GetLogGroupFieldsWithContext(ctx, request) if err != nil { return nil, models.NewHttpError("GetLogGroupFields error", http.StatusInternalServerError, err) } diff --git a/pkg/tsdb/cloudwatch/routes/log_group_fields_test.go b/pkg/tsdb/cloudwatch/routes/log_group_fields_test.go index 382244cacd3..e1c1de40eb4 100644 --- a/pkg/tsdb/cloudwatch/routes/log_group_fields_test.go +++ b/pkg/tsdb/cloudwatch/routes/log_group_fields_test.go @@ -31,7 +31,7 @@ func TestLogGroupFieldsRoute(t *testing.T) { t.Run("returns 500 if GetLogGroupFields method fails", func(t *testing.T) { mockLogsService := mocks.LogsService{} - mockLogsService.On("GetLogGroupFields", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroupField]{}, fmt.Errorf("error from api")) + mockLogsService.On("GetLogGroupFieldsWithContext", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroupField]{}, fmt.Errorf("error from api")) newLogGroupsService = func(_ context.Context, pluginCtx backend.PluginContext, reqCtxFactory models.RequestContextFactoryFunc, region string) (models.LogGroupsProvider, error) { return &mockLogsService, nil } @@ -47,7 +47,7 @@ func TestLogGroupFieldsRoute(t *testing.T) { t.Run("returns valid json response if everything is ok", func(t *testing.T) { mockLogsService := mocks.LogsService{} - mockLogsService.On("GetLogGroupFields", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroupField]{ + mockLogsService.On("GetLogGroupFieldsWithContext", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroupField]{ { AccountId: new(string), Value: resources.LogGroupField{ diff --git a/pkg/tsdb/cloudwatch/routes/log_groups.go b/pkg/tsdb/cloudwatch/routes/log_groups.go index 15f5c6d3fd8..34c9f35ab01 100644 --- a/pkg/tsdb/cloudwatch/routes/log_groups.go +++ b/pkg/tsdb/cloudwatch/routes/log_groups.go @@ -24,7 +24,7 @@ func LogGroupsHandler(ctx context.Context, pluginCtx backend.PluginContext, reqC return nil, models.NewHttpError("newLogGroupsService error", http.StatusInternalServerError, err) } - logGroups, err := service.GetLogGroups(ctx, request) + logGroups, err := service.GetLogGroupsWithContext(ctx, request) if err != nil { return nil, models.NewHttpError("GetLogGroups error", http.StatusInternalServerError, err) } diff --git a/pkg/tsdb/cloudwatch/routes/log_groups_test.go b/pkg/tsdb/cloudwatch/routes/log_groups_test.go index 7e1b264a128..b1f9e53037e 100644 --- a/pkg/tsdb/cloudwatch/routes/log_groups_test.go +++ b/pkg/tsdb/cloudwatch/routes/log_groups_test.go @@ -7,10 +7,10 @@ import ( "net/http/httptest" "testing" + "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/mocks" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources" @@ -29,7 +29,7 @@ func TestLogGroupsRoute(t *testing.T) { t.Run("successfully returns 1 log group with account id", func(t *testing.T) { mockLogsService := mocks.LogsService{} - mockLogsService.On("GetLogGroups", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{{ + mockLogsService.On("GetLogGroupsWithContext", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{{ Value: resources.LogGroup{ Arn: "some arn", Name: "some name", @@ -51,7 +51,7 @@ func TestLogGroupsRoute(t *testing.T) { t.Run("successfully returns multiple log groups with account id", func(t *testing.T) { mockLogsService := mocks.LogsService{} - mockLogsService.On("GetLogGroups", mock.Anything).Return( + mockLogsService.On("GetLogGroupsWithContext", mock.Anything).Return( []resources.ResourceResponse[resources.LogGroup]{ { Value: resources.LogGroup{ @@ -97,7 +97,7 @@ func TestLogGroupsRoute(t *testing.T) { t.Run("returns error when both logGroupPrefix and logGroup Pattern are provided", func(t *testing.T) { mockLogsService := mocks.LogsService{} - mockLogsService.On("GetLogGroups", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{}, nil) + mockLogsService.On("GetLogGroupsWithContext", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{}, nil) newLogGroupsService = func(_ context.Context, pluginCtx backend.PluginContext, reqCtxFactory models.RequestContextFactoryFunc, region string) (models.LogGroupsProvider, error) { return &mockLogsService, nil } @@ -113,7 +113,7 @@ func TestLogGroupsRoute(t *testing.T) { t.Run("passes default log group limit and nil for logGroupNamePrefix, accountId, and logGroupPattern", func(t *testing.T) { mockLogsService := mocks.LogsService{} - mockLogsService.On("GetLogGroups", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{}, nil) + mockLogsService.On("GetLogGroupsWithContext", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{}, nil) newLogGroupsService = func(_ context.Context, pluginCtx backend.PluginContext, reqCtxFactory models.RequestContextFactoryFunc, region string) (models.LogGroupsProvider, error) { return &mockLogsService, nil } @@ -123,7 +123,7 @@ func TestLogGroupsRoute(t *testing.T) { handler := http.HandlerFunc(ResourceRequestMiddleware(LogGroupsHandler, logger, reqCtxFunc)) handler.ServeHTTP(rr, req) - mockLogsService.AssertCalled(t, "GetLogGroups", resources.LogGroupsRequest{ + mockLogsService.AssertCalled(t, "GetLogGroupsWithContext", resources.LogGroupsRequest{ Limit: 50, ResourceRequest: resources.ResourceRequest{}, LogGroupNamePrefix: nil, @@ -133,7 +133,7 @@ func TestLogGroupsRoute(t *testing.T) { t.Run("passes default log group limit and nil for logGroupNamePrefix when both are absent", func(t *testing.T) { mockLogsService := mocks.LogsService{} - mockLogsService.On("GetLogGroups", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{}, nil) + mockLogsService.On("GetLogGroupsWithContext", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{}, nil) newLogGroupsService = func(_ context.Context, pluginCtx backend.PluginContext, reqCtxFactory models.RequestContextFactoryFunc, region string) (models.LogGroupsProvider, error) { return &mockLogsService, nil } @@ -143,7 +143,7 @@ func TestLogGroupsRoute(t *testing.T) { handler := http.HandlerFunc(ResourceRequestMiddleware(LogGroupsHandler, logger, reqCtxFunc)) handler.ServeHTTP(rr, req) - mockLogsService.AssertCalled(t, "GetLogGroups", resources.LogGroupsRequest{ + mockLogsService.AssertCalled(t, "GetLogGroupsWithContext", resources.LogGroupsRequest{ Limit: 50, LogGroupNamePrefix: nil, }) @@ -151,7 +151,7 @@ func TestLogGroupsRoute(t *testing.T) { t.Run("passes log group limit from query parameter", func(t *testing.T) { mockLogsService := mocks.LogsService{} - mockLogsService.On("GetLogGroups", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{}, nil) + mockLogsService.On("GetLogGroupsWithContext", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{}, nil) newLogGroupsService = func(_ context.Context, pluginCtx backend.PluginContext, reqCtxFactory models.RequestContextFactoryFunc, region string) (models.LogGroupsProvider, error) { return &mockLogsService, nil } @@ -161,14 +161,14 @@ func TestLogGroupsRoute(t *testing.T) { handler := http.HandlerFunc(ResourceRequestMiddleware(LogGroupsHandler, logger, reqCtxFunc)) handler.ServeHTTP(rr, req) - mockLogsService.AssertCalled(t, "GetLogGroups", resources.LogGroupsRequest{ + mockLogsService.AssertCalled(t, "GetLogGroupsWithContext", resources.LogGroupsRequest{ Limit: 2, }) }) t.Run("passes logGroupPrefix from query parameter", func(t *testing.T) { mockLogsService := mocks.LogsService{} - mockLogsService.On("GetLogGroups", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{}, nil) + mockLogsService.On("GetLogGroupsWithContext", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{}, nil) newLogGroupsService = func(_ context.Context, pluginCtx backend.PluginContext, reqCtxFactory models.RequestContextFactoryFunc, region string) (models.LogGroupsProvider, error) { return &mockLogsService, nil } @@ -178,7 +178,7 @@ func TestLogGroupsRoute(t *testing.T) { handler := http.HandlerFunc(ResourceRequestMiddleware(LogGroupsHandler, logger, reqCtxFunc)) handler.ServeHTTP(rr, req) - mockLogsService.AssertCalled(t, "GetLogGroups", resources.LogGroupsRequest{ + mockLogsService.AssertCalled(t, "GetLogGroupsWithContext", resources.LogGroupsRequest{ Limit: 50, LogGroupNamePrefix: utils.Pointer("some-prefix"), }) @@ -186,7 +186,7 @@ func TestLogGroupsRoute(t *testing.T) { t.Run("passes logGroupPattern from query parameter", func(t *testing.T) { mockLogsService := mocks.LogsService{} - mockLogsService.On("GetLogGroups", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{}, nil) + mockLogsService.On("GetLogGroupsWithContext", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{}, nil) newLogGroupsService = func(_ context.Context, pluginCtx backend.PluginContext, reqCtxFactory models.RequestContextFactoryFunc, region string) (models.LogGroupsProvider, error) { return &mockLogsService, nil } @@ -196,7 +196,7 @@ func TestLogGroupsRoute(t *testing.T) { handler := http.HandlerFunc(ResourceRequestMiddleware(LogGroupsHandler, logger, reqCtxFunc)) handler.ServeHTTP(rr, req) - mockLogsService.AssertCalled(t, "GetLogGroups", resources.LogGroupsRequest{ + mockLogsService.AssertCalled(t, "GetLogGroupsWithContext", resources.LogGroupsRequest{ Limit: 50, LogGroupNamePattern: utils.Pointer("some-pattern"), }) @@ -204,7 +204,7 @@ func TestLogGroupsRoute(t *testing.T) { t.Run("passes logGroupPattern from query parameter", func(t *testing.T) { mockLogsService := mocks.LogsService{} - mockLogsService.On("GetLogGroups", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{}, nil) + mockLogsService.On("GetLogGroupsWithContext", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{}, nil) newLogGroupsService = func(_ context.Context, pluginCtx backend.PluginContext, reqCtxFactory models.RequestContextFactoryFunc, region string) (models.LogGroupsProvider, error) { return &mockLogsService, nil } @@ -214,7 +214,7 @@ func TestLogGroupsRoute(t *testing.T) { handler := http.HandlerFunc(ResourceRequestMiddleware(LogGroupsHandler, logger, reqCtxFunc)) handler.ServeHTTP(rr, req) - mockLogsService.AssertCalled(t, "GetLogGroups", resources.LogGroupsRequest{ + mockLogsService.AssertCalled(t, "GetLogGroupsWithContext", resources.LogGroupsRequest{ Limit: 50, ResourceRequest: resources.ResourceRequest{AccountId: utils.Pointer("some-account-id")}, }) @@ -222,7 +222,7 @@ func TestLogGroupsRoute(t *testing.T) { t.Run("returns error if service returns error", func(t *testing.T) { mockLogsService := mocks.LogsService{} - mockLogsService.On("GetLogGroups", mock.Anything). + mockLogsService.On("GetLogGroupsWithContext", mock.Anything). Return([]resources.ResourceResponse[resources.LogGroup]{}, fmt.Errorf("some error")) newLogGroupsService = func(_ context.Context, pluginCtx backend.PluginContext, reqCtxFactory models.RequestContextFactoryFunc, region string) (models.LogGroupsProvider, error) { return &mockLogsService, nil diff --git a/pkg/tsdb/cloudwatch/services/accounts.go b/pkg/tsdb/cloudwatch/services/accounts.go index 94691d258ee..f05b18335a3 100644 --- a/pkg/tsdb/cloudwatch/services/accounts.go +++ b/pkg/tsdb/cloudwatch/services/accounts.go @@ -4,10 +4,9 @@ import ( "context" "errors" "fmt" - "strings" - oam "github.com/aws/aws-sdk-go-v2/service/oam" - oamtypes "github.com/aws/aws-sdk-go-v2/service/oam/types" + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/service/oam" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources" ) @@ -24,14 +23,20 @@ func NewAccountsService(oamClient models.OAMAPIProvider) models.AccountsProvider func (a *AccountsService) GetAccountsForCurrentUserOrRole(ctx context.Context) ([]resources.ResourceResponse[resources.Account], error) { var nextToken *string - sinks := []oamtypes.ListSinksItem{} + sinks := []*oam.ListSinksItem{} for { - response, err := a.ListSinks(ctx, &oam.ListSinksInput{NextToken: nextToken}) + response, err := a.ListSinksWithContext(ctx, &oam.ListSinksInput{NextToken: nextToken}) if err != nil { - // TODO: this is a bit hacky, figure out how to do it right in v2 - if strings.Contains(err.Error(), "AccessDeniedException") { - return nil, fmt.Errorf("%w: %s", ErrAccessDeniedException, err.Error()) + var aerr awserr.Error + if errors.As(err, &aerr) { + switch aerr.Code() { + // unlike many other services, OAM doesn't define this error code. however, it's returned in case calling role/user has insufficient permissions + case "AccessDeniedException": + return nil, fmt.Errorf("%w: %s", ErrAccessDeniedException, aerr.Message()) + } } + } + if err != nil { return nil, fmt.Errorf("ListSinks error: %w", err) } @@ -57,7 +62,7 @@ func (a *AccountsService) GetAccountsForCurrentUserOrRole(ctx context.Context) ( nextToken = nil for { - links, err := a.ListAttachedLinks(ctx, &oam.ListAttachedLinksInput{ + links, err := a.ListAttachedLinksWithContext(ctx, &oam.ListAttachedLinksInput{ SinkIdentifier: sinkIdentifier, NextToken: nextToken, }) diff --git a/pkg/tsdb/cloudwatch/services/accounts_test.go b/pkg/tsdb/cloudwatch/services/accounts_test.go index ba75ad4a9a0..dbbadaf660c 100644 --- a/pkg/tsdb/cloudwatch/services/accounts_test.go +++ b/pkg/tsdb/cloudwatch/services/accounts_test.go @@ -2,14 +2,12 @@ package services import ( "context" - "errors" "fmt" "testing" - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/oam" - oamtypes "github.com/aws/aws-sdk-go-v2/service/oam/types" - + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/service/oam" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/mocks" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources" "github.com/stretchr/testify/assert" @@ -20,20 +18,21 @@ import ( func TestHandleGetAccounts(t *testing.T) { t.Run("Should return an error in case of insufficient permissions from ListSinks", func(t *testing.T) { fakeOAMClient := &mocks.FakeOAMClient{} - fakeOAMClient.On("ListSinks", mock.Anything).Return(&oam.ListSinksOutput{}, errors.New("AccessDeniedException")) + fakeOAMClient.On("ListSinksWithContext", mock.Anything).Return(&oam.ListSinksOutput{}, awserr.New("AccessDeniedException", + "AWS message", nil)) accounts := NewAccountsService(fakeOAMClient) resp, err := accounts.GetAccountsForCurrentUserOrRole(context.Background()) assert.Error(t, err) assert.Nil(t, resp) - assert.Equal(t, "access denied. please check your IAM policy: AccessDeniedException", err.Error()) + assert.Equal(t, err.Error(), "access denied. please check your IAM policy: AWS message") assert.ErrorIs(t, err, ErrAccessDeniedException) }) t.Run("Should return an error in case of any error from ListSinks", func(t *testing.T) { fakeOAMClient := &mocks.FakeOAMClient{} - fakeOAMClient.On("ListSinks", mock.Anything).Return(&oam.ListSinksOutput{}, fmt.Errorf("some error")) + fakeOAMClient.On("ListSinksWithContext", mock.Anything).Return(&oam.ListSinksOutput{}, fmt.Errorf("some error")) accounts := NewAccountsService(fakeOAMClient) resp, err := accounts.GetAccountsForCurrentUserOrRole(context.Background()) @@ -45,7 +44,7 @@ func TestHandleGetAccounts(t *testing.T) { t.Run("Should return empty array in case no monitoring account exists", func(t *testing.T) { fakeOAMClient := &mocks.FakeOAMClient{} - fakeOAMClient.On("ListSinks", mock.Anything).Return(&oam.ListSinksOutput{}, nil) + fakeOAMClient.On("ListSinksWithContext", mock.Anything).Return(&oam.ListSinksOutput{}, nil) accounts := NewAccountsService(fakeOAMClient) resp, err := accounts.GetAccountsForCurrentUserOrRole(context.Background()) @@ -56,26 +55,26 @@ func TestHandleGetAccounts(t *testing.T) { t.Run("Should return one monitoring account (the first) even though ListSinks returns multiple sinks", func(t *testing.T) { fakeOAMClient := &mocks.FakeOAMClient{} - fakeOAMClient.On("ListSinks", mock.Anything).Return(&oam.ListSinksOutput{ - Items: []oamtypes.ListSinksItem{ + fakeOAMClient.On("ListSinksWithContext", mock.Anything).Return(&oam.ListSinksOutput{ + Items: []*oam.ListSinksItem{ {Name: aws.String("Account 1"), Arn: aws.String("arn:aws:logs:us-east-1:123456789012:log-group:my-log-group1")}, {Name: aws.String("Account 2"), Arn: aws.String("arn:aws:logs:us-east-1:123456789012:log-group:my-log-group2")}, }, NextToken: new(string), }, nil).Once() - fakeOAMClient.On("ListSinks", mock.Anything).Return(&oam.ListSinksOutput{ - Items: []oamtypes.ListSinksItem{ + fakeOAMClient.On("ListSinksWithContext", mock.Anything).Return(&oam.ListSinksOutput{ + Items: []*oam.ListSinksItem{ {Name: aws.String("Account 3"), Arn: aws.String("arn:aws:logs:us-east-1:123456789012:log-group:my-log-group3")}, }, NextToken: nil, }, nil) - fakeOAMClient.On("ListAttachedLinks", mock.Anything).Return(&oam.ListAttachedLinksOutput{}, nil) + fakeOAMClient.On("ListAttachedLinksWithContext", mock.Anything).Return(&oam.ListAttachedLinksOutput{}, nil) accounts := NewAccountsService(fakeOAMClient) resp, err := accounts.GetAccountsForCurrentUserOrRole(context.Background()) assert.NoError(t, err) - fakeOAMClient.AssertNumberOfCalls(t, "ListSinks", 2) + fakeOAMClient.AssertNumberOfCalls(t, "ListSinksWithContext", 2) require.Len(t, resp, 1) assert.True(t, resp[0].Value.IsMonitoringAccount) assert.Equal(t, "Account 1", resp[0].Value.Label) @@ -84,28 +83,28 @@ func TestHandleGetAccounts(t *testing.T) { t.Run("Should merge the first sink with attached links", func(t *testing.T) { fakeOAMClient := &mocks.FakeOAMClient{} - fakeOAMClient.On("ListSinks", mock.Anything).Return(&oam.ListSinksOutput{ - Items: []oamtypes.ListSinksItem{ + fakeOAMClient.On("ListSinksWithContext", mock.Anything).Return(&oam.ListSinksOutput{ + Items: []*oam.ListSinksItem{ {Name: aws.String("Account 1"), Arn: aws.String("arn:aws:logs:us-east-1:123456789012:log-group:my-log-group1")}, {Name: aws.String("Account 2"), Arn: aws.String("arn:aws:logs:us-east-1:123456789012:log-group:my-log-group2")}, }, NextToken: new(string), }, nil).Once() - fakeOAMClient.On("ListSinks", mock.Anything).Return(&oam.ListSinksOutput{ - Items: []oamtypes.ListSinksItem{ + fakeOAMClient.On("ListSinksWithContext", mock.Anything).Return(&oam.ListSinksOutput{ + Items: []*oam.ListSinksItem{ {Name: aws.String("Account 3"), Arn: aws.String("arn:aws:logs:us-east-1:123456789012:log-group:my-log-group3")}, }, NextToken: nil, }, nil) - fakeOAMClient.On("ListAttachedLinks", mock.Anything).Return(&oam.ListAttachedLinksOutput{ - Items: []oamtypes.ListAttachedLinksItem{ + fakeOAMClient.On("ListAttachedLinksWithContext", mock.Anything).Return(&oam.ListAttachedLinksOutput{ + Items: []*oam.ListAttachedLinksItem{ {Label: aws.String("Account 10"), LinkArn: aws.String("arn:aws:logs:us-east-1:123456789013:log-group:my-log-group10")}, {Label: aws.String("Account 11"), LinkArn: aws.String("arn:aws:logs:us-east-1:123456789014:log-group:my-log-group11")}, }, NextToken: new(string), }, nil).Once() - fakeOAMClient.On("ListAttachedLinks", mock.Anything).Return(&oam.ListAttachedLinksOutput{ - Items: []oamtypes.ListAttachedLinksItem{ + fakeOAMClient.On("ListAttachedLinksWithContext", mock.Anything).Return(&oam.ListAttachedLinksOutput{ + Items: []*oam.ListAttachedLinksItem{ {Label: aws.String("Account 12"), LinkArn: aws.String("arn:aws:logs:us-east-1:123456789012:log-group:my-log-group12")}, }, NextToken: nil, @@ -115,8 +114,8 @@ func TestHandleGetAccounts(t *testing.T) { resp, err := accounts.GetAccountsForCurrentUserOrRole(context.Background()) assert.NoError(t, err) - fakeOAMClient.AssertNumberOfCalls(t, "ListSinks", 2) - fakeOAMClient.AssertNumberOfCalls(t, "ListAttachedLinks", 2) + fakeOAMClient.AssertNumberOfCalls(t, "ListSinksWithContext", 2) + fakeOAMClient.AssertNumberOfCalls(t, "ListAttachedLinksWithContext", 2) expectedAccounts := []resources.ResourceResponse[resources.Account]{ {Value: resources.Account{Id: "123456789012", Label: "Account 1", Arn: "arn:aws:logs:us-east-1:123456789012:log-group:my-log-group1", IsMonitoringAccount: true}}, {Value: resources.Account{Id: "123456789013", Label: "Account 10", Arn: "arn:aws:logs:us-east-1:123456789013:log-group:my-log-group10", IsMonitoringAccount: false}}, @@ -128,34 +127,34 @@ func TestHandleGetAccounts(t *testing.T) { t.Run("Should call ListAttachedLinks with arn of first sink", func(t *testing.T) { fakeOAMClient := &mocks.FakeOAMClient{} - fakeOAMClient.On("ListSinks", mock.Anything).Return(&oam.ListSinksOutput{ - Items: []oamtypes.ListSinksItem{ + fakeOAMClient.On("ListSinksWithContext", mock.Anything).Return(&oam.ListSinksOutput{ + Items: []*oam.ListSinksItem{ {Name: aws.String("Account 1"), Arn: aws.String("arn:aws:logs:us-east-1:123456789012:log-group:my-log-group1")}, }, NextToken: new(string), }, nil).Once() - fakeOAMClient.On("ListSinks", mock.Anything).Return(&oam.ListSinksOutput{ - Items: []oamtypes.ListSinksItem{ + fakeOAMClient.On("ListSinksWithContext", mock.Anything).Return(&oam.ListSinksOutput{ + Items: []*oam.ListSinksItem{ {Name: aws.String("Account 3"), Arn: aws.String("arn:aws:logs:us-east-1:123456789012:log-group:my-log-group3")}, }, NextToken: nil, }, nil).Once() - fakeOAMClient.On("ListAttachedLinks", mock.Anything).Return(&oam.ListAttachedLinksOutput{}, nil) + fakeOAMClient.On("ListAttachedLinksWithContext", mock.Anything).Return(&oam.ListAttachedLinksOutput{}, nil) accounts := NewAccountsService(fakeOAMClient) _, _ = accounts.GetAccountsForCurrentUserOrRole(context.Background()) - fakeOAMClient.AssertCalled(t, "ListAttachedLinks", &oam.ListAttachedLinksInput{ + fakeOAMClient.AssertCalled(t, "ListAttachedLinksWithContext", &oam.ListAttachedLinksInput{ SinkIdentifier: aws.String("arn:aws:logs:us-east-1:123456789012:log-group:my-log-group1"), }) }) t.Run("Should return an error in case of any error from ListAttachedLinks", func(t *testing.T) { fakeOAMClient := &mocks.FakeOAMClient{} - fakeOAMClient.On("ListSinks", mock.Anything).Return(&oam.ListSinksOutput{ - Items: []oamtypes.ListSinksItem{{Name: aws.String("Account 1"), Arn: aws.String("arn:aws:logs:us-east-1:123456789012:log-group:my-log-group1")}}, + fakeOAMClient.On("ListSinksWithContext", mock.Anything).Return(&oam.ListSinksOutput{ + Items: []*oam.ListSinksItem{{Name: aws.String("Account 1"), Arn: aws.String("arn:aws:logs:us-east-1:123456789012:log-group:my-log-group1")}}, }, nil) - fakeOAMClient.On("ListAttachedLinks", mock.Anything).Return(&oam.ListAttachedLinksOutput{}, fmt.Errorf("some error")).Once() + fakeOAMClient.On("ListAttachedLinksWithContext", mock.Anything).Return(&oam.ListAttachedLinksOutput{}, fmt.Errorf("some error")).Once() accounts := NewAccountsService(fakeOAMClient) resp, err := accounts.GetAccountsForCurrentUserOrRole(context.Background()) diff --git a/pkg/tsdb/cloudwatch/services/list_metrics.go b/pkg/tsdb/cloudwatch/services/list_metrics.go index e702cc025d1..3f6088ce32b 100644 --- a/pkg/tsdb/cloudwatch/services/list_metrics.go +++ b/pkg/tsdb/cloudwatch/services/list_metrics.go @@ -5,10 +5,8 @@ import ( "fmt" "sort" - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/cloudwatch" - cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" - + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/cloudwatch" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources" ) @@ -32,7 +30,7 @@ func (l *ListMetricsService) GetDimensionKeysByDimensionFilter(ctx context.Conte setDimensionFilter(input, r.DimensionFilter) setAccount(input, r.ResourceRequest) - accountMetrics, err := l.ListMetricsWithPageLimit(ctx, input) + metrics, err := l.ListMetricsWithPageLimit(ctx, input) if err != nil { return nil, fmt.Errorf("%v: %w", "unable to call AWS API", err) } @@ -40,8 +38,8 @@ func (l *ListMetricsService) GetDimensionKeysByDimensionFilter(ctx context.Conte response := []resources.ResourceResponse[string]{} // remove duplicates dupCheck := make(map[string]struct{}) - for _, accountMetric := range accountMetrics { - for _, dim := range accountMetric.Metric.Dimensions { + for _, metric := range metrics { + for _, dim := range metric.Dimensions { if _, exists := dupCheck[*dim.Name]; exists { continue } @@ -60,7 +58,7 @@ func (l *ListMetricsService) GetDimensionKeysByDimensionFilter(ctx context.Conte } dupCheck[*dim.Name] = struct{}{} - response = append(response, resources.ResourceResponse[string]{AccountId: accountMetric.AccountId, Value: *dim.Name}) + response = append(response, resources.ResourceResponse[string]{AccountId: metric.AccountId, Value: *dim.Name}) } } @@ -75,15 +73,15 @@ func (l *ListMetricsService) GetDimensionValuesByDimensionFilter(ctx context.Con setDimensionFilter(input, r.DimensionFilter) setAccount(input, r.ResourceRequest) - accountMetrics, err := l.ListMetricsWithPageLimit(ctx, input) + metrics, err := l.ListMetricsWithPageLimit(ctx, input) if err != nil { return nil, fmt.Errorf("%v: %w", "unable to call AWS API", err) } response := []resources.ResourceResponse[string]{} dupCheck := make(map[string]bool) - for _, metric := range accountMetrics { - for _, dim := range metric.Metric.Dimensions { + for _, metric := range metrics { + for _, dim := range metric.Dimensions { if *dim.Name == r.DimensionKey { if _, exists := dupCheck[*dim.Value]; exists { continue @@ -104,19 +102,19 @@ func (l *ListMetricsService) GetDimensionValuesByDimensionFilter(ctx context.Con func (l *ListMetricsService) GetMetricsByNamespace(ctx context.Context, r resources.MetricsRequest) ([]resources.ResourceResponse[resources.Metric], error) { input := &cloudwatch.ListMetricsInput{Namespace: aws.String(r.Namespace)} setAccount(input, r.ResourceRequest) - accountMetrics, err := l.ListMetricsWithPageLimit(ctx, input) + metrics, err := l.ListMetricsWithPageLimit(ctx, input) if err != nil { return nil, err } response := []resources.ResourceResponse[resources.Metric]{} dupCheck := make(map[string]struct{}) - for _, accountMetric := range accountMetrics { - if _, exists := dupCheck[*accountMetric.Metric.MetricName]; exists { + for _, metric := range metrics { + if _, exists := dupCheck[*metric.MetricName]; exists { continue } - dupCheck[*accountMetric.Metric.MetricName] = struct{}{} - response = append(response, resources.ResourceResponse[resources.Metric]{AccountId: accountMetric.AccountId, Value: resources.Metric{Name: *accountMetric.Metric.MetricName, Namespace: *accountMetric.Metric.Namespace}}) + dupCheck[*metric.MetricName] = struct{}{} + response = append(response, resources.ResourceResponse[resources.Metric]{AccountId: metric.AccountId, Value: resources.Metric{Name: *metric.MetricName, Namespace: *metric.Namespace}}) } return response, nil @@ -124,7 +122,7 @@ func (l *ListMetricsService) GetMetricsByNamespace(ctx context.Context, r resour func setDimensionFilter(input *cloudwatch.ListMetricsInput, dimensionFilter []*resources.Dimension) { for _, dimension := range dimensionFilter { - df := cloudwatchtypes.DimensionFilter{ + df := &cloudwatch.DimensionFilter{ Name: aws.String(dimension.Name), } if dimension.Value != "" { diff --git a/pkg/tsdb/cloudwatch/services/list_metrics_test.go b/pkg/tsdb/cloudwatch/services/list_metrics_test.go index daf5c9f458b..7f832878443 100644 --- a/pkg/tsdb/cloudwatch/services/list_metrics_test.go +++ b/pkg/tsdb/cloudwatch/services/list_metrics_test.go @@ -4,10 +4,8 @@ import ( "context" "testing" - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/cloudwatch" - cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" - + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/cloudwatch" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/mocks" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/utils" @@ -20,20 +18,20 @@ const useLinkedAccountsId = "all" var metricResponse = []resources.MetricResponse{ { - Metric: cloudwatchtypes.Metric{ + Metric: &cloudwatch.Metric{ MetricName: aws.String("CPUUtilization"), Namespace: aws.String("AWS/EC2"), - Dimensions: []cloudwatchtypes.Dimension{ + Dimensions: []*cloudwatch.Dimension{ {Name: aws.String("InstanceId"), Value: aws.String("i-1234567890abcdef0")}, {Name: aws.String("InstanceType"), Value: aws.String("t2.micro")}, }, }, }, { - Metric: cloudwatchtypes.Metric{ + Metric: &cloudwatch.Metric{ MetricName: aws.String("CPUUtilization"), Namespace: aws.String("AWS/EC2"), - Dimensions: []cloudwatchtypes.Dimension{ + Dimensions: []*cloudwatch.Dimension{ {Name: aws.String("InstanceId"), Value: aws.String("i-5234567890abcdef0")}, {Name: aws.String("InstanceType"), Value: aws.String("t2.micro")}, {Name: aws.String("AutoScalingGroupName"), Value: aws.String("my-asg")}, @@ -41,10 +39,10 @@ var metricResponse = []resources.MetricResponse{ }, }, { - Metric: cloudwatchtypes.Metric{ + Metric: &cloudwatch.Metric{ MetricName: aws.String("CPUUtilization"), Namespace: aws.String("AWS/EC2"), - Dimensions: []cloudwatchtypes.Dimension{ + Dimensions: []*cloudwatch.Dimension{ {Name: aws.String("InstanceId"), Value: aws.String("i-64234567890abcdef0")}, {Name: aws.String("InstanceType"), Value: aws.String("t3.micro")}, {Name: aws.String("AutoScalingGroupName"), Value: aws.String("my-asg2")}, @@ -88,7 +86,7 @@ func TestListMetricsService_GetDimensionKeysByDimensionFilter(t *testing.T) { listMetricsWithPageLimitInput: &cloudwatch.ListMetricsInput{ MetricName: aws.String("CPUUtilization"), Namespace: aws.String("AWS/EC2"), - Dimensions: []cloudwatchtypes.DimensionFilter{{Name: aws.String("InstanceId")}}, + Dimensions: []*cloudwatch.DimensionFilter{{Name: aws.String("InstanceId")}}, IncludeLinkedAccounts: aws.Bool(true), }, }, @@ -103,7 +101,7 @@ func TestListMetricsService_GetDimensionKeysByDimensionFilter(t *testing.T) { listMetricsWithPageLimitInput: &cloudwatch.ListMetricsInput{ MetricName: aws.String("CPUUtilization"), Namespace: aws.String("AWS/EC2"), - Dimensions: []cloudwatchtypes.DimensionFilter{{Name: aws.String("InstanceId")}}, + Dimensions: []*cloudwatch.DimensionFilter{{Name: aws.String("InstanceId")}}, IncludeLinkedAccounts: aws.Bool(true), OwningAccount: aws.String("1234567890"), }, @@ -116,7 +114,7 @@ func TestListMetricsService_GetDimensionKeysByDimensionFilter(t *testing.T) { MetricName: "", DimensionFilter: []*resources.Dimension{{Name: "InstanceId", Value: ""}}, }, - listMetricsWithPageLimitInput: &cloudwatch.ListMetricsInput{Dimensions: []cloudwatchtypes.DimensionFilter{{Name: aws.String("InstanceId")}}}, + listMetricsWithPageLimitInput: &cloudwatch.ListMetricsInput{Dimensions: []*cloudwatch.DimensionFilter{{Name: aws.String("InstanceId")}}}, }, } @@ -165,7 +163,7 @@ func TestListMetricsService_GetDimensionValuesByDimensionFilter(t *testing.T) { listMetricsWithPageLimitInput: &cloudwatch.ListMetricsInput{ MetricName: aws.String("CPUUtilization"), Namespace: aws.String("AWS/EC2"), - Dimensions: []cloudwatchtypes.DimensionFilter{{Name: aws.String("InstanceId")}}, + Dimensions: []*cloudwatch.DimensionFilter{{Name: aws.String("InstanceId")}}, IncludeLinkedAccounts: aws.Bool(true), }, }, @@ -180,7 +178,7 @@ func TestListMetricsService_GetDimensionValuesByDimensionFilter(t *testing.T) { listMetricsWithPageLimitInput: &cloudwatch.ListMetricsInput{ MetricName: aws.String("CPUUtilization"), Namespace: aws.String("AWS/EC2"), - Dimensions: []cloudwatchtypes.DimensionFilter{{Name: aws.String("InstanceId")}}, + Dimensions: []*cloudwatch.DimensionFilter{{Name: aws.String("InstanceId")}}, IncludeLinkedAccounts: aws.Bool(true), OwningAccount: aws.String("1234567890"), }, diff --git a/pkg/tsdb/cloudwatch/services/log_groups.go b/pkg/tsdb/cloudwatch/services/log_groups.go index ad0011b6456..1bbc99a67bd 100644 --- a/pkg/tsdb/cloudwatch/services/log_groups.go +++ b/pkg/tsdb/cloudwatch/services/log_groups.go @@ -3,9 +3,9 @@ package services import ( "context" - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" - + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/request" + "github.com/aws/aws-sdk-go/service/cloudwatchlogs" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/utils" @@ -20,9 +20,9 @@ func NewLogGroupsService(logsClient models.CloudWatchLogsAPIProvider, isCrossAcc return &LogGroupsService{logGroupsAPI: logsClient, isCrossAccountEnabled: isCrossAccountEnabled} } -func (s *LogGroupsService) GetLogGroups(ctx context.Context, req resources.LogGroupsRequest) ([]resources.ResourceResponse[resources.LogGroup], error) { +func (s *LogGroupsService) GetLogGroupsWithContext(ctx context.Context, req resources.LogGroupsRequest) ([]resources.ResourceResponse[resources.LogGroup], error) { input := &cloudwatchlogs.DescribeLogGroupsInput{ - Limit: aws.Int32(req.Limit), + Limit: aws.Int64(req.Limit), LogGroupNamePrefix: req.LogGroupNamePrefix, } @@ -33,13 +33,13 @@ func (s *LogGroupsService) GetLogGroups(ctx context.Context, req resources.LogGr } if !req.IsTargetingAllAccounts() { // TODO: accept more than one account id in search - input.AccountIdentifiers = []string{*req.AccountId} + input.AccountIdentifiers = []*string{req.AccountId} } } result := []resources.ResourceResponse[resources.LogGroup]{} for { - response, err := s.logGroupsAPI.DescribeLogGroups(ctx, input) + response, err := s.logGroupsAPI.DescribeLogGroupsWithContext(ctx, input) if err != nil || response == nil { return nil, err } @@ -63,7 +63,7 @@ func (s *LogGroupsService) GetLogGroups(ctx context.Context, req resources.LogGr return result, nil } -func (s *LogGroupsService) GetLogGroupFields(ctx context.Context, request resources.LogGroupFieldsRequest) ([]resources.ResourceResponse[resources.LogGroupField], error) { +func (s *LogGroupsService) GetLogGroupFieldsWithContext(ctx context.Context, request resources.LogGroupFieldsRequest, option ...request.Option) ([]resources.ResourceResponse[resources.LogGroupField], error) { input := &cloudwatchlogs.GetLogGroupFieldsInput{ LogGroupName: aws.String(request.LogGroupName), } @@ -73,7 +73,7 @@ func (s *LogGroupsService) GetLogGroupFields(ctx context.Context, request resour // input.LogGroupName = nil // } - getLogGroupFieldsOutput, err := s.logGroupsAPI.GetLogGroupFields(ctx, input) + getLogGroupFieldsOutput, err := s.logGroupsAPI.GetLogGroupFieldsWithContext(ctx, input) if err != nil { return nil, err } @@ -83,7 +83,7 @@ func (s *LogGroupsService) GetLogGroupFields(ctx context.Context, request resour result = append(result, resources.ResourceResponse[resources.LogGroupField]{ Value: resources.LogGroupField{ Name: *logGroupField.Name, - Percent: int64(logGroupField.Percent), + Percent: *logGroupField.Percent, }, }) } diff --git a/pkg/tsdb/cloudwatch/services/log_groups_test.go b/pkg/tsdb/cloudwatch/services/log_groups_test.go index 992044bc1be..6b9a09170c3 100644 --- a/pkg/tsdb/cloudwatch/services/log_groups_test.go +++ b/pkg/tsdb/cloudwatch/services/log_groups_test.go @@ -5,14 +5,11 @@ import ( "fmt" "testing" - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" - cloudwatchlogstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" - + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/cloudwatchlogs" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/mocks" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/utils" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) @@ -20,9 +17,9 @@ import ( func TestGetLogGroups(t *testing.T) { t.Run("Should map log groups response", func(t *testing.T) { mockLogsAPI := &mocks.LogsAPI{} - mockLogsAPI.On("DescribeLogGroups", mock.Anything).Return( + mockLogsAPI.On("DescribeLogGroupsWithContext", mock.Anything).Return( &cloudwatchlogs.DescribeLogGroupsOutput{ - LogGroups: []cloudwatchlogstypes.LogGroup{ + LogGroups: []*cloudwatchlogs.LogGroup{ {Arn: utils.Pointer("arn:aws:logs:us-east-1:111:log-group:group_a"), LogGroupName: utils.Pointer("group_a")}, {Arn: utils.Pointer("arn:aws:logs:us-east-1:222:log-group:group_b"), LogGroupName: utils.Pointer("group_b")}, {Arn: utils.Pointer("arn:aws:logs:us-east-1:333:log-group:group_c"), LogGroupName: utils.Pointer("group_c")}, @@ -30,7 +27,7 @@ func TestGetLogGroups(t *testing.T) { }, nil) service := NewLogGroupsService(mockLogsAPI, false) - resp, err := service.GetLogGroups(context.Background(), resources.LogGroupsRequest{}) + resp, err := service.GetLogGroupsWithContext(context.Background(), resources.LogGroupsRequest{}) assert.NoError(t, err) assert.Equal(t, []resources.ResourceResponse[resources.LogGroup]{ @@ -51,10 +48,10 @@ func TestGetLogGroups(t *testing.T) { t.Run("Should return an empty error if api doesn't return any data", func(t *testing.T) { mockLogsAPI := &mocks.LogsAPI{} - mockLogsAPI.On("DescribeLogGroups", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) + mockLogsAPI.On("DescribeLogGroupsWithContext", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) service := NewLogGroupsService(mockLogsAPI, false) - resp, err := service.GetLogGroups(context.Background(), resources.LogGroupsRequest{}) + resp, err := service.GetLogGroupsWithContext(context.Background(), resources.LogGroupsRequest{}) assert.NoError(t, err) assert.Equal(t, []resources.ResourceResponse[resources.LogGroup]{}, resp) @@ -63,41 +60,41 @@ func TestGetLogGroups(t *testing.T) { t.Run("Should only use LogGroupNamePrefix even if LogGroupNamePattern passed in resource call", func(t *testing.T) { // TODO: use LogGroupNamePattern when we have accounted for its behavior, still a little unexpected at the moment mockLogsAPI := &mocks.LogsAPI{} - mockLogsAPI.On("DescribeLogGroups", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) + mockLogsAPI.On("DescribeLogGroupsWithContext", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) service := NewLogGroupsService(mockLogsAPI, false) - _, err := service.GetLogGroups(context.Background(), resources.LogGroupsRequest{ + _, err := service.GetLogGroupsWithContext(context.Background(), resources.LogGroupsRequest{ Limit: 0, LogGroupNamePrefix: utils.Pointer("test"), }) assert.NoError(t, err) - mockLogsAPI.AssertCalled(t, "DescribeLogGroups", &cloudwatchlogs.DescribeLogGroupsInput{ - Limit: aws.Int32(0), + mockLogsAPI.AssertCalled(t, "DescribeLogGroupsWithContext", &cloudwatchlogs.DescribeLogGroupsInput{ + Limit: utils.Pointer(int64(0)), LogGroupNamePrefix: utils.Pointer("test"), }) }) t.Run("Should call api without LogGroupNamePrefix nor LogGroupNamePattern if not passed in resource call", func(t *testing.T) { mockLogsAPI := &mocks.LogsAPI{} - mockLogsAPI.On("DescribeLogGroups", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) + mockLogsAPI.On("DescribeLogGroupsWithContext", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) service := NewLogGroupsService(mockLogsAPI, false) - _, err := service.GetLogGroups(context.Background(), resources.LogGroupsRequest{}) + _, err := service.GetLogGroupsWithContext(context.Background(), resources.LogGroupsRequest{}) assert.NoError(t, err) - mockLogsAPI.AssertCalled(t, "DescribeLogGroups", &cloudwatchlogs.DescribeLogGroupsInput{ - Limit: aws.Int32(0), + mockLogsAPI.AssertCalled(t, "DescribeLogGroupsWithContext", &cloudwatchlogs.DescribeLogGroupsInput{ + Limit: utils.Pointer(int64(0)), }) }) t.Run("Should return an error when API returns error", func(t *testing.T) { mockLogsAPI := &mocks.LogsAPI{} - mockLogsAPI.On("DescribeLogGroups", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, + mockLogsAPI.On("DescribeLogGroupsWithContext", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, fmt.Errorf("some error")) service := NewLogGroupsService(mockLogsAPI, false) - _, err := service.GetLogGroups(context.Background(), resources.LogGroupsRequest{}) + _, err := service.GetLogGroupsWithContext(context.Background(), resources.LogGroupsRequest{}) assert.Error(t, err) assert.Equal(t, "some error", err.Error()) @@ -111,21 +108,21 @@ func TestGetLogGroups(t *testing.T) { ListAllLogGroups: false, } - mockLogsAPI.On("DescribeLogGroups", &cloudwatchlogs.DescribeLogGroupsInput{ - Limit: aws.Int32(req.Limit), + mockLogsAPI.On("DescribeLogGroupsWithContext", &cloudwatchlogs.DescribeLogGroupsInput{ + Limit: aws.Int64(req.Limit), LogGroupNamePrefix: req.LogGroupNamePrefix, }).Return(&cloudwatchlogs.DescribeLogGroupsOutput{ - LogGroups: []cloudwatchlogstypes.LogGroup{ + LogGroups: []*cloudwatchlogs.LogGroup{ {Arn: utils.Pointer("arn:aws:logs:us-east-1:111:log-group:group_a"), LogGroupName: utils.Pointer("group_a")}, }, NextToken: aws.String("next_token"), }, nil) service := NewLogGroupsService(mockLogsAPI, false) - resp, err := service.GetLogGroups(context.Background(), req) + resp, err := service.GetLogGroupsWithContext(context.Background(), req) assert.NoError(t, err) - mockLogsAPI.AssertNumberOfCalls(t, "DescribeLogGroups", 1) + mockLogsAPI.AssertNumberOfCalls(t, "DescribeLogGroupsWithContext", 1) assert.Equal(t, []resources.ResourceResponse[resources.LogGroup]{ { AccountId: utils.Pointer("111"), @@ -143,30 +140,30 @@ func TestGetLogGroups(t *testing.T) { } // first call - mockLogsAPI.On("DescribeLogGroups", &cloudwatchlogs.DescribeLogGroupsInput{ - Limit: aws.Int32(req.Limit), + mockLogsAPI.On("DescribeLogGroupsWithContext", &cloudwatchlogs.DescribeLogGroupsInput{ + Limit: aws.Int64(req.Limit), LogGroupNamePrefix: req.LogGroupNamePrefix, }).Return(&cloudwatchlogs.DescribeLogGroupsOutput{ - LogGroups: []cloudwatchlogstypes.LogGroup{ + LogGroups: []*cloudwatchlogs.LogGroup{ {Arn: utils.Pointer("arn:aws:logs:us-east-1:111:log-group:group_a"), LogGroupName: utils.Pointer("group_a")}, }, NextToken: utils.Pointer("token"), }, nil) // second call - mockLogsAPI.On("DescribeLogGroups", &cloudwatchlogs.DescribeLogGroupsInput{ - Limit: aws.Int32(req.Limit), + mockLogsAPI.On("DescribeLogGroupsWithContext", &cloudwatchlogs.DescribeLogGroupsInput{ + Limit: aws.Int64(req.Limit), LogGroupNamePrefix: req.LogGroupNamePrefix, NextToken: utils.Pointer("token"), }).Return(&cloudwatchlogs.DescribeLogGroupsOutput{ - LogGroups: []cloudwatchlogstypes.LogGroup{ + LogGroups: []*cloudwatchlogs.LogGroup{ {Arn: utils.Pointer("arn:aws:logs:us-east-1:222:log-group:group_b"), LogGroupName: utils.Pointer("group_b")}, }, }, nil) service := NewLogGroupsService(mockLogsAPI, false) - resp, err := service.GetLogGroups(context.Background(), req) + resp, err := service.GetLogGroupsWithContext(context.Background(), req) assert.NoError(t, err) - mockLogsAPI.AssertNumberOfCalls(t, "DescribeLogGroups", 2) + mockLogsAPI.AssertNumberOfCalls(t, "DescribeLogGroupsWithContext", 2) assert.Equal(t, []resources.ResourceResponse[resources.LogGroup]{ { AccountId: utils.Pointer("111"), @@ -183,36 +180,36 @@ func TestGetLogGroups(t *testing.T) { func TestGetLogGroupsCrossAccountQuerying(t *testing.T) { t.Run("Should not includeLinkedAccounts or accountId if isCrossAccountEnabled is set to false", func(t *testing.T) { mockLogsAPI := &mocks.LogsAPI{} - mockLogsAPI.On("DescribeLogGroups", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) + mockLogsAPI.On("DescribeLogGroupsWithContext", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) service := NewLogGroupsService(mockLogsAPI, false) - _, err := service.GetLogGroups(context.Background(), resources.LogGroupsRequest{ + _, err := service.GetLogGroupsWithContext(context.Background(), resources.LogGroupsRequest{ ResourceRequest: resources.ResourceRequest{AccountId: utils.Pointer("accountId")}, LogGroupNamePrefix: utils.Pointer("prefix"), }) assert.NoError(t, err) - mockLogsAPI.AssertCalled(t, "DescribeLogGroups", &cloudwatchlogs.DescribeLogGroupsInput{ - Limit: aws.Int32(0), + mockLogsAPI.AssertCalled(t, "DescribeLogGroupsWithContext", &cloudwatchlogs.DescribeLogGroupsInput{ + Limit: utils.Pointer(int64(0)), LogGroupNamePrefix: utils.Pointer("prefix"), }) }) t.Run("Should replace LogGroupNamePrefix if LogGroupNamePattern passed in resource call", func(t *testing.T) { mockLogsAPI := &mocks.LogsAPI{} - mockLogsAPI.On("DescribeLogGroups", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) + mockLogsAPI.On("DescribeLogGroupsWithContext", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) service := NewLogGroupsService(mockLogsAPI, true) - _, err := service.GetLogGroups(context.Background(), resources.LogGroupsRequest{ + _, err := service.GetLogGroupsWithContext(context.Background(), resources.LogGroupsRequest{ ResourceRequest: resources.ResourceRequest{AccountId: utils.Pointer("accountId")}, LogGroupNamePrefix: utils.Pointer("prefix"), LogGroupNamePattern: utils.Pointer("pattern"), }) assert.NoError(t, err) - mockLogsAPI.AssertCalled(t, "DescribeLogGroups", &cloudwatchlogs.DescribeLogGroupsInput{ - AccountIdentifiers: []string{"accountId"}, - Limit: aws.Int32(0), + mockLogsAPI.AssertCalled(t, "DescribeLogGroupsWithContext", &cloudwatchlogs.DescribeLogGroupsInput{ + AccountIdentifiers: []*string{utils.Pointer("accountId")}, + Limit: utils.Pointer(int64(0)), LogGroupNamePrefix: utils.Pointer("pattern"), IncludeLinkedAccounts: utils.Pointer(true), }) @@ -220,34 +217,34 @@ func TestGetLogGroupsCrossAccountQuerying(t *testing.T) { t.Run("Should includeLinkedAccounts,and accountId if isCrossAccountEnabled is set to true", func(t *testing.T) { mockLogsAPI := &mocks.LogsAPI{} - mockLogsAPI.On("DescribeLogGroups", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) + mockLogsAPI.On("DescribeLogGroupsWithContext", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) service := NewLogGroupsService(mockLogsAPI, true) - _, err := service.GetLogGroups(context.Background(), resources.LogGroupsRequest{ + _, err := service.GetLogGroupsWithContext(context.Background(), resources.LogGroupsRequest{ ResourceRequest: resources.ResourceRequest{AccountId: utils.Pointer("accountId")}, }) assert.NoError(t, err) - mockLogsAPI.AssertCalled(t, "DescribeLogGroups", &cloudwatchlogs.DescribeLogGroupsInput{ - Limit: aws.Int32(0), + mockLogsAPI.AssertCalled(t, "DescribeLogGroupsWithContext", &cloudwatchlogs.DescribeLogGroupsInput{ + Limit: utils.Pointer(int64(0)), IncludeLinkedAccounts: utils.Pointer(true), - AccountIdentifiers: []string{"accountId"}, + AccountIdentifiers: []*string{utils.Pointer("accountId")}, }) }) t.Run("Should should not override prefix is there is no logGroupNamePattern", func(t *testing.T) { mockLogsAPI := &mocks.LogsAPI{} - mockLogsAPI.On("DescribeLogGroups", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) + mockLogsAPI.On("DescribeLogGroupsWithContext", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) service := NewLogGroupsService(mockLogsAPI, true) - _, err := service.GetLogGroups(context.Background(), resources.LogGroupsRequest{ + _, err := service.GetLogGroupsWithContext(context.Background(), resources.LogGroupsRequest{ ResourceRequest: resources.ResourceRequest{AccountId: utils.Pointer("accountId")}, LogGroupNamePrefix: utils.Pointer("prefix"), }) assert.NoError(t, err) - mockLogsAPI.AssertCalled(t, "DescribeLogGroups", &cloudwatchlogs.DescribeLogGroupsInput{ - AccountIdentifiers: []string{"accountId"}, - Limit: aws.Int32(0), + mockLogsAPI.AssertCalled(t, "DescribeLogGroupsWithContext", &cloudwatchlogs.DescribeLogGroupsInput{ + AccountIdentifiers: []*string{utils.Pointer("accountId")}, + Limit: utils.Pointer(int64(0)), LogGroupNamePrefix: utils.Pointer("prefix"), IncludeLinkedAccounts: utils.Pointer(true), }) @@ -255,26 +252,26 @@ func TestGetLogGroupsCrossAccountQuerying(t *testing.T) { t.Run("Should not includeLinkedAccounts, or accountId if accountId is nil", func(t *testing.T) { mockLogsAPI := &mocks.LogsAPI{} - mockLogsAPI.On("DescribeLogGroups", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) + mockLogsAPI.On("DescribeLogGroupsWithContext", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) service := NewLogGroupsService(mockLogsAPI, true) - _, err := service.GetLogGroups(context.Background(), resources.LogGroupsRequest{ + _, err := service.GetLogGroupsWithContext(context.Background(), resources.LogGroupsRequest{ LogGroupNamePrefix: utils.Pointer("prefix"), }) assert.NoError(t, err) - mockLogsAPI.AssertCalled(t, "DescribeLogGroups", &cloudwatchlogs.DescribeLogGroupsInput{ - Limit: aws.Int32(0), + mockLogsAPI.AssertCalled(t, "DescribeLogGroupsWithContext", &cloudwatchlogs.DescribeLogGroupsInput{ + Limit: utils.Pointer(int64(0)), LogGroupNamePrefix: utils.Pointer("prefix"), }) }) t.Run("Should should not override prefix is there is no logGroupNamePattern", func(t *testing.T) { mockLogsAPI := &mocks.LogsAPI{} - mockLogsAPI.On("DescribeLogGroups", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) + mockLogsAPI.On("DescribeLogGroupsWithContext", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{}, nil) service := NewLogGroupsService(mockLogsAPI, true) - _, err := service.GetLogGroups(context.Background(), resources.LogGroupsRequest{ + _, err := service.GetLogGroupsWithContext(context.Background(), resources.LogGroupsRequest{ ResourceRequest: resources.ResourceRequest{ AccountId: utils.Pointer("accountId"), }, @@ -282,10 +279,10 @@ func TestGetLogGroupsCrossAccountQuerying(t *testing.T) { }) assert.NoError(t, err) - mockLogsAPI.AssertCalled(t, "DescribeLogGroups", &cloudwatchlogs.DescribeLogGroupsInput{ - AccountIdentifiers: []string{"accountId"}, + mockLogsAPI.AssertCalled(t, "DescribeLogGroupsWithContext", &cloudwatchlogs.DescribeLogGroupsInput{ + AccountIdentifiers: []*string{utils.Pointer("accountId")}, IncludeLinkedAccounts: utils.Pointer(true), - Limit: aws.Int32(0), + Limit: utils.Pointer(int64(0)), LogGroupNamePrefix: utils.Pointer("prefix"), }) }) @@ -294,24 +291,24 @@ func TestGetLogGroupsCrossAccountQuerying(t *testing.T) { func TestGetLogGroupFields(t *testing.T) { t.Run("Should map log group fields response", func(t *testing.T) { mockLogsAPI := &mocks.LogsAPI{} - mockLogsAPI.On("GetLogGroupFields", mock.Anything).Return( + mockLogsAPI.On("GetLogGroupFieldsWithContext", mock.Anything).Return( &cloudwatchlogs.GetLogGroupFieldsOutput{ - LogGroupFields: []cloudwatchlogstypes.LogGroupField{ + LogGroupFields: []*cloudwatchlogs.LogGroupField{ { - Name: aws.String("field1"), - Percent: 10, + Name: utils.Pointer("field1"), + Percent: utils.Pointer(int64(10)), }, { - Name: aws.String("field2"), - Percent: 10, + Name: utils.Pointer("field2"), + Percent: utils.Pointer(int64(10)), }, { - Name: aws.String("field3"), - Percent: 10, + Name: utils.Pointer("field3"), + Percent: utils.Pointer(int64(10)), }, }, }, nil) service := NewLogGroupsService(mockLogsAPI, false) - resp, err := service.GetLogGroupFields(context.Background(), resources.LogGroupFieldsRequest{}) + resp, err := service.GetLogGroupFieldsWithContext(context.Background(), resources.LogGroupFieldsRequest{}) assert.NoError(t, err) assert.Equal(t, []resources.ResourceResponse[resources.LogGroupField]{ @@ -359,16 +356,16 @@ func TestGetLogGroupFields(t *testing.T) { // remove this test once the above test is uncommented t.Run("Should only set LogGroupName as api input in case both LogGroupName and LogGroupARN are specified", func(t *testing.T) { mockLogsAPI := &mocks.LogsAPI{} - mockLogsAPI.On("GetLogGroupFields", mock.Anything).Return( + mockLogsAPI.On("GetLogGroupFieldsWithContext", mock.Anything).Return( &cloudwatchlogs.GetLogGroupFieldsOutput{}, nil) service := NewLogGroupsService(mockLogsAPI, false) - resp, err := service.GetLogGroupFields(context.Background(), resources.LogGroupFieldsRequest{ + resp, err := service.GetLogGroupFieldsWithContext(context.Background(), resources.LogGroupFieldsRequest{ LogGroupName: "logGroupName", LogGroupARN: "logGroupARN", }) - mockLogsAPI.AssertCalled(t, "GetLogGroupFields", &cloudwatchlogs.GetLogGroupFieldsInput{ + mockLogsAPI.AssertCalled(t, "GetLogGroupFieldsWithContext", &cloudwatchlogs.GetLogGroupFieldsInput{ LogGroupIdentifier: nil, LogGroupName: utils.Pointer("logGroupName"), }) @@ -378,16 +375,16 @@ func TestGetLogGroupFields(t *testing.T) { t.Run("Should only set LogGroupName as api input in case only LogGroupName is specified", func(t *testing.T) { mockLogsAPI := &mocks.LogsAPI{} - mockLogsAPI.On("GetLogGroupFields", mock.Anything).Return( + mockLogsAPI.On("GetLogGroupFieldsWithContext", mock.Anything).Return( &cloudwatchlogs.GetLogGroupFieldsOutput{}, nil) service := NewLogGroupsService(mockLogsAPI, false) - resp, err := service.GetLogGroupFields(context.Background(), resources.LogGroupFieldsRequest{ + resp, err := service.GetLogGroupFieldsWithContext(context.Background(), resources.LogGroupFieldsRequest{ LogGroupName: "logGroupName", LogGroupARN: "", }) - mockLogsAPI.AssertCalled(t, "GetLogGroupFields", &cloudwatchlogs.GetLogGroupFieldsInput{ + mockLogsAPI.AssertCalled(t, "GetLogGroupFieldsWithContext", &cloudwatchlogs.GetLogGroupFieldsInput{ LogGroupIdentifier: nil, LogGroupName: utils.Pointer("logGroupName"), }) diff --git a/pkg/tsdb/cloudwatch/services/regions.go b/pkg/tsdb/cloudwatch/services/regions.go index e0de066389b..2f8fc345e07 100644 --- a/pkg/tsdb/cloudwatch/services/regions.go +++ b/pkg/tsdb/cloudwatch/services/regions.go @@ -4,9 +4,7 @@ import ( "context" "sort" - "github.com/aws/aws-sdk-go-v2/service/ec2" - ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" - + "github.com/aws/aws-sdk-go/service/ec2" "github.com/grafana/grafana-plugin-sdk-go/backend/log" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/constants" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" @@ -25,7 +23,7 @@ func NewRegionsService(ec2client models.EC2APIProvider, logger log.Logger) model } } -func mergeEC2RegionsAndConstantRegions(regions map[string]struct{}, ec2Regions []ec2types.Region) { +func mergeEC2RegionsAndConstantRegions(regions map[string]struct{}, ec2Regions []*ec2.Region) { for _, region := range ec2Regions { if _, ok := regions[*region.RegionName]; !ok { regions[*region.RegionName] = struct{}{} @@ -38,7 +36,7 @@ func (r *RegionsService) GetRegions(ctx context.Context) ([]resources.ResourceRe result := make([]resources.ResourceResponse[resources.Region], 0) - ec2Regions, err := r.DescribeRegions(ctx, &ec2.DescribeRegionsInput{}) + ec2Regions, err := r.DescribeRegionsWithContext(ctx, &ec2.DescribeRegionsInput{}) // we ignore this error and always send default regions // we only fetch incase a user has enabled additional regions // but we still log it in case the user is expecting to fetch regions specific to their account and are unable to @@ -46,9 +44,7 @@ func (r *RegionsService) GetRegions(ctx context.Context) ([]resources.ResourceRe r.Error("Failed to get regions: ", "error", err) } - if ec2Regions != nil { - mergeEC2RegionsAndConstantRegions(regions, ec2Regions.Regions) - } + mergeEC2RegionsAndConstantRegions(regions, ec2Regions.Regions) for region := range regions { result = append(result, resources.ResourceResponse[resources.Region]{ diff --git a/pkg/tsdb/cloudwatch/services/regions_test.go b/pkg/tsdb/cloudwatch/services/regions_test.go index b352a493ba8..bb559ea5907 100644 --- a/pkg/tsdb/cloudwatch/services/regions_test.go +++ b/pkg/tsdb/cloudwatch/services/regions_test.go @@ -4,9 +4,7 @@ import ( "context" "testing" - "github.com/aws/aws-sdk-go-v2/service/ec2" - ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" - + "github.com/aws/aws-sdk-go/service/ec2" "github.com/grafana/grafana-plugin-sdk-go/backend/log" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/mocks" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources" @@ -19,14 +17,14 @@ var testLogger = log.New().With("logger", "test.logger") func TestRegions(t *testing.T) { t.Run("returns regions from the api and merges them with default regions", func(t *testing.T) { mockRegions := &ec2.DescribeRegionsOutput{ - Regions: []ec2types.Region{ + Regions: []*ec2.Region{ { RegionName: utils.Pointer("earth-1"), }, }, } ec2Mock := &mocks.EC2Mock{} - ec2Mock.On("DescribeRegions").Return(mockRegions, nil) + ec2Mock.On("DescribeRegionsWithContext").Return(mockRegions, nil) regions, err := NewRegionsService(ec2Mock, testLogger).GetRegions(context.Background()) assert.NoError(t, err) assert.Contains(t, regions, resources.ResourceResponse[resources.Region]{ @@ -44,9 +42,9 @@ func TestRegions(t *testing.T) { t.Run("always returns default regions, even if fetch fails", func(t *testing.T) { ec2Mock := &mocks.EC2Mock{} mockRegions := &ec2.DescribeRegionsOutput{ - Regions: []ec2types.Region{}, + Regions: []*ec2.Region{}, } - ec2Mock.On("DescribeRegions").Return(mockRegions, assert.AnError) + ec2Mock.On("DescribeRegionsWithContext").Return(mockRegions, assert.AnError) regions, err := NewRegionsService(ec2Mock, testLogger).GetRegions(context.Background()) assert.NoError(t, err) assert.Contains(t, regions, resources.ResourceResponse[resources.Region]{ diff --git a/pkg/tsdb/cloudwatch/sort_frame_test.go b/pkg/tsdb/cloudwatch/sort_frame_test.go index 2f136431d3e..cc7408d9c67 100644 --- a/pkg/tsdb/cloudwatch/sort_frame_test.go +++ b/pkg/tsdb/cloudwatch/sort_frame_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go/aws" "github.com/grafana/grafana-plugin-sdk-go/data" "github.com/stretchr/testify/assert" diff --git a/pkg/tsdb/cloudwatch/test_utils.go b/pkg/tsdb/cloudwatch/test_utils.go index e4ac75e7840..5d56ee96222 100644 --- a/pkg/tsdb/cloudwatch/test_utils.go +++ b/pkg/tsdb/cloudwatch/test_utils.go @@ -4,18 +4,17 @@ import ( "context" "strings" - "github.com/aws/smithy-go" - - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/cloudwatch" - "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" - cloudwatchlogstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types" - "github.com/aws/aws-sdk-go-v2/service/ec2" - ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" - "github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi" - resourcegroupstaggingapitypes "github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi/types" - - "github.com/grafana/grafana-aws-sdk/pkg/awsauth" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/request" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/cloudwatch" + "github.com/aws/aws-sdk-go/service/cloudwatch/cloudwatchiface" + "github.com/aws/aws-sdk-go/service/cloudwatchlogs" + "github.com/aws/aws-sdk-go/service/cloudwatchlogs/cloudwatchlogsiface" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/aws/aws-sdk-go/service/ec2/ec2iface" + "github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi" + "github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi/resourcegroupstaggingapiiface" "github.com/grafana/grafana-aws-sdk/pkg/awsds" "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend/datasource" @@ -27,6 +26,8 @@ import ( ) type fakeCWLogsClient struct { + cloudwatchlogsiface.CloudWatchLogsAPI + calls logsQueryCalls logGroups []cloudwatchlogs.DescribeLogGroupsOutput @@ -37,104 +38,83 @@ type fakeCWLogsClient struct { } type logsQueryCalls struct { - startQuery []*cloudwatchlogs.StartQueryInput - getEvents []*cloudwatchlogs.GetLogEventsInput - describeLogGroups []*cloudwatchlogs.DescribeLogGroupsInput + startQueryWithContext []*cloudwatchlogs.StartQueryInput + getEventsWithContext []*cloudwatchlogs.GetLogEventsInput + describeLogGroups []*cloudwatchlogs.DescribeLogGroupsInput } -func (m *fakeCWLogsClient) GetQueryResults(_ context.Context, _ *cloudwatchlogs.GetQueryResultsInput, _ ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.GetQueryResultsOutput, error) { +func (m *fakeCWLogsClient) GetQueryResultsWithContext(ctx context.Context, input *cloudwatchlogs.GetQueryResultsInput, option ...request.Option) (*cloudwatchlogs.GetQueryResultsOutput, error) { return &m.queryResults, nil } -func (m *fakeCWLogsClient) StartQuery(_ context.Context, input *cloudwatchlogs.StartQueryInput, _ ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.StartQueryOutput, error) { - m.calls.startQuery = append(m.calls.startQuery, input) +func (m *fakeCWLogsClient) StartQueryWithContext(ctx context.Context, input *cloudwatchlogs.StartQueryInput, option ...request.Option) (*cloudwatchlogs.StartQueryOutput, error) { + m.calls.startQueryWithContext = append(m.calls.startQueryWithContext, input) return &cloudwatchlogs.StartQueryOutput{ QueryId: aws.String("abcd-efgh-ijkl-mnop"), }, nil } -func (m *fakeCWLogsClient) StopQuery(_ context.Context, _ *cloudwatchlogs.StopQueryInput, _ ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.StopQueryOutput, error) { +func (m *fakeCWLogsClient) StopQueryWithContext(ctx context.Context, input *cloudwatchlogs.StopQueryInput, option ...request.Option) (*cloudwatchlogs.StopQueryOutput, error) { return &cloudwatchlogs.StopQueryOutput{ - Success: true, + Success: aws.Bool(true), }, nil } type mockLogsSyncClient struct { + cloudwatchlogsiface.CloudWatchLogsAPI + mock.Mock } -func (m *mockLogsSyncClient) StopQuery(context.Context, *cloudwatchlogs.StopQueryInput, ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.StopQueryOutput, error) { - return nil, nil -} - -func (m *mockLogsSyncClient) GetLogEvents(context.Context, *cloudwatchlogs.GetLogEventsInput, ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.GetLogEventsOutput, error) { - return nil, nil -} - -func (m *mockLogsSyncClient) DescribeLogGroups(context.Context, *cloudwatchlogs.DescribeLogGroupsInput, ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.DescribeLogGroupsOutput, error) { - return nil, nil -} - -func (m *mockLogsSyncClient) GetQueryResults(ctx context.Context, input *cloudwatchlogs.GetQueryResultsInput, optFns ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.GetQueryResultsOutput, error) { - args := m.Called(ctx, input, optFns) +func (m *mockLogsSyncClient) GetQueryResultsWithContext(ctx context.Context, input *cloudwatchlogs.GetQueryResultsInput, option ...request.Option) (*cloudwatchlogs.GetQueryResultsOutput, error) { + args := m.Called(ctx, input, option) return args.Get(0).(*cloudwatchlogs.GetQueryResultsOutput), args.Error(1) } -func (m *mockLogsSyncClient) StartQuery(ctx context.Context, input *cloudwatchlogs.StartQueryInput, optFns ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.StartQueryOutput, error) { - args := m.Called(ctx, input, optFns) +func (m *mockLogsSyncClient) StartQueryWithContext(ctx context.Context, input *cloudwatchlogs.StartQueryInput, option ...request.Option) (*cloudwatchlogs.StartQueryOutput, error) { + args := m.Called(ctx, input, option) return args.Get(0).(*cloudwatchlogs.StartQueryOutput), args.Error(1) } -func (m *fakeCWLogsClient) DescribeLogGroups(_ context.Context, input *cloudwatchlogs.DescribeLogGroupsInput, _ ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.DescribeLogGroupsOutput, error) { +func (m *fakeCWLogsClient) DescribeLogGroupsWithContext(ctx context.Context, input *cloudwatchlogs.DescribeLogGroupsInput, option ...request.Option) (*cloudwatchlogs.DescribeLogGroupsOutput, error) { m.calls.describeLogGroups = append(m.calls.describeLogGroups, input) output := &m.logGroups[m.logGroupsIndex] m.logGroupsIndex++ return output, nil } -func (m *fakeCWLogsClient) GetLogGroupFields(_ context.Context, _ *cloudwatchlogs.GetLogGroupFieldsInput, _ ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.GetLogGroupFieldsOutput, error) { +func (m *fakeCWLogsClient) GetLogGroupFieldsWithContext(ctx context.Context, input *cloudwatchlogs.GetLogGroupFieldsInput, option ...request.Option) (*cloudwatchlogs.GetLogGroupFieldsOutput, error) { return &m.logGroupFields, nil } -func (m *fakeCWLogsClient) GetLogEvents(_ context.Context, input *cloudwatchlogs.GetLogEventsInput, _ ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.GetLogEventsOutput, error) { - m.calls.getEvents = append(m.calls.getEvents, input) +func (m *fakeCWLogsClient) GetLogEventsWithContext(ctx context.Context, input *cloudwatchlogs.GetLogEventsInput, option ...request.Option) (*cloudwatchlogs.GetLogEventsOutput, error) { + m.calls.getEventsWithContext = append(m.calls.getEventsWithContext, input) return &cloudwatchlogs.GetLogEventsOutput{ - Events: []cloudwatchlogstypes.OutputLogEvent{}, + Events: []*cloudwatchlogs.OutputLogEvent{}, }, nil } type fakeCWAnnotationsClient struct { + cloudwatchiface.CloudWatchAPI calls annontationsQueryCalls describeAlarmsForMetricOutput *cloudwatch.DescribeAlarmsForMetricOutput describeAlarmsOutput *cloudwatch.DescribeAlarmsOutput } -func (c *fakeCWAnnotationsClient) DescribeAlarmHistory(ctx context.Context, input *cloudwatch.DescribeAlarmHistoryInput, f ...func(*cloudwatch.Options)) (*cloudwatch.DescribeAlarmHistoryOutput, error) { - return nil, nil -} - -func (c *fakeCWAnnotationsClient) GetMetricData(ctx context.Context, input *cloudwatch.GetMetricDataInput, f ...func(*cloudwatch.Options)) (*cloudwatch.GetMetricDataOutput, error) { - return nil, nil -} - -func (c *fakeCWAnnotationsClient) ListMetrics(ctx context.Context, input *cloudwatch.ListMetricsInput, f ...func(*cloudwatch.Options)) (*cloudwatch.ListMetricsOutput, error) { - return nil, nil -} - type annontationsQueryCalls struct { describeAlarmsForMetric []*cloudwatch.DescribeAlarmsForMetricInput describeAlarms []*cloudwatch.DescribeAlarmsInput } -func (c *fakeCWAnnotationsClient) DescribeAlarmsForMetric(_ context.Context, params *cloudwatch.DescribeAlarmsForMetricInput, _ ...func(*cloudwatch.Options)) (*cloudwatch.DescribeAlarmsForMetricOutput, error) { +func (c *fakeCWAnnotationsClient) DescribeAlarmsForMetric(params *cloudwatch.DescribeAlarmsForMetricInput) (*cloudwatch.DescribeAlarmsForMetricOutput, error) { c.calls.describeAlarmsForMetric = append(c.calls.describeAlarmsForMetric, params) return c.describeAlarmsForMetricOutput, nil } -func (c *fakeCWAnnotationsClient) DescribeAlarms(_ context.Context, params *cloudwatch.DescribeAlarmsInput, _ ...func(*cloudwatch.Options)) (*cloudwatch.DescribeAlarmsOutput, error) { +func (c *fakeCWAnnotationsClient) DescribeAlarms(params *cloudwatch.DescribeAlarmsInput) (*cloudwatch.DescribeAlarmsOutput, error) { c.calls.describeAlarms = append(c.calls.describeAlarms, params) return c.describeAlarmsOutput, nil @@ -142,14 +122,16 @@ func (c *fakeCWAnnotationsClient) DescribeAlarms(_ context.Context, params *clou // Please use mockEC2Client above, we are slowly migrating towards using testify's mocks only type oldEC2Client struct { + ec2iface.EC2API + regions []string - reservations []ec2types.Reservation + reservations []*ec2.Reservation } -func (c oldEC2Client) DescribeRegions(_ context.Context, _ *ec2.DescribeRegionsInput, _ ...func(*ec2.Options)) (*ec2.DescribeRegionsOutput, error) { - regions := []ec2types.Region{} +func (c oldEC2Client) DescribeRegionsWithContext(ctx aws.Context, in *ec2.DescribeRegionsInput, option ...request.Option) (*ec2.DescribeRegionsOutput, error) { + regions := []*ec2.Region{} for _, region := range c.regions { - regions = append(regions, ec2types.Region{ + regions = append(regions, &ec2.Region{ RegionName: aws.String(region), }) } @@ -158,10 +140,11 @@ func (c oldEC2Client) DescribeRegions(_ context.Context, _ *ec2.DescribeRegionsI }, nil } -func (c oldEC2Client) DescribeInstances(_ context.Context, in *ec2.DescribeInstancesInput, _ ...func(*ec2.Options)) (*ec2.DescribeInstancesOutput, error) { - reservations := []ec2types.Reservation{} +func (c oldEC2Client) DescribeInstancesPagesWithContext(ctx aws.Context, in *ec2.DescribeInstancesInput, + fn func(*ec2.DescribeInstancesOutput, bool) bool, opts ...request.Option) error { + reservations := []*ec2.Reservation{} for _, r := range c.reservations { - instances := []ec2types.Instance{} + instances := []*ec2.Instance{} for _, inst := range r.Instances { if len(in.InstanceIds) == 0 { instances = append(instances, inst) @@ -169,85 +152,97 @@ func (c oldEC2Client) DescribeInstances(_ context.Context, in *ec2.DescribeInsta } for _, id := range in.InstanceIds { - if *inst.InstanceId == id { + if *inst.InstanceId == *id { instances = append(instances, inst) } } } - reservation := ec2types.Reservation{Instances: instances} + reservation := &ec2.Reservation{Instances: instances} reservations = append(reservations, reservation) } - return &ec2.DescribeInstancesOutput{ + fn(&ec2.DescribeInstancesOutput{ Reservations: reservations, - }, nil + }, true) + return nil } type fakeRGTAClient struct { - tagMapping []resourcegroupstaggingapitypes.ResourceTagMapping + resourcegroupstaggingapiiface.ResourceGroupsTaggingAPIAPI + + tagMapping []*resourcegroupstaggingapi.ResourceTagMapping } -func (c fakeRGTAClient) GetResources(_ context.Context, _ *resourcegroupstaggingapi.GetResourcesInput, _ ...func(*resourcegroupstaggingapi.Options)) (*resourcegroupstaggingapi.GetResourcesOutput, error) { - return &resourcegroupstaggingapi.GetResourcesOutput{ +func (c fakeRGTAClient) GetResourcesPagesWithContext(ctx context.Context, in *resourcegroupstaggingapi.GetResourcesInput, + fn func(*resourcegroupstaggingapi.GetResourcesOutput, bool) bool, opts ...request.Option) error { + fn(&resourcegroupstaggingapi.GetResourcesOutput{ ResourceTagMappingList: c.tagMapping, - }, nil + }, true) + return nil } type fakeCheckHealthClient struct { - listMetricsFunction func(context.Context, *cloudwatch.ListMetricsInput, ...func(*cloudwatch.Options)) (*cloudwatch.ListMetricsOutput, error) - describeLogGroupsFunction func(context.Context, *cloudwatchlogs.DescribeLogGroupsInput, ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.DescribeLogGroupsOutput, error) - - models.CWClient + listMetricsPages func(input *cloudwatch.ListMetricsInput, fn func(*cloudwatch.ListMetricsOutput, bool) bool) error + describeLogGroups func(input *cloudwatchlogs.DescribeLogGroupsInput) (*cloudwatchlogs.DescribeLogGroupsOutput, error) } -func (c fakeCheckHealthClient) ListMetrics(ctx context.Context, input *cloudwatch.ListMetricsInput, _ ...func(*cloudwatch.Options)) (*cloudwatch.ListMetricsOutput, error) { - if c.listMetricsFunction != nil { - return c.listMetricsFunction(ctx, input) +func (c fakeCheckHealthClient) ListMetricsPagesWithContext(ctx aws.Context, input *cloudwatch.ListMetricsInput, fn func(*cloudwatch.ListMetricsOutput, bool) bool, opts ...request.Option) error { + if c.listMetricsPages != nil { + return c.listMetricsPages(input, fn) } - return &cloudwatch.ListMetricsOutput{}, nil + return nil } -func (c fakeCheckHealthClient) DescribeLogGroups(ctx context.Context, input *cloudwatchlogs.DescribeLogGroupsInput, _ ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.DescribeLogGroupsOutput, error) { - if c.describeLogGroupsFunction != nil { - return c.describeLogGroupsFunction(ctx, input) +func (c fakeCheckHealthClient) DescribeLogGroupsWithContext(ctx context.Context, input *cloudwatchlogs.DescribeLogGroupsInput, option ...request.Option) (*cloudwatchlogs.DescribeLogGroupsOutput, error) { + if c.describeLogGroups != nil { + return c.describeLogGroups(input) } return nil, nil } -func (c fakeCheckHealthClient) GetLogGroupFields(_ context.Context, _ *cloudwatchlogs.GetLogGroupFieldsInput, _ ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.GetLogGroupFieldsOutput, error) { +func (c fakeCheckHealthClient) GetLogGroupFieldsWithContext(ctx context.Context, input *cloudwatchlogs.GetLogGroupFieldsInput, option ...request.Option) (*cloudwatchlogs.GetLogGroupFieldsOutput, error) { return nil, nil } -func testInstanceManagerWithSettings(settings models.CloudWatchSettings, awsAuthShouldFail bool) instancemgmt.InstanceManager { - return datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { - return DataSource{ - Settings: settings, - AWSConfigProvider: awsauth.NewFakeConfigProvider(awsAuthShouldFail), - tagValueCache: cache.New(0, 0), - }, nil - }) -} - -func testInstanceManager(pageLimit int, getAWSConfigShouldFail bool) instancemgmt.InstanceManager { - return testInstanceManagerWithSettings(models.CloudWatchSettings{ - AWSDatasourceSettings: awsds.AWSDatasourceSettings{ - Region: "us-east-1", - AuthType: awsds.AuthTypeKeys, - AccessKey: "nothing", - SecretKey: "nowhere", +func testInstanceManager(pageLimit int) instancemgmt.InstanceManager { + return datasource.NewInstanceManager((func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{ + AWSDatasourceSettings: awsds.AWSDatasourceSettings{ + Region: "us-east-1", + }, + GrafanaSettings: awsds.AuthSettings{ListMetricsPageLimit: pageLimit}, }, - GrafanaSettings: awsds.AuthSettings{ListMetricsPageLimit: pageLimit}, - }, getAWSConfigShouldFail) + sessions: &fakeSessionCache{}, + tagValueCache: cache.New(0, 0)}, nil + })) } func defaultTestInstanceManager() instancemgmt.InstanceManager { - return testInstanceManager(1000, false) + return testInstanceManager(1000) } -type FakeCredentialsProvider struct { +type mockSessionCache struct { + mock.Mock } -func (fcp *FakeCredentialsProvider) Retrieve(_ context.Context) (aws.Credentials, error) { - return aws.Credentials{}, nil +func (c *mockSessionCache) GetSessionWithAuthSettings(config awsds.GetSessionConfig, auth awsds.AuthSettings) (*session.Session, error) { + args := c.Called(config) + return args.Get(0).(*session.Session), args.Error(1) +} + +type fakeSessionCache struct { + getSessionWithAuthSettings func(c awsds.GetSessionConfig, a awsds.AuthSettings) (*session.Session, error) + calledRegions []string +} + +func (s *fakeSessionCache) GetSessionWithAuthSettings(c awsds.GetSessionConfig, a awsds.AuthSettings) (*session.Session, error) { + s.calledRegions = append(s.calledRegions, c.Settings.Region) + + if s.getSessionWithAuthSettings != nil { + return s.getSessionWithAuthSettings(c, a) + } + return &session.Session{ + Config: &aws.Config{}, + }, nil } type mockedCallResourceResponseSenderForOauth struct { @@ -259,25 +254,25 @@ func (s *mockedCallResourceResponseSenderForOauth) Send(resp *backend.CallResour return nil } -type fakeSmithyError struct { +type fakeAWSError struct { code string message string } -func (f fakeSmithyError) Error() string { - return f.message +func (e fakeAWSError) OrigErr() error { + return nil } -func (f fakeSmithyError) ErrorCode() string { - return f.code +func (e fakeAWSError) Error() string { + return e.message } -func (f fakeSmithyError) ErrorMessage() string { - return f.message +func (e fakeAWSError) Code() string { + return e.code } -func (f fakeSmithyError) ErrorFault() smithy.ErrorFault { - return 0 +func (e fakeAWSError) Message() string { + return e.message } func contextWithFeaturesEnabled(enabled ...string) context.Context { diff --git a/pkg/tsdb/cloudwatch/time_series_query_test.go b/pkg/tsdb/cloudwatch/time_series_query_test.go index e51f0c58551..c2e65bb5434 100644 --- a/pkg/tsdb/cloudwatch/time_series_query_test.go +++ b/pkg/tsdb/cloudwatch/time_series_query_test.go @@ -6,12 +6,16 @@ import ( "testing" "time" - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/cloudwatch" - cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" - + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/cloudwatch" + "github.com/aws/aws-sdk-go/service/cloudwatch/cloudwatchiface" + "github.com/grafana/grafana-aws-sdk/pkg/awsds" "github.com/grafana/grafana-plugin-sdk-go/backend" + "github.com/grafana/grafana-plugin-sdk-go/backend/datasource" + "github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt" "github.com/grafana/grafana-plugin-sdk-go/backend/log" + "github.com/stretchr/testify/mock" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/features" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/kinds/dataquery" @@ -19,7 +23,6 @@ import ( "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" ) @@ -33,19 +36,19 @@ func TestTimeSeriesQuery(t *testing.T) { }) var api mocks.MetricsAPI - NewCWClient = func(aws.Config) models.CWClient { + NewCWClient = func(sess *session.Session) cloudwatchiface.CloudWatchAPI { return &api } t.Run("Custom metrics", func(t *testing.T) { api = mocks.MetricsAPI{} - api.On("GetMetricData", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{ - MetricDataResults: []cloudwatchtypes.MetricDataResult{ + api.On("GetMetricDataWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{ + MetricDataResults: []*cloudwatch.MetricDataResult{ { - StatusCode: "Complete", Id: aws.String("a"), Label: aws.String("NetworkOut"), Values: []float64{1.0}, Timestamps: []time.Time{now}, + StatusCode: aws.String("Complete"), Id: aws.String("a"), Label: aws.String("NetworkOut"), Values: []*float64{aws.Float64(1.0)}, Timestamps: []*time.Time{&now}, }, { - StatusCode: "Complete", Id: aws.String("b"), Label: aws.String("NetworkIn"), Values: []float64{1.0}, Timestamps: []time.Time{now}, + StatusCode: aws.String("Complete"), Id: aws.String("b"), Label: aws.String("NetworkIn"), Values: []*float64{aws.Float64(1.0)}, Timestamps: []*time.Time{&now}, }}}, nil) im := defaultTestInstanceManager() @@ -142,23 +145,28 @@ func TestTimeSeriesQuery(t *testing.T) { func Test_executeTimeSeriesQuery_getCWClient_is_called_once_per_region_and_GetMetricData_is_called_once_per_grouping_of_queries_by_region(t *testing.T) { /* TODO: This test aims to verify the logic to group regions which has been extracted from ParseMetricDataQueries. It should be replaced by a test at a lower level when grouping by regions is incorporated into a separate business logic layer */ - // FIXME: this test is broken - it only works because we're recovering from the panic that the Mock - // produces - see time_series_query.go line 78. If that recover is commented out, the test fails. - t.Skip("skipping broken test") origNewCWClient := NewCWClient t.Cleanup(func() { NewCWClient = origNewCWClient }) var mockMetricClient mocks.MetricsAPI - NewCWClient = func(aws.Config) models.CWClient { + NewCWClient = func(sess *session.Session) cloudwatchiface.CloudWatchAPI { return &mockMetricClient } - t.Run("Queries with the same region should call GetMetricData 1 time", func(t *testing.T) { - im := defaultTestInstanceManager() + t.Run("Queries with the same region should call GetSessionWithAuthSettings with that region 1 time and call GetMetricDataWithContext 1 time", func(t *testing.T) { + mockSessionCache := &mockSessionCache{} + mockSessionCache.On("GetSessionWithAuthSettings", mock.MatchedBy( + func(config awsds.GetSessionConfig) bool { + return config.Settings.Region == "us-east-1" + })). // region from queries is asserted here + Return(&session.Session{Config: &aws.Config{}}, nil).Once() + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{}, sessions: mockSessionCache}, nil + }) mockMetricClient = mocks.MetricsAPI{} - mockMetricClient.On("GetMetricData", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{}, nil) + mockMetricClient.On("GetMetricDataWithContext", mock.Anything, mock.Anything, mock.Anything).Return(nil, nil) executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ @@ -194,16 +202,31 @@ func Test_executeTimeSeriesQuery_getCWClient_is_called_once_per_region_and_GetMe }) require.NoError(t, err) + mockSessionCache.AssertExpectations(t) // method is defined to only return "Once()", // AssertExpectations will fail if those methods were not called Once(), so expected number of calls is asserted by this line - mockMetricClient.AssertNumberOfCalls(t, "GetMetricData", 1) + mockMetricClient.AssertNumberOfCalls(t, "GetMetricDataWithContext", 1) // GetMetricData is asserted to have been called 1 time for the 1 region present in the queries }) - t.Run("3 queries with 2 regions calls GetMetricData 2 times", func(t *testing.T) { - im := defaultTestInstanceManager() + t.Run("3 queries with 2 regions calls GetSessionWithAuthSettings 2 times and calls GetMetricDataWithContext 2 times", func(t *testing.T) { + sessionCache := &mockSessionCache{} + sessionCache.On("GetSessionWithAuthSettings", mock.MatchedBy( + func(config awsds.GetSessionConfig) bool { + return config.Settings.Region == "us-east-1" + })). + Return(&session.Session{Config: &aws.Config{}}, nil).Once() + sessionCache.On("GetSessionWithAuthSettings", mock.MatchedBy( + func(config awsds.GetSessionConfig) bool { + return config.Settings.Region == "us-east-2" + })). + Return(&session.Session{Config: &aws.Config{}}, nil).Once() + + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{}, sessions: sessionCache}, nil + }) mockMetricClient = mocks.MetricsAPI{} - mockMetricClient.On("GetMetricData", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{}, nil) + mockMetricClient.On("GetMetricDataWithContext", mock.Anything, mock.Anything, mock.Anything).Return(nil, nil) executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ @@ -251,16 +274,26 @@ func Test_executeTimeSeriesQuery_getCWClient_is_called_once_per_region_and_GetMe }) require.NoError(t, err) + sessionCache.AssertExpectations(t) // method is defined to only return "Once()" for each region. // AssertExpectations will fail if those methods were not called Once(), so expected number of calls is asserted by this line - mockMetricClient.AssertNumberOfCalls(t, "GetMetricData", 2) + mockMetricClient.AssertNumberOfCalls(t, "GetMetricDataWithContext", 2) // GetMetricData is asserted to have been called 2 times, presumably once for each group of regions (2 regions total) }) - t.Run("3 queries with 2 time ranges calls GetMetricData 2 times", func(t *testing.T) { - im := defaultTestInstanceManager() + t.Run("3 queries with 2 time ranges calls GetSessionWithAuthSettings 2 times and calls GetMetricDataWithContext 2 times", func(t *testing.T) { + sessionCache := &mockSessionCache{} + sessionCache.On("GetSessionWithAuthSettings", mock.MatchedBy( + func(config awsds.GetSessionConfig) bool { + return config.Settings.Region == "us-east-2" + })). + Return(&session.Session{Config: &aws.Config{}}, nil).Times(2) + + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{}, sessions: sessionCache}, nil + }) mockMetricClient = mocks.MetricsAPI{} - mockMetricClient.On("GetMetricData", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{}, nil) + mockMetricClient.On("GetMetricDataWithContext", mock.Anything, mock.Anything, mock.Anything).Return(nil, nil) executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ @@ -308,7 +341,8 @@ func Test_executeTimeSeriesQuery_getCWClient_is_called_once_per_region_and_GetMe }) require.NoError(t, err) - mockMetricClient.AssertNumberOfCalls(t, "GetMetricData", 2) + sessionCache.AssertExpectations(t) // method is defined to return twice (once for each batch) + mockMetricClient.AssertNumberOfCalls(t, "GetMetricDataWithContext", 2) // GetMetricData is asserted to have been called 2 times, presumably once for each time range (2 time ranges total) }) } @@ -374,7 +408,7 @@ func newTestQuery(t testing.TB, p queryParameters) json.RawMessage { return marshalled } -func Test_QueryData_timeSeriesQuery_GetMetricData(t *testing.T) { +func Test_QueryData_timeSeriesQuery_GetMetricDataWithContext(t *testing.T) { origNewCWClient := NewCWClient t.Cleanup(func() { NewCWClient = origNewCWClient @@ -382,15 +416,17 @@ func Test_QueryData_timeSeriesQuery_GetMetricData(t *testing.T) { var api mocks.MetricsAPI - NewCWClient = func(aws.Config) models.CWClient { + NewCWClient = func(sess *session.Session) cloudwatchiface.CloudWatchAPI { return &api } - im := defaultTestInstanceManager() + im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil + }) t.Run("passes query label as GetMetricData label", func(t *testing.T) { api = mocks.MetricsAPI{} - api.On("GetMetricData", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{}, nil) + api.On("GetMetricDataWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{}, nil) executor := newExecutor(im, log.NewNullLogger()) query := newTestQuery(t, queryParameters{ Label: aws.String("${PROP('Period')} some words ${PROP('Dim.InstanceId')}"), @@ -429,7 +465,7 @@ func Test_QueryData_timeSeriesQuery_GetMetricData(t *testing.T) { for name, tc := range testCases { t.Run(name, func(t *testing.T) { api = mocks.MetricsAPI{} - api.On("GetMetricData", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{}, nil) + api.On("GetMetricDataWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{}, nil) executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ @@ -463,21 +499,21 @@ func Test_QueryData_response_data_frame_name_is_always_response_label(t *testing NewCWClient = origNewCWClient }) - api := mocks.MetricsAPI{Metrics: []cloudwatchtypes.Metric{ - {MetricName: aws.String(""), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("InstanceId"), Value: aws.String("i-00645d91ed77d87ac")}}}, + api := mocks.MetricsAPI{Metrics: []*cloudwatch.Metric{ + {MetricName: aws.String(""), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("InstanceId"), Value: aws.String("i-00645d91ed77d87ac")}}}, }} - api.On("ListMetricsPages").Return(nil) + api.On("ListMetricsPagesWithContext").Return(nil) - NewCWClient = func(aws.Config) models.CWClient { + NewCWClient = func(sess *session.Session) cloudwatchiface.CloudWatchAPI { return &api } labelFromGetMetricData := "some label" - api.On("GetMetricData", mock.Anything, mock.Anything, mock.Anything). + api.On("GetMetricDataWithContext", mock.Anything, mock.Anything, mock.Anything). Return(&cloudwatch.GetMetricDataOutput{ - MetricDataResults: []cloudwatchtypes.MetricDataResult{ - {StatusCode: "Complete", Id: aws.String(queryId), Label: aws.String(labelFromGetMetricData), - Values: []float64{1.0}, Timestamps: []time.Time{{}}}, + MetricDataResults: []*cloudwatch.MetricDataResult{ + {StatusCode: aws.String("Complete"), Id: aws.String(queryId), Label: aws.String(labelFromGetMetricData), + Values: []*float64{aws.Float64(1.0)}, Timestamps: []*time.Time{{}}}, }}, nil) im := defaultTestInstanceManager() @@ -630,14 +666,14 @@ func TestTimeSeriesQuery_CrossAccountQuerying(t *testing.T) { }) var api mocks.MetricsAPI - NewCWClient = func(aws.Config) models.CWClient { + NewCWClient = func(sess *session.Session) cloudwatchiface.CloudWatchAPI { return &api } im := defaultTestInstanceManager() t.Run("should call GetMetricDataInput with AccountId nil when no AccountId is provided", func(t *testing.T) { api = mocks.MetricsAPI{} - api.On("GetMetricData", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{}, nil) + api.On("GetMetricDataWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{}, nil) executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(contextWithFeaturesEnabled(features.FlagCloudWatchCrossAccountQuerying), &backend.QueryDataRequest{ @@ -678,7 +714,7 @@ func TestTimeSeriesQuery_CrossAccountQuerying(t *testing.T) { t.Run("should call GetMetricDataInput with AccountId nil when feature flag is false", func(t *testing.T) { api = mocks.MetricsAPI{} - api.On("GetMetricData", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{}, nil) + api.On("GetMetricDataWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{}, nil) executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ PluginContext: backend.PluginContext{ @@ -719,7 +755,7 @@ func TestTimeSeriesQuery_CrossAccountQuerying(t *testing.T) { t.Run("should call GetMetricDataInput with AccountId in a MetricStat query", func(t *testing.T) { api = mocks.MetricsAPI{} - api.On("GetMetricData", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{}, nil) + api.On("GetMetricDataWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{}, nil) executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(contextWithFeaturesEnabled(features.FlagCloudWatchCrossAccountQuerying), &backend.QueryDataRequest{ PluginContext: backend.PluginContext{ @@ -760,7 +796,7 @@ func TestTimeSeriesQuery_CrossAccountQuerying(t *testing.T) { t.Run("should GetMetricDataInput with AccountId in an inferred search expression query", func(t *testing.T) { api = mocks.MetricsAPI{} - api.On("GetMetricData", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{}, nil) + api.On("GetMetricDataWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatch.GetMetricDataOutput{}, nil) executor := newExecutor(im, log.NewNullLogger()) _, err := executor.QueryData(contextWithFeaturesEnabled(features.FlagCloudWatchCrossAccountQuerying), &backend.QueryDataRequest{ PluginContext: backend.PluginContext{