Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add autodiscover for aws_ec2 #14823

Merged
merged 29 commits into from
Feb 6, 2020
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
3506757
Add autodiscover for aws_ec2
kaiyan-sheng Nov 27, 2019
07469cd
Add aws_ec2 autodiscover
kaiyan-sheng Nov 27, 2019
dfc6269
test with logp.Debug
kaiyan-sheng Nov 27, 2019
4d350ea
Merge remote-tracking branch 'upstream/master' into autodiscover_aws_ec2
kaiyan-sheng Dec 6, 2019
e5899f8
Add comment to DefaultConfig function
kaiyan-sheng Dec 6, 2019
fcdba2f
Merge remote-tracking branch 'upstream/master' into autodiscover_aws_ec2
kaiyan-sheng Dec 17, 2019
c042a08
Change ec2 metadata mapping
kaiyan-sheng Dec 17, 2019
5662544
Update changelog
kaiyan-sheng Dec 17, 2019
a4e7f4f
Update changelog
kaiyan-sheng Dec 17, 2019
580c13a
change instanceId to instanceID
kaiyan-sheng Dec 17, 2019
f06521d
Collect from all regions if regions is not specified
kaiyan-sheng Dec 18, 2019
3a5fa25
Add GetRegions into aws_elb provider
kaiyan-sheng Dec 18, 2019
fef6fde
Add aws_ec2 provider into autodiscover doc
kaiyan-sheng Dec 19, 2019
543e18a
update autodiscover doc
kaiyan-sheng Dec 19, 2019
4605f1f
try to fix doc
kaiyan-sheng Dec 19, 2019
556830a
Add unit tests
kaiyan-sheng Dec 19, 2019
0f7afa6
add comments
kaiyan-sheng Dec 19, 2019
6af9ca2
Fix metricbeat doc
kaiyan-sheng Dec 19, 2019
672c42b
Make houndci happy
kaiyan-sheng Dec 19, 2019
cd459b4
Merge remote-tracking branch 'upstream/master' into autodiscover_aws_ec2
kaiyan-sheng Jan 3, 2020
397dd24
Merge remote-tracking branch 'upstream/master' into autodiscover_aws_ec2
kaiyan-sheng Jan 8, 2020
6d13377
Merge remote-tracking branch 'upstream/master' into autodiscover_aws_ec2
kaiyan-sheng Jan 24, 2020
780a34a
Merge remote-tracking branch 'upstream/master' into autodiscover_aws_ec2
kaiyan-sheng Feb 3, 2020
8e70d89
update changelog
kaiyan-sheng Feb 3, 2020
eb0d83f
update changelog
kaiyan-sheng Feb 3, 2020
6505d1a
Merge remote-tracking branch 'upstream/master' into autodiscover_aws_ec2
kaiyan-sheng Feb 5, 2020
48f121c
Add aws prefix to meta ec2
kaiyan-sheng Feb 6, 2020
656379e
update field names from ec2 to aws.ec2
kaiyan-sheng Feb 6, 2020
286d2ad
Add aws.ec2.* to autodiscover template
kaiyan-sheng Feb 6, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.next.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d
*Affecting all Beats*

- Add document_id setting to decode_json_fields processor. {pull}15859[15859]

- Add `aws_ec2` provider for autodiscover. {issue}12518[12518] {pull}14823[14823]

*Auditbeat*

Expand Down
45 changes: 45 additions & 0 deletions libbeat/docs/shared-autodiscover.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,51 @@ ifdef::autodiscoverHints[]
include::../../{beatname_lc}/docs/autodiscover-hints.asciidoc[]
endif::autodiscoverHints[]

ifdef::autodiscoverAWSEC2[]
[float]
===== Amazon EC2s

*Note: This provider is experimental*

The Amazon EC2 autodiscover provider discovers https://aws.amazon.com/ec2/[EC2 instances].
This is useful for users to launch Metricbeat modules to monitor services running on AWS EC2 instances.
For example, to gather MySQL metrics from mysql servers running on EC2 instances with specific tag `service: mysql`.

This provider will load AWS credentials using the standard AWS environment variables and shared credentials files
see https://docs.aws.amazon.com/general/latest/gr/aws-access-keys-best-practices.html[Best Practices for Managing AWS Access Keys]
for more information. If you do not wish to use these, you may explicitly set the `access_key_id` and
`secret_access_key` variables.

These are the available fields during within config templating.
The `ec2.*` fields and `cloud.*` fields will be available on each emitted event.

* cloud.availability_zone
* cloud.instance.id
* cloud.machine.type
* cloud.provider
* cloud.region

* ec2.architecture
* ec2.image.id
* ec2.kernel.id
* ec2.monitoring.state
* ec2.private.dns_name
* ec2.private.ip
* ec2.public.dns_name
* ec2.public.ip
* ec2.root_device_name
* ec2.state.code
* ec2.state.name
* ec2.subnet.id
* ec2.tags
* ec2.vpc.id
kaiyan-sheng marked this conversation as resolved.
Show resolved Hide resolved

