Skip to content

Commit 33fae51

Browse files
committed
test: Add concurrency recovery integration test
Add TestConcurrencyRecovery that validates agent recovery when IAM permissions are restored mid-test. Uses a separate inline deny policy on 'recovery-test-*' pattern (independent from the existing static deny on 'aws-restricted-log-group-name-*'). Test flow: 1. Attach inline deny policy to instance role 2. Start agent, write logs — recovery group fails 3. Remove deny policy, wait for IAM propagation 4. Write more logs — recovery group publishes Also adds IAM utility functions (PutRoleDenyPolicy, DeleteRoleInlinePolicy, GetInstanceRoleName) and required IAM permissions to Terraform. 🤖 Assisted by AI
1 parent 93db166 commit 33fae51

7 files changed

Lines changed: 260 additions & 14 deletions

File tree

go.mod

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/aws/amazon-cloudwatch-agent-test
22

3-
go 1.20
3+
go 1.23
44

55
// Avoid checksum mismatch for go-collectd https://github.com/collectd/go-collectd/issues/94
66
replace collectd.org v0.5.0 => github.com/collectd/go-collectd v0.5.0
@@ -9,7 +9,7 @@ require (
99
collectd.org v0.5.0
1010
github.com/DataDog/datadog-go v4.8.3+incompatible
1111
github.com/aws/aws-sdk-go v1.48.12
12-
github.com/aws/aws-sdk-go-v2 v1.23.5
12+
github.com/aws/aws-sdk-go-v2 v1.41.1
1313
github.com/aws/aws-sdk-go-v2/config v1.25.11
1414
github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.12.9
1515
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.9
@@ -20,8 +20,10 @@ require (
2020
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.26.3
2121
github.com/aws/aws-sdk-go-v2/service/ec2 v1.138.2
2222
github.com/aws/aws-sdk-go-v2/service/ecs v1.35.2
23+
github.com/aws/aws-sdk-go-v2/service/iam v1.53.2
2324
github.com/aws/aws-sdk-go-v2/service/s3 v1.47.2
2425
github.com/aws/aws-sdk-go-v2/service/ssm v1.44.2
26+
github.com/aws/aws-sdk-go-v2/service/sts v1.26.2
2527
github.com/aws/aws-sdk-go-v2/service/xray v1.23.2
2628
github.com/aws/aws-xray-sdk-go v1.8.3
2729
github.com/cenkalti/backoff/v4 v4.2.1
@@ -51,8 +53,8 @@ require (
5153
github.com/andybalholm/brotli v1.0.6 // indirect
5254
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.3 // indirect
5355
github.com/aws/aws-sdk-go-v2/credentials v1.16.9 // indirect
54-
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.8 // indirect
55-
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.8 // indirect
56+
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17 // indirect
57+
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.17 // indirect
5658
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.1 // indirect
5759
github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.8 // indirect
5860
github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.18.2 // indirect
@@ -63,8 +65,7 @@ require (
6365
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.8 // indirect
6466
github.com/aws/aws-sdk-go-v2/service/sso v1.18.2 // indirect
6567
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.2 // indirect
66-
github.com/aws/aws-sdk-go-v2/service/sts v1.26.2 // indirect
67-
github.com/aws/smithy-go v1.18.1 // indirect
68+
github.com/aws/smithy-go v1.24.0 // indirect
6869
github.com/davecgh/go-spew v1.1.1 // indirect
6970
github.com/go-logr/logr v1.3.0 // indirect
7071
github.com/go-logr/stdr v1.2.2 // indirect

go.sum

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBp
4646
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
4747
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
4848
github.com/DATA-DOG/go-sqlmock v1.4.1 h1:ThlnYciV1iM/V0OSF/dtkqWb6xo5qITT1TJBG1MRDJM=
49+
github.com/DATA-DOG/go-sqlmock v1.4.1/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
4950
github.com/DataDog/datadog-go v4.8.3+incompatible h1:fNGaYSuObuQb5nzeTQqowRAd9bpDIRRV4/gUtIBjh8Q=
5051
github.com/DataDog/datadog-go v4.8.3+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
5152
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
@@ -58,8 +59,8 @@ github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHG
5859
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
5960
github.com/aws/aws-sdk-go v1.48.12 h1:n+eGzflzzvYubu2cOjqpVll7lF+Ci0ThyCpg5kzfzbo=
6061
github.com/aws/aws-sdk-go v1.48.12/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
61-
github.com/aws/aws-sdk-go-v2 v1.23.5 h1:xK6C4udTyDMd82RFvNkDQxtAd00xlzFUtX4fF2nMZyg=
62-
github.com/aws/aws-sdk-go-v2 v1.23.5/go.mod h1:t3szzKfP0NeRU27uBFczDivYJjsmSnqI8kIvKyWb9ds=
62+
github.com/aws/aws-sdk-go-v2 v1.41.1 h1:ABlyEARCDLN034NhxlRUSZr4l71mh+T5KAeGh6cerhU=
63+
github.com/aws/aws-sdk-go-v2 v1.41.1/go.mod h1:MayyLB8y+buD9hZqkCW3kX1AKq07Y5pXxtgB+rRFhz0=
6364
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.3 h1:Zx9+31KyB8wQna6SXFWOewlgoY5uGdDAu6PTOEU3OQI=
6465
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.3/go.mod h1:zxbEJhRdKTH1nqS2qu6UJ7zGe25xaHxZXaC2CvuQFnA=
6566
github.com/aws/aws-sdk-go-v2/config v1.25.11 h1:RWzp7jhPRliIcACefGkKp03L0Yofmd2p8M25kbiyvno=
@@ -72,10 +73,10 @@ github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.9 h1:FZVFahMyZle6WcogZCOxo6D
7273
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.9/go.mod h1:kjq7REMIkxdtcEC9/4BVXjOsNY5isz6jQbEgk6osRTU=
7374
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.4 h1:TUCNKBd4/JEefsZDxo5deRmrRRPZHqGyBYiUAeBKOWU=
7475
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.4/go.mod h1:egDkcl+zsgFqS6VO142bKboip5Pe1sNMwN55Xy38QsM=
75-
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.8 h1:8GVZIR0y6JRIUNSYI1xAMF4HDfV8H/bOsZ/8AD/uY5Q=
76-
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.8/go.mod h1:rwBfu0SoUkBUZndVgPZKAD9Y2JigaZtRP68unRiYToQ=
77-
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.8 h1:ZE2ds/qeBkhk3yqYvS3CDCFNvd9ir5hMjlVStLZWrvM=
78-
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.8/go.mod h1:/lAPPymDYL023+TS6DJmjuL42nxix2AvEvfjqOBRODk=
76+
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17 h1:xOLELNKGp2vsiteLsvLPwxC+mYmO6OZ8PYgiuPJzF8U=
77+
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17/go.mod h1:5M5CI3D12dNOtH3/mk6minaRwI2/37ifCURZISxA/IQ=
78+
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.17 h1:WWLqlh79iO48yLkj1v3ISRNiv+3KdQoZ6JWyfcsyQik=
79+
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.17/go.mod h1:EhG22vHRrvF8oXSTYStZhJc1aUgKtnJe+aOiFEV90cM=
7980
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.1 h1:uR9lXYjdPX0xY+NhvaJ4dD8rpSRz5VY81ccIIoNG+lw=
8081
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.1/go.mod h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY=
8182
github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.8 h1:abKT+RuM1sdCNZIGIfZpLkvxEX3Rpsto019XG/rkYG8=
@@ -94,6 +95,8 @@ github.com/aws/aws-sdk-go-v2/service/ec2 v1.138.2 h1:e3Imv1oXz+W3Tfclflkh72t5TUP
9495
github.com/aws/aws-sdk-go-v2/service/ec2 v1.138.2/go.mod h1:d1hAqgLDOPaSO1Piy/0bBmj6oAplFwv6p0cquHntNHM=
9596
github.com/aws/aws-sdk-go-v2/service/ecs v1.35.2 h1:yIr1T8uPhZT2cKCBeO39utfzG/RKJn3SxbuBOdj18Nc=
9697
github.com/aws/aws-sdk-go-v2/service/ecs v1.35.2/go.mod h1:MvDz+yXfa2sSEfHB57rdf83deKJIeKEopqHFhVmaRlk=
98+
github.com/aws/aws-sdk-go-v2/service/iam v1.53.2 h1:62G6btFUwAa5uR5iPlnlNVAM0zJSLbWgDfKOfUC7oW4=
99+
github.com/aws/aws-sdk-go-v2/service/iam v1.53.2/go.mod h1:av9clChrbZbJ5E21msSsiT2oghl2BJHfQGhCkXmhyu8=
97100
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.3 h1:e3PCNeEaev/ZF01cQyNZgmYE9oYYePIMJs2mWSKG514=
98101
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.3/go.mod h1:gIeeNyaL8tIEqZrzAnTeyhHcE0yysCtcaP+N9kxLZ+E=
99102
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.8 h1:xyfOAYV/ujzZOo01H9+OnyeiRKmTEp6EsITTsmq332Q=
@@ -118,8 +121,8 @@ github.com/aws/aws-sdk-go-v2/service/xray v1.23.2 h1:mFHM/R2FYnCkmUB52SqJncU5TWD
118121
github.com/aws/aws-sdk-go-v2/service/xray v1.23.2/go.mod h1:zz5H6SRVFHj93yt3lxA8Ql63c/pY90YjNvvalulrCTk=
119122
github.com/aws/aws-xray-sdk-go v1.8.3 h1:S8GdgVncBRhzbNnNUgTPwhEqhwt2alES/9rLASyhxjU=
120123
github.com/aws/aws-xray-sdk-go v1.8.3/go.mod h1:tv8uLMOSCABolrIF8YCcp3ghyswArsan8dfLCA1ZATk=
121-
github.com/aws/smithy-go v1.18.1 h1:pOdBTUfXNazOlxLrgeYalVnuTpKreACHtc62xLwIB3c=
122-
github.com/aws/smithy-go v1.18.1/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE=
124+
github.com/aws/smithy-go v1.24.0 h1:LpilSUItNPFr1eY85RYgTIg5eIEPtvFbskaFcmmIUnk=
125+
github.com/aws/smithy-go v1.24.0/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0=
123126
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
124127
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
125128
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@@ -252,6 +255,7 @@ github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB7
252255
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
253256
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
254257
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw=
258+
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
255259
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.1 h1:6UKoz5ujsI55KNpsJH3UwCq3T8kKbZwNZBNPuTTje8U=
256260
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.1/go.mod h1:YvJ2f6MplWDhfxiUC3KpyTy76kYUZA4W3pTv/wdKQ9Y=
257261
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
@@ -279,6 +283,7 @@ github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6K
279283
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
280284
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
281285
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
286+
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
282287
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
283288
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
284289
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@@ -327,6 +332,7 @@ github.com/qri-io/jsonschema v0.2.1 h1:NNFoKms+kut6ABPf6xiKNM5214jzxAhDBrPHCJ97W
327332
github.com/qri-io/jsonschema v0.2.1/go.mod h1:g7DPkiOsK1xv6T/Ao5scXRkd+yTFygcANPBaaqW+VrI=
328333
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
329334
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
335+
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
330336
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
331337
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
332338
github.com/shirou/gopsutil/v3 v3.23.3 h1:Syt5vVZXUDXPEXpIBt5ziWsJ4LdSAAxF4l/xZeQgSEE=
@@ -392,6 +398,7 @@ go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+
392398
go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
393399
go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
394400
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
401+
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
395402
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
396403
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
397404
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@@ -505,6 +512,7 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
505512
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
506513
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
507514
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
515+
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
508516
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
509517
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
510518
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -748,6 +756,7 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
748756
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
749757
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
750758
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
759+
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
751760
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
752761
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
753762
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=

terraform/setup/iam.tf

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ data "aws_iam_policy_document" "user-managed-policy-document" {
8888
"s3:GetObject",
8989
"s3:ListBucket",
9090
"s3:PutObject",
91+
"iam:PutRolePolicy",
92+
"iam:DeleteRolePolicy",
93+
"iam:GetInstanceProfile",
9194
]
9295
resources = ["*"]
9396
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
//go:build !windows
5+
6+
package cloudwatchlogs_concurrency
7+
8+
import (
9+
"fmt"
10+
"os"
11+
"testing"
12+
"time"
13+
14+
"github.com/stretchr/testify/assert"
15+
"github.com/stretchr/testify/require"
16+
17+
"github.com/aws/amazon-cloudwatch-agent-test/environment"
18+
"github.com/aws/amazon-cloudwatch-agent-test/util/awsservice"
19+
"github.com/aws/amazon-cloudwatch-agent-test/util/common"
20+
)
21+
22+
const (
23+
recoveryPolicyName = "cwagent-recovery-test-deny"
24+
logGroupPattern = "arn:aws:logs:*:*:log-group:recovery-test-*:*"
25+
iamPropagationDelay = 30 * time.Second
26+
)
27+
28+
// TestConcurrencyRecovery validates that the agent recovers and publishes logs
29+
// after IAM deny permissions are removed mid-test.
30+
func TestConcurrencyRecovery(t *testing.T) {
31+
env := environment.GetEnvironmentMetaData()
32+
instanceId := env.InstanceId
33+
if instanceId == "" {
34+
instanceId = awsservice.GetInstanceId()
35+
}
36+
37+
// Discover instance role
38+
roleName, err := awsservice.GetInstanceRoleName()
39+
require.NoError(t, err, "Failed to get instance role name")
40+
41+
// Create inline deny policy (separate from the static Terraform-managed deny)
42+
err = awsservice.PutRoleDenyPolicy(roleName, recoveryPolicyName, logGroupPattern)
43+
require.NoError(t, err, "Failed to create deny policy")
44+
45+
policyCreated := true
46+
defer func() {
47+
if policyCreated {
48+
if cleanupErr := awsservice.DeleteRoleInlinePolicy(roleName, recoveryPolicyName); cleanupErr != nil {
49+
t.Logf("Warning: failed to cleanup deny policy: %v", cleanupErr)
50+
}
51+
}
52+
}()
53+
54+
// Wait for IAM policy to propagate
55+
time.Sleep(iamPropagationDelay)
56+
57+
allowedLogGroup := fmt.Sprintf("recovery-allowed-%s", instanceId)
58+
recoveryLogGroup := fmt.Sprintf("recovery-test-target-%s", instanceId)
59+
60+
defer awsservice.DeleteLogGroupAndStream(allowedLogGroup, instanceId)
61+
defer awsservice.DeleteLogGroupAndStream(recoveryLogGroup, instanceId)
62+
63+
// Create log files
64+
allowedFile, err := os.Create("/tmp/recovery_allowed.log")
65+
require.NoError(t, err)
66+
defer allowedFile.Close()
67+
defer os.Remove("/tmp/recovery_allowed.log")
68+
69+
recoveryFile, err := os.Create("/tmp/recovery_target.log")
70+
require.NoError(t, err)
71+
defer recoveryFile.Close()
72+
defer os.Remove("/tmp/recovery_target.log")
73+
74+
common.DeleteFile(common.AgentLogFile)
75+
common.TouchFile(common.AgentLogFile)
76+
77+
common.CopyFile("resources/config_recovery.json", configOutputPath)
78+
common.StartAgent(configOutputPath, true, false)
79+
defer common.StopAgent()
80+
81+
time.Sleep(sleepForFlush)
82+
83+
// Phase 1 — Write while denied
84+
start := time.Now()
85+
writeLogLines(t, allowedFile, 10)
86+
writeLogLines(t, recoveryFile, 10)
87+
time.Sleep(sleepForFlush)
88+
phase1End := time.Now()
89+
90+
// Verify allowed group has logs
91+
err = awsservice.ValidateLogs(allowedLogGroup, instanceId, &start, &phase1End,
92+
awsservice.AssertLogsCount(20))
93+
assert.NoError(t, err, "Allowed log group should have logs")
94+
95+
// Verify recovery group does NOT have logs
96+
err = awsservice.ValidateLogs(recoveryLogGroup, instanceId, &start, &phase1End,
97+
awsservice.AssertLogsCount(0))
98+
assert.Error(t, err, "Recovery log group should not exist while denied")
99+
assert.Contains(t, err.Error(), "ResourceNotFoundException")
100+
101+
// Phase 2 — Remove deny policy to grant permission
102+
err = awsservice.DeleteRoleInlinePolicy(roleName, recoveryPolicyName)
103+
assert.NoError(t, err, "Failed to delete deny policy")
104+
policyCreated = false
105+
106+
t.Logf("Deny policy removed, waiting %v for IAM propagation...", iamPropagationDelay)
107+
time.Sleep(iamPropagationDelay)
108+
109+
// Phase 3 — Write more logs after permission restored
110+
recoveryStart := time.Now()
111+
writeLogLines(t, recoveryFile, 10)
112+
time.Sleep(sleepForFlush)
113+
114+
common.StopAgent()
115+
end := time.Now()
116+
117+
// Phase 4 — Verify recovery group now has logs
118+
err = awsservice.ValidateLogs(recoveryLogGroup, instanceId, &recoveryStart, &end,
119+
awsservice.AssertLogsCount(20))
120+
assert.NoError(t, err, "Recovery log group should have logs after permissions restored")
121+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"agent": {
3+
"run_as_user": "root",
4+
"debug": true
5+
},
6+
"logs": {
7+
"logs_collected": {
8+
"files": {
9+
"collect_list": [
10+
{
11+
"file_path": "/tmp/recovery_allowed.log",
12+
"log_group_name": "recovery-allowed-{instance_id}",
13+
"log_stream_name": "{instance_id}",
14+
"timezone": "UTC",
15+
"retention_in_days": 7
16+
},
17+
{
18+
"file_path": "/tmp/recovery_target.log",
19+
"log_group_name": "recovery-test-target-{instance_id}",
20+
"log_stream_name": "{instance_id}",
21+
"timezone": "UTC",
22+
"retention_in_days": 7
23+
}
24+
]
25+
}
26+
},
27+
"force_flush_interval": 5,
28+
"concurrency": 2
29+
}
30+
}

util/awsservice/constant.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import (
1818
"github.com/aws/aws-sdk-go-v2/service/dynamodb"
1919
"github.com/aws/aws-sdk-go-v2/service/ec2"
2020
"github.com/aws/aws-sdk-go-v2/service/ecs"
21+
"github.com/aws/aws-sdk-go-v2/service/iam"
22+
// "github.com/aws/aws-sdk-go-v2/service/iam"
2123
"github.com/aws/aws-sdk-go-v2/service/s3"
2224
"github.com/aws/aws-sdk-go-v2/service/ssm"
2325
"github.com/aws/aws-sdk-go-v2/service/sts"
@@ -57,6 +59,7 @@ var (
5759
S3Client *s3.Client
5860
CloudformationClient *cloudformation.Client
5961
XrayClient *xray.Client
62+
IamClient *iam.Client
6063
)
6164

6265
func init() {
@@ -99,6 +102,7 @@ func ConfigureAWSClients(region string) error {
99102
S3Client = s3.NewFromConfig(awsCfg)
100103
CloudformationClient = cloudformation.NewFromConfig(awsCfg)
101104
XrayClient = xray.NewFromConfig(awsCfg)
105+
IamClient = iam.NewFromConfig(awsCfg)
102106

103107
return nil
104108
}

0 commit comments

Comments
 (0)