From 5040dd9e68190690d04343d74afa6ceb3d501c26 Mon Sep 17 00:00:00 2001 From: Julien Pivotto <291750+roidelapluie@users.noreply.github.com> Date: Fri, 29 May 2026 16:17:03 +0200 Subject: [PATCH] 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> --- discovery/azure/azure.go | 111 +++++++++++++++++++++++++++++++--- discovery/azure/azure_test.go | 8 +-- 2 files changed, 107 insertions(+), 12 deletions(-) diff --git a/discovery/azure/azure.go b/discovery/azure/azure.go index 0ac9a9af4e..30f9d88703 100644 --- a/discovery/azure/azure.go +++ b/discovery/azure/azure.go @@ -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 } diff --git a/discovery/azure/azure_test.go b/discovery/azure/azure_test.go index dd2eeb0a3f..4b95055ab8 100644 --- a/discovery/azure/azure_test.go +++ b/discovery/azure/azure_test.go @@ -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, }, }