mirror of
https://github.com/prometheus/prometheus.git
synced 2026-05-14 17:16:54 +02:00
Merge pull request #16088 from rlees85/ipv6-only-ec2-sd
discovery: Allow EC2 Service Discovery to work with IPv6-only instances
This commit is contained in:
commit
63f2bf9b9a
@ -55,6 +55,7 @@ const (
|
||||
ec2LabelInstanceType = ec2Label + "instance_type"
|
||||
ec2LabelOwnerID = ec2Label + "owner_id"
|
||||
ec2LabelPlatform = ec2Label + "platform"
|
||||
ec2LabelDefaultIPv6Address = ec2Label + "default_ipv6_address"
|
||||
ec2LabelPrimaryIPv6Addresses = ec2Label + "primary_ipv6_addresses"
|
||||
ec2LabelPrimarySubnetID = ec2Label + "primary_subnet_id"
|
||||
ec2LabelPrivateDNS = ec2Label + "private_dns_name"
|
||||
@ -306,7 +307,9 @@ func (d *EC2Discovery) refresh(ctx context.Context) ([]*targetgroup.Group, error
|
||||
|
||||
for _, r := range p.Reservations {
|
||||
for _, inst := range r.Instances {
|
||||
if inst.PrivateIpAddress == nil {
|
||||
defaultIPv6Addr, primaryIPv6Addrs, ipv6Addrs := getInstanceIPv6Addresses(&inst)
|
||||
|
||||
if inst.PrivateIpAddress == nil && defaultIPv6Addr == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
@ -319,12 +322,20 @@ func (d *EC2Discovery) refresh(ctx context.Context) ([]*targetgroup.Group, error
|
||||
labels[ec2LabelOwnerID] = model.LabelValue(*r.OwnerId)
|
||||
}
|
||||
|
||||
labels[ec2LabelPrivateIP] = model.LabelValue(*inst.PrivateIpAddress)
|
||||
if defaultIPv6Addr != nil {
|
||||
labels[ec2LabelDefaultIPv6Address] = model.LabelValue(*defaultIPv6Addr)
|
||||
}
|
||||
|
||||
if inst.PrivateIpAddress != nil {
|
||||
labels[ec2LabelPrivateIP] = model.LabelValue(*inst.PrivateIpAddress)
|
||||
labels[model.AddressLabel] = model.LabelValue(net.JoinHostPort(*inst.PrivateIpAddress, strconv.Itoa(d.cfg.Port)))
|
||||
} else {
|
||||
labels[model.AddressLabel] = model.LabelValue(net.JoinHostPort(*defaultIPv6Addr, strconv.Itoa(d.cfg.Port)))
|
||||
}
|
||||
|
||||
if inst.PrivateDnsName != nil {
|
||||
labels[ec2LabelPrivateDNS] = model.LabelValue(*inst.PrivateDnsName)
|
||||
}
|
||||
addr := net.JoinHostPort(*inst.PrivateIpAddress, strconv.Itoa(d.cfg.Port))
|
||||
labels[model.AddressLabel] = model.LabelValue(addr)
|
||||
|
||||
if inst.Platform != "" {
|
||||
labels[ec2LabelPlatform] = model.LabelValue(inst.Platform)
|
||||
@ -334,6 +345,21 @@ func (d *EC2Discovery) refresh(ctx context.Context) ([]*targetgroup.Group, error
|
||||
labels[ec2LabelPublicIP] = model.LabelValue(*inst.PublicIpAddress)
|
||||
labels[ec2LabelPublicDNS] = model.LabelValue(*inst.PublicDnsName)
|
||||
}
|
||||
|
||||
if primaryIPv6Addrs != nil {
|
||||
labels[ec2LabelPrimaryIPv6Addresses] = model.LabelValue(
|
||||
ec2LabelSeparator +
|
||||
strings.Join(primaryIPv6Addrs, ec2LabelSeparator) +
|
||||
ec2LabelSeparator)
|
||||
}
|
||||
|
||||
if ipv6Addrs != nil {
|
||||
labels[ec2LabelIPv6Addresses] = model.LabelValue(
|
||||
ec2LabelSeparator +
|
||||
strings.Join(ipv6Addrs, ec2LabelSeparator) +
|
||||
ec2LabelSeparator)
|
||||
}
|
||||
|
||||
labels[ec2LabelAMI] = model.LabelValue(*inst.ImageId)
|
||||
labels[ec2LabelAZ] = model.LabelValue(*inst.Placement.AvailabilityZone)
|
||||
azID, ok := d.azToAZID[*inst.Placement.AvailabilityZone]
|
||||
@ -359,8 +385,6 @@ func (d *EC2Discovery) refresh(ctx context.Context) ([]*targetgroup.Group, error
|
||||
labels[ec2LabelPrimarySubnetID] = model.LabelValue(*inst.SubnetId)
|
||||
|
||||
var subnets []string
|
||||
var ipv6addrs []string
|
||||
var primaryipv6addrs []string
|
||||
subnetsMap := make(map[string]struct{})
|
||||
for _, eni := range inst.NetworkInterfaces {
|
||||
if eni.SubnetId == nil {
|
||||
@ -371,36 +395,11 @@ func (d *EC2Discovery) refresh(ctx context.Context) ([]*targetgroup.Group, error
|
||||
subnetsMap[*eni.SubnetId] = struct{}{}
|
||||
subnets = append(subnets, *eni.SubnetId)
|
||||
}
|
||||
|
||||
for _, ipv6addr := range eni.Ipv6Addresses {
|
||||
ipv6addrs = append(ipv6addrs, *ipv6addr.Ipv6Address)
|
||||
if *ipv6addr.IsPrimaryIpv6 {
|
||||
// we might have to extend the slice with more than one element
|
||||
// that could leave empty strings in the list which is intentional
|
||||
// to keep the position/device index information
|
||||
for int32(len(primaryipv6addrs)) <= *eni.Attachment.DeviceIndex {
|
||||
primaryipv6addrs = append(primaryipv6addrs, "")
|
||||
}
|
||||
primaryipv6addrs[*eni.Attachment.DeviceIndex] = *ipv6addr.Ipv6Address
|
||||
}
|
||||
}
|
||||
}
|
||||
labels[ec2LabelSubnetID] = model.LabelValue(
|
||||
ec2LabelSeparator +
|
||||
strings.Join(subnets, ec2LabelSeparator) +
|
||||
ec2LabelSeparator)
|
||||
if len(ipv6addrs) > 0 {
|
||||
labels[ec2LabelIPv6Addresses] = model.LabelValue(
|
||||
ec2LabelSeparator +
|
||||
strings.Join(ipv6addrs, ec2LabelSeparator) +
|
||||
ec2LabelSeparator)
|
||||
}
|
||||
if len(primaryipv6addrs) > 0 {
|
||||
labels[ec2LabelPrimaryIPv6Addresses] = model.LabelValue(
|
||||
ec2LabelSeparator +
|
||||
strings.Join(primaryipv6addrs, ec2LabelSeparator) +
|
||||
ec2LabelSeparator)
|
||||
}
|
||||
}
|
||||
|
||||
for _, t := range inst.Tags {
|
||||
@ -417,3 +416,39 @@ func (d *EC2Discovery) refresh(ctx context.Context) ([]*targetgroup.Group, error
|
||||
|
||||
return []*targetgroup.Group{tg}, nil
|
||||
}
|
||||
|
||||
func getInstanceIPv6Addresses(i *ec2Types.Instance) (*string, []string, []string) {
|
||||
var primaryIPv6Addrs []string
|
||||
var ipv6Addrs []string
|
||||
|
||||
if i.VpcId != nil {
|
||||
for _, eni := range i.NetworkInterfaces {
|
||||
if eni.SubnetId == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, ipv6addr := range eni.Ipv6Addresses {
|
||||
ipv6Addrs = append(ipv6Addrs, *ipv6addr.Ipv6Address)
|
||||
if *ipv6addr.IsPrimaryIpv6 {
|
||||
// we might have to extend the slice with more than one element
|
||||
// that could leave empty strings in the list which is intentional
|
||||
// to keep the position/device index information
|
||||
for int32(len(primaryIPv6Addrs)) <= *eni.Attachment.DeviceIndex {
|
||||
primaryIPv6Addrs = append(primaryIPv6Addrs, "")
|
||||
}
|
||||
primaryIPv6Addrs[*eni.Attachment.DeviceIndex] = *ipv6addr.Ipv6Address
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find an IPv6 address we can use by default. Pick the first primary one if
|
||||
// there is any available, if not then pick the first non-primary address.
|
||||
for _, ipv6addr := range append(primaryIPv6Addrs, ipv6Addrs...) {
|
||||
if ipv6addr != "" {
|
||||
return &ipv6addr, primaryIPv6Addrs, ipv6Addrs
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil, primaryIPv6Addrs, ipv6Addrs
|
||||
}
|
||||
|
||||
@ -108,7 +108,7 @@ func TestEC2DiscoveryRefresh(t *testing.T) {
|
||||
expected []*targetgroup.Group
|
||||
}{
|
||||
{
|
||||
name: "NoPrivateIp",
|
||||
name: "NoPrivateIpOrIpv6",
|
||||
ec2Data: &ec2DataStore{
|
||||
region: "region-noprivateip",
|
||||
azToAZID: map[string]string{
|
||||
@ -351,6 +351,7 @@ func TestEC2DiscoveryRefresh(t *testing.T) {
|
||||
"__meta_ec2_instance_type": model.LabelValue("instance-type-ipv6"),
|
||||
"__meta_ec2_ipv6_addresses": model.LabelValue(",2001:db8:2::1:1,2001:db8:2::2:1,2001:db8:2::2:2,2001:db8:2::3:1,"),
|
||||
"__meta_ec2_owner_id": model.LabelValue(""),
|
||||
"__meta_ec2_default_ipv6_address": model.LabelValue("2001:db8:2::2:2"),
|
||||
"__meta_ec2_primary_ipv6_addresses": model.LabelValue(",,2001:db8:2::2:2,,2001:db8:2::3:1,"),
|
||||
"__meta_ec2_primary_subnet_id": model.LabelValue("azid-2"),
|
||||
"__meta_ec2_private_ip": model.LabelValue("9.10.11.12"),
|
||||
@ -362,6 +363,69 @@ func TestEC2DiscoveryRefresh(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Ipv6-Only",
|
||||
ec2Data: &ec2DataStore{
|
||||
region: "region-ipv6-only",
|
||||
azToAZID: map[string]string{
|
||||
"azname-a": "azid-1",
|
||||
"azname-b": "azid-2",
|
||||
"azname-c": "azid-3",
|
||||
},
|
||||
|
||||
instances: []ec2Types.Instance{
|
||||
{
|
||||
// just the minimum needed for the refresh work
|
||||
ImageId: strptr("ami-ipv6-only"),
|
||||
InstanceId: strptr("instance-id-ipv6-only"),
|
||||
InstanceType: "instance-type-ipv6-only",
|
||||
Placement: &ec2Types.Placement{AvailabilityZone: strptr("azname-b")},
|
||||
State: &ec2Types.InstanceState{Name: "running"},
|
||||
SubnetId: strptr("azid-2"),
|
||||
VpcId: strptr("vpc-ipv6-only"),
|
||||
// network interfaces
|
||||
NetworkInterfaces: []ec2Types.InstanceNetworkInterface{
|
||||
// interface without primary IPv6, index 0
|
||||
{
|
||||
Attachment: &ec2Types.InstanceNetworkInterfaceAttachment{
|
||||
DeviceIndex: aws.Int32(0),
|
||||
},
|
||||
Ipv6Addresses: []ec2Types.InstanceIpv6Address{
|
||||
{
|
||||
Ipv6Address: strptr("2001:db8:2::1:1"),
|
||||
IsPrimaryIpv6: boolptr(false),
|
||||
},
|
||||
},
|
||||
SubnetId: strptr("azid-2"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: []*targetgroup.Group{
|
||||
{
|
||||
Source: "region-ipv6-only",
|
||||
Targets: []model.LabelSet{
|
||||
{
|
||||
"__address__": model.LabelValue("[2001:db8:2::1:1]:4242"),
|
||||
"__meta_ec2_ami": model.LabelValue("ami-ipv6-only"),
|
||||
"__meta_ec2_availability_zone": model.LabelValue("azname-b"),
|
||||
"__meta_ec2_availability_zone_id": model.LabelValue("azid-2"),
|
||||
"__meta_ec2_instance_id": model.LabelValue("instance-id-ipv6-only"),
|
||||
"__meta_ec2_instance_state": model.LabelValue("running"),
|
||||
"__meta_ec2_instance_type": model.LabelValue("instance-type-ipv6-only"),
|
||||
"__meta_ec2_ipv6_addresses": model.LabelValue(",2001:db8:2::1:1,"),
|
||||
"__meta_ec2_owner_id": model.LabelValue(""),
|
||||
"__meta_ec2_default_ipv6_address": model.LabelValue("2001:db8:2::1:1"),
|
||||
"__meta_ec2_primary_subnet_id": model.LabelValue("azid-2"),
|
||||
"__meta_ec2_region": model.LabelValue("region-ipv6-only"),
|
||||
"__meta_ec2_subnet_id": model.LabelValue(",azid-2,"),
|
||||
"__meta_ec2_vpc_id": model.LabelValue("vpc-ipv6-only"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
client := newMockEC2Client(tt.ec2Data)
|
||||
|
||||
@ -902,6 +902,7 @@ The following meta labels are available on targets during [relabeling](#relabel_
|
||||
* `__meta_ec2_ipv6_addresses`: comma separated list of IPv6 addresses assigned to the instance's network interfaces, if present
|
||||
* `__meta_ec2_owner_id`: the ID of the AWS account that owns the EC2 instance
|
||||
* `__meta_ec2_platform`: the Operating System platform, set to 'windows' on Windows servers, absent otherwise
|
||||
* `__meta_ec2_default_ipv6_address`: the first primary IPv6 address found if present, otherwise first non-primary IPv6 address, if present
|
||||
* `__meta_ec2_primary_ipv6_addresses`: comma separated list of the Primary IPv6 addresses of the instance, if present. The list is ordered based on the position of each corresponding network interface in the attachment order.
|
||||
* `__meta_ec2_primary_subnet_id`: the subnet ID of the primary network interface, if available
|
||||
* `__meta_ec2_private_dns_name`: the private DNS name of the instance, if available
|
||||
@ -1832,6 +1833,7 @@ The following meta labels are available on targets during [relabeling](#relabel_
|
||||
* `__meta_ec2_ipv6_addresses`: comma separated list of IPv6 addresses assigned to the instance's network interfaces, if present
|
||||
* `__meta_ec2_owner_id`: the ID of the AWS account that owns the EC2 instance
|
||||
* `__meta_ec2_platform`: the Operating System platform, set to 'windows' on Windows servers, absent otherwise
|
||||
* `__meta_ec2_default_ipv6_address`: the first primary IPv6 address found if present, otherwise first non-primary IPv6 address, if present
|
||||
* `__meta_ec2_primary_ipv6_addresses`: comma separated list of the Primary IPv6 addresses of the instance, if present. The list is ordered based on the position of each corresponding network interface in the attachment order.
|
||||
* `__meta_ec2_primary_subnet_id`: the subnet ID of the primary network interface, if available
|
||||
* `__meta_ec2_private_dns_name`: the private DNS name of the instance, if available
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user