diff --git a/discovery/gce/gce.go b/discovery/gce/gce.go index 6525ef9d90..507a3238a3 100644 --- a/discovery/gce/gce.go +++ b/discovery/gce/gce.go @@ -17,7 +17,6 @@ import ( "context" "errors" "fmt" - "net/http" "strconv" "strings" "time" @@ -113,18 +112,34 @@ func (c *SDConfig) UnmarshalYAML(unmarshal func(any) error) error { return nil } +// instancesLister lists the instances of a project/zone, paging through the +// results and invoking f for each page. +type instancesLister func(ctx context.Context, f func(*compute.InstanceList) error) error + +// newInstancesLister captures *compute.Service in a closure instead of storing +// it on the interface-boxed Discovery struct. This keeps the binary smaller: +// otherwise reflection keeps every Compute method live, defeating dead-code +// elimination. +func newInstancesLister(svc *compute.Service, project, zone, filter string) instancesLister { + isvc := compute.NewInstancesService(svc) + return func(ctx context.Context, f func(*compute.InstanceList) error) error { + ilc := isvc.List(project, zone) + if filter != "" { + ilc = ilc.Filter(filter) + } + return ilc.Pages(ctx, f) + } +} + // Discovery periodically performs GCE-SD requests. It implements // the Discoverer interface. type Discovery struct { *refresh.Discovery - project string - zone string - filter string - client *http.Client - svc *compute.Service - isvc *compute.InstancesService - port int - tagSeparator string + project string + zone string + listInstances instancesLister + port int + tagSeparator string } // NewDiscovery returns a new Discovery which periodically refreshes its targets. @@ -137,20 +152,18 @@ func NewDiscovery(conf SDConfig, opts discovery.DiscovererOptions) (*Discovery, d := &Discovery{ project: conf.Project, zone: conf.Zone, - filter: conf.Filter, port: conf.Port, tagSeparator: conf.TagSeparator, } - var err error - d.client, err = google.DefaultClient(context.Background(), compute.ComputeReadonlyScope) + client, err := google.DefaultClient(context.Background(), compute.ComputeReadonlyScope) if err != nil { return nil, fmt.Errorf("error setting up communication with GCE service: %w", err) } - d.svc, err = compute.NewService(context.Background(), option.WithHTTPClient(d.client)) + svc, err := compute.NewService(context.Background(), option.WithHTTPClient(client)) if err != nil { return nil, fmt.Errorf("error setting up communication with GCE service: %w", err) } - d.isvc = compute.NewInstancesService(d.svc) + d.listInstances = newInstancesLister(svc, conf.Project, conf.Zone, conf.Filter) d.Discovery = refresh.NewDiscovery( refresh.Options{ @@ -170,11 +183,7 @@ func (d *Discovery) refresh(ctx context.Context) ([]*targetgroup.Group, error) { Source: fmt.Sprintf("GCE_%s_%s", d.project, d.zone), } - ilc := d.isvc.List(d.project, d.zone) - if d.filter != "" { - ilc = ilc.Filter(d.filter) - } - err := ilc.Pages(ctx, func(l *compute.InstanceList) error { + err := d.listInstances(ctx, func(l *compute.InstanceList) error { for _, inst := range l.Items { if len(inst.NetworkInterfaces) == 0 { continue