Adding upstream version 1.34.4.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
e393c3af3f
commit
4978089aab
4963 changed files with 677545 additions and 0 deletions
255
plugins/inputs/ecs/README.md
Normal file
255
plugins/inputs/ecs/README.md
Normal file
|
@ -0,0 +1,255 @@
|
|||
# Amazon Elastic Container Service Input Plugin
|
||||
|
||||
This plugin gathers statistics on running containers in a Task from the
|
||||
[Amazon Elastic Container Service][ecs] using the [Amazon ECS metadata][metadata]
|
||||
and the [v2][v2_endpoint] or [v3][v3_endpoint] statistics API endpoints.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> The telegraf container must be run in the same Task as the workload it is
|
||||
> inspecting.
|
||||
|
||||
The amazon-ecs-agent (though it _is_ a container running on the host) is not
|
||||
present in the metadata/stats endpoints.
|
||||
|
||||
⭐ Telegraf v1.11.0
|
||||
🏷️ cloud
|
||||
💻 all
|
||||
|
||||
[ecs]: https://aws.amazon.com/ecs/
|
||||
[metadata]: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-metadata-endpoint.html
|
||||
[v2_endpoint]: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-metadata-endpoint-v2.html
|
||||
[v3_endpoint]: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-metadata-endpoint-v3.html
|
||||
|
||||
## Global configuration options <!-- @/docs/includes/plugin_config.md -->
|
||||
|
||||
In addition to the plugin-specific configuration settings, plugins support
|
||||
additional global and plugin configuration settings. These settings are used to
|
||||
modify metrics, tags, and field or create aliases and configure ordering, etc.
|
||||
See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
|
||||
|
||||
[CONFIGURATION.md]: ../../../docs/CONFIGURATION.md#plugins
|
||||
|
||||
## Configuration
|
||||
|
||||
```toml @sample.conf
|
||||
# Read metrics about ECS containers
|
||||
[[inputs.ecs]]
|
||||
## ECS metadata url.
|
||||
## Metadata v2 API is used if set explicitly. Otherwise,
|
||||
## v3 metadata endpoint API is used if available.
|
||||
# endpoint_url = ""
|
||||
|
||||
## Containers to include and exclude. Globs accepted.
|
||||
## Note that an empty array for both will include all containers
|
||||
# container_name_include = []
|
||||
# container_name_exclude = []
|
||||
|
||||
## Container states to include and exclude. Globs accepted.
|
||||
## When empty only containers in the "RUNNING" state will be captured.
|
||||
## Possible values are "NONE", "PULLED", "CREATED", "RUNNING",
|
||||
## "RESOURCES_PROVISIONED", "STOPPED".
|
||||
# container_status_include = []
|
||||
# container_status_exclude = []
|
||||
|
||||
## ecs labels to include and exclude as tags. Globs accepted.
|
||||
## Note that an empty array for both will include all labels as tags
|
||||
ecs_label_include = [ "com.amazonaws.ecs.*" ]
|
||||
ecs_label_exclude = []
|
||||
|
||||
## Timeout for queries.
|
||||
# timeout = "5s"
|
||||
```
|
||||
|
||||
## Configuration (enforce v2 metadata)
|
||||
|
||||
```toml
|
||||
# Read metrics about ECS containers
|
||||
[[inputs.ecs]]
|
||||
## ECS metadata url.
|
||||
## Metadata v2 API is used if set explicitly. Otherwise,
|
||||
## v3 metadata endpoint API is used if available.
|
||||
endpoint_url = "http://169.254.170.2"
|
||||
|
||||
## Containers to include and exclude. Globs accepted.
|
||||
## Note that an empty array for both will include all containers
|
||||
# container_name_include = []
|
||||
# container_name_exclude = []
|
||||
|
||||
## Container states to include and exclude. Globs accepted.
|
||||
## When empty only containers in the "RUNNING" state will be captured.
|
||||
## Possible values are "NONE", "PULLED", "CREATED", "RUNNING",
|
||||
## "RESOURCES_PROVISIONED", "STOPPED".
|
||||
# container_status_include = []
|
||||
# container_status_exclude = []
|
||||
|
||||
## ecs labels to include and exclude as tags. Globs accepted.
|
||||
## Note that an empty array for both will include all labels as tags
|
||||
ecs_label_include = [ "com.amazonaws.ecs.*" ]
|
||||
ecs_label_exclude = []
|
||||
|
||||
## Timeout for queries.
|
||||
# timeout = "5s"
|
||||
```
|
||||
|
||||
## Metrics
|
||||
|
||||
- ecs_task
|
||||
- tags:
|
||||
- cluster
|
||||
- task_arn
|
||||
- family
|
||||
- revision
|
||||
- id
|
||||
- name
|
||||
- fields:
|
||||
- desired_status (string)
|
||||
- known_status (string)
|
||||
- limit_cpu (float)
|
||||
- limit_mem (float)
|
||||
|
||||
- ecs_container_mem
|
||||
- tags:
|
||||
- cluster
|
||||
- task_arn
|
||||
- family
|
||||
- revision
|
||||
- id
|
||||
- name
|
||||
- fields:
|
||||
- container_id
|
||||
- active_anon
|
||||
- active_file
|
||||
- cache
|
||||
- hierarchical_memory_limit
|
||||
- inactive_anon
|
||||
- inactive_file
|
||||
- mapped_file
|
||||
- pgfault
|
||||
- pgmajfault
|
||||
- pgpgin
|
||||
- pgpgout
|
||||
- rss
|
||||
- rss_huge
|
||||
- total_active_anon
|
||||
- total_active_file
|
||||
- total_cache
|
||||
- total_inactive_anon
|
||||
- total_inactive_file
|
||||
- total_mapped_file
|
||||
- total_pgfault
|
||||
- total_pgmajfault
|
||||
- total_pgpgin
|
||||
- total_pgpgout
|
||||
- total_rss
|
||||
- total_rss_huge
|
||||
- total_unevictable
|
||||
- total_writeback
|
||||
- unevictable
|
||||
- writeback
|
||||
- fail_count
|
||||
- limit
|
||||
- max_usage
|
||||
- usage
|
||||
- usage_percent
|
||||
|
||||
- ecs_container_cpu
|
||||
- tags:
|
||||
- cluster
|
||||
- task_arn
|
||||
- family
|
||||
- revision
|
||||
- id
|
||||
- name
|
||||
- cpu
|
||||
- fields:
|
||||
- container_id
|
||||
- usage_total
|
||||
- usage_in_usermode
|
||||
- usage_in_kernelmode
|
||||
- usage_system
|
||||
- throttling_periods
|
||||
- throttling_throttled_periods
|
||||
- throttling_throttled_time
|
||||
- usage_percent
|
||||
- usage_total
|
||||
|
||||
- ecs_container_net
|
||||
- tags:
|
||||
- cluster
|
||||
- task_arn
|
||||
- family
|
||||
- revision
|
||||
- id
|
||||
- name
|
||||
- network
|
||||
- fields:
|
||||
- container_id
|
||||
- rx_packets
|
||||
- rx_dropped
|
||||
- rx_bytes
|
||||
- rx_errors
|
||||
- tx_packets
|
||||
- tx_dropped
|
||||
- tx_bytes
|
||||
- tx_errors
|
||||
|
||||
- ecs_container_blkio
|
||||
- tags:
|
||||
- cluster
|
||||
- task_arn
|
||||
- family
|
||||
- revision
|
||||
- id
|
||||
- name
|
||||
- device
|
||||
- fields:
|
||||
- container_id
|
||||
- io_service_bytes_recursive_async
|
||||
- io_service_bytes_recursive_read
|
||||
- io_service_bytes_recursive_sync
|
||||
- io_service_bytes_recursive_total
|
||||
- io_service_bytes_recursive_write
|
||||
- io_serviced_recursive_async
|
||||
- io_serviced_recursive_read
|
||||
- io_serviced_recursive_sync
|
||||
- io_serviced_recursive_total
|
||||
- io_serviced_recursive_write
|
||||
|
||||
- ecs_container_meta
|
||||
- tags:
|
||||
- cluster
|
||||
- task_arn
|
||||
- family
|
||||
- revision
|
||||
- id
|
||||
- name
|
||||
- fields:
|
||||
- container_id
|
||||
- docker_name
|
||||
- image
|
||||
- image_id
|
||||
- desired_status
|
||||
- known_status
|
||||
- limit_cpu
|
||||
- limit_mem
|
||||
- created_at
|
||||
- started_at
|
||||
- type
|
||||
|
||||
## Example Output
|
||||
|
||||
```text
|
||||
ecs_task,cluster=test,family=nginx,host=c4b301d4a123,revision=2,task_arn=arn:aws:ecs:aws-region-1:012345678901:task/a1234abc-a0a0-0a01-ab01-0abc012a0a0a desired_status="RUNNING",known_status="RUNNING",limit_cpu=0.5,limit_mem=512 1542641488000000000
|
||||
ecs_container_mem,cluster=test,com.amazonaws.ecs.cluster=test,com.amazonaws.ecs.container-name=~internal~ecs~pause,com.amazonaws.ecs.task-arn=arn:aws:ecs:aws-region-1:012345678901:task/a1234abc-a0a0-0a01-ab01-0abc012a0a0a,com.amazonaws.ecs.task-definition-family=nginx,com.amazonaws.ecs.task-definition-version=2,family=nginx,host=c4b301d4a123,id=e6af031b91deb3136a2b7c42f262ed2ab554e2fe2736998c7d8edf4afe708dba,name=~internal~ecs~pause,revision=2,task_arn=arn:aws:ecs:aws-region-1:012345678901:task/a1234abc-a0a0-0a01-ab01-0abc012a0a0a active_anon=40960i,active_file=8192i,cache=790528i,pgpgin=1243i,total_pgfault=1298i,total_rss=40960i,limit=1033658368i,max_usage=4825088i,hierarchical_memory_limit=536870912i,rss=40960i,total_active_file=8192i,total_mapped_file=618496i,usage_percent=0.05349543109392212,container_id="e6af031b91deb3136a2b7c42f262ed2ab554e2fe2736998c7d8edf4afe708dba",pgfault=1298i,pgmajfault=6i,pgpgout=1040i,total_active_anon=40960i,total_inactive_file=782336i,total_pgpgin=1243i,usage=552960i,inactive_file=782336i,mapped_file=618496i,total_cache=790528i,total_pgpgout=1040i 1542642001000000000
|
||||
ecs_container_cpu,cluster=test,com.amazonaws.ecs.cluster=test,com.amazonaws.ecs.container-name=~internal~ecs~pause,com.amazonaws.ecs.task-arn=arn:aws:ecs:aws-region-1:012345678901:task/a1234abc-a0a0-0a01-ab01-0abc012a0a0a,com.amazonaws.ecs.task-definition-family=nginx,com.amazonaws.ecs.task-definition-version=2,cpu=cpu-total,family=nginx,host=c4b301d4a123,id=e6af031b91deb3136a2b7c42f262ed2ab554e2fe2736998c7d8edf4afe708dba,name=~internal~ecs~pause,revision=2,task_arn=arn:aws:ecs:aws-region-1:012345678901:task/a1234abc-a0a0-0a01-ab01-0abc012a0a0a usage_in_kernelmode=0i,throttling_throttled_periods=0i,throttling_periods=0i,throttling_throttled_time=0i,container_id="e6af031b91deb3136a2b7c42f262ed2ab554e2fe2736998c7d8edf4afe708dba",usage_percent=0,usage_total=26426156i,usage_in_usermode=20000000i,usage_system=2336100000000i 1542642001000000000
|
||||
ecs_container_cpu,cluster=test,com.amazonaws.ecs.cluster=test,com.amazonaws.ecs.container-name=~internal~ecs~pause,com.amazonaws.ecs.task-arn=arn:aws:ecs:aws-region-1:012345678901:task/a1234abc-a0a0-0a01-ab01-0abc012a0a0a,com.amazonaws.ecs.task-definition-family=nginx,com.amazonaws.ecs.task-definition-version=2,cpu=cpu0,family=nginx,host=c4b301d4a123,id=e6af031b91deb3136a2b7c42f262ed2ab554e2fe2736998c7d8edf4afe708dba,name=~internal~ecs~pause,revision=2,task_arn=arn:aws:ecs:aws-region-1:012345678901:task/a1234abc-a0a0-0a01-ab01-0abc012a0a0a container_id="e6af031b91deb3136a2b7c42f262ed2ab554e2fe2736998c7d8edf4afe708dba",usage_total=26426156i 1542642001000000000
|
||||
ecs_container_net,cluster=test,com.amazonaws.ecs.cluster=test,com.amazonaws.ecs.container-name=~internal~ecs~pause,com.amazonaws.ecs.task-arn=arn:aws:ecs:aws-region-1:012345678901:task/a1234abc-a0a0-0a01-ab01-0abc012a0a0a,com.amazonaws.ecs.task-definition-family=nginx,com.amazonaws.ecs.task-definition-version=2,family=nginx,host=c4b301d4a123,id=e6af031b91deb3136a2b7c42f262ed2ab554e2fe2736998c7d8edf4afe708dba,name=~internal~ecs~pause,network=eth0,revision=2,task_arn=arn:aws:ecs:aws-region-1:012345678901:task/a1234abc-a0a0-0a01-ab01-0abc012a0a0a rx_errors=0i,rx_packets=36i,tx_errors=0i,tx_bytes=648i,container_id="e6af031b91deb3136a2b7c42f262ed2ab554e2fe2736998c7d8edf4afe708dba",rx_dropped=0i,rx_bytes=5338i,tx_packets=8i,tx_dropped=0i 1542642001000000000
|
||||
ecs_container_net,cluster=test,com.amazonaws.ecs.cluster=test,com.amazonaws.ecs.container-name=~internal~ecs~pause,com.amazonaws.ecs.task-arn=arn:aws:ecs:aws-region-1:012345678901:task/a1234abc-a0a0-0a01-ab01-0abc012a0a0a,com.amazonaws.ecs.task-definition-family=nginx,com.amazonaws.ecs.task-definition-version=2,family=nginx,host=c4b301d4a123,id=e6af031b91deb3136a2b7c42f262ed2ab554e2fe2736998c7d8edf4afe708dba,name=~internal~ecs~pause,network=eth5,revision=2,task_arn=arn:aws:ecs:aws-region-1:012345678901:task/a1234abc-a0a0-0a01-ab01-0abc012a0a0a rx_errors=0i,tx_packets=9i,rx_packets=26i,tx_errors=0i,rx_bytes=4641i,tx_dropped=0i,tx_bytes=690i,container_id="e6af031b91deb3136a2b7c42f262ed2ab554e2fe2736998c7d8edf4afe708dba",rx_dropped=0i 1542642001000000000
|
||||
ecs_container_net,cluster=test,com.amazonaws.ecs.cluster=test,com.amazonaws.ecs.container-name=~internal~ecs~pause,com.amazonaws.ecs.task-arn=arn:aws:ecs:aws-region-1:012345678901:task/a1234abc-a0a0-0a01-ab01-0abc012a0a0a,com.amazonaws.ecs.task-definition-family=nginx,com.amazonaws.ecs.task-definition-version=2,family=nginx,host=c4b301d4a123,id=e6af031b91deb3136a2b7c42f262ed2ab554e2fe2736998c7d8edf4afe708dba,name=~internal~ecs~pause,network=total,revision=2,task_arn=arn:aws:ecs:aws-region-1:012345678901:task/a1234abc-a0a0-0a01-ab01-0abc012a0a0a rx_dropped=0i,rx_bytes=9979i,rx_errors=0i,rx_packets=62i,tx_bytes=1338i,container_id="e6af031b91deb3136a2b7c42f262ed2ab554e2fe2736998c7d8edf4afe708dba",tx_packets=17i,tx_dropped=0i,tx_errors=0i 1542642001000000000
|
||||
ecs_container_blkio,cluster=test,com.amazonaws.ecs.cluster=test,com.amazonaws.ecs.container-name=~internal~ecs~pause,com.amazonaws.ecs.task-arn=arn:aws:ecs:aws-region-1:012345678901:task/a1234abc-a0a0-0a01-ab01-0abc012a0a0a,com.amazonaws.ecs.task-definition-family=nginx,com.amazonaws.ecs.task-definition-version=2,device=253:1,family=nginx,host=c4b301d4a123,id=e6af031b91deb3136a2b7c42f262ed2ab554e2fe2736998c7d8edf4afe708dba,name=~internal~ecs~pause,revision=2,task_arn=arn:aws:ecs:aws-region-1:012345678901:task/a1234abc-a0a0-0a01-ab01-0abc012a0a0a io_service_bytes_recursive_sync=790528i,io_service_bytes_recursive_total=790528i,io_serviced_recursive_sync=10i,io_serviced_recursive_write=0i,io_serviced_recursive_async=0i,io_serviced_recursive_total=10i,container_id="e6af031b91deb3136a2b7c42f262ed2ab554e2fe2736998c7d8edf4afe708dba",io_service_bytes_recursive_read=790528i,io_service_bytes_recursive_write=0i,io_service_bytes_recursive_async=0i,io_serviced_recursive_read=10i 1542642001000000000
|
||||
ecs_container_blkio,cluster=test,com.amazonaws.ecs.cluster=test,com.amazonaws.ecs.container-name=~internal~ecs~pause,com.amazonaws.ecs.task-arn=arn:aws:ecs:aws-region-1:012345678901:task/a1234abc-a0a0-0a01-ab01-0abc012a0a0a,com.amazonaws.ecs.task-definition-family=nginx,com.amazonaws.ecs.task-definition-version=2,device=253:2,family=nginx,host=c4b301d4a123,id=e6af031b91deb3136a2b7c42f262ed2ab554e2fe2736998c7d8edf4afe708dba,name=~internal~ecs~pause,revision=2,task_arn=arn:aws:ecs:aws-region-1:012345678901:task/a1234abc-a0a0-0a01-ab01-0abc012a0a0a io_service_bytes_recursive_sync=790528i,io_service_bytes_recursive_total=790528i,io_serviced_recursive_async=0i,io_serviced_recursive_total=10i,container_id="e6af031b91deb3136a2b7c42f262ed2ab554e2fe2736998c7d8edf4afe708dba",io_service_bytes_recursive_read=790528i,io_service_bytes_recursive_write=0i,io_service_bytes_recursive_async=0i,io_serviced_recursive_read=10i,io_serviced_recursive_write=0i,io_serviced_recursive_sync=10i 1542642001000000000
|
||||
ecs_container_blkio,cluster=test,com.amazonaws.ecs.cluster=test,com.amazonaws.ecs.container-name=~internal~ecs~pause,com.amazonaws.ecs.task-arn=arn:aws:ecs:aws-region-1:012345678901:task/a1234abc-a0a0-0a01-ab01-0abc012a0a0a,com.amazonaws.ecs.task-definition-family=nginx,com.amazonaws.ecs.task-definition-version=2,device=253:4,family=nginx,host=c4b301d4a123,id=e6af031b91deb3136a2b7c42f262ed2ab554e2fe2736998c7d8edf4afe708dba,name=~internal~ecs~pause,revision=2,task_arn=arn:aws:ecs:aws-region-1:012345678901:task/a1234abc-a0a0-0a01-ab01-0abc012a0a0a io_service_bytes_recursive_write=0i,io_service_bytes_recursive_sync=790528i,io_service_bytes_recursive_async=0i,io_service_bytes_recursive_total=790528i,io_serviced_recursive_async=0i,container_id="e6af031b91deb3136a2b7c42f262ed2ab554e2fe2736998c7d8edf4afe708dba",io_service_bytes_recursive_read=790528i,io_serviced_recursive_read=10i,io_serviced_recursive_write=0i,io_serviced_recursive_sync=10i,io_serviced_recursive_total=10i 1542642001000000000
|
||||
ecs_container_blkio,cluster=test,com.amazonaws.ecs.cluster=test,com.amazonaws.ecs.container-name=~internal~ecs~pause,com.amazonaws.ecs.task-arn=arn:aws:ecs:aws-region-1:012345678901:task/a1234abc-a0a0-0a01-ab01-0abc012a0a0a,com.amazonaws.ecs.task-definition-family=nginx,com.amazonaws.ecs.task-definition-version=2,device=202:26368,family=nginx,host=c4b301d4a123,id=e6af031b91deb3136a2b7c42f262ed2ab554e2fe2736998c7d8edf4afe708dba,name=~internal~ecs~pause,revision=2,task_arn=arn:aws:ecs:aws-region-1:012345678901:task/a1234abc-a0a0-0a01-ab01-0abc012a0a0a io_serviced_recursive_read=10i,io_serviced_recursive_write=0i,io_serviced_recursive_sync=10i,io_serviced_recursive_async=0i,io_serviced_recursive_total=10i,container_id="e6af031b91deb3136a2b7c42f262ed2ab554e2fe2736998c7d8edf4afe708dba",io_service_bytes_recursive_sync=790528i,io_service_bytes_recursive_total=790528i,io_service_bytes_recursive_async=0i,io_service_bytes_recursive_read=790528i,io_service_bytes_recursive_write=0i 1542642001000000000
|
||||
ecs_container_blkio,cluster=test,com.amazonaws.ecs.cluster=test,com.amazonaws.ecs.container-name=~internal~ecs~pause,com.amazonaws.ecs.task-arn=arn:aws:ecs:aws-region-1:012345678901:task/a1234abc-a0a0-0a01-ab01-0abc012a0a0a,com.amazonaws.ecs.task-definition-family=nginx,com.amazonaws.ecs.task-definition-version=2,device=total,family=nginx,host=c4b301d4a123,id=e6af031b91deb3136a2b7c42f262ed2ab554e2fe2736998c7d8edf4afe708dba,name=~internal~ecs~pause,revision=2,task_arn=arn:aws:ecs:aws-region-1:012345678901:task/a1234abc-a0a0-0a01-ab01-0abc012a0a0a io_serviced_recursive_async=0i,io_serviced_recursive_read=40i,io_serviced_recursive_sync=40i,io_serviced_recursive_write=0i,io_serviced_recursive_total=40i,io_service_bytes_recursive_read=3162112i,io_service_bytes_recursive_write=0i,io_service_bytes_recursive_async=0i,container_id="e6af031b91deb3136a2b7c42f262ed2ab554e2fe2736998c7d8edf4afe708dba",io_service_bytes_recursive_sync=3162112i,io_service_bytes_recursive_total=3162112i 1542642001000000000
|
||||
ecs_container_meta,cluster=test,com.amazonaws.ecs.cluster=test,com.amazonaws.ecs.container-name=~internal~ecs~pause,com.amazonaws.ecs.task-arn=arn:aws:ecs:aws-region-1:012345678901:task/a1234abc-a0a0-0a01-ab01-0abc012a0a0a,com.amazonaws.ecs.task-definition-family=nginx,com.amazonaws.ecs.task-definition-version=2,family=nginx,host=c4b301d4a123,id=e6af031b91deb3136a2b7c42f262ed2ab554e2fe2736998c7d8edf4afe708dba,name=~internal~ecs~pause,revision=2,task_arn=arn:aws:ecs:aws-region-1:012345678901:task/a1234abc-a0a0-0a01-ab01-0abc012a0a0a limit_mem=0,type="CNI_PAUSE",container_id="e6af031b91deb3136a2b7c42f262ed2ab554e2fe2736998c7d8edf4afe708dba",docker_name="ecs-nginx-2-internalecspause",limit_cpu=0,known_status="RESOURCES_PROVISIONED",image="amazon/amazon-ecs-pause:0.1.0",image_id="",desired_status="RESOURCES_PROVISIONED" 1542642001000000000
|
||||
```
|
61
plugins/inputs/ecs/cgroupv2_test.go
Normal file
61
plugins/inputs/ecs/cgroupv2_test.go
Normal file
|
@ -0,0 +1,61 @@
|
|||
package ecs
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/influxdata/telegraf/plugins/parsers/influx"
|
||||
"github.com/influxdata/telegraf/testutil"
|
||||
)
|
||||
|
||||
const cgroupID = "c69461b2c836cc3f0e3e5deb07b1f16e25f6009da2a48bb0adc7dd580befaf55"
|
||||
|
||||
func TestParseCgroupV2Stats(t *testing.T) {
|
||||
parser := &influx.Parser{}
|
||||
require.NoError(t, parser.Init())
|
||||
expected, err := testutil.ParseMetricsFromFile("testdata/cgroupv2/stats.out", parser)
|
||||
require.NoError(t, err)
|
||||
|
||||
stats, err := os.Open("testdata/cgroupv2/stats.json")
|
||||
require.NoError(t, err)
|
||||
parsedStats, err := unmarshalStats(stats)
|
||||
require.NoError(t, err)
|
||||
|
||||
tags := map[string]string{
|
||||
"test_tag": "test",
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
memstats(cgroupID, parsedStats[cgroupID], &acc, tags, time.Now())
|
||||
cpustats(cgroupID, parsedStats[cgroupID], &acc, tags, time.Now())
|
||||
netstats(cgroupID, parsedStats[cgroupID], &acc, tags, time.Now())
|
||||
blkstats(cgroupID, parsedStats[cgroupID], &acc, tags, time.Now())
|
||||
|
||||
actual := acc.GetTelegrafMetrics()
|
||||
testutil.RequireMetricsEqual(t, expected, actual, testutil.IgnoreTime())
|
||||
}
|
||||
|
||||
func TestParseCgroupV2Meta(t *testing.T) {
|
||||
parser := &influx.Parser{}
|
||||
require.NoError(t, parser.Init())
|
||||
expected, err := testutil.ParseMetricsFromFile("testdata/cgroupv2/meta.out", parser)
|
||||
require.NoError(t, err)
|
||||
|
||||
meta, err := os.Open("testdata/cgroupv2/meta.json")
|
||||
require.NoError(t, err)
|
||||
validMeta, err := unmarshalTask(meta)
|
||||
require.NoError(t, err)
|
||||
|
||||
tags := map[string]string{
|
||||
"test_tag": "test",
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
metastats(cgroupID, &validMeta.Containers[0], &acc, tags, time.Now())
|
||||
|
||||
actual := acc.GetTelegrafMetrics()
|
||||
testutil.RequireMetricsEqual(t, expected, actual, testutil.IgnoreTime())
|
||||
}
|
173
plugins/inputs/ecs/client.go
Normal file
173
plugins/inputs/ecs/client.go
Normal file
|
@ -0,0 +1,173 @@
|
|||
package ecs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
)
|
||||
|
||||
var (
|
||||
// https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-metadata-endpoint-v2.html
|
||||
ecsMetadataPathV2 = "/v2/metadata"
|
||||
ecsMetaStatsPathV2 = "/v2/stats"
|
||||
|
||||
// https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-metadata-endpoint-v3.html
|
||||
// https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-metadata-endpoint-v4.html
|
||||
ecsMetadataPath = "/task"
|
||||
ecsMetaStatsPath = "/task/stats"
|
||||
)
|
||||
|
||||
// client is the ECS client contract
|
||||
type client interface {
|
||||
task() (*ecsTask, error)
|
||||
containerStats() (map[string]*container.StatsResponse, error)
|
||||
}
|
||||
|
||||
type httpClient interface {
|
||||
Do(req *http.Request) (*http.Response, error)
|
||||
}
|
||||
|
||||
// newClient constructs an ECS client with the passed configuration params
|
||||
func newClient(timeout time.Duration, endpoint string, version int) (*ecsClient, error) {
|
||||
if version < 2 || version > 4 {
|
||||
const msg = "expected metadata version 2, 3 or 4, got %d"
|
||||
return nil, fmt.Errorf(msg, version)
|
||||
}
|
||||
|
||||
baseURL, err := url.Parse(endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c := &http.Client{
|
||||
Timeout: timeout,
|
||||
}
|
||||
|
||||
return &ecsClient{
|
||||
client: c,
|
||||
baseURL: baseURL,
|
||||
taskURL: resolveTaskURL(baseURL, version),
|
||||
statsURL: resolveStatsURL(baseURL, version),
|
||||
version: version,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func resolveTaskURL(base *url.URL, version int) string {
|
||||
var path string
|
||||
switch version {
|
||||
case 2:
|
||||
path = ecsMetadataPathV2
|
||||
case 3:
|
||||
path = ecsMetadataPath
|
||||
case 4:
|
||||
path = ecsMetadataPath
|
||||
default:
|
||||
const msg = "resolveTaskURL: unexpected version %d"
|
||||
panic(fmt.Errorf(msg, version))
|
||||
}
|
||||
return resolveURL(base, path)
|
||||
}
|
||||
|
||||
func resolveStatsURL(base *url.URL, version int) string {
|
||||
var path string
|
||||
switch version {
|
||||
case 2:
|
||||
path = ecsMetaStatsPathV2
|
||||
case 3:
|
||||
path = ecsMetaStatsPath
|
||||
case 4:
|
||||
path = ecsMetaStatsPath
|
||||
default:
|
||||
// Should never happen.
|
||||
const msg = "resolveStatsURL: unexpected version %d"
|
||||
panic(fmt.Errorf(msg, version))
|
||||
}
|
||||
return resolveURL(base, path)
|
||||
}
|
||||
|
||||
// resolveURL returns a URL string by concatenating the string representation of base
|
||||
// and path. This is consistent with AWS metadata documentation:
|
||||
// https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-metadata-endpoint-v3.html#task-metadata-endpoint-v3-paths
|
||||
func resolveURL(base *url.URL, path string) string {
|
||||
return base.String() + path
|
||||
}
|
||||
|
||||
// ecsClient contains ECS connection config
|
||||
type ecsClient struct {
|
||||
client httpClient
|
||||
version int
|
||||
baseURL *url.URL
|
||||
taskURL string
|
||||
statsURL string
|
||||
}
|
||||
|
||||
// task calls the ECS metadata endpoint and returns a populated task
|
||||
func (c *ecsClient) task() (*ecsTask, error) {
|
||||
req, err := http.NewRequest("GET", c.taskURL, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp, err := c.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
//nolint:errcheck // LimitReader returns io.EOF and we're not interested in read errors.
|
||||
body, _ := io.ReadAll(io.LimitReader(resp.Body, 200))
|
||||
return nil, fmt.Errorf("%s returned HTTP status %s: %q", c.taskURL, resp.Status, body)
|
||||
}
|
||||
|
||||
task, err := unmarshalTask(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return task, nil
|
||||
}
|
||||
|
||||
// containerStats calls the ECS stats endpoint and returns a populated container stats map
|
||||
func (c *ecsClient) containerStats() (map[string]*container.StatsResponse, error) {
|
||||
req, err := http.NewRequest("GET", c.statsURL, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp, err := c.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
//nolint:errcheck // LimitReader returns io.EOF and we're not interested in read errors.
|
||||
body, _ := io.ReadAll(io.LimitReader(resp.Body, 200))
|
||||
return nil, fmt.Errorf("%s returned HTTP status %s: %q", c.statsURL, resp.Status, body)
|
||||
}
|
||||
|
||||
return unmarshalStats(resp.Body)
|
||||
}
|
||||
|
||||
// pollSync executes task and containerStats in parallel.
|
||||
// If both succeed, both structs are returned.
|
||||
// If either errors, a single error is returned.
|
||||
func pollSync(c client) (*ecsTask, map[string]*container.StatsResponse, error) {
|
||||
var task *ecsTask
|
||||
var stats map[string]*container.StatsResponse
|
||||
var err error
|
||||
|
||||
if stats, err = c.containerStats(); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if task, err = c.task(); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return task, stats, nil
|
||||
}
|
320
plugins/inputs/ecs/client_test.go
Normal file
320
plugins/inputs/ecs/client_test.go
Normal file
|
@ -0,0 +1,320 @@
|
|||
package ecs
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type pollMock struct {
|
||||
getTask func() (*ecsTask, error)
|
||||
getStats func() (map[string]*container.StatsResponse, error)
|
||||
}
|
||||
|
||||
func (p *pollMock) task() (*ecsTask, error) {
|
||||
return p.getTask()
|
||||
}
|
||||
|
||||
func (p *pollMock) containerStats() (map[string]*container.StatsResponse, error) {
|
||||
return p.getStats()
|
||||
}
|
||||
|
||||
func TestEcsClient_PollSync(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
mock *pollMock
|
||||
want *ecsTask
|
||||
want1 map[string]*container.StatsResponse
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "success",
|
||||
mock: &pollMock{
|
||||
getTask: func() (*ecsTask, error) {
|
||||
return &validMeta, nil
|
||||
},
|
||||
getStats: func() (map[string]*container.StatsResponse, error) {
|
||||
return validStats, nil
|
||||
},
|
||||
},
|
||||
want: &validMeta,
|
||||
want1: validStats,
|
||||
},
|
||||
{
|
||||
name: "task err",
|
||||
mock: &pollMock{
|
||||
getTask: func() (*ecsTask, error) {
|
||||
return nil, errors.New("err")
|
||||
},
|
||||
getStats: func() (map[string]*container.StatsResponse, error) {
|
||||
return validStats, nil
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "stats err",
|
||||
mock: &pollMock{
|
||||
getTask: func() (*ecsTask, error) {
|
||||
return &validMeta, nil
|
||||
},
|
||||
getStats: func() (map[string]*container.StatsResponse, error) {
|
||||
return nil, errors.New("err")
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, got1, err := pollSync(tt.mock)
|
||||
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("ecsClient.pollSync() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
require.Equal(t, tt.want, got, "ecsClient.pollSync() got = %v, want %v", got, tt.want)
|
||||
require.Equal(t, tt.want1, got1, "ecsClient.pollSync() got1 = %v, want %v", got1, tt.want1)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type mockDo struct {
|
||||
do func() (*http.Response, error)
|
||||
}
|
||||
|
||||
func (m mockDo) Do(*http.Request) (*http.Response, error) {
|
||||
return m.do()
|
||||
}
|
||||
|
||||
func TestEcsClient_Task(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
client httpClient
|
||||
want *ecsTask
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "happy",
|
||||
client: mockDo{
|
||||
do: func() (*http.Response, error) {
|
||||
rc, err := os.Open("testdata/metadata.golden")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
Body: io.NopCloser(rc),
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
want: &validMeta,
|
||||
},
|
||||
{
|
||||
name: "do err",
|
||||
client: mockDo{
|
||||
do: func() (*http.Response, error) {
|
||||
return nil, errors.New("err")
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "malformed 500 resp",
|
||||
client: mockDo{
|
||||
do: func() (*http.Response, error) {
|
||||
return &http.Response{
|
||||
StatusCode: http.StatusInternalServerError,
|
||||
Body: io.NopCloser(bytes.NewReader([]byte("foo"))),
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "malformed 200 resp",
|
||||
client: mockDo{
|
||||
do: func() (*http.Response, error) {
|
||||
return &http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
Body: io.NopCloser(bytes.NewReader([]byte("foo"))),
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &ecsClient{
|
||||
client: tt.client,
|
||||
taskURL: "abc",
|
||||
}
|
||||
got, err := c.task()
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("ecsClient.task() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
require.Equal(t, tt.want, got, "ecsClient.task() = %v, want %v", got, tt.want)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEcsClient_ContainerStats(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
client httpClient
|
||||
want map[string]*container.StatsResponse
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "happy",
|
||||
client: mockDo{
|
||||
do: func() (*http.Response, error) {
|
||||
rc, err := os.Open("testdata/stats.golden")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
Body: io.NopCloser(rc),
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
want: validStats,
|
||||
},
|
||||
{
|
||||
name: "do err",
|
||||
client: mockDo{
|
||||
do: func() (*http.Response, error) {
|
||||
return nil, errors.New("err")
|
||||
},
|
||||
},
|
||||
want: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "malformed 200 resp",
|
||||
client: mockDo{
|
||||
do: func() (*http.Response, error) {
|
||||
return &http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
Body: io.NopCloser(bytes.NewReader([]byte("foo"))),
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
want: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "malformed 500 resp",
|
||||
client: mockDo{
|
||||
do: func() (*http.Response, error) {
|
||||
return &http.Response{
|
||||
StatusCode: http.StatusInternalServerError,
|
||||
Body: io.NopCloser(bytes.NewReader([]byte("foo"))),
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
want: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &ecsClient{
|
||||
client: tt.client,
|
||||
statsURL: "abc",
|
||||
}
|
||||
got, err := c.containerStats()
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("ecsClient.containerStats() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
require.Equal(t, tt.want, got, "ecsClient.containerStats() = %v, want %v", got, tt.want)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveTaskURL(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
base string
|
||||
ver int
|
||||
exp string
|
||||
}{
|
||||
{
|
||||
name: "default v2 endpoint",
|
||||
base: v2Endpoint,
|
||||
ver: 2,
|
||||
exp: "http://169.254.170.2/v2/metadata",
|
||||
},
|
||||
{
|
||||
name: "custom v2 endpoint",
|
||||
base: "http://192.168.0.1",
|
||||
ver: 2,
|
||||
exp: "http://192.168.0.1/v2/metadata",
|
||||
},
|
||||
{
|
||||
name: "theoretical v3 endpoint",
|
||||
base: "http://169.254.170.2/v3/metadata",
|
||||
ver: 3,
|
||||
exp: "http://169.254.170.2/v3/metadata/task",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
baseURL, err := url.Parse(tt.base)
|
||||
require.NoError(t, err)
|
||||
|
||||
act := resolveTaskURL(baseURL, tt.ver)
|
||||
require.Equal(t, tt.exp, act)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveStatsURL(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
base string
|
||||
ver int
|
||||
exp string
|
||||
}{
|
||||
{
|
||||
name: "default v2 endpoint",
|
||||
base: v2Endpoint,
|
||||
ver: 2,
|
||||
exp: "http://169.254.170.2/v2/stats",
|
||||
},
|
||||
{
|
||||
name: "custom v2 endpoint",
|
||||
base: "http://192.168.0.1",
|
||||
ver: 2,
|
||||
exp: "http://192.168.0.1/v2/stats",
|
||||
},
|
||||
{
|
||||
name: "theoretical v3 endpoint",
|
||||
base: "http://169.254.170.2/v3/metadata",
|
||||
ver: 3,
|
||||
exp: "http://169.254.170.2/v3/metadata/task/stats",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
baseURL, err := url.Parse(tt.base)
|
||||
require.NoError(t, err)
|
||||
|
||||
act := resolveStatsURL(baseURL, tt.ver)
|
||||
require.Equal(t, tt.exp, act)
|
||||
})
|
||||
}
|
||||
}
|
244
plugins/inputs/ecs/ecs.go
Normal file
244
plugins/inputs/ecs/ecs.go
Normal file
|
@ -0,0 +1,244 @@
|
|||
//go:generate ../../../tools/readme_config_includer/generator
|
||||
package ecs
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
"github.com/influxdata/telegraf/config"
|
||||
"github.com/influxdata/telegraf/filter"
|
||||
"github.com/influxdata/telegraf/plugins/inputs"
|
||||
)
|
||||
|
||||
//go:embed sample.conf
|
||||
var sampleConfig string
|
||||
|
||||
const (
|
||||
v2Endpoint = "http://169.254.170.2"
|
||||
)
|
||||
|
||||
type Ecs struct {
|
||||
EndpointURL string `toml:"endpoint_url"`
|
||||
Timeout config.Duration `toml:"timeout"`
|
||||
|
||||
ContainerNameInclude []string `toml:"container_name_include"`
|
||||
ContainerNameExclude []string `toml:"container_name_exclude"`
|
||||
|
||||
ContainerStatusInclude []string `toml:"container_status_include"`
|
||||
ContainerStatusExclude []string `toml:"container_status_exclude"`
|
||||
|
||||
LabelInclude []string `toml:"ecs_label_include"`
|
||||
LabelExclude []string `toml:"ecs_label_exclude"`
|
||||
|
||||
newClient func(timeout time.Duration, endpoint string, version int) (*ecsClient, error)
|
||||
|
||||
client client
|
||||
filtersCreated bool
|
||||
labelFilter filter.Filter
|
||||
containerNameFilter filter.Filter
|
||||
statusFilter filter.Filter
|
||||
metadataVersion int
|
||||
}
|
||||
|
||||
func (*Ecs) SampleConfig() string {
|
||||
return sampleConfig
|
||||
}
|
||||
|
||||
func (ecs *Ecs) Gather(acc telegraf.Accumulator) error {
|
||||
err := initSetup(ecs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
task, stats, err := pollSync(ecs.client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mergeTaskStats(task, stats)
|
||||
|
||||
taskTags := map[string]string{
|
||||
"cluster": task.Cluster,
|
||||
"task_arn": task.TaskARN,
|
||||
"family": task.Family,
|
||||
"revision": task.Revision,
|
||||
}
|
||||
|
||||
// accumulate metrics
|
||||
accTask(task, taskTags, acc)
|
||||
ecs.accContainers(task, taskTags, acc)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func initSetup(ecs *Ecs) error {
|
||||
if ecs.client == nil {
|
||||
resolveEndpoint(ecs)
|
||||
|
||||
c, err := ecs.newClient(time.Duration(ecs.Timeout), ecs.EndpointURL, ecs.metadataVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ecs.client = c
|
||||
}
|
||||
|
||||
// Create filters
|
||||
if !ecs.filtersCreated {
|
||||
err := ecs.createContainerNameFilters()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ecs.createContainerStatusFilters()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ecs.createLabelFilters()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ecs.filtersCreated = true
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resolveEndpoint(ecs *Ecs) {
|
||||
if ecs.EndpointURL != "" {
|
||||
// Use metadata v2 API since endpoint is set explicitly.
|
||||
ecs.metadataVersion = 2
|
||||
return
|
||||
}
|
||||
|
||||
// Auto-detect metadata endpoint version.
|
||||
|
||||
// Use metadata v4 if available.
|
||||
// https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-metadata-endpoint-v4.html
|
||||
v4Endpoint := os.Getenv("ECS_CONTAINER_METADATA_URI_V4")
|
||||
if v4Endpoint != "" {
|
||||
ecs.EndpointURL = v4Endpoint
|
||||
ecs.metadataVersion = 4
|
||||
return
|
||||
}
|
||||
|
||||
// Use metadata v3 if available.
|
||||
// https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-metadata-endpoint-v3.html
|
||||
v3Endpoint := os.Getenv("ECS_CONTAINER_METADATA_URI")
|
||||
if v3Endpoint != "" {
|
||||
ecs.EndpointURL = v3Endpoint
|
||||
ecs.metadataVersion = 3
|
||||
return
|
||||
}
|
||||
|
||||
// Use v2 endpoint if nothing else is available.
|
||||
ecs.EndpointURL = v2Endpoint
|
||||
ecs.metadataVersion = 2
|
||||
}
|
||||
|
||||
func accTask(task *ecsTask, tags map[string]string, acc telegraf.Accumulator) {
|
||||
taskFields := map[string]interface{}{
|
||||
"desired_status": task.DesiredStatus,
|
||||
"known_status": task.KnownStatus,
|
||||
"limit_cpu": task.Limits["CPU"],
|
||||
"limit_mem": task.Limits["Memory"],
|
||||
}
|
||||
|
||||
acc.AddFields("ecs_task", taskFields, tags)
|
||||
}
|
||||
|
||||
func (ecs *Ecs) accContainers(task *ecsTask, taskTags map[string]string, acc telegraf.Accumulator) {
|
||||
for i := range task.Containers {
|
||||
c := &task.Containers[i]
|
||||
if !ecs.containerNameFilter.Match(c.Name) {
|
||||
continue
|
||||
}
|
||||
|
||||
if !ecs.statusFilter.Match(strings.ToUpper(c.KnownStatus)) {
|
||||
continue
|
||||
}
|
||||
|
||||
// add matching ECS container Labels
|
||||
containerTags := map[string]string{
|
||||
"id": c.ID,
|
||||
"name": c.Name,
|
||||
}
|
||||
for k, v := range c.Labels {
|
||||
if ecs.labelFilter.Match(k) {
|
||||
containerTags[k] = v
|
||||
}
|
||||
}
|
||||
tags := mergeTags(taskTags, containerTags)
|
||||
|
||||
parseContainerStats(c, acc, tags)
|
||||
}
|
||||
}
|
||||
|
||||
// returns a new map with the same content values as the input map
|
||||
func copyTags(in map[string]string) map[string]string {
|
||||
out := make(map[string]string)
|
||||
for k, v := range in {
|
||||
out[k] = v
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// returns a new map with the merged content values of the two input maps
|
||||
func mergeTags(a, b map[string]string) map[string]string {
|
||||
c := copyTags(a)
|
||||
for k, v := range b {
|
||||
c[k] = v
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func (ecs *Ecs) createContainerNameFilters() error {
|
||||
containerNameFilter, err := filter.NewIncludeExcludeFilter(ecs.ContainerNameInclude, ecs.ContainerNameExclude)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ecs.containerNameFilter = containerNameFilter
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ecs *Ecs) createLabelFilters() error {
|
||||
labelFilter, err := filter.NewIncludeExcludeFilter(ecs.LabelInclude, ecs.LabelExclude)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ecs.labelFilter = labelFilter
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ecs *Ecs) createContainerStatusFilters() error {
|
||||
if len(ecs.ContainerStatusInclude) == 0 && len(ecs.ContainerStatusExclude) == 0 {
|
||||
ecs.ContainerStatusInclude = []string{"RUNNING"}
|
||||
}
|
||||
|
||||
// ECS uses uppercase status names, normalizing for comparison.
|
||||
for i, include := range ecs.ContainerStatusInclude {
|
||||
ecs.ContainerStatusInclude[i] = strings.ToUpper(include)
|
||||
}
|
||||
for i, exclude := range ecs.ContainerStatusExclude {
|
||||
ecs.ContainerStatusExclude[i] = strings.ToUpper(exclude)
|
||||
}
|
||||
|
||||
statusFilter, err := filter.NewIncludeExcludeFilter(ecs.ContainerStatusInclude, ecs.ContainerStatusExclude)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ecs.statusFilter = statusFilter
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
inputs.Add("ecs", func() telegraf.Input {
|
||||
return &Ecs{
|
||||
EndpointURL: "",
|
||||
Timeout: config.Duration(5 * time.Second),
|
||||
newClient: newClient,
|
||||
filtersCreated: false,
|
||||
}
|
||||
})
|
||||
}
|
840
plugins/inputs/ecs/ecs_test.go
Normal file
840
plugins/inputs/ecs/ecs_test.go
Normal file
|
@ -0,0 +1,840 @@
|
|||
package ecs
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// codified golden objects for tests
|
||||
|
||||
// stats
|
||||
const pauseStatsKey = "e6af031b91deb3136a2b7c42f262ed2ab554e2fe2736998c7d8edf4afe708dba"
|
||||
const nginxStatsKey = "fffe894e232d46c76475cfeabf4907f712e8b92618a37fca3ef0805bbbfb0299"
|
||||
|
||||
var pauseStatsRead = mustParseNano("2018-11-19T15:40:00.936081344Z")
|
||||
var pauseStatsPreRead = mustParseNano("2018-11-19T15:39:59.933000984Z")
|
||||
|
||||
var nginxStatsRead = mustParseNano("2018-11-19T15:40:00.93733207Z")
|
||||
var nginxStatsPreRead = mustParseNano("2018-11-19T15:39:59.934291009Z")
|
||||
|
||||
func mustParseNano(value string) time.Time {
|
||||
t, err := time.Parse(time.RFC3339Nano, value)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
var validStats = map[string]*container.StatsResponse{
|
||||
pauseStatsKey: {
|
||||
Read: pauseStatsRead,
|
||||
PreRead: pauseStatsPreRead,
|
||||
BlkioStats: container.BlkioStats{
|
||||
IoServiceBytesRecursive: []container.BlkioStatEntry{
|
||||
{
|
||||
Major: 202,
|
||||
Minor: 26368,
|
||||
Op: "Read",
|
||||
Value: 790528,
|
||||
},
|
||||
{
|
||||
Major: 202,
|
||||
Minor: 26368,
|
||||
Op: "Write",
|
||||
},
|
||||
{
|
||||
Major: 202,
|
||||
Minor: 26368,
|
||||
Op: "Sync",
|
||||
Value: 790528,
|
||||
},
|
||||
{
|
||||
Major: 202,
|
||||
Minor: 26368,
|
||||
Op: "Async",
|
||||
},
|
||||
{
|
||||
Major: 202,
|
||||
Minor: 26368,
|
||||
Op: "Total",
|
||||
Value: 790528,
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 1,
|
||||
Op: "Read",
|
||||
Value: 790528,
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 1,
|
||||
Op: "Write",
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 1,
|
||||
Op: "Sync",
|
||||
Value: 790528,
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 1,
|
||||
Op: "Async",
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 1,
|
||||
Op: "Total",
|
||||
Value: 790528,
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 2,
|
||||
Op: "Read",
|
||||
Value: 790528,
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 2,
|
||||
Op: "Write",
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 2,
|
||||
Op: "Sync",
|
||||
Value: 790528,
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 2,
|
||||
Op: "Async",
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 2,
|
||||
Op: "Total",
|
||||
Value: 790528,
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 4,
|
||||
Op: "Read",
|
||||
Value: 790528,
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 4,
|
||||
Op: "Write",
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 4,
|
||||
Op: "Sync",
|
||||
Value: 790528,
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 4,
|
||||
Op: "Async",
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 4,
|
||||
Op: "Total",
|
||||
Value: 790528,
|
||||
},
|
||||
},
|
||||
IoServicedRecursive: []container.BlkioStatEntry{
|
||||
{
|
||||
Major: 202,
|
||||
Minor: 26368,
|
||||
Op: "Read",
|
||||
Value: 10,
|
||||
},
|
||||
{
|
||||
Major: 202,
|
||||
Minor: 26368,
|
||||
Op: "Write",
|
||||
},
|
||||
{
|
||||
Major: 202,
|
||||
Minor: 26368,
|
||||
Op: "Sync",
|
||||
Value: 10,
|
||||
},
|
||||
{
|
||||
Major: 202,
|
||||
Minor: 26368,
|
||||
Op: "Async",
|
||||
},
|
||||
{
|
||||
Major: 202,
|
||||
Minor: 26368,
|
||||
Op: "Total",
|
||||
Value: 10,
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 1,
|
||||
Op: "Read",
|
||||
Value: 10,
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 1,
|
||||
Op: "Write",
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 1,
|
||||
Op: "Sync",
|
||||
Value: 10,
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 1,
|
||||
Op: "Async",
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 1,
|
||||
Op: "Total",
|
||||
Value: 10,
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 2,
|
||||
Op: "Read",
|
||||
Value: 10,
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 2,
|
||||
Op: "Write",
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 2,
|
||||
Op: "Sync",
|
||||
Value: 10,
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 2,
|
||||
Op: "Async",
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 2,
|
||||
Op: "Total",
|
||||
Value: 10,
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 4,
|
||||
Op: "Read",
|
||||
Value: 10,
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 4,
|
||||
Op: "Write",
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 4,
|
||||
Op: "Sync",
|
||||
Value: 10,
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 4,
|
||||
Op: "Async",
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 4,
|
||||
Op: "Total",
|
||||
Value: 10,
|
||||
},
|
||||
},
|
||||
},
|
||||
CPUStats: container.CPUStats{
|
||||
CPUUsage: container.CPUUsage{
|
||||
PercpuUsage: []uint64{
|
||||
26426156,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
},
|
||||
UsageInUsermode: 20000000,
|
||||
TotalUsage: 26426156,
|
||||
},
|
||||
SystemUsage: 2336100000000,
|
||||
OnlineCPUs: 1,
|
||||
ThrottlingData: container.ThrottlingData{},
|
||||
},
|
||||
PreCPUStats: container.CPUStats{
|
||||
CPUUsage: container.CPUUsage{
|
||||
PercpuUsage: []uint64{
|
||||
26426156,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
},
|
||||
UsageInUsermode: 20000000,
|
||||
TotalUsage: 26426156,
|
||||
},
|
||||
SystemUsage: 2335090000000,
|
||||
OnlineCPUs: 1,
|
||||
ThrottlingData: container.ThrottlingData{},
|
||||
},
|
||||
MemoryStats: container.MemoryStats{
|
||||
Stats: map[string]uint64{
|
||||
"cache": 790528,
|
||||
"mapped_file": 618496,
|
||||
"total_inactive_file": 782336,
|
||||
"pgpgout": 1040,
|
||||
"rss": 40960,
|
||||
"total_mapped_file": 618496,
|
||||
"pgpgin": 1243,
|
||||
"pgmajfault": 6,
|
||||
"total_rss": 40960,
|
||||
"hierarchical_memory_limit": 536870912,
|
||||
"total_pgfault": 1298,
|
||||
"total_active_file": 8192,
|
||||
"active_anon": 40960,
|
||||
"total_active_anon": 40960,
|
||||
"total_pgpgout": 1040,
|
||||
"total_cache": 790528,
|
||||
"active_file": 8192,
|
||||
"pgfault": 1298,
|
||||
"inactive_file": 782336,
|
||||
"total_pgpgin": 1243,
|
||||
"hierarchical_memsw_limit": 9223372036854772000,
|
||||
},
|
||||
MaxUsage: 4825088,
|
||||
Usage: 1343488,
|
||||
Limit: 1033658368,
|
||||
},
|
||||
Networks: map[string]container.NetworkStats{
|
||||
"eth0": {
|
||||
RxBytes: uint64(5338),
|
||||
RxDropped: uint64(0),
|
||||
RxErrors: uint64(0),
|
||||
RxPackets: uint64(36),
|
||||
TxBytes: uint64(648),
|
||||
TxDropped: uint64(0),
|
||||
TxErrors: uint64(0),
|
||||
TxPackets: uint64(8),
|
||||
},
|
||||
"eth5": {
|
||||
RxBytes: uint64(4641),
|
||||
RxDropped: uint64(0),
|
||||
RxErrors: uint64(0),
|
||||
RxPackets: uint64(26),
|
||||
TxBytes: uint64(690),
|
||||
TxDropped: uint64(0),
|
||||
TxErrors: uint64(0),
|
||||
TxPackets: uint64(9),
|
||||
},
|
||||
},
|
||||
},
|
||||
nginxStatsKey: {
|
||||
Read: nginxStatsRead,
|
||||
PreRead: nginxStatsPreRead,
|
||||
BlkioStats: container.BlkioStats{
|
||||
IoServiceBytesRecursive: []container.BlkioStatEntry{
|
||||
{
|
||||
Major: 202,
|
||||
Minor: 26368,
|
||||
Op: "Read",
|
||||
Value: 5730304,
|
||||
},
|
||||
{
|
||||
Major: 202,
|
||||
Minor: 26368,
|
||||
Op: "Write",
|
||||
},
|
||||
{
|
||||
Major: 202,
|
||||
Minor: 26368,
|
||||
Op: "Sync",
|
||||
Value: 5730304,
|
||||
},
|
||||
{
|
||||
Major: 202,
|
||||
Minor: 26368,
|
||||
Op: "Async",
|
||||
},
|
||||
{
|
||||
Major: 202,
|
||||
Minor: 26368,
|
||||
Op: "Total",
|
||||
Value: 5730304,
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 1,
|
||||
Op: "Read",
|
||||
Value: 5730304,
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 1,
|
||||
Op: "Write",
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 1,
|
||||
Op: "Sync",
|
||||
Value: 5730304,
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 1,
|
||||
Op: "Async",
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 1,
|
||||
Op: "Total",
|
||||
Value: 5730304,
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 2,
|
||||
Op: "Read",
|
||||
Value: 5730304,
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 2,
|
||||
Op: "Write",
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 2,
|
||||
Op: "Sync",
|
||||
Value: 5730304,
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 2,
|
||||
Op: "Async",
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 2,
|
||||
Op: "Total",
|
||||
Value: 5730304,
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 5,
|
||||
Op: "Read",
|
||||
Value: 5730304,
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 5,
|
||||
Op: "Write",
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 5,
|
||||
Op: "Sync",
|
||||
Value: 5730304,
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 5,
|
||||
Op: "Async",
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 5,
|
||||
Op: "Total",
|
||||
Value: 5730304,
|
||||
},
|
||||
},
|
||||
IoServicedRecursive: []container.BlkioStatEntry{
|
||||
{
|
||||
Major: 202,
|
||||
Minor: 26368,
|
||||
Op: "Read",
|
||||
Value: 156,
|
||||
},
|
||||
{
|
||||
Major: 202,
|
||||
Minor: 26368,
|
||||
Op: "Write",
|
||||
},
|
||||
{
|
||||
Major: 202,
|
||||
Minor: 26368,
|
||||
Op: "Sync",
|
||||
Value: 156,
|
||||
},
|
||||
{
|
||||
Major: 202,
|
||||
Minor: 26368,
|
||||
Op: "Async",
|
||||
},
|
||||
{
|
||||
Major: 202,
|
||||
Minor: 26368,
|
||||
Op: "Total",
|
||||
Value: 156,
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 1,
|
||||
Op: "Read",
|
||||
Value: 156,
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 1,
|
||||
Op: "Write",
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 1,
|
||||
Op: "Sync",
|
||||
Value: 156,
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 1,
|
||||
Op: "Async",
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 1,
|
||||
Op: "Total",
|
||||
Value: 156,
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 2,
|
||||
Op: "Read",
|
||||
Value: 156,
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 2,
|
||||
Op: "Write",
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 2,
|
||||
Op: "Sync",
|
||||
Value: 156,
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 2,
|
||||
Op: "Async",
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 2,
|
||||
Op: "Total",
|
||||
Value: 156,
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 5,
|
||||
Op: "Read",
|
||||
Value: 147,
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 5,
|
||||
Op: "Write",
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 5,
|
||||
Op: "Sync",
|
||||
Value: 147,
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 5,
|
||||
Op: "Async",
|
||||
},
|
||||
{
|
||||
Major: 253,
|
||||
Minor: 5,
|
||||
Op: "Total",
|
||||
Value: 147,
|
||||
},
|
||||
},
|
||||
},
|
||||
CPUStats: container.CPUStats{
|
||||
CPUUsage: container.CPUUsage{
|
||||
PercpuUsage: []uint64{
|
||||
65599511,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
},
|
||||
UsageInUsermode: 40000000,
|
||||
TotalUsage: 65599511,
|
||||
UsageInKernelmode: 10000000,
|
||||
},
|
||||
SystemUsage: 2336100000000,
|
||||
OnlineCPUs: 1,
|
||||
ThrottlingData: container.ThrottlingData{},
|
||||
},
|
||||
PreCPUStats: container.CPUStats{
|
||||
CPUUsage: container.CPUUsage{
|
||||
PercpuUsage: []uint64{
|
||||
65599511,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
},
|
||||
UsageInUsermode: 40000000,
|
||||
TotalUsage: 65599511,
|
||||
UsageInKernelmode: 10000000,
|
||||
},
|
||||
SystemUsage: 2335090000000,
|
||||
OnlineCPUs: 1,
|
||||
ThrottlingData: container.ThrottlingData{},
|
||||
},
|
||||
MemoryStats: container.MemoryStats{
|
||||
Stats: map[string]uint64{
|
||||
"cache": 5787648,
|
||||
"mapped_file": 3616768,
|
||||
"total_inactive_file": 4321280,
|
||||
"pgpgout": 1674,
|
||||
"rss": 1597440,
|
||||
"total_mapped_file": 3616768,
|
||||
"pgpgin": 3477,
|
||||
"pgmajfault": 40,
|
||||
"total_rss": 1597440,
|
||||
"total_inactive_anon": 4096,
|
||||
"hierarchical_memory_limit": 536870912,
|
||||
"total_pgfault": 2924,
|
||||
"total_active_file": 1462272,
|
||||
"active_anon": 1597440,
|
||||
"total_active_anon": 1597440,
|
||||
"total_pgpgout": 1674,
|
||||
"total_cache": 5787648,
|
||||
"inactive_anon": 4096,
|
||||
"active_file": 1462272,
|
||||
"pgfault": 2924,
|
||||
"inactive_file": 4321280,
|
||||
"total_pgpgin": 3477,
|
||||
"hierarchical_memsw_limit": 9223372036854772000,
|
||||
},
|
||||
MaxUsage: 8667136,
|
||||
Usage: 8179712,
|
||||
Limit: 1033658368,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// meta
|
||||
var metaPauseCreated = mustParseNano("2018-11-19T15:31:26.641964373Z")
|
||||
var metaPauseStarted = mustParseNano("2018-11-19T15:31:27.035698679Z")
|
||||
var metaCreated = mustParseNano("2018-11-19T15:31:27.614884084Z")
|
||||
var metaStarted = mustParseNano("2018-11-19T15:31:27.975996351Z")
|
||||
var metaPullStart = mustParseNano("2018-11-19T15:31:27.197327103Z")
|
||||
var metaPullStop = mustParseNano("2018-11-19T15:31:27.609089471Z")
|
||||
|
||||
var validMeta = ecsTask{
|
||||
Cluster: "test",
|
||||
TaskARN: "arn:aws:ecs:aws-region-1:012345678901:task/a1234abc-a0a0-0a01-ab01-0abc012a0a0a",
|
||||
Family: "nginx",
|
||||
Revision: "2",
|
||||
DesiredStatus: "RUNNING",
|
||||
KnownStatus: "RUNNING",
|
||||
Containers: []ecsContainer{
|
||||
{
|
||||
ID: pauseStatsKey,
|
||||
Name: "~internal~ecs~pause",
|
||||
DockerName: "ecs-nginx-2-internalecspause",
|
||||
Image: "amazon/amazon-ecs-pause:0.1.0",
|
||||
ImageID: "",
|
||||
Labels: map[string]string{
|
||||
"com.amazonaws.ecs.cluster": "test",
|
||||
"com.amazonaws.ecs.container-name": "~internal~ecs~pause",
|
||||
"com.amazonaws.ecs.task-arn": "arn:aws:ecs:aws-region-1:012345678901:task/a1234abc-a0a0-0a01-ab01-0abc012a0a0a",
|
||||
"com.amazonaws.ecs.task-definition-family": "nginx",
|
||||
"com.amazonaws.ecs.task-definition-version": "2",
|
||||
},
|
||||
DesiredStatus: "RESOURCES_PROVISIONED",
|
||||
KnownStatus: "RESOURCES_PROVISIONED",
|
||||
Limits: map[string]float64{
|
||||
"CPU": 0,
|
||||
"Memory": 0,
|
||||
},
|
||||
CreatedAt: metaPauseCreated,
|
||||
StartedAt: metaPauseStarted,
|
||||
Type: "CNI_PAUSE",
|
||||
Networks: []network{
|
||||
{
|
||||
NetworkMode: "awsvpc",
|
||||
IPv4Addresses: []string{
|
||||
"172.31.25.181",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: nginxStatsKey,
|
||||
Name: "nginx",
|
||||
DockerName: "ecs-nginx-2-nginx",
|
||||
Image: "nginx:alpine",
|
||||
ImageID: "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
Labels: map[string]string{
|
||||
"com.amazonaws.ecs.cluster": "test",
|
||||
"com.amazonaws.ecs.container-name": "nginx",
|
||||
"com.amazonaws.ecs.task-arn": "arn:aws:ecs:aws-region-1:012345678901:task/a1234abc-a0a0-0a01-ab01-0abc012a0a0a",
|
||||
"com.amazonaws.ecs.task-definition-family": "nginx",
|
||||
"com.amazonaws.ecs.task-definition-version": "2",
|
||||
},
|
||||
DesiredStatus: "RUNNING",
|
||||
KnownStatus: "RUNNING",
|
||||
Limits: map[string]float64{
|
||||
"CPU": 0,
|
||||
"Memory": 0,
|
||||
},
|
||||
CreatedAt: metaCreated,
|
||||
StartedAt: metaStarted,
|
||||
Type: "NORMAL",
|
||||
Networks: []network{
|
||||
{
|
||||
NetworkMode: "awsvpc",
|
||||
IPv4Addresses: []string{
|
||||
"172.31.25.181",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Limits: map[string]float64{
|
||||
"CPU": 0.5,
|
||||
"Memory": 512,
|
||||
},
|
||||
PullStartedAt: metaPullStart,
|
||||
PullStoppedAt: metaPullStop,
|
||||
}
|
||||
|
||||
func TestResolveEndpoint(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
given Ecs
|
||||
exp Ecs
|
||||
setEnv func(*testing.T)
|
||||
}{
|
||||
{
|
||||
name: "Endpoint is explicitly set => use v2 metadata",
|
||||
given: Ecs{
|
||||
EndpointURL: "192.162.0.1/custom_endpoint",
|
||||
},
|
||||
exp: Ecs{
|
||||
EndpointURL: "192.162.0.1/custom_endpoint",
|
||||
metadataVersion: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Endpoint is not set, ECS_CONTAINER_METADATA_URI is not set => use v2 metadata",
|
||||
given: Ecs{
|
||||
EndpointURL: "",
|
||||
},
|
||||
exp: Ecs{
|
||||
EndpointURL: v2Endpoint,
|
||||
metadataVersion: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Endpoint is not set, ECS_CONTAINER_METADATA_URI is set => use v3 metadata",
|
||||
setEnv: func(t *testing.T) {
|
||||
t.Setenv("ECS_CONTAINER_METADATA_URI", "v3-endpoint.local")
|
||||
},
|
||||
given: Ecs{
|
||||
EndpointURL: "",
|
||||
},
|
||||
exp: Ecs{
|
||||
EndpointURL: "v3-endpoint.local",
|
||||
metadataVersion: 3,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Endpoint is not set, ECS_CONTAINER_METADATA_URI_V4 is set => use v4 metadata",
|
||||
setEnv: func(t *testing.T) {
|
||||
t.Setenv("ECS_CONTAINER_METADATA_URI_V4", "v4-endpoint.local")
|
||||
},
|
||||
given: Ecs{
|
||||
EndpointURL: "",
|
||||
},
|
||||
exp: Ecs{
|
||||
EndpointURL: "v4-endpoint.local",
|
||||
metadataVersion: 4,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.setEnv != nil {
|
||||
tt.setEnv(t)
|
||||
}
|
||||
|
||||
act := tt.given
|
||||
resolveEndpoint(&act)
|
||||
require.Equal(t, tt.exp, act)
|
||||
})
|
||||
}
|
||||
}
|
26
plugins/inputs/ecs/sample.conf
Normal file
26
plugins/inputs/ecs/sample.conf
Normal file
|
@ -0,0 +1,26 @@
|
|||
# Read metrics about ECS containers
|
||||
[[inputs.ecs]]
|
||||
## ECS metadata url.
|
||||
## Metadata v2 API is used if set explicitly. Otherwise,
|
||||
## v3 metadata endpoint API is used if available.
|
||||
# endpoint_url = ""
|
||||
|
||||
## Containers to include and exclude. Globs accepted.
|
||||
## Note that an empty array for both will include all containers
|
||||
# container_name_include = []
|
||||
# container_name_exclude = []
|
||||
|
||||
## Container states to include and exclude. Globs accepted.
|
||||
## When empty only containers in the "RUNNING" state will be captured.
|
||||
## Possible values are "NONE", "PULLED", "CREATED", "RUNNING",
|
||||
## "RESOURCES_PROVISIONED", "STOPPED".
|
||||
# container_status_include = []
|
||||
# container_status_exclude = []
|
||||
|
||||
## ecs labels to include and exclude as tags. Globs accepted.
|
||||
## Note that an empty array for both will include all labels as tags
|
||||
ecs_label_include = [ "com.amazonaws.ecs.*" ]
|
||||
ecs_label_exclude = []
|
||||
|
||||
## Timeout for queries.
|
||||
# timeout = "5s"
|
295
plugins/inputs/ecs/stats.go
Normal file
295
plugins/inputs/ecs/stats.go
Normal file
|
@ -0,0 +1,295 @@
|
|||
package ecs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
"github.com/influxdata/telegraf/plugins/common/docker"
|
||||
)
|
||||
|
||||
func parseContainerStats(c *ecsContainer, acc telegraf.Accumulator, tags map[string]string) {
|
||||
id := c.ID
|
||||
stats := &c.Stats
|
||||
tm := stats.Read
|
||||
|
||||
if tm.Before(time.Unix(0, 0)) {
|
||||
tm = time.Now()
|
||||
}
|
||||
|
||||
metastats(id, c, acc, tags, tm)
|
||||
memstats(id, stats, acc, tags, tm)
|
||||
cpustats(id, stats, acc, tags, tm)
|
||||
netstats(id, stats, acc, tags, tm)
|
||||
blkstats(id, stats, acc, tags, tm)
|
||||
}
|
||||
|
||||
func metastats(id string, c *ecsContainer, acc telegraf.Accumulator, tags map[string]string, tm time.Time) {
|
||||
metafields := map[string]interface{}{
|
||||
"container_id": id,
|
||||
"docker_name": c.DockerName,
|
||||
"image": c.Image,
|
||||
"image_id": c.ImageID,
|
||||
"desired_status": c.DesiredStatus,
|
||||
"known_status": c.KnownStatus,
|
||||
"limit_cpu": c.Limits["CPU"],
|
||||
"limit_mem": c.Limits["Memory"],
|
||||
"created_at": c.CreatedAt,
|
||||
"started_at": c.StartedAt,
|
||||
"type": c.Type,
|
||||
}
|
||||
|
||||
acc.AddFields("ecs_container_meta", metafields, tags, tm)
|
||||
}
|
||||
|
||||
func memstats(id string, stats *container.StatsResponse, acc telegraf.Accumulator, tags map[string]string, tm time.Time) {
|
||||
memfields := map[string]interface{}{
|
||||
"container_id": id,
|
||||
}
|
||||
|
||||
memstats := []string{
|
||||
"active_anon",
|
||||
"active_file",
|
||||
"cache",
|
||||
"hierarchical_memory_limit",
|
||||
"inactive_anon",
|
||||
"inactive_file",
|
||||
"mapped_file",
|
||||
"pgfault",
|
||||
"pgmajfault",
|
||||
"pgpgin",
|
||||
"pgpgout",
|
||||
"rss",
|
||||
"rss_huge",
|
||||
"total_active_anon",
|
||||
"total_active_file",
|
||||
"total_cache",
|
||||
"total_inactive_anon",
|
||||
"total_inactive_file",
|
||||
"total_mapped_file",
|
||||
"total_pgfault",
|
||||
"total_pgmajfault",
|
||||
"total_pgpgin",
|
||||
"total_pgpgout",
|
||||
"total_rss",
|
||||
"total_rss_huge",
|
||||
"total_unevictable",
|
||||
"total_writeback",
|
||||
"unevictable",
|
||||
"writeback",
|
||||
}
|
||||
|
||||
for _, field := range memstats {
|
||||
if value, ok := stats.MemoryStats.Stats[field]; ok {
|
||||
memfields[field] = value
|
||||
}
|
||||
}
|
||||
if stats.MemoryStats.Failcnt != 0 {
|
||||
memfields["fail_count"] = stats.MemoryStats.Failcnt
|
||||
}
|
||||
|
||||
memfields["limit"] = stats.MemoryStats.Limit
|
||||
memfields["max_usage"] = stats.MemoryStats.MaxUsage
|
||||
|
||||
mem := docker.CalculateMemUsageUnixNoCache(stats.MemoryStats)
|
||||
memLimit := float64(stats.MemoryStats.Limit)
|
||||
memfields["usage"] = uint64(mem)
|
||||
memfields["usage_percent"] = docker.CalculateMemPercentUnixNoCache(memLimit, mem)
|
||||
|
||||
acc.AddFields("ecs_container_mem", memfields, tags, tm)
|
||||
}
|
||||
|
||||
func cpustats(id string, stats *container.StatsResponse, acc telegraf.Accumulator, tags map[string]string, tm time.Time) {
|
||||
cpufields := map[string]interface{}{
|
||||
"usage_total": stats.CPUStats.CPUUsage.TotalUsage,
|
||||
"usage_in_usermode": stats.CPUStats.CPUUsage.UsageInUsermode,
|
||||
"usage_in_kernelmode": stats.CPUStats.CPUUsage.UsageInKernelmode,
|
||||
"usage_system": stats.CPUStats.SystemUsage,
|
||||
"throttling_periods": stats.CPUStats.ThrottlingData.Periods,
|
||||
"throttling_throttled_periods": stats.CPUStats.ThrottlingData.ThrottledPeriods,
|
||||
"throttling_throttled_time": stats.CPUStats.ThrottlingData.ThrottledTime,
|
||||
"container_id": id,
|
||||
}
|
||||
|
||||
previousCPU := stats.PreCPUStats.CPUUsage.TotalUsage
|
||||
previousSystem := stats.PreCPUStats.SystemUsage
|
||||
cpuPercent := docker.CalculateCPUPercentUnix(previousCPU, previousSystem, stats)
|
||||
cpufields["usage_percent"] = cpuPercent
|
||||
|
||||
cputags := copyTags(tags)
|
||||
cputags["cpu"] = "cpu-total"
|
||||
acc.AddFields("ecs_container_cpu", cpufields, cputags, tm)
|
||||
|
||||
// If we have OnlineCPUs field, then use it to restrict stats gathering to only Online CPUs
|
||||
// (https://github.com/moby/moby/commit/115f91d7575d6de6c7781a96a082f144fd17e400)
|
||||
var percpuusage []uint64
|
||||
if stats.CPUStats.OnlineCPUs > 0 && len(stats.CPUStats.CPUUsage.PercpuUsage) > 0 {
|
||||
percpuusage = stats.CPUStats.CPUUsage.PercpuUsage[:stats.CPUStats.OnlineCPUs]
|
||||
} else {
|
||||
percpuusage = stats.CPUStats.CPUUsage.PercpuUsage
|
||||
}
|
||||
|
||||
for i, percpu := range percpuusage {
|
||||
percputags := copyTags(tags)
|
||||
percputags["cpu"] = fmt.Sprintf("cpu%d", i)
|
||||
fields := map[string]interface{}{
|
||||
"usage_total": percpu,
|
||||
"container_id": id,
|
||||
}
|
||||
acc.AddFields("ecs_container_cpu", fields, percputags, tm)
|
||||
}
|
||||
}
|
||||
|
||||
func netstats(id string, stats *container.StatsResponse, acc telegraf.Accumulator, tags map[string]string, tm time.Time) {
|
||||
totalNetworkStatMap := make(map[string]interface{})
|
||||
for network, netstats := range stats.Networks {
|
||||
netfields := map[string]interface{}{
|
||||
"rx_dropped": netstats.RxDropped,
|
||||
"rx_bytes": netstats.RxBytes,
|
||||
"rx_errors": netstats.RxErrors,
|
||||
"tx_packets": netstats.TxPackets,
|
||||
"tx_dropped": netstats.TxDropped,
|
||||
"rx_packets": netstats.RxPackets,
|
||||
"tx_errors": netstats.TxErrors,
|
||||
"tx_bytes": netstats.TxBytes,
|
||||
"container_id": id,
|
||||
}
|
||||
|
||||
nettags := copyTags(tags)
|
||||
nettags["network"] = network
|
||||
acc.AddFields("ecs_container_net", netfields, nettags, tm)
|
||||
|
||||
for field, value := range netfields {
|
||||
if field == "container_id" {
|
||||
continue
|
||||
}
|
||||
|
||||
var uintV uint64
|
||||
switch v := value.(type) {
|
||||
case uint64:
|
||||
uintV = v
|
||||
case int64:
|
||||
uintV = uint64(v)
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
_, ok := totalNetworkStatMap[field]
|
||||
if ok {
|
||||
totalNetworkStatMap[field] = totalNetworkStatMap[field].(uint64) + uintV
|
||||
} else {
|
||||
totalNetworkStatMap[field] = uintV
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// totalNetworkStatMap could be empty if container is running with --net=host.
|
||||
if len(totalNetworkStatMap) != 0 {
|
||||
nettags := copyTags(tags)
|
||||
nettags["network"] = "total"
|
||||
totalNetworkStatMap["container_id"] = id
|
||||
acc.AddFields("ecs_container_net", totalNetworkStatMap, nettags, tm)
|
||||
}
|
||||
}
|
||||
|
||||
func blkstats(id string, stats *container.StatsResponse, acc telegraf.Accumulator, tags map[string]string, tm time.Time) {
|
||||
blkioStats := stats.BlkioStats
|
||||
// Make a map of devices to their block io stats
|
||||
deviceStatMap := make(map[string]map[string]interface{})
|
||||
|
||||
for _, metric := range blkioStats.IoServiceBytesRecursive {
|
||||
device := fmt.Sprintf("%d:%d", metric.Major, metric.Minor)
|
||||
_, ok := deviceStatMap[device]
|
||||
if !ok {
|
||||
deviceStatMap[device] = make(map[string]interface{})
|
||||
}
|
||||
|
||||
field := "io_service_bytes_recursive_" + strings.ToLower(metric.Op)
|
||||
deviceStatMap[device][field] = metric.Value
|
||||
}
|
||||
|
||||
for _, metric := range blkioStats.IoServicedRecursive {
|
||||
device := fmt.Sprintf("%d:%d", metric.Major, metric.Minor)
|
||||
_, ok := deviceStatMap[device]
|
||||
if !ok {
|
||||
deviceStatMap[device] = make(map[string]interface{})
|
||||
}
|
||||
|
||||
field := "io_serviced_recursive_" + strings.ToLower(metric.Op)
|
||||
deviceStatMap[device][field] = metric.Value
|
||||
}
|
||||
|
||||
for _, metric := range blkioStats.IoQueuedRecursive {
|
||||
device := fmt.Sprintf("%d:%d", metric.Major, metric.Minor)
|
||||
field := "io_queue_recursive_" + strings.ToLower(metric.Op)
|
||||
deviceStatMap[device][field] = metric.Value
|
||||
}
|
||||
|
||||
for _, metric := range blkioStats.IoServiceTimeRecursive {
|
||||
device := fmt.Sprintf("%d:%d", metric.Major, metric.Minor)
|
||||
field := "io_service_time_recursive_" + strings.ToLower(metric.Op)
|
||||
deviceStatMap[device][field] = metric.Value
|
||||
}
|
||||
|
||||
for _, metric := range blkioStats.IoWaitTimeRecursive {
|
||||
device := fmt.Sprintf("%d:%d", metric.Major, metric.Minor)
|
||||
field := "io_wait_time_" + strings.ToLower(metric.Op)
|
||||
deviceStatMap[device][field] = metric.Value
|
||||
}
|
||||
|
||||
for _, metric := range blkioStats.IoMergedRecursive {
|
||||
device := fmt.Sprintf("%d:%d", metric.Major, metric.Minor)
|
||||
field := "io_merged_recursive_" + strings.ToLower(metric.Op)
|
||||
deviceStatMap[device][field] = metric.Value
|
||||
}
|
||||
|
||||
for _, metric := range blkioStats.IoTimeRecursive {
|
||||
device := fmt.Sprintf("%d:%d", metric.Major, metric.Minor)
|
||||
deviceStatMap[device]["io_time_recursive"] = metric.Value
|
||||
}
|
||||
|
||||
for _, metric := range blkioStats.SectorsRecursive {
|
||||
device := fmt.Sprintf("%d:%d", metric.Major, metric.Minor)
|
||||
deviceStatMap[device]["sectors_recursive"] = metric.Value
|
||||
}
|
||||
|
||||
totalStatMap := make(map[string]interface{})
|
||||
for device, fields := range deviceStatMap {
|
||||
fields["container_id"] = id
|
||||
|
||||
iotags := copyTags(tags)
|
||||
iotags["device"] = device
|
||||
acc.AddFields("ecs_container_blkio", fields, iotags, tm)
|
||||
|
||||
for field, value := range fields {
|
||||
if field == "container_id" {
|
||||
continue
|
||||
}
|
||||
|
||||
var uintV uint64
|
||||
switch v := value.(type) {
|
||||
case uint64:
|
||||
uintV = v
|
||||
case int64:
|
||||
uintV = uint64(v)
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
_, ok := totalStatMap[field]
|
||||
if ok {
|
||||
totalStatMap[field] = totalStatMap[field].(uint64) + uintV
|
||||
} else {
|
||||
totalStatMap[field] = uintV
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
totalStatMap["container_id"] = id
|
||||
iotags := copyTags(tags)
|
||||
iotags["device"] = "total"
|
||||
acc.AddFields("ecs_container_blkio", totalStatMap, iotags, tm)
|
||||
}
|
226
plugins/inputs/ecs/stats_test.go
Normal file
226
plugins/inputs/ecs/stats_test.go
Normal file
|
@ -0,0 +1,226 @@
|
|||
package ecs
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/influxdata/telegraf/testutil"
|
||||
)
|
||||
|
||||
func Test_metastats(t *testing.T) {
|
||||
var mockAcc testutil.Accumulator
|
||||
|
||||
tags := map[string]string{
|
||||
"test_tag": "test",
|
||||
}
|
||||
tm := time.Now()
|
||||
|
||||
metastats(nginxStatsKey, &validMeta.Containers[1], &mockAcc, tags, tm)
|
||||
mockAcc.AssertContainsTaggedFields(
|
||||
t,
|
||||
"ecs_container_meta",
|
||||
map[string]interface{}{
|
||||
"container_id": nginxStatsKey,
|
||||
"docker_name": "ecs-nginx-2-nginx",
|
||||
"image": "nginx:alpine",
|
||||
"image_id": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"desired_status": "RUNNING",
|
||||
"known_status": "RUNNING",
|
||||
"limit_cpu": float64(0),
|
||||
"limit_mem": float64(0),
|
||||
"created_at": metaCreated,
|
||||
"started_at": metaStarted,
|
||||
"type": "NORMAL",
|
||||
},
|
||||
tags,
|
||||
)
|
||||
}
|
||||
|
||||
func Test_memstats(t *testing.T) {
|
||||
var mockAcc testutil.Accumulator
|
||||
|
||||
tags := map[string]string{
|
||||
"test_tag": "test",
|
||||
}
|
||||
tm := time.Now()
|
||||
|
||||
memstats(nginxStatsKey, validStats[nginxStatsKey], &mockAcc, tags, tm)
|
||||
mockAcc.AssertContainsTaggedFields(
|
||||
t,
|
||||
"ecs_container_mem",
|
||||
map[string]interface{}{
|
||||
"active_anon": uint64(1597440),
|
||||
"active_file": uint64(1462272),
|
||||
"cache": uint64(5787648),
|
||||
"container_id": nginxStatsKey,
|
||||
"hierarchical_memory_limit": uint64(536870912),
|
||||
"inactive_anon": uint64(4096),
|
||||
"inactive_file": uint64(4321280),
|
||||
"limit": uint64(1033658368),
|
||||
"mapped_file": uint64(3616768),
|
||||
"max_usage": uint64(8667136),
|
||||
"pgmajfault": uint64(40),
|
||||
"pgpgin": uint64(3477),
|
||||
"pgpgout": uint64(1674),
|
||||
"pgfault": uint64(2924),
|
||||
"rss": uint64(1597440),
|
||||
"total_active_anon": uint64(1597440),
|
||||
"total_active_file": uint64(1462272),
|
||||
"total_cache": uint64(5787648),
|
||||
"total_inactive_anon": uint64(4096),
|
||||
"total_inactive_file": uint64(4321280),
|
||||
"total_mapped_file": uint64(3616768),
|
||||
"total_pgfault": uint64(2924),
|
||||
"total_pgpgout": uint64(1674),
|
||||
"total_pgpgin": uint64(3477),
|
||||
"total_rss": uint64(1597440),
|
||||
"usage": uint64(2392064),
|
||||
"usage_percent": float64(0.23141727228778164),
|
||||
},
|
||||
map[string]string{
|
||||
"test_tag": "test",
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func Test_cpustats(t *testing.T) {
|
||||
var mockAcc testutil.Accumulator
|
||||
|
||||
tags := map[string]string{
|
||||
"test_tag": "test",
|
||||
}
|
||||
tm := time.Now()
|
||||
|
||||
cpustats(nginxStatsKey, validStats[nginxStatsKey], &mockAcc, tags, tm)
|
||||
mockAcc.AssertContainsTaggedFields(
|
||||
t,
|
||||
"ecs_container_cpu",
|
||||
map[string]interface{}{
|
||||
"container_id": nginxStatsKey,
|
||||
"throttling_periods": uint64(0),
|
||||
"throttling_throttled_periods": uint64(0),
|
||||
"throttling_throttled_time": uint64(0),
|
||||
"usage_in_usermode": uint64(40000000),
|
||||
"usage_in_kernelmode": uint64(10000000),
|
||||
"usage_percent": float64(0),
|
||||
"usage_system": uint64(2336100000000),
|
||||
"usage_total": uint64(65599511),
|
||||
},
|
||||
map[string]string{
|
||||
"test_tag": "test",
|
||||
"cpu": "cpu-total",
|
||||
},
|
||||
)
|
||||
mockAcc.AssertContainsTaggedFields(
|
||||
t,
|
||||
"ecs_container_cpu",
|
||||
map[string]interface{}{
|
||||
"container_id": nginxStatsKey,
|
||||
"usage_total": uint64(65599511),
|
||||
},
|
||||
map[string]string{
|
||||
"test_tag": "test",
|
||||
"cpu": "cpu0",
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func Test_netstats(t *testing.T) {
|
||||
var mockAcc testutil.Accumulator
|
||||
|
||||
tags := map[string]string{
|
||||
"test_tag": "test",
|
||||
}
|
||||
tm := time.Now()
|
||||
|
||||
netstats(pauseStatsKey, validStats[pauseStatsKey], &mockAcc, tags, tm)
|
||||
mockAcc.AssertContainsTaggedFields(
|
||||
t,
|
||||
"ecs_container_net",
|
||||
map[string]interface{}{
|
||||
"container_id": pauseStatsKey,
|
||||
"rx_bytes": uint64(5338),
|
||||
"rx_dropped": uint64(0),
|
||||
"rx_errors": uint64(0),
|
||||
"rx_packets": uint64(36),
|
||||
"tx_bytes": uint64(648),
|
||||
"tx_dropped": uint64(0),
|
||||
"tx_errors": uint64(0),
|
||||
"tx_packets": uint64(8),
|
||||
},
|
||||
map[string]string{
|
||||
"test_tag": "test",
|
||||
"network": "eth0",
|
||||
},
|
||||
)
|
||||
mockAcc.AssertContainsTaggedFields(
|
||||
t,
|
||||
"ecs_container_net",
|
||||
map[string]interface{}{
|
||||
"container_id": pauseStatsKey,
|
||||
"rx_bytes": uint64(4641),
|
||||
"rx_dropped": uint64(0),
|
||||
"rx_errors": uint64(0),
|
||||
"rx_packets": uint64(26),
|
||||
"tx_bytes": uint64(690),
|
||||
"tx_dropped": uint64(0),
|
||||
"tx_errors": uint64(0),
|
||||
"tx_packets": uint64(9),
|
||||
},
|
||||
map[string]string{
|
||||
"test_tag": "test",
|
||||
"network": "eth5",
|
||||
},
|
||||
)
|
||||
mockAcc.AssertContainsTaggedFields(
|
||||
t,
|
||||
"ecs_container_net",
|
||||
map[string]interface{}{
|
||||
"container_id": pauseStatsKey,
|
||||
"rx_bytes": uint64(9979),
|
||||
"rx_dropped": uint64(0),
|
||||
"rx_errors": uint64(0),
|
||||
"rx_packets": uint64(62),
|
||||
"tx_bytes": uint64(1338),
|
||||
"tx_dropped": uint64(0),
|
||||
"tx_errors": uint64(0),
|
||||
"tx_packets": uint64(17),
|
||||
},
|
||||
map[string]string{
|
||||
"test_tag": "test",
|
||||
"network": "total",
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func Test_blkstats(t *testing.T) {
|
||||
var mockAcc testutil.Accumulator
|
||||
|
||||
tags := map[string]string{
|
||||
"test_tag": "test",
|
||||
}
|
||||
tm := time.Now()
|
||||
|
||||
blkstats(nginxStatsKey, validStats[nginxStatsKey], &mockAcc, tags, tm)
|
||||
mockAcc.AssertContainsTaggedFields(
|
||||
t,
|
||||
"ecs_container_blkio",
|
||||
map[string]interface{}{
|
||||
"container_id": nginxStatsKey,
|
||||
"io_service_bytes_recursive_read": uint64(5730304),
|
||||
"io_service_bytes_recursive_write": uint64(0),
|
||||
"io_service_bytes_recursive_sync": uint64(5730304),
|
||||
"io_service_bytes_recursive_async": uint64(0),
|
||||
"io_service_bytes_recursive_total": uint64(5730304),
|
||||
"io_serviced_recursive_read": uint64(156),
|
||||
"io_serviced_recursive_write": uint64(0),
|
||||
"io_serviced_recursive_sync": uint64(156),
|
||||
"io_serviced_recursive_async": uint64(0),
|
||||
"io_serviced_recursive_total": uint64(156),
|
||||
},
|
||||
map[string]string{
|
||||
"test_tag": "test",
|
||||
"device": "202:26368",
|
||||
},
|
||||
)
|
||||
}
|
48
plugins/inputs/ecs/testdata/cgroupv2/meta.json
vendored
Normal file
48
plugins/inputs/ecs/testdata/cgroupv2/meta.json
vendored
Normal file
|
@ -0,0 +1,48 @@
|
|||
{
|
||||
"Cluster": "ecs-cluster-prod",
|
||||
"TaskARN": "arn:aws:ecs:eu-west-2:<account_id>:task/ecs-cluster-prod/a22cfc58cdb04b27ada2caa3fd526463",
|
||||
"Family": "nginx-telegraf",
|
||||
"Revision": "3",
|
||||
"DesiredStatus": "RUNNING",
|
||||
"KnownStatus": "RUNNING",
|
||||
"Containers": [
|
||||
{
|
||||
"DockerId": "c69461b2c836cc3f0e3e5deb07b1f16e25f6009da2a48bb0adc7dd580befaf55",
|
||||
"Name": "~internal~ecs~pause",
|
||||
"DockerName": "ecs-nginx-telegraf-3-internalecspause-f0cc8fe3bfc9a8efd601",
|
||||
"Image": "amazon/amazon-ecs-pause:0.1.0",
|
||||
"ImageID": "",
|
||||
"Labels": {
|
||||
"com.amazonaws.ecs.cluster": "ecs-cluster-prod",
|
||||
"com.amazonaws.ecs.container-name": "~internal~ecs~pause",
|
||||
"com.amazonaws.ecs.task-arn": "arn:aws:ecs:eu-west-2:<account_id>:task/ecs-cluster-prod/a22cfc58cdb04b27ada2caa3fd526463",
|
||||
"com.amazonaws.ecs.task-definition-family": "nginx-telegraf",
|
||||
"com.amazonaws.ecs.task-definition-version": "3"
|
||||
},
|
||||
"DesiredStatus": "RESOURCES_PROVISIONED",
|
||||
"KnownStatus": "RESOURCES_PROVISIONED",
|
||||
"Limits": {
|
||||
"CPU": 2,
|
||||
"Memory": 0
|
||||
},
|
||||
"CreatedAt": "2023-10-26T15:57:02.520698803Z",
|
||||
"StartedAt": "2023-10-26T15:57:02.806357646Z",
|
||||
"Type": "CNI_PAUSE",
|
||||
"Networks": [
|
||||
{
|
||||
"NetworkMode": "awsvpc",
|
||||
"IPv4Addresses": [
|
||||
"10.1.4.120"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"Limits": {
|
||||
"CPU": 0.5,
|
||||
"Memory": 1024
|
||||
},
|
||||
"PullStartedAt": "2023-10-26T15:57:03.528549391Z",
|
||||
"PullStoppedAt": "2023-10-26T15:57:18.310985676Z",
|
||||
"AvailabilityZone": "eu-west-2b"
|
||||
}
|
1
plugins/inputs/ecs/testdata/cgroupv2/meta.out
vendored
Normal file
1
plugins/inputs/ecs/testdata/cgroupv2/meta.out
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
ecs_container_meta,test_tag=test container_id="c69461b2c836cc3f0e3e5deb07b1f16e25f6009da2a48bb0adc7dd580befaf55",desired_status="RESOURCES_PROVISIONED",docker_name="ecs-nginx-telegraf-3-internalecspause-f0cc8fe3bfc9a8efd601",image="amazon/amazon-ecs-pause:0.1.0",image_id="",known_status="RESOURCES_PROVISIONED",limit_cpu=2,limit_mem=0,type="CNI_PAUSE" 1698343335413572143
|
101
plugins/inputs/ecs/testdata/cgroupv2/stats.json
vendored
Normal file
101
plugins/inputs/ecs/testdata/cgroupv2/stats.json
vendored
Normal file
|
@ -0,0 +1,101 @@
|
|||
{
|
||||
"c69461b2c836cc3f0e3e5deb07b1f16e25f6009da2a48bb0adc7dd580befaf55": {
|
||||
"read": "2023-10-26T15:58:32.690109934Z",
|
||||
"preread": "2023-10-26T15:58:31.642796089Z",
|
||||
"pids_stats": {
|
||||
"current": 1,
|
||||
"limit": 4618
|
||||
},
|
||||
"blkio_stats": {
|
||||
"io_service_bytes_recursive": null,
|
||||
"io_serviced_recursive": null,
|
||||
"io_queue_recursive": null,
|
||||
"io_service_time_recursive": null,
|
||||
"io_wait_time_recursive": null,
|
||||
"io_merged_recursive": null,
|
||||
"io_time_recursive": null,
|
||||
"sectors_recursive": null
|
||||
},
|
||||
"num_procs": 0,
|
||||
"storage_stats": {},
|
||||
"cpu_stats": {
|
||||
"cpu_usage": {
|
||||
"total_usage": 34818000,
|
||||
"usage_in_kernelmode": 5397000,
|
||||
"usage_in_usermode": 29420000
|
||||
},
|
||||
"system_cpu_usage": 16970500000000,
|
||||
"online_cpus": 2,
|
||||
"throttling_data": {
|
||||
"periods": 0,
|
||||
"throttled_periods": 0,
|
||||
"throttled_time": 0
|
||||
}
|
||||
},
|
||||
"precpu_stats": {
|
||||
"cpu_usage": {
|
||||
"total_usage": 34818000,
|
||||
"usage_in_kernelmode": 5397000,
|
||||
"usage_in_usermode": 29420000
|
||||
},
|
||||
"system_cpu_usage": 16968420000000,
|
||||
"online_cpus": 2,
|
||||
"throttling_data": {
|
||||
"periods": 0,
|
||||
"throttled_periods": 0,
|
||||
"throttled_time": 0
|
||||
}
|
||||
},
|
||||
"memory_stats": {
|
||||
"usage": 303104,
|
||||
"stats": {
|
||||
"active_anon": 4096,
|
||||
"active_file": 0,
|
||||
"anon": 45056,
|
||||
"anon_thp": 0,
|
||||
"file": 0,
|
||||
"file_dirty": 0,
|
||||
"file_mapped": 0,
|
||||
"file_writeback": 0,
|
||||
"inactive_anon": 40960,
|
||||
"inactive_file": 0,
|
||||
"kernel_stack": 16384,
|
||||
"pgactivate": 1,
|
||||
"pgdeactivate": 0,
|
||||
"pgfault": 1113,
|
||||
"pglazyfree": 0,
|
||||
"pglazyfreed": 0,
|
||||
"pgmajfault": 0,
|
||||
"pgrefill": 0,
|
||||
"pgscan": 0,
|
||||
"pgsteal": 0,
|
||||
"shmem": 0,
|
||||
"slab": 190768,
|
||||
"slab_reclaimable": 66320,
|
||||
"slab_unreclaimable": 124448,
|
||||
"sock": 0,
|
||||
"thp_collapse_alloc": 0,
|
||||
"thp_fault_alloc": 0,
|
||||
"unevictable": 0,
|
||||
"workingset_activate": 0,
|
||||
"workingset_nodereclaim": 0,
|
||||
"workingset_refault": 0
|
||||
},
|
||||
"limit": 4051226624
|
||||
},
|
||||
"name": "/ecs-nginx-telegraf-3-internalecspause-f0cc8fe3bfc9a8efd601",
|
||||
"id": "c69461b2c836cc3f0e3e5deb07b1f16e25f6009da2a48bb0adc7dd580befaf55",
|
||||
"networks": {
|
||||
"eth0": {
|
||||
"rx_bytes": 14,
|
||||
"rx_packets": 0,
|
||||
"rx_errors": 0,
|
||||
"rx_dropped": 0,
|
||||
"tx_bytes": 21,
|
||||
"tx_packets": 0,
|
||||
"tx_errors": 0,
|
||||
"tx_dropped": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
5
plugins/inputs/ecs/testdata/cgroupv2/stats.out
vendored
Normal file
5
plugins/inputs/ecs/testdata/cgroupv2/stats.out
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
ecs_container_mem,test_tag=test active_anon=4096u,active_file=0u,container_id="c69461b2c836cc3f0e3e5deb07b1f16e25f6009da2a48bb0adc7dd580befaf55",inactive_anon=40960u,inactive_file=0u,limit=4051226624u,max_usage=0u,pgfault=1113u,pgmajfault=0u,unevictable=0u,usage=303104u,usage_percent=0.00748178337406187 1698343335413563073
|
||||
ecs_container_cpu,cpu=cpu-total,test_tag=test container_id="c69461b2c836cc3f0e3e5deb07b1f16e25f6009da2a48bb0adc7dd580befaf55",throttling_periods=0u,throttling_throttled_periods=0u,throttling_throttled_time=0u,usage_in_kernelmode=5397000u,usage_in_usermode=29420000u,usage_percent=0,usage_system=16970500000000u,usage_total=34818000u 1698343335413566953
|
||||
ecs_container_net,network=eth0,test_tag=test container_id="c69461b2c836cc3f0e3e5deb07b1f16e25f6009da2a48bb0adc7dd580befaf55",rx_bytes=14u,rx_dropped=0u,rx_errors=0u,rx_packets=0u,tx_bytes=21u,tx_dropped=0u,tx_errors=0u,tx_packets=0u 1698343335413572143
|
||||
ecs_container_net,network=total,test_tag=test container_id="c69461b2c836cc3f0e3e5deb07b1f16e25f6009da2a48bb0adc7dd580befaf55",rx_bytes=14u,rx_dropped=0u,rx_errors=0u,rx_packets=0u,tx_bytes=21u,tx_dropped=0u,tx_errors=0u,tx_packets=0u 1698343335413572143
|
||||
ecs_container_blkio,device=total,test_tag=test container_id="c69461b2c836cc3f0e3e5deb07b1f16e25f6009da2a48bb0adc7dd580befaf55" 1698343335413575563
|
78
plugins/inputs/ecs/testdata/metadata.golden
vendored
Normal file
78
plugins/inputs/ecs/testdata/metadata.golden
vendored
Normal file
|
@ -0,0 +1,78 @@
|
|||
{
|
||||
"Cluster": "test",
|
||||
"TaskARN": "arn:aws:ecs:aws-region-1:012345678901:task/a1234abc-a0a0-0a01-ab01-0abc012a0a0a",
|
||||
"Family": "nginx",
|
||||
"Revision": "2",
|
||||
"DesiredStatus": "RUNNING",
|
||||
"KnownStatus": "RUNNING",
|
||||
"Containers": [
|
||||
{
|
||||
"DockerId": "e6af031b91deb3136a2b7c42f262ed2ab554e2fe2736998c7d8edf4afe708dba",
|
||||
"Name": "~internal~ecs~pause",
|
||||
"DockerName": "ecs-nginx-2-internalecspause",
|
||||
"Image": "amazon/amazon-ecs-pause:0.1.0",
|
||||
"ImageID": "",
|
||||
"Labels": {
|
||||
"com.amazonaws.ecs.cluster": "test",
|
||||
"com.amazonaws.ecs.container-name": "~internal~ecs~pause",
|
||||
"com.amazonaws.ecs.task-arn": "arn:aws:ecs:aws-region-1:012345678901:task/a1234abc-a0a0-0a01-ab01-0abc012a0a0a",
|
||||
"com.amazonaws.ecs.task-definition-family": "nginx",
|
||||
"com.amazonaws.ecs.task-definition-version": "2"
|
||||
},
|
||||
"DesiredStatus": "RESOURCES_PROVISIONED",
|
||||
"KnownStatus": "RESOURCES_PROVISIONED",
|
||||
"Limits": {
|
||||
"CPU": 0,
|
||||
"Memory": 0
|
||||
},
|
||||
"CreatedAt": "2018-11-19T15:31:26.641964373Z",
|
||||
"StartedAt": "2018-11-19T15:31:27.035698679Z",
|
||||
"Type": "CNI_PAUSE",
|
||||
"Networks": [
|
||||
{
|
||||
"NetworkMode": "awsvpc",
|
||||
"IPv4Addresses": [
|
||||
"172.31.25.181"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"DockerId": "fffe894e232d46c76475cfeabf4907f712e8b92618a37fca3ef0805bbbfb0299",
|
||||
"Name": "nginx",
|
||||
"DockerName": "ecs-nginx-2-nginx",
|
||||
"Image": "nginx:alpine",
|
||||
"ImageID": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"Labels": {
|
||||
"com.amazonaws.ecs.cluster": "test",
|
||||
"com.amazonaws.ecs.container-name": "nginx",
|
||||
"com.amazonaws.ecs.task-arn": "arn:aws:ecs:aws-region-1:012345678901:task/a1234abc-a0a0-0a01-ab01-0abc012a0a0a",
|
||||
"com.amazonaws.ecs.task-definition-family": "nginx",
|
||||
"com.amazonaws.ecs.task-definition-version": "2"
|
||||
},
|
||||
"DesiredStatus": "RUNNING",
|
||||
"KnownStatus": "RUNNING",
|
||||
"Limits": {
|
||||
"CPU": 0,
|
||||
"Memory": 0
|
||||
},
|
||||
"CreatedAt": "2018-11-19T15:31:27.614884084Z",
|
||||
"StartedAt": "2018-11-19T15:31:27.975996351Z",
|
||||
"Type": "NORMAL",
|
||||
"Networks": [
|
||||
{
|
||||
"NetworkMode": "awsvpc",
|
||||
"IPv4Addresses": [
|
||||
"172.31.25.181"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"Limits": {
|
||||
"CPU": 0.5,
|
||||
"Memory": 512
|
||||
},
|
||||
"PullStartedAt": "2018-11-19T15:31:27.197327103Z",
|
||||
"PullStoppedAt": "2018-11-19T15:31:27.609089471Z"
|
||||
}
|
663
plugins/inputs/ecs/testdata/stats.golden
vendored
Normal file
663
plugins/inputs/ecs/testdata/stats.golden
vendored
Normal file
|
@ -0,0 +1,663 @@
|
|||
{
|
||||
"e6af031b91deb3136a2b7c42f262ed2ab554e2fe2736998c7d8edf4afe708dba": {
|
||||
"read": "2018-11-19T15:40:00.936081344Z",
|
||||
"preread": "2018-11-19T15:39:59.933000984Z",
|
||||
"num_procs": 0,
|
||||
"pids_stats": {},
|
||||
"networks": {
|
||||
"eth0": {
|
||||
"rx_bytes": 5338,
|
||||
"rx_dropped": 0,
|
||||
"rx_errors": 0,
|
||||
"rx_packets": 36,
|
||||
"tx_bytes": 648,
|
||||
"tx_dropped": 0,
|
||||
"tx_errors": 0,
|
||||
"tx_packets": 8
|
||||
},
|
||||
"eth5": {
|
||||
"rx_bytes": 4641,
|
||||
"rx_dropped": 0,
|
||||
"rx_errors": 0,
|
||||
"rx_packets": 26,
|
||||
"tx_bytes": 690,
|
||||
"tx_dropped": 0,
|
||||
"tx_errors": 0,
|
||||
"tx_packets": 9
|
||||
}
|
||||
},
|
||||
"memory_stats": {
|
||||
"stats": {
|
||||
"cache": 790528,
|
||||
"mapped_file": 618496,
|
||||
"total_inactive_file": 782336,
|
||||
"pgpgout": 1040,
|
||||
"rss": 40960,
|
||||
"total_mapped_file": 618496,
|
||||
"pgpgin": 1243,
|
||||
"pgmajfault": 6,
|
||||
"total_rss": 40960,
|
||||
"hierarchical_memory_limit": 536870912,
|
||||
"total_pgfault": 1298,
|
||||
"total_active_file": 8192,
|
||||
"active_anon": 40960,
|
||||
"total_active_anon": 40960,
|
||||
"total_pgpgout": 1040,
|
||||
"total_cache": 790528,
|
||||
"active_file": 8192,
|
||||
"pgfault": 1298,
|
||||
"inactive_file": 782336,
|
||||
"total_pgpgin": 1243,
|
||||
"hierarchical_memsw_limit": 9223372036854772000
|
||||
},
|
||||
"max_usage": 4825088,
|
||||
"usage": 1343488,
|
||||
"limit": 1033658368
|
||||
},
|
||||
"blkio_stats": {
|
||||
"io_service_bytes_recursive": [
|
||||
{
|
||||
"major": 202,
|
||||
"minor": 26368,
|
||||
"op": "Read",
|
||||
"value": 790528
|
||||
},
|
||||
{
|
||||
"major": 202,
|
||||
"minor": 26368,
|
||||
"op": "Write"
|
||||
},
|
||||
{
|
||||
"major": 202,
|
||||
"minor": 26368,
|
||||
"op": "Sync",
|
||||
"value": 790528
|
||||
},
|
||||
{
|
||||
"major": 202,
|
||||
"minor": 26368,
|
||||
"op": "Async"
|
||||
},
|
||||
{
|
||||
"major": 202,
|
||||
"minor": 26368,
|
||||
"op": "Total",
|
||||
"value": 790528
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 1,
|
||||
"op": "Read",
|
||||
"value": 790528
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 1,
|
||||
"op": "Write"
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 1,
|
||||
"op": "Sync",
|
||||
"value": 790528
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 1,
|
||||
"op": "Async"
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 1,
|
||||
"op": "Total",
|
||||
"value": 790528
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 2,
|
||||
"op": "Read",
|
||||
"value": 790528
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 2,
|
||||
"op": "Write"
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 2,
|
||||
"op": "Sync",
|
||||
"value": 790528
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 2,
|
||||
"op": "Async"
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 2,
|
||||
"op": "Total",
|
||||
"value": 790528
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 4,
|
||||
"op": "Read",
|
||||
"value": 790528
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 4,
|
||||
"op": "Write"
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 4,
|
||||
"op": "Sync",
|
||||
"value": 790528
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 4,
|
||||
"op": "Async"
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 4,
|
||||
"op": "Total",
|
||||
"value": 790528
|
||||
}
|
||||
],
|
||||
"io_serviced_recursive": [
|
||||
{
|
||||
"major": 202,
|
||||
"minor": 26368,
|
||||
"op": "Read",
|
||||
"value": 10
|
||||
},
|
||||
{
|
||||
"major": 202,
|
||||
"minor": 26368,
|
||||
"op": "Write"
|
||||
},
|
||||
{
|
||||
"major": 202,
|
||||
"minor": 26368,
|
||||
"op": "Sync",
|
||||
"value": 10
|
||||
},
|
||||
{
|
||||
"major": 202,
|
||||
"minor": 26368,
|
||||
"op": "Async"
|
||||
},
|
||||
{
|
||||
"major": 202,
|
||||
"minor": 26368,
|
||||
"op": "Total",
|
||||
"value": 10
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 1,
|
||||
"op": "Read",
|
||||
"value": 10
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 1,
|
||||
"op": "Write"
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 1,
|
||||
"op": "Sync",
|
||||
"value": 10
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 1,
|
||||
"op": "Async"
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 1,
|
||||
"op": "Total",
|
||||
"value": 10
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 2,
|
||||
"op": "Read",
|
||||
"value": 10
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 2,
|
||||
"op": "Write"
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 2,
|
||||
"op": "Sync",
|
||||
"value": 10
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 2,
|
||||
"op": "Async"
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 2,
|
||||
"op": "Total",
|
||||
"value": 10
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 4,
|
||||
"op": "Read",
|
||||
"value": 10
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 4,
|
||||
"op": "Write"
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 4,
|
||||
"op": "Sync",
|
||||
"value": 10
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 4,
|
||||
"op": "Async"
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 4,
|
||||
"op": "Total",
|
||||
"value": 10
|
||||
}
|
||||
]
|
||||
},
|
||||
"cpu_stats": {
|
||||
"cpu_usage": {
|
||||
"percpu_usage": [
|
||||
26426156,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"usage_in_usermode": 20000000,
|
||||
"total_usage": 26426156
|
||||
},
|
||||
"system_cpu_usage": 2336100000000,
|
||||
"online_cpus": 1,
|
||||
"throttling_data": {}
|
||||
},
|
||||
"precpu_stats": {
|
||||
"cpu_usage": {
|
||||
"percpu_usage": [
|
||||
26426156,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"usage_in_usermode": 20000000,
|
||||
"total_usage": 26426156
|
||||
},
|
||||
"system_cpu_usage": 2335090000000,
|
||||
"online_cpus": 1,
|
||||
"throttling_data": {}
|
||||
},
|
||||
"storage_stats": {}
|
||||
},
|
||||
"fffe894e232d46c76475cfeabf4907f712e8b92618a37fca3ef0805bbbfb0299": {
|
||||
"read": "2018-11-19T15:40:00.93733207Z",
|
||||
"preread": "2018-11-19T15:39:59.934291009Z",
|
||||
"num_procs": 0,
|
||||
"pids_stats": {},
|
||||
"network": {},
|
||||
"memory_stats": {
|
||||
"stats": {
|
||||
"cache": 5787648,
|
||||
"mapped_file": 3616768,
|
||||
"total_inactive_file": 4321280,
|
||||
"pgpgout": 1674,
|
||||
"rss": 1597440,
|
||||
"total_mapped_file": 3616768,
|
||||
"pgpgin": 3477,
|
||||
"pgmajfault": 40,
|
||||
"total_rss": 1597440,
|
||||
"total_inactive_anon": 4096,
|
||||
"hierarchical_memory_limit": 536870912,
|
||||
"total_pgfault": 2924,
|
||||
"total_active_file": 1462272,
|
||||
"active_anon": 1597440,
|
||||
"total_active_anon": 1597440,
|
||||
"total_pgpgout": 1674,
|
||||
"total_cache": 5787648,
|
||||
"inactive_anon": 4096,
|
||||
"active_file": 1462272,
|
||||
"pgfault": 2924,
|
||||
"inactive_file": 4321280,
|
||||
"total_pgpgin": 3477,
|
||||
"hierarchical_memsw_limit": 9223372036854772000
|
||||
},
|
||||
"max_usage": 8667136,
|
||||
"usage": 8179712,
|
||||
"limit": 1033658368
|
||||
},
|
||||
"blkio_stats": {
|
||||
"io_service_bytes_recursive": [
|
||||
{
|
||||
"major": 202,
|
||||
"minor": 26368,
|
||||
"op": "Read",
|
||||
"value": 5730304
|
||||
},
|
||||
{
|
||||
"major": 202,
|
||||
"minor": 26368,
|
||||
"op": "Write"
|
||||
},
|
||||
{
|
||||
"major": 202,
|
||||
"minor": 26368,
|
||||
"op": "Sync",
|
||||
"value": 5730304
|
||||
},
|
||||
{
|
||||
"major": 202,
|
||||
"minor": 26368,
|
||||
"op": "Async"
|
||||
},
|
||||
{
|
||||
"major": 202,
|
||||
"minor": 26368,
|
||||
"op": "Total",
|
||||
"value": 5730304
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 1,
|
||||
"op": "Read",
|
||||
"value": 5730304
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 1,
|
||||
"op": "Write"
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 1,
|
||||
"op": "Sync",
|
||||
"value": 5730304
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 1,
|
||||
"op": "Async"
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 1,
|
||||
"op": "Total",
|
||||
"value": 5730304
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 2,
|
||||
"op": "Read",
|
||||
"value": 5730304
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 2,
|
||||
"op": "Write"
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 2,
|
||||
"op": "Sync",
|
||||
"value": 5730304
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 2,
|
||||
"op": "Async"
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 2,
|
||||
"op": "Total",
|
||||
"value": 5730304
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 5,
|
||||
"op": "Read",
|
||||
"value": 5730304
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 5,
|
||||
"op": "Write"
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 5,
|
||||
"op": "Sync",
|
||||
"value": 5730304
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 5,
|
||||
"op": "Async"
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 5,
|
||||
"op": "Total",
|
||||
"value": 5730304
|
||||
}
|
||||
],
|
||||
"io_serviced_recursive": [
|
||||
{
|
||||
"major": 202,
|
||||
"minor": 26368,
|
||||
"op": "Read",
|
||||
"value": 156
|
||||
},
|
||||
{
|
||||
"major": 202,
|
||||
"minor": 26368,
|
||||
"op": "Write"
|
||||
},
|
||||
{
|
||||
"major": 202,
|
||||
"minor": 26368,
|
||||
"op": "Sync",
|
||||
"value": 156
|
||||
},
|
||||
{
|
||||
"major": 202,
|
||||
"minor": 26368,
|
||||
"op": "Async"
|
||||
},
|
||||
{
|
||||
"major": 202,
|
||||
"minor": 26368,
|
||||
"op": "Total",
|
||||
"value": 156
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 1,
|
||||
"op": "Read",
|
||||
"value": 156
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 1,
|
||||
"op": "Write"
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 1,
|
||||
"op": "Sync",
|
||||
"value": 156
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 1,
|
||||
"op": "Async"
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 1,
|
||||
"op": "Total",
|
||||
"value": 156
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 2,
|
||||
"op": "Read",
|
||||
"value": 156
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 2,
|
||||
"op": "Write"
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 2,
|
||||
"op": "Sync",
|
||||
"value": 156
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 2,
|
||||
"op": "Async"
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 2,
|
||||
"op": "Total",
|
||||
"value": 156
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 5,
|
||||
"op": "Read",
|
||||
"value": 147
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 5,
|
||||
"op": "Write"
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 5,
|
||||
"op": "Sync",
|
||||
"value": 147
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 5,
|
||||
"op": "Async"
|
||||
},
|
||||
{
|
||||
"major": 253,
|
||||
"minor": 5,
|
||||
"op": "Total",
|
||||
"value": 147
|
||||
}
|
||||
]
|
||||
},
|
||||
"cpu_stats": {
|
||||
"cpu_usage": {
|
||||
"percpu_usage": [
|
||||
65599511,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"usage_in_usermode": 40000000,
|
||||
"total_usage": 65599511,
|
||||
"usage_in_kernelmode": 10000000
|
||||
},
|
||||
"system_cpu_usage": 2336100000000,
|
||||
"online_cpus": 1,
|
||||
"throttling_data": {}
|
||||
},
|
||||
"precpu_stats": {
|
||||
"cpu_usage": {
|
||||
"percpu_usage": [
|
||||
65599511,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"usage_in_usermode": 40000000,
|
||||
"total_usage": 65599511,
|
||||
"usage_in_kernelmode": 10000000
|
||||
},
|
||||
"system_cpu_usage": 2335090000000,
|
||||
"online_cpus": 1,
|
||||
"throttling_data": {}
|
||||
},
|
||||
"storage_stats": {}
|
||||
}
|
||||
}
|
78
plugins/inputs/ecs/types.go
Normal file
78
plugins/inputs/ecs/types.go
Normal file
|
@ -0,0 +1,78 @@
|
|||
package ecs
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
)
|
||||
|
||||
// ecsTask is the ECS task representation
|
||||
type ecsTask struct {
|
||||
Cluster string
|
||||
TaskARN string
|
||||
Family string
|
||||
Revision string
|
||||
DesiredStatus string
|
||||
KnownStatus string
|
||||
Containers []ecsContainer
|
||||
Limits map[string]float64
|
||||
PullStartedAt time.Time
|
||||
PullStoppedAt time.Time
|
||||
}
|
||||
|
||||
// ecsContainer is the ECS metadata container representation
|
||||
type ecsContainer struct {
|
||||
ID string `json:"DockerId"`
|
||||
Name string
|
||||
DockerName string
|
||||
Image string
|
||||
ImageID string
|
||||
Labels map[string]string
|
||||
DesiredStatus string
|
||||
KnownStatus string
|
||||
Limits map[string]float64
|
||||
CreatedAt time.Time
|
||||
StartedAt time.Time
|
||||
Stats container.StatsResponse
|
||||
Type string
|
||||
Networks []network
|
||||
}
|
||||
|
||||
// network is a docker network configuration
|
||||
type network struct {
|
||||
NetworkMode string
|
||||
IPv4Addresses []string
|
||||
}
|
||||
|
||||
func unmarshalTask(r io.Reader) (*ecsTask, error) {
|
||||
task := &ecsTask{}
|
||||
err := json.NewDecoder(r).Decode(task)
|
||||
return task, err
|
||||
}
|
||||
|
||||
// docker parsers
|
||||
func unmarshalStats(r io.Reader) (map[string]*container.StatsResponse, error) {
|
||||
var statsMap map[string]*container.StatsResponse
|
||||
if err := json.NewDecoder(r).Decode(&statsMap); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return statsMap, nil
|
||||
}
|
||||
|
||||
// interleaves Stats in to the Container objects in the ecsTask
|
||||
func mergeTaskStats(task *ecsTask, stats map[string]*container.StatsResponse) {
|
||||
for i := range task.Containers {
|
||||
c := &task.Containers[i]
|
||||
if strings.Trim(c.ID, " ") == "" {
|
||||
continue
|
||||
}
|
||||
stat, ok := stats[c.ID]
|
||||
if !ok || stat == nil {
|
||||
continue
|
||||
}
|
||||
c.Stats = *stat
|
||||
}
|
||||
}
|
47
plugins/inputs/ecs/types_test.go
Normal file
47
plugins/inputs/ecs/types_test.go
Normal file
|
@ -0,0 +1,47 @@
|
|||
package ecs
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_parseTask(t *testing.T) {
|
||||
r, err := os.Open("testdata/metadata.golden")
|
||||
require.NoError(t, err)
|
||||
|
||||
parsed, err := unmarshalTask(r)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, validMeta, *parsed)
|
||||
}
|
||||
|
||||
func Test_parseStats(t *testing.T) {
|
||||
r, err := os.Open("testdata/stats.golden")
|
||||
require.NoError(t, err)
|
||||
|
||||
parsed, err := unmarshalStats(r)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, validStats, parsed)
|
||||
}
|
||||
|
||||
func Test_mergeTaskStats(t *testing.T) {
|
||||
metadata, err := os.Open("testdata/metadata.golden")
|
||||
require.NoError(t, err)
|
||||
|
||||
parsedMetadata, err := unmarshalTask(metadata)
|
||||
require.NoError(t, err)
|
||||
|
||||
stats, err := os.Open("testdata/stats.golden")
|
||||
require.NoError(t, err)
|
||||
|
||||
parsedStats, err := unmarshalStats(stats)
|
||||
require.NoError(t, err)
|
||||
|
||||
mergeTaskStats(parsedMetadata, parsedStats)
|
||||
|
||||
for i := range parsedMetadata.Containers {
|
||||
require.Equal(t, validStats[parsedMetadata.Containers[i].ID], &parsedMetadata.Containers[i].Stats)
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue