fix: dial the ochre appliance customer ENI, not the management NIC (#851)

Both the endpoint ENI and the management NIC carry the rds-db-instance tag,
so a first-match pick non-deterministically dialed the unreachable management
NIC. Select the endpoint ENI by its RDS description, falling back to any usable
tagged NIC.
This commit is contained in:
Josh Siviour
2026-08-21 14:03:59 +10:00
committed by GitHub
parent 7bfeaf885c
commit 3bb35deea2
2 changed files with 61 additions and 1 deletions
+16 -1
View File
@@ -6,6 +6,7 @@ import (
"fmt"
"log/slog"
"net/netip"
"strings"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
@@ -135,6 +136,11 @@ func ensureDaemonENI(ctx context.Context, vpcSvc vpcProvisioner, subnetID, nodeI
// (tests/e2e/harness/rds.go) duplicates it the same way.
const applianceInstanceTagKey = "spinifex:rds-db-instance"
// endpointENIDescriptionPrefix mirrors the description handlers/rds gives the
// customer (endpoint) ENI in resolveCustomerENI. The management NIC shares the
// rds-db-instance tag, so description is what tells the reachable ENI apart.
const endpointENIDescriptionPrefix = "RDS endpoint ENI for "
// resolveApplianceTarget finds the appliance's own customer ENI by the tag
// handlers/rds stamps on it at launch, returning the real private IP to dial
// and the subnet it lives in -- never the vanity endpoint hostname, which is
@@ -152,13 +158,22 @@ func resolveApplianceTarget(ctx context.Context, deps HostPortDeps, identifier s
if err != nil {
return "", "", "", fmt.Errorf("ochrevector: describe appliance ENI for %s: %w", identifier, err)
}
// Both the endpoint ENI and the management NIC carry the rds-db-instance
// tag, but only the endpoint ENI (in the customer VPC) is reachable from the
// daemon. Prefer it by description; fall back to any usable tagged NIC.
var ni *ec2.NetworkInterface
if out != nil {
for _, cand := range out.NetworkInterfaces {
if aws.StringValue(cand.PrivateIpAddress) != "" && aws.StringValue(cand.SubnetId) != "" {
if aws.StringValue(cand.PrivateIpAddress) == "" || aws.StringValue(cand.SubnetId) == "" {
continue
}
if strings.HasPrefix(aws.StringValue(cand.Description), endpointENIDescriptionPrefix) {
ni = cand
break
}
if ni == nil {
ni = cand
}
}
}
if ni == nil {
@@ -98,6 +98,26 @@ func (f *fakeVPC) putApplianceENI(eniID, identifier, subnetID, ip string) {
}
}
// putTaggedENIWithDescription seeds an appliance-tagged ENI that also carries
// a description, so a test can present both the endpoint ENI and the
// management NIC that share the rds-db-instance tag at launch.
func (f *fakeVPC) putTaggedENIWithDescription(eniID, identifier, subnetID, ip, description string) {
f.mu.Lock()
defer f.mu.Unlock()
if f.records == nil {
f.records = map[string]*ec2.NetworkInterface{}
}
f.records[eniID] = &ec2.NetworkInterface{
NetworkInterfaceId: aws.String(eniID),
SubnetId: aws.String(subnetID),
PrivateIpAddress: aws.String(ip),
Description: aws.String(description),
TagSet: []*ec2.Tag{
{Key: aws.String(applianceInstanceTagKey), Value: aws.String(identifier)},
},
}
}
// tagValue returns the value of key in tags, or "" if absent.
func tagValue(tags []*ec2.Tag, key string) string {
for _, t := range tags {
@@ -404,6 +424,31 @@ func TestResolveApplianceTarget_FindsTheTaggedApplianceENI(t *testing.T) {
assert.Equal(t, "10.244.1.0/24", cidr)
}
// The endpoint ENI and the management NIC share the rds-db-instance tag, but
// only the endpoint ENI (described "RDS endpoint ENI for ...") is reachable
// from the daemon. The resolver must pick it by description regardless of the
// order DescribeNetworkInterfaces returns them -- map iteration is unordered,
// so a repeat loop exercises both orderings.
func TestResolveApplianceTarget_PrefersEndpointENIOverManagementNIC(t *testing.T) {
const mgmtSubnet = "subnet-mgmt-0001"
const mgmtIP = "10.251.185.4"
for range 16 {
h := newHostPortHarness()
h.putSubnet(testApplianceSubnet, "10.244.1.0/24")
h.putSubnet(mgmtSubnet, "10.251.185.0/24")
h.vpc.putTaggedENIWithDescription("eni-mgmt", testApplianceIdentifier, mgmtSubnet, mgmtIP,
"RDS management NIC for "+testApplianceIdentifier)
h.vpc.putTaggedENIWithDescription("eni-endpoint", testApplianceIdentifier, testApplianceSubnet, testApplianceEndpoint,
endpointENIDescriptionPrefix+testApplianceIdentifier)
dialIP, subnetID, cidr, err := resolveApplianceTarget(t.Context(), h.deps(), testApplianceIdentifier)
require.NoError(t, err)
require.Equal(t, testApplianceEndpoint, dialIP, "must dial the endpoint ENI, never the management NIC")
require.Equal(t, testApplianceSubnet, subnetID)
require.Equal(t, "10.244.1.0/24", cidr)
}
}
// An ENI tagged for a different identifier must never match.
func TestResolveApplianceTarget_IgnoresOtherIdentifiersTags(t *testing.T) {
h := newHostPortHarness()