include::../../{beatname_lc}/docs/autodiscover-aws-ec2-config.asciidoc[]

This autodiscover provider takes our standard <<aws-credentials-config,AWS credentials options>>.

endif::autodiscoverAWSEC2[]

[[configuration-autodiscover-advanced]]
=== Advanced usage

Expand Down
25 changes: 25 additions & 0 deletions metricbeat/docs/autodiscover-aws-ec2-config.asciidoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{beatname_uc} supports templates for modules:

["source","yaml",subs="attributes"]
-------------------------------------------------------------------------------------
metricbeat.autodiscover:
providers:
- type: aws_ec2
period: 1m
credential_profile_name: elastic-beats
templates:
- condition:
equals:
meta.ec2.tags.service: "mysql"
kaiyan-sheng marked this conversation as resolved.
Show resolved Hide resolved
config:
- module: mysql
metricsets: ["status", "galera_status"]
period: 10s
hosts: ["root:password@tcp(${data.meta.ec2.public.ip}:3306)/"]
username: root
password: password
-------------------------------------------------------------------------------------

This autodiscover provider takes our standard AWS credentials options.
With this configuration, `mysql` metricbeat module will be launched for all EC2
instances that have `service: mysql` as a tag.
2 changes: 2 additions & 0 deletions metricbeat/docs/configuring-howto.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,9 @@ include::{libbeat-dir}/shared-env-vars.asciidoc[]

:autodiscoverJolokia:
:autodiscoverHints:
:autodiscoverAWSEC2:
include::{libbeat-dir}/shared-autodiscover.asciidoc[]
:autodiscoverAWSEC2!:

:standalone:
include::{libbeat-dir}/yaml.asciidoc[]
Expand Down
35 changes: 35 additions & 0 deletions x-pack/libbeat/autodiscover/providers/aws/aws.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,38 @@
// you may not use this file except in compliance with the Elastic License.

package aws

import (
"context"

"github.com/aws/aws-sdk-go-v2/service/ec2"
"github.com/aws/aws-sdk-go-v2/service/ec2/ec2iface"
"github.com/pkg/errors"
)

// SafeString makes handling AWS *string types easier.
// The AWS lib never returns plain strings, always using pointers, probably for memory efficiency reasons.
// This is a bit odd, because strings are just pointers into byte arrays, however this is the choice they've made.
// This will return the plain version of the given string or an empty string if the pointer is null
func SafeString(str *string) string {
if str == nil {
return ""
}

return *str
}

