mirror of
https://github.com/prometheus/prometheus.git
synced 2026-06-08 16:12:16 -04:00
Merge pull request #18818 from roidelapluie/roidelapluie/aws-sd-binary-size
discovery/aws: keep AWS SDK clients out of interfaces to shrink the binary
This commit is contained in:
commit
27925e446c
6 changed files with 213 additions and 16 deletions
|
|
@ -150,6 +150,47 @@ type ec2Client interface {
|
|||
DescribeInstances(ctx context.Context, params *ec2.DescribeInstancesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeInstancesOutput, error)
|
||||
}
|
||||
|
||||
// ec2ClientAdapter holds the EC2 API calls that AWS discovery actually uses as
|
||||
// method-value closures over the concrete *ec2.Client.
|
||||
//
|
||||
// It exists purely to keep the binary small. The Go linker, once reflection
|
||||
// (reflect.Value.Method/Call plus struct-field traversal, both reachable via
|
||||
// the YAML/config machinery) is live, conservatively retains every exported
|
||||
// method of any concrete type that is reachable through an interface — and a
|
||||
// type stored as a field of an interface-boxed struct counts. *ec2.Client has
|
||||
// ~470 operation methods; retaining all of them pulls in ~1,500 serializers and
|
||||
// roughly 21 MB. By capturing only the needed methods as func values, the
|
||||
// concrete *ec2.Client is hidden inside closure contexts (which reflection
|
||||
// cannot traverse) and never appears as a field of a boxed type, so dead-code
|
||||
// elimination drops the unused operations.
|
||||
type ec2ClientAdapter struct {
|
||||
describeAvailabilityZones func(ctx context.Context, params *ec2.DescribeAvailabilityZonesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeAvailabilityZonesOutput, error)
|
||||
describeInstances func(ctx context.Context, params *ec2.DescribeInstancesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeInstancesOutput, error)
|
||||
describeNetworkInterfaces func(ctx context.Context, params *ec2.DescribeNetworkInterfacesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeNetworkInterfacesOutput, error)
|
||||
}
|
||||
|
||||
// newEC2ClientAdapter wraps a concrete *ec2.Client, capturing only the API
|
||||
// calls AWS discovery needs. See the ec2ClientAdapter doc comment for why.
|
||||
func newEC2ClientAdapter(c *ec2.Client) ec2ClientAdapter {
|
||||
return ec2ClientAdapter{
|
||||
describeAvailabilityZones: c.DescribeAvailabilityZones,
|
||||
describeInstances: c.DescribeInstances,
|
||||
describeNetworkInterfaces: c.DescribeNetworkInterfaces,
|
||||
}
|
||||
}
|
||||
|
||||
func (a ec2ClientAdapter) DescribeAvailabilityZones(ctx context.Context, params *ec2.DescribeAvailabilityZonesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeAvailabilityZonesOutput, error) {
|
||||
return a.describeAvailabilityZones(ctx, params, optFns...)
|
||||
}
|
||||
|
||||
func (a ec2ClientAdapter) DescribeInstances(ctx context.Context, params *ec2.DescribeInstancesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeInstancesOutput, error) {
|
||||
return a.describeInstances(ctx, params, optFns...)
|
||||
}
|
||||
|
||||
func (a ec2ClientAdapter) DescribeNetworkInterfaces(ctx context.Context, params *ec2.DescribeNetworkInterfacesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeNetworkInterfacesOutput, error) {
|
||||
return a.describeNetworkInterfaces(ctx, params, optFns...)
|
||||
}
|
||||
|
||||
// EC2Discovery periodically performs EC2-SD requests. It implements
|
||||
// the Discoverer interface.
|
||||
type EC2Discovery struct {
|
||||
|
|
@ -235,12 +276,12 @@ func (d *EC2Discovery) ec2Client(ctx context.Context) (ec2Client, error) {
|
|||
cfg.Credentials = aws.NewCredentialsCache(assumeProvider)
|
||||
}
|
||||
|
||||
d.ec2 = ec2.NewFromConfig(cfg, func(options *ec2.Options) {
|
||||
d.ec2 = newEC2ClientAdapter(ec2.NewFromConfig(cfg, func(options *ec2.Options) {
|
||||
if d.cfg.Endpoint != "" {
|
||||
options.BaseEndpoint = &d.cfg.Endpoint
|
||||
}
|
||||
options.HTTPClient = httpClient
|
||||
})
|
||||
}))
|
||||
|
||||
return d.ec2, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -162,6 +162,60 @@ type ecsClient interface {
|
|||
DescribeContainerInstances(context.Context, *ecs.DescribeContainerInstancesInput, ...func(*ecs.Options)) (*ecs.DescribeContainerInstancesOutput, error)
|
||||
}
|
||||
|
||||
// ecsClientAdapter captures only the ECS API calls AWS discovery uses as
|
||||
// method-value closures, keeping the concrete *ecs.Client out of any
|
||||
// interface-boxed struct field. See ec2ClientAdapter for the full rationale:
|
||||
// this stops the linker from retaining the entire ECS API surface (~2 MB).
|
||||
type ecsClientAdapter struct {
|
||||
listClusters func(context.Context, *ecs.ListClustersInput, ...func(*ecs.Options)) (*ecs.ListClustersOutput, error)
|
||||
describeClusters func(context.Context, *ecs.DescribeClustersInput, ...func(*ecs.Options)) (*ecs.DescribeClustersOutput, error)
|
||||
listServices func(context.Context, *ecs.ListServicesInput, ...func(*ecs.Options)) (*ecs.ListServicesOutput, error)
|
||||
describeServices func(context.Context, *ecs.DescribeServicesInput, ...func(*ecs.Options)) (*ecs.DescribeServicesOutput, error)
|
||||
listTasks func(context.Context, *ecs.ListTasksInput, ...func(*ecs.Options)) (*ecs.ListTasksOutput, error)
|
||||
describeTasks func(context.Context, *ecs.DescribeTasksInput, ...func(*ecs.Options)) (*ecs.DescribeTasksOutput, error)
|
||||
describeContainerInstances func(context.Context, *ecs.DescribeContainerInstancesInput, ...func(*ecs.Options)) (*ecs.DescribeContainerInstancesOutput, error)
|
||||
}
|
||||
|
||||
func newECSClientAdapter(c *ecs.Client) ecsClientAdapter {
|
||||
return ecsClientAdapter{
|
||||
listClusters: c.ListClusters,
|
||||
describeClusters: c.DescribeClusters,
|
||||
listServices: c.ListServices,
|
||||
describeServices: c.DescribeServices,
|
||||
listTasks: c.ListTasks,
|
||||
describeTasks: c.DescribeTasks,
|
||||
describeContainerInstances: c.DescribeContainerInstances,
|
||||
}
|
||||
}
|
||||
|
||||
func (a ecsClientAdapter) ListClusters(ctx context.Context, params *ecs.ListClustersInput, optFns ...func(*ecs.Options)) (*ecs.ListClustersOutput, error) {
|
||||
return a.listClusters(ctx, params, optFns...)
|
||||
}
|
||||
|
||||
func (a ecsClientAdapter) DescribeClusters(ctx context.Context, params *ecs.DescribeClustersInput, optFns ...func(*ecs.Options)) (*ecs.DescribeClustersOutput, error) {
|
||||
return a.describeClusters(ctx, params, optFns...)
|
||||
}
|
||||
|
||||
func (a ecsClientAdapter) ListServices(ctx context.Context, params *ecs.ListServicesInput, optFns ...func(*ecs.Options)) (*ecs.ListServicesOutput, error) {
|
||||
return a.listServices(ctx, params, optFns...)
|
||||
}
|
||||
|
||||
func (a ecsClientAdapter) DescribeServices(ctx context.Context, params *ecs.DescribeServicesInput, optFns ...func(*ecs.Options)) (*ecs.DescribeServicesOutput, error) {
|
||||
return a.describeServices(ctx, params, optFns...)
|
||||
}
|
||||
|
||||
func (a ecsClientAdapter) ListTasks(ctx context.Context, params *ecs.ListTasksInput, optFns ...func(*ecs.Options)) (*ecs.ListTasksOutput, error) {
|
||||
return a.listTasks(ctx, params, optFns...)
|
||||
}
|
||||
|
||||
func (a ecsClientAdapter) DescribeTasks(ctx context.Context, params *ecs.DescribeTasksInput, optFns ...func(*ecs.Options)) (*ecs.DescribeTasksOutput, error) {
|
||||
return a.describeTasks(ctx, params, optFns...)
|
||||
}
|
||||
|
||||
func (a ecsClientAdapter) DescribeContainerInstances(ctx context.Context, params *ecs.DescribeContainerInstancesInput, optFns ...func(*ecs.Options)) (*ecs.DescribeContainerInstancesOutput, error) {
|
||||
return a.describeContainerInstances(ctx, params, optFns...)
|
||||
}
|
||||
|
||||
type ecsEC2Client interface {
|
||||
DescribeInstances(context.Context, *ec2.DescribeInstancesInput, ...func(*ec2.Options)) (*ec2.DescribeInstancesOutput, error)
|
||||
DescribeNetworkInterfaces(context.Context, *ec2.DescribeNetworkInterfacesInput, ...func(*ec2.Options)) (*ec2.DescribeNetworkInterfacesOutput, error)
|
||||
|
|
@ -250,16 +304,16 @@ func (d *ECSDiscovery) initEcsClient(ctx context.Context) error {
|
|||
cfg.Credentials = aws.NewCredentialsCache(assumeProvider)
|
||||
}
|
||||
|
||||
d.ecs = ecs.NewFromConfig(cfg, func(options *ecs.Options) {
|
||||
d.ecs = newECSClientAdapter(ecs.NewFromConfig(cfg, func(options *ecs.Options) {
|
||||
if d.cfg.Endpoint != "" {
|
||||
options.BaseEndpoint = &d.cfg.Endpoint
|
||||
}
|
||||
options.HTTPClient = client
|
||||
})
|
||||
}))
|
||||
|
||||
d.ec2 = ec2.NewFromConfig(cfg, func(options *ec2.Options) {
|
||||
d.ec2 = newEC2ClientAdapter(ec2.NewFromConfig(cfg, func(options *ec2.Options) {
|
||||
options.HTTPClient = client
|
||||
})
|
||||
}))
|
||||
|
||||
// Test credentials by making a simple API call
|
||||
testCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
|
||||
|
|
|
|||
|
|
@ -246,6 +246,37 @@ type elasticacheClient interface {
|
|||
ListTagsForResource(ctx context.Context, params *elasticache.ListTagsForResourceInput, optFns ...func(*elasticache.Options)) (*elasticache.ListTagsForResourceOutput, error)
|
||||
}
|
||||
|
||||
// elasticacheClientAdapter captures only the ElastiCache API calls AWS
|
||||
// discovery uses as method-value closures, keeping the concrete
|
||||
// *elasticache.Client out of any interface-boxed struct field. See
|
||||
// ec2ClientAdapter for the full rationale: this stops the linker from retaining
|
||||
// the entire ElastiCache API surface (~2.5 MB).
|
||||
type elasticacheClientAdapter struct {
|
||||
describeServerlessCaches func(ctx context.Context, params *elasticache.DescribeServerlessCachesInput, optFns ...func(*elasticache.Options)) (*elasticache.DescribeServerlessCachesOutput, error)
|
||||
describeCacheClusters func(ctx context.Context, params *elasticache.DescribeCacheClustersInput, optFns ...func(*elasticache.Options)) (*elasticache.DescribeCacheClustersOutput, error)
|
||||
listTagsForResource func(ctx context.Context, params *elasticache.ListTagsForResourceInput, optFns ...func(*elasticache.Options)) (*elasticache.ListTagsForResourceOutput, error)
|
||||
}
|
||||
|
||||
func newElastiCacheClientAdapter(c *elasticache.Client) elasticacheClientAdapter {
|
||||
return elasticacheClientAdapter{
|
||||
describeServerlessCaches: c.DescribeServerlessCaches,
|
||||
describeCacheClusters: c.DescribeCacheClusters,
|
||||
listTagsForResource: c.ListTagsForResource,
|
||||
}
|
||||
}
|
||||
|
||||
func (a elasticacheClientAdapter) DescribeServerlessCaches(ctx context.Context, params *elasticache.DescribeServerlessCachesInput, optFns ...func(*elasticache.Options)) (*elasticache.DescribeServerlessCachesOutput, error) {
|
||||
return a.describeServerlessCaches(ctx, params, optFns...)
|
||||
}
|
||||
|
||||
func (a elasticacheClientAdapter) DescribeCacheClusters(ctx context.Context, params *elasticache.DescribeCacheClustersInput, optFns ...func(*elasticache.Options)) (*elasticache.DescribeCacheClustersOutput, error) {
|
||||
return a.describeCacheClusters(ctx, params, optFns...)
|
||||
}
|
||||
|
||||
func (a elasticacheClientAdapter) ListTagsForResource(ctx context.Context, params *elasticache.ListTagsForResourceInput, optFns ...func(*elasticache.Options)) (*elasticache.ListTagsForResourceOutput, error) {
|
||||
return a.listTagsForResource(ctx, params, optFns...)
|
||||
}
|
||||
|
||||
// ElasticacheDiscovery periodically performs Elasticache-SD requests.
|
||||
// It implements the Discoverer interface.
|
||||
type ElasticacheDiscovery struct {
|
||||
|
|
@ -328,12 +359,12 @@ func (d *ElasticacheDiscovery) initElasticacheClient(ctx context.Context) error
|
|||
cfg.Credentials = aws.NewCredentialsCache(assumeProvider)
|
||||
}
|
||||
|
||||
d.elasticacheClient = elasticache.NewFromConfig(cfg, func(options *elasticache.Options) {
|
||||
d.elasticacheClient = newElastiCacheClientAdapter(elasticache.NewFromConfig(cfg, func(options *elasticache.Options) {
|
||||
if d.cfg.Endpoint != "" {
|
||||
options.BaseEndpoint = &d.cfg.Endpoint
|
||||
}
|
||||
options.HTTPClient = client
|
||||
})
|
||||
}))
|
||||
|
||||
// Test credentials by making a simple API call
|
||||
testCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
|
||||
|
|
|
|||
|
|
@ -119,12 +119,29 @@ func (c *LightsailSDConfig) UnmarshalYAML(unmarshal func(any) error) error {
|
|||
return c.HTTPClientConfig.Validate()
|
||||
}
|
||||
|
||||
// lightsailClientAdapter captures only the Lightsail API calls AWS discovery
|
||||
// uses as method-value closures, keeping the concrete *lightsail.Client out of
|
||||
// any interface-boxed struct field. See ec2ClientAdapter for the full
|
||||
// rationale: this stops the linker from retaining the entire Lightsail API
|
||||
// surface (~3.4 MB).
|
||||
type lightsailClientAdapter struct {
|
||||
getInstances func(ctx context.Context, params *lightsail.GetInstancesInput, optFns ...func(*lightsail.Options)) (*lightsail.GetInstancesOutput, error)
|
||||
}
|
||||
|
||||
func newLightsailClientAdapter(c *lightsail.Client) *lightsailClientAdapter {
|
||||
return &lightsailClientAdapter{getInstances: c.GetInstances}
|
||||
}
|
||||
|
||||
func (a *lightsailClientAdapter) GetInstances(ctx context.Context, params *lightsail.GetInstancesInput, optFns ...func(*lightsail.Options)) (*lightsail.GetInstancesOutput, error) {
|
||||
return a.getInstances(ctx, params, optFns...)
|
||||
}
|
||||
|
||||
// LightsailDiscovery periodically performs Lightsail-SD requests. It implements
|
||||
// the Discoverer interface.
|
||||
type LightsailDiscovery struct {
|
||||
*refresh.Discovery
|
||||
cfg *LightsailSDConfig
|
||||
lightsail *lightsail.Client
|
||||
lightsail *lightsailClientAdapter
|
||||
}
|
||||
|
||||
// NewLightsailDiscovery returns a new LightsailDiscovery which periodically refreshes its targets.
|
||||
|
|
@ -154,7 +171,7 @@ func NewLightsailDiscovery(conf *LightsailSDConfig, opts discovery.DiscovererOpt
|
|||
return d, nil
|
||||
}
|
||||
|
||||
func (d *LightsailDiscovery) lightsailClient(ctx context.Context) (*lightsail.Client, error) {
|
||||
func (d *LightsailDiscovery) lightsailClient(ctx context.Context) (*lightsailClientAdapter, error) {
|
||||
if d.lightsail != nil {
|
||||
return d.lightsail, nil
|
||||
}
|
||||
|
|
@ -198,12 +215,12 @@ func (d *LightsailDiscovery) lightsailClient(ctx context.Context) (*lightsail.Cl
|
|||
cfg.Credentials = aws.NewCredentialsCache(assumeProvider)
|
||||
}
|
||||
|
||||
d.lightsail = lightsail.NewFromConfig(cfg, func(options *lightsail.Options) {
|
||||
d.lightsail = newLightsailClientAdapter(lightsail.NewFromConfig(cfg, func(options *lightsail.Options) {
|
||||
if d.cfg.Endpoint != "" {
|
||||
options.BaseEndpoint = &d.cfg.Endpoint
|
||||
}
|
||||
options.HTTPClient = httpClient
|
||||
})
|
||||
}))
|
||||
|
||||
return d.lightsail, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -158,6 +158,36 @@ type mskClient interface {
|
|||
ListNodes(context.Context, *kafka.ListNodesInput, ...func(*kafka.Options)) (*kafka.ListNodesOutput, error)
|
||||
}
|
||||
|
||||
// mskClientAdapter captures only the MSK (Kafka) API calls AWS discovery uses
|
||||
// as method-value closures, keeping the concrete *kafka.Client out of any
|
||||
// interface-boxed struct field. See ec2ClientAdapter for the full rationale:
|
||||
// this stops the linker from retaining the entire MSK API surface (~1.4 MB).
|
||||
type mskClientAdapter struct {
|
||||
describeClusterV2 func(context.Context, *kafka.DescribeClusterV2Input, ...func(*kafka.Options)) (*kafka.DescribeClusterV2Output, error)
|
||||
listClustersV2 func(context.Context, *kafka.ListClustersV2Input, ...func(*kafka.Options)) (*kafka.ListClustersV2Output, error)
|
||||
listNodes func(context.Context, *kafka.ListNodesInput, ...func(*kafka.Options)) (*kafka.ListNodesOutput, error)
|
||||
}
|
||||
|
||||
func newMSKClientAdapter(c *kafka.Client) mskClientAdapter {
|
||||
return mskClientAdapter{
|
||||
describeClusterV2: c.DescribeClusterV2,
|
||||
listClustersV2: c.ListClustersV2,
|
||||
listNodes: c.ListNodes,
|
||||
}
|
||||
}
|
||||
|
||||
func (a mskClientAdapter) DescribeClusterV2(ctx context.Context, params *kafka.DescribeClusterV2Input, optFns ...func(*kafka.Options)) (*kafka.DescribeClusterV2Output, error) {
|
||||
return a.describeClusterV2(ctx, params, optFns...)
|
||||
}
|
||||
|
||||
func (a mskClientAdapter) ListClustersV2(ctx context.Context, params *kafka.ListClustersV2Input, optFns ...func(*kafka.Options)) (*kafka.ListClustersV2Output, error) {
|
||||
return a.listClustersV2(ctx, params, optFns...)
|
||||
}
|
||||
|
||||
func (a mskClientAdapter) ListNodes(ctx context.Context, params *kafka.ListNodesInput, optFns ...func(*kafka.Options)) (*kafka.ListNodesOutput, error) {
|
||||
return a.listNodes(ctx, params, optFns...)
|
||||
}
|
||||
|
||||
// MSKDiscovery periodically performs MSK-SD requests. It implements
|
||||
// the Discoverer interface.
|
||||
type MSKDiscovery struct {
|
||||
|
|
@ -240,12 +270,12 @@ func (d *MSKDiscovery) initMskClient(ctx context.Context) error {
|
|||
cfg.Credentials = aws.NewCredentialsCache(assumeProvider)
|
||||
}
|
||||
|
||||
d.msk = kafka.NewFromConfig(cfg, func(options *kafka.Options) {
|
||||
d.msk = newMSKClientAdapter(kafka.NewFromConfig(cfg, func(options *kafka.Options) {
|
||||
if d.cfg.Endpoint != "" {
|
||||
options.BaseEndpoint = &d.cfg.Endpoint
|
||||
}
|
||||
options.HTTPClient = client
|
||||
})
|
||||
}))
|
||||
|
||||
// Test credentials by making a simple API call
|
||||
testCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
|
||||
|
|
|
|||
|
|
@ -276,6 +276,30 @@ type rdsClient interface {
|
|||
DescribeDBInstances(context.Context, *rds.DescribeDBInstancesInput, ...func(*rds.Options)) (*rds.DescribeDBInstancesOutput, error)
|
||||
}
|
||||
|
||||
// rdsClientAdapter captures only the RDS API calls AWS discovery uses as
|
||||
// method-value closures, keeping the concrete *rds.Client out of any
|
||||
// interface-boxed struct field. See ec2ClientAdapter for the full rationale:
|
||||
// this stops the linker from retaining the entire RDS API surface (~5 MB).
|
||||
type rdsClientAdapter struct {
|
||||
describeDBClusters func(context.Context, *rds.DescribeDBClustersInput, ...func(*rds.Options)) (*rds.DescribeDBClustersOutput, error)
|
||||
describeDBInstances func(context.Context, *rds.DescribeDBInstancesInput, ...func(*rds.Options)) (*rds.DescribeDBInstancesOutput, error)
|
||||
}
|
||||
|
||||
func newRDSClientAdapter(c *rds.Client) rdsClientAdapter {
|
||||
return rdsClientAdapter{
|
||||
describeDBClusters: c.DescribeDBClusters,
|
||||
describeDBInstances: c.DescribeDBInstances,
|
||||
}
|
||||
}
|
||||
|
||||
func (a rdsClientAdapter) DescribeDBClusters(ctx context.Context, params *rds.DescribeDBClustersInput, optFns ...func(*rds.Options)) (*rds.DescribeDBClustersOutput, error) {
|
||||
return a.describeDBClusters(ctx, params, optFns...)
|
||||
}
|
||||
|
||||
func (a rdsClientAdapter) DescribeDBInstances(ctx context.Context, params *rds.DescribeDBInstancesInput, optFns ...func(*rds.Options)) (*rds.DescribeDBInstancesOutput, error) {
|
||||
return a.describeDBInstances(ctx, params, optFns...)
|
||||
}
|
||||
|
||||
// RDSDiscovery periodically performs RDS-SD requests. It implements
|
||||
// the Discoverer interface.
|
||||
type RDSDiscovery struct {
|
||||
|
|
@ -358,12 +382,12 @@ func (d *RDSDiscovery) initRdsClient(ctx context.Context) error {
|
|||
cfg.Credentials = aws.NewCredentialsCache(assumeProvider)
|
||||
}
|
||||
|
||||
d.rds = rds.NewFromConfig(cfg, func(options *rds.Options) {
|
||||
d.rds = newRDSClientAdapter(rds.NewFromConfig(cfg, func(options *rds.Options) {
|
||||
if d.cfg.Endpoint != "" {
|
||||
options.BaseEndpoint = &d.cfg.Endpoint
|
||||
}
|
||||
options.HTTPClient = client
|
||||
})
|
||||
}))
|
||||
|
||||
// Test credentials by making a simple API call
|
||||
testCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
|
||||
|
|
|
|||
Loading…
Reference in a new issue