discovery/azure: keep SD clients from defeating dead-code elimination

The azureClient struct held the armcompute and armnetwork SDK clients as
concrete fields while satisfying the client interface. Once such a client is
reachable through an interface, Go's linker conservatively retains every
exported method of the concrete type plus the entire (de)serializer graph those
operations drag in, even though discovery calls only a handful of them.

Wrap each SDK client in a small adapter that captures only the operations
discovery uses as method-value closures, and box the adapters instead of the raw
clients. The concrete clients then live only inside closure contexts, which
reflection cannot traverse, so dead-code elimination drops the unused
operations.

This drops the retained operations per client from ~60 down to the 2-3 actually
used (UsedInIface markers go from 244+66 to 0), shrinking both the prometheus
and promtool binaries by ~3.2 MB each. No functional or API change.

Signed-off-by: Julien Pivotto <291750+roidelapluie@users.noreply.github.com>
This commit is contained in:
Julien Pivotto 2026-05-29 16:17:03 +02:00
parent 55226fb8b2
commit 5040dd9e68
2 changed files with 107 additions and 12 deletions

View file

@ -30,6 +30,7 @@ import (
"github.com/Azure/azure-sdk-for-go/sdk/azcore/arm"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5"
@ -221,15 +222,105 @@ type client interface {
// azureClient represents multiple Azure Resource Manager providers.
type azureClient struct {
nic *armnetwork.InterfacesClient
vm *armcompute.VirtualMachinesClient
vmss *armcompute.VirtualMachineScaleSetsClient
vmssvm *armcompute.VirtualMachineScaleSetVMsClient
nic interfacesClientAdapter
vm virtualMachinesClientAdapter
vmss virtualMachineScaleSetsClientAdapter
vmssvm virtualMachineScaleSetVMsClientAdapter
logger *slog.Logger
}
var _ client = &azureClient{}
// The *ClientAdapter types below capture only the operations discovery uses as
// closures, hiding the concrete SDK clients from reflection so dead-code
// elimination can drop the unused operations and shrink the binary.
// virtualMachinesClientAdapter adapts *armcompute.VirtualMachinesClient.
type virtualMachinesClientAdapter struct {
newListAllPager func(options *armcompute.VirtualMachinesClientListAllOptions) *runtime.Pager[armcompute.VirtualMachinesClientListAllResponse]
newListPager func(resourceGroupName string, options *armcompute.VirtualMachinesClientListOptions) *runtime.Pager[armcompute.VirtualMachinesClientListResponse]
}
func newVirtualMachinesClientAdapter(c *armcompute.VirtualMachinesClient) virtualMachinesClientAdapter {
return virtualMachinesClientAdapter{
newListAllPager: c.NewListAllPager,
newListPager: c.NewListPager,
}
}
// NewListAllPager lists all virtual machines in the subscription.
func (a virtualMachinesClientAdapter) NewListAllPager(options *armcompute.VirtualMachinesClientListAllOptions) *runtime.Pager[armcompute.VirtualMachinesClientListAllResponse] {
return a.newListAllPager(options)
}
// NewListPager lists the virtual machines in a resource group.
func (a virtualMachinesClientAdapter) NewListPager(resourceGroupName string, options *armcompute.VirtualMachinesClientListOptions) *runtime.Pager[armcompute.VirtualMachinesClientListResponse] {
return a.newListPager(resourceGroupName, options)
}
// virtualMachineScaleSetsClientAdapter adapts *armcompute.VirtualMachineScaleSetsClient.
type virtualMachineScaleSetsClientAdapter struct {
newListAllPager func(options *armcompute.VirtualMachineScaleSetsClientListAllOptions) *runtime.Pager[armcompute.VirtualMachineScaleSetsClientListAllResponse]
newListPager func(resourceGroupName string, options *armcompute.VirtualMachineScaleSetsClientListOptions) *runtime.Pager[armcompute.VirtualMachineScaleSetsClientListResponse]
}
func newVirtualMachineScaleSetsClientAdapter(c *armcompute.VirtualMachineScaleSetsClient) virtualMachineScaleSetsClientAdapter {
return virtualMachineScaleSetsClientAdapter{
newListAllPager: c.NewListAllPager,
newListPager: c.NewListPager,
}
}
// NewListAllPager lists all scale sets in the subscription.
func (a virtualMachineScaleSetsClientAdapter) NewListAllPager(options *armcompute.VirtualMachineScaleSetsClientListAllOptions) *runtime.Pager[armcompute.VirtualMachineScaleSetsClientListAllResponse] {
return a.newListAllPager(options)
}
// NewListPager lists the scale sets in a resource group.
func (a virtualMachineScaleSetsClientAdapter) NewListPager(resourceGroupName string, options *armcompute.VirtualMachineScaleSetsClientListOptions) *runtime.Pager[armcompute.VirtualMachineScaleSetsClientListResponse] {
return a.newListPager(resourceGroupName, options)
}
// virtualMachineScaleSetVMsClientAdapter adapts *armcompute.VirtualMachineScaleSetVMsClient.
type virtualMachineScaleSetVMsClientAdapter struct {
newListPager func(resourceGroupName, virtualMachineScaleSetName string, options *armcompute.VirtualMachineScaleSetVMsClientListOptions) *runtime.Pager[armcompute.VirtualMachineScaleSetVMsClientListResponse]
}
func newVirtualMachineScaleSetVMsClientAdapter(c *armcompute.VirtualMachineScaleSetVMsClient) virtualMachineScaleSetVMsClientAdapter {
return virtualMachineScaleSetVMsClientAdapter{
newListPager: c.NewListPager,
}
}
// NewListPager lists the virtual machines of a scale set.
func (a virtualMachineScaleSetVMsClientAdapter) NewListPager(resourceGroupName, virtualMachineScaleSetName string, options *armcompute.VirtualMachineScaleSetVMsClientListOptions) *runtime.Pager[armcompute.VirtualMachineScaleSetVMsClientListResponse] {
return a.newListPager(resourceGroupName, virtualMachineScaleSetName, options)
}
// interfacesClientAdapter adapts *armnetwork.InterfacesClient.
type interfacesClientAdapter struct {
get func(ctx context.Context, resourceGroupName, networkInterfaceName string, options *armnetwork.InterfacesClientGetOptions) (armnetwork.InterfacesClientGetResponse, error)
getVirtualMachineScaleSetNetworkInterface func(ctx context.Context, resourceGroupName, virtualMachineScaleSetName, virtualMachineIndex, networkInterfaceName string, options *armnetwork.InterfacesClientGetVirtualMachineScaleSetNetworkInterfaceOptions) (armnetwork.InterfacesClientGetVirtualMachineScaleSetNetworkInterfaceResponse, error)
}
func newInterfacesClientAdapter(c *armnetwork.InterfacesClient) interfacesClientAdapter {
return interfacesClientAdapter{
get: c.Get,
getVirtualMachineScaleSetNetworkInterface: c.GetVirtualMachineScaleSetNetworkInterface,
}
}
// Get retrieves a network interface.
func (a interfacesClientAdapter) Get(ctx context.Context, resourceGroupName, networkInterfaceName string, options *armnetwork.InterfacesClientGetOptions) (armnetwork.InterfacesClientGetResponse, error) {
return a.get(ctx, resourceGroupName, networkInterfaceName, options)
}
// GetVirtualMachineScaleSetNetworkInterface retrieves a scale set VM network interface.
func (a interfacesClientAdapter) GetVirtualMachineScaleSetNetworkInterface(ctx context.Context, resourceGroupName, virtualMachineScaleSetName, virtualMachineIndex, networkInterfaceName string, options *armnetwork.InterfacesClientGetVirtualMachineScaleSetNetworkInterfaceOptions) (armnetwork.InterfacesClientGetVirtualMachineScaleSetNetworkInterfaceResponse, error) {
return a.getVirtualMachineScaleSetNetworkInterface(ctx, resourceGroupName, virtualMachineScaleSetName, virtualMachineIndex, networkInterfaceName, options)
}
// createAzureClient is a helper method for creating an Azure compute client to ARM.
func (d *Discovery) createAzureClient() (client, error) {
cloudConfiguration, err := CloudConfigurationFromName(d.cfg.Environment)
@ -264,25 +355,29 @@ func (d *Discovery) createAzureClient() (client, error) {
},
}
c.vm, err = armcompute.NewVirtualMachinesClient(d.cfg.SubscriptionID, credential, options)
vmClient, err := armcompute.NewVirtualMachinesClient(d.cfg.SubscriptionID, credential, options)
if err != nil {
return &azureClient{}, err
}
c.vm = newVirtualMachinesClientAdapter(vmClient)
c.nic, err = armnetwork.NewInterfacesClient(d.cfg.SubscriptionID, credential, options)
nicClient, err := armnetwork.NewInterfacesClient(d.cfg.SubscriptionID, credential, options)
if err != nil {
return &azureClient{}, err
}
c.nic = newInterfacesClientAdapter(nicClient)
c.vmss, err = armcompute.NewVirtualMachineScaleSetsClient(d.cfg.SubscriptionID, credential, options)
vmssClient, err := armcompute.NewVirtualMachineScaleSetsClient(d.cfg.SubscriptionID, credential, options)
if err != nil {
return &azureClient{}, err
}
c.vmss = newVirtualMachineScaleSetsClientAdapter(vmssClient)
c.vmssvm, err = armcompute.NewVirtualMachineScaleSetVMsClient(d.cfg.SubscriptionID, credential, options)
vmssvmClient, err := armcompute.NewVirtualMachineScaleSetVMsClient(d.cfg.SubscriptionID, credential, options)
if err != nil {
return &azureClient{}, err
}
c.vmssvm = newVirtualMachineScaleSetVMsClientAdapter(vmssvmClient)
return &c, nil
}

View file

@ -738,10 +738,10 @@ func createMockAzureClient(t *testing.T, vmResp []armcompute.VirtualMachinesClie
return &mockAzureClient{
azureClient: azureClient{
vm: vmClient,
vmss: vmssClient,
vmssvm: vmssvmClient,
nic: interfacesClient,
vm: newVirtualMachinesClientAdapter(vmClient),
vmss: newVirtualMachineScaleSetsClientAdapter(vmssClient),
vmssvm: newVirtualMachineScaleSetVMsClientAdapter(vmssvmClient),
nic: newInterfacesClientAdapter(interfacesClient),
logger: logger,
},
}