// GetRegions makes DescribeRegions API call to list all regions from AWS
func GetRegions(svc ec2iface.ClientAPI) (completeRegionsList []string, err error) {
input := &ec2.DescribeRegionsInput{}
req := svc.DescribeRegionsRequest(input)
output, err := req.Send(context.TODO())
if err != nil {
err = errors.Wrap(err, "Failed DescribeRegions")
return
}
for _, region := range output.Regions {
completeRegionsList = append(completeRegionsList, *region.RegionName)
}
return
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,31 @@
// or more contributor license agreements. Licensed under the Elastic License;
// you may not use this file except in compliance with the Elastic License.

package elb
package aws

import (
"time"

"github.com/elastic/beats/x-pack/libbeat/common/aws"

"github.com/elastic/beats/libbeat/autodiscover/template"
"github.com/elastic/beats/libbeat/common"
)

// Config for the aws_elb autodiscover provider.
// Config for all aws autodiscover providers.
type Config struct {
Type string `config:"type"`

// Standard autodiscover fields.

// Hints are currently not supported, but may be implemented in a later release
HintsEnabled bool `config:"hints.enabled"`
Builders []*common.Config `config:"builders"`
Appenders []*common.Config `config:"appenders"`
Templates template.MapperSettings `config:"templates"`
Type string `config:"type"`
Templates template.MapperSettings `config:"templates"`

// Period defines how often to poll the AWS API.
Period time.Duration `config:"period" validate:"nonzero,required"`

// AWS Specific autodiscover fields

Regions []string `config:"regions" validate:"required"`
Regions []string `config:"regions"`
AWSConfig aws.ConfigAWS `config:",inline"`
kaiyan-sheng marked this conversation as resolved.
Show resolved Hide resolved
}

func defaultConfig() *Config {
// DefaultConfig for all aws autodiscover providers.
func DefaultConfig() *Config {
kaiyan-sheng marked this conversation as resolved.
Show resolved Hide resolved
kaiyan-sheng marked this conversation as resolved.
Show resolved Hide resolved
kaiyan-sheng marked this conversation as resolved.
Show resolved Hide resolved
kaiyan-sheng marked this conversation as resolved.
Show resolved Hide resolved
return &Config{
Period: time.Minute,
}
Expand Down
11 changes: 11 additions & 0 deletions x-pack/libbeat/autodiscover/providers/aws/ec2/_meta/fields.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
- key: ec2_listener
title: "EC2 Listener"
description: >
AWS EC2 Listeners
short_config: false
release: experimental
fields:
- name: ec2_listener
type: group
description: >
Represents an AWS EC2 Listener, e.g. state of an EC2.
134 changes: 134 additions & 0 deletions x-pack/libbeat/autodiscover/providers/aws/ec2/ec2.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
// or more contributor license agreements. Licensed under the Elastic License;
// you may not use this file except in compliance with the Elastic License.

package ec2

import (
"github.com/aws/aws-sdk-go-v2/service/ec2"
"github.com/pkg/errors"

"github.com/elastic/beats/libbeat/common"
"github.com/elastic/beats/libbeat/logp"
awsauto "github.com/elastic/beats/x-pack/libbeat/autodiscover/providers/aws"
)

type ec2Instance struct {
ec2Instance ec2.Instance
}

// toMap converts this ec2Instance into the form consumed as metadata in the autodiscovery process.
func (i *ec2Instance) toMap() common.MapStr {
architecture, err := i.ec2Instance.Architecture.MarshalValue()
if err != nil {
logp.Error(errors.Wrap(err, "MarshalValue failed for architecture: "))
}

m := common.MapStr{
"image": i.toImage(),
"vpc": i.toVpc(),
"subnet": i.toSubnet(),
"private": i.toPrivate(),
"public": i.toPublic(),
"monitoring": i.toMonitoringState(),
"kernel": i.toKernel(),
"state": i.stateMap(),
"architecture": architecture,
"root_device_name": awsauto.SafeString(i.ec2Instance.RootDeviceName),
}

for _, tag := range i.ec2Instance.Tags {
m.Put("tags."+awsauto.SafeString(tag.Key), awsauto.SafeString(tag.Value))
}
return m
}

func (i *ec2Instance) instanceID() string {
return awsauto.SafeString(i.ec2Instance.InstanceId)
}

func (i *ec2Instance) toImage() common.MapStr {
m := common.MapStr{}
m["id"] = awsauto.SafeString(i.ec2Instance.ImageId)
return m
}

func (i *ec2Instance) toMonitoringState() common.MapStr {
monitoringState, err := i.ec2Instance.Monitoring.State.MarshalValue()
if err != nil {
logp.Error(errors.Wrap(err, "MarshalValue failed for monitoring state: "))
kaiyan-sheng marked this conversation as resolved.
Show resolved Hide resolved
}

m := common.MapStr{}
m["state"] = monitoringState
return m
}

func (i *ec2Instance) toPrivate() common.MapStr {
m := common.MapStr{}
m["ip"] = awsauto.SafeString(i.ec2Instance.PrivateIpAddress)
m["dns_name"] = awsauto.SafeString(i.ec2Instance.PrivateDnsName)
return m
}

func (i *ec2Instance) toPublic() common.MapStr {
m := common.MapStr{}
m["ip"] = awsauto.SafeString(i.ec2Instance.PublicIpAddress)
m["dns_name"] = awsauto.SafeString(i.ec2Instance.PublicDnsName)
return m
}

func (i *ec2Instance) toVpc() common.MapStr {
m := common.MapStr{}
m["id"] = awsauto.SafeString(i.ec2Instance.VpcId)
return m
}

func (i *ec2Instance) toSubnet() common.MapStr {
m := common.MapStr{}
m["id"] = awsauto.SafeString(i.ec2Instance.SubnetId)
return m
}

func (i *ec2Instance) toKernel() common.MapStr {
m := common.MapStr{}
m["id"] = awsauto.SafeString(i.ec2Instance.KernelId)
return m
}

func (i *ec2Instance) toCloudMap() common.MapStr {
m := common.MapStr{}
availabilityZone := awsauto.SafeString(i.ec2Instance.Placement.AvailabilityZone)
m["availability_zone"] = availabilityZone
m["provider"] = "aws"

// The region is just an AZ with the last character removed
m["region"] = availabilityZone[:len(availabilityZone)-1]

instance := common.MapStr{}
instance["id"] = i.instanceID()
m["instance"] = instance

instanceType, err := i.ec2Instance.InstanceType.MarshalValue()
if err != nil {
logp.Error(errors.Wrap(err, "MarshalValue failed for instance type: "))
}
kaiyan-sheng marked this conversation as resolved.
Show resolved Hide resolved
machine := common.MapStr{}
machine["type"] = instanceType
m["machine"] = machine
return m
}

// stateMap converts the State part of the ec2 struct into a friendlier map with 'reason' and 'code' fields.
func (i *ec2Instance) stateMap() (stateMap common.MapStr) {
state := i.ec2Instance.State
stateMap = common.MapStr{}
nameString, err := state.Name.MarshalValue()
if err != nil {
logp.Error(errors.Wrap(err, "MarshalValue failed for instance state name: "))
}

stateMap["name"] = nameString
stateMap["code"] = state.Code
return stateMap
}
Loading