Files
threshold-plugin-azure/plugin/network_processor.go
T
hashicorp-copywrite[bot] d7826fb66c [COMPLIANCE] Add Copyright and License Headers (#26)
Co-authored-by: hashicorp-copywrite[bot] <110428419+hashicorp-copywrite[bot]@users.noreply.github.com>
2025-02-04 09:56:48 -08:00

281 lines
7.5 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package plugin
import (
"context"
"fmt"
"strings"
"github.com/Azure/azure-sdk-for-go/profiles/latest/compute/mgmt/compute"
"github.com/Azure/azure-sdk-for-go/profiles/latest/network/mgmt/network"
)
type networkInfo struct {
IpAddresses []string
}
// Common interface for IP configuration processing
type networkProcessor interface {
processIPConfigurations(ctx context.Context, ipConfigs []network.InterfaceIPConfiguration, ifName string) (*networkInfo, error)
getPublicIPAddress(ctx context.Context, publicIPRef *network.PublicIPAddress, ipConf *network.InterfaceIPConfiguration, ifName string) (string, error)
getNetworkInterface(ctx context.Context, interfaceID string) (*network.Interface, string, error)
}
// Standard VM network processor
type vmNetworkProcessor struct {
clients *azureClients
resourceGroup string
}
// VMSS network processor
type vmssNetworkProcessor struct {
clients *azureClients
resourceGroup string
vmssName string
instanceID string
}
func processNetworkProfile(
ctx context.Context,
interfaces []compute.NetworkInterfaceReference,
processor networkProcessor,
targetInfo *networkInfo,
) error {
for _, ifaceRef := range interfaces {
if ifaceRef.ID == nil {
return fmt.Errorf("nil ID for network interface")
}
iface, ifName, err := processor.getNetworkInterface(ctx, *ifaceRef.ID)
if err != nil {
return err
}
if iface.InterfacePropertiesFormat == nil || iface.InterfacePropertiesFormat.IPConfigurations == nil {
continue
}
networkInfo, err := processor.processIPConfigurations(ctx, *iface.InterfacePropertiesFormat.IPConfigurations, ifName)
if err != nil {
return err
}
targetInfo.IpAddresses = append(targetInfo.IpAddresses, networkInfo.IpAddresses...)
}
return nil
}
func (p *vmNetworkProcessor) getNetworkInterface(ctx context.Context, interfaceID string) (*network.Interface, string, error) {
ifResGroup, ifName, err := splitId(interfaceID, constMsNetworkService, constNetworkInterfacesResource)
if err != nil {
return nil, "", fmt.Errorf("error splitting network interface id %q: %w", interfaceID, err)
}
iface, err := p.clients.ifClient.Get(ctx, ifResGroup, ifName, "")
if err != nil {
return nil, "", fmt.Errorf("error fetching network interface: %w", err)
}
return &iface, ifName, nil
}
func (p *vmNetworkProcessor) processIPConfigurations(
ctx context.Context,
ipConfigs []network.InterfaceIPConfiguration,
ifName string,
) (
*networkInfo,
error,
) {
tempInfo := &networkInfo{}
for _, ipconf := range ipConfigs {
// Process private IP
if ipconf.PrivateIPAddress != nil {
tempInfo.IpAddresses = append(tempInfo.IpAddresses, *ipconf.PrivateIPAddress)
}
// Process public IP if available
if ipconf.PublicIPAddress != nil {
ipAddress, err := p.getPublicIPAddress(ctx, ipconf.PublicIPAddress, &ipconf, ifName)
if err != nil {
return nil, err
}
tempInfo.IpAddresses = append(tempInfo.IpAddresses, ipAddress)
}
}
return tempInfo, nil
}
func (p *vmNetworkProcessor) getPublicIPAddress(
ctx context.Context,
publicIP *network.PublicIPAddress,
ipConf *network.InterfaceIPConfiguration,
ifName string,
) (
string,
error,
) {
if publicIP.ID == nil {
return "", fmt.Errorf("ip configuration has public IP address info but nil id")
}
ipResGroup, ipName, err := splitId(*publicIP.ID, constMsNetworkService, constPublicIpAddressesResource)
if err != nil {
return "", fmt.Errorf("error splitting public ip address id: %w", err)
}
pipInfo, err := p.clients.pipClient.Get(ctx, ipResGroup, ipName, "")
if err != nil {
return "", fmt.Errorf("error fetching public IP information: %w", err)
}
if pipInfo.PublicIPAddressPropertiesFormat == nil {
return "", fmt.Errorf("nil public ip address properties format for public ip %q", *publicIP.ID)
}
if pipInfo.PublicIPAddressPropertiesFormat.IPAddress == nil {
return "", fmt.Errorf("nil IP address for public ip %q", *publicIP.ID)
}
return *pipInfo.PublicIPAddressPropertiesFormat.IPAddress, nil
}
func (p *vmssNetworkProcessor) getNetworkInterface(ctx context.Context, interfaceID string) (*network.Interface, string, error) {
ifName := extractResourceSuffix(interfaceID)
iface, err := p.clients.ifClient.GetVirtualMachineScaleSetNetworkInterface(
ctx,
p.resourceGroup,
p.vmssName,
p.instanceID,
ifName,
"")
if err != nil {
return nil, "", fmt.Errorf("error fetching VMSS network interface: %w", err)
}
return &iface, ifName, nil
}
func (p *vmssNetworkProcessor) processIPConfigurations(
ctx context.Context,
ipConfigs []network.InterfaceIPConfiguration,
ifName string,
) (
*networkInfo,
error,
) {
tempInfo := &networkInfo{}
for _, ipconf := range ipConfigs {
if ipconf.PrivateIPAddress != nil {
tempInfo.IpAddresses = append(tempInfo.IpAddresses, *ipconf.PrivateIPAddress)
}
if ipconf.PublicIPAddress != nil {
ipAddress, err := p.getPublicIPAddress(ctx, ipconf.PublicIPAddress, &ipconf, ifName)
if err != nil {
return nil, err
}
tempInfo.IpAddresses = append(tempInfo.IpAddresses, ipAddress)
}
}
return tempInfo, nil
}
func (p *vmssNetworkProcessor) getPublicIPAddress(
ctx context.Context,
publicIP *network.PublicIPAddress,
ipConf *network.InterfaceIPConfiguration,
ifName string,
) (
string,
error,
) {
if publicIP == nil || publicIP.ID == nil || ipConf.Name == nil {
return "", fmt.Errorf("invalid public IP configuration")
}
pipName := extractResourceSuffix(*publicIP.ID)
pipInfo, err := p.clients.pipClient.GetVirtualMachineScaleSetPublicIPAddress(
ctx,
p.resourceGroup,
p.vmssName,
p.instanceID,
ifName,
*ipConf.Name,
pipName,
"")
if err != nil {
return "", fmt.Errorf("error fetching public IP information: %w", err)
}
if pipInfo.PublicIPAddressPropertiesFormat == nil {
return "", fmt.Errorf("nil public ip address properties format")
}
if pipInfo.PublicIPAddressPropertiesFormat.IPAddress == nil {
return "", fmt.Errorf("nil IP address for public ip %q", *publicIP.ID)
}
return *pipInfo.PublicIPAddressPropertiesFormat.IPAddress, nil
}
func processVMNetworkInterfaces(
ctx context.Context,
vm compute.VirtualMachine,
clients *azureClients,
resourceGroup string,
netInfo *networkInfo,
) error {
if vm.VirtualMachineProperties == nil || vm.VirtualMachineProperties.NetworkProfile == nil {
return fmt.Errorf("error fetching network profile")
}
processor := &vmNetworkProcessor{
clients: clients,
resourceGroup: resourceGroup,
}
return processNetworkProfile(ctx, *vm.VirtualMachineProperties.NetworkProfile.NetworkInterfaces, processor, netInfo)
}
func processVMSSNetworkInterfaces(
ctx context.Context,
vmssvm compute.VirtualMachineScaleSetVM,
resourceGroup, vmssName string,
clients *azureClients,
netInfo *networkInfo,
) error {
if vmssvm.VirtualMachineScaleSetVMProperties == nil ||
vmssvm.VirtualMachineScaleSetVMProperties.NetworkProfile == nil {
return fmt.Errorf("error fetching network profile")
}
if vmssvm.InstanceID == nil {
return fmt.Errorf("instance ID is nil")
}
processor := &vmssNetworkProcessor{
clients: clients,
resourceGroup: resourceGroup,
vmssName: vmssName,
instanceID: *vmssvm.InstanceID,
}
return processNetworkProfile(ctx,
*vmssvm.VirtualMachineScaleSetVMProperties.NetworkProfile.NetworkInterfaces, processor, netInfo)
}
// extractResourceSuffix extracts the resource name from the end of an Azure resource ID
func extractResourceSuffix(resourceID string) string {
parts := strings.Split(resourceID, "/")
if len(parts) > 0 {
return parts[len(parts)-1]
}
return ""
}