github.phpd.cn/hashicorp/packer@v1.3.2/builder/azure/arm/resource_resolver.go (about)

     1  package arm
     2  
     3  // Code to resolve resources that are required by the API.  These resources
     4  // can most likely be resolved without asking the user, thereby reducing the
     5  // amount of configuration they need to provide.
     6  //
     7  // Resource resolver differs from config retriever because resource resolver
     8  // requires a client to communicate with the Azure API.  A config retriever is
     9  // used to determine values without use of a client.
    10  
    11  import (
    12  	"context"
    13  	"fmt"
    14  	"strings"
    15  
    16  	"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-04-01/compute"
    17  )
    18  
    19  type resourceResolver struct {
    20  	client                          *AzureClient
    21  	findVirtualNetworkResourceGroup func(*AzureClient, string) (string, error)
    22  	findVirtualNetworkSubnet        func(*AzureClient, string, string) (string, error)
    23  }
    24  
    25  func newResourceResolver(client *AzureClient) *resourceResolver {
    26  	return &resourceResolver{
    27  		client:                          client,
    28  		findVirtualNetworkResourceGroup: findVirtualNetworkResourceGroup,
    29  		findVirtualNetworkSubnet:        findVirtualNetworkSubnet,
    30  	}
    31  }
    32  
    33  func (s *resourceResolver) Resolve(c *Config) error {
    34  	if s.shouldResolveResourceGroup(c) {
    35  		resourceGroupName, err := s.findVirtualNetworkResourceGroup(s.client, c.VirtualNetworkName)
    36  		if err != nil {
    37  			return err
    38  		}
    39  
    40  		subnetName, err := s.findVirtualNetworkSubnet(s.client, resourceGroupName, c.VirtualNetworkName)
    41  		if err != nil {
    42  			return err
    43  		}
    44  
    45  		c.VirtualNetworkResourceGroupName = resourceGroupName
    46  		c.VirtualNetworkSubnetName = subnetName
    47  	}
    48  
    49  	if s.shouldResolveManagedImageName(c) {
    50  		image, err := findManagedImageByName(s.client, c.CustomManagedImageName, c.CustomManagedImageResourceGroupName)
    51  		if err != nil {
    52  			return err
    53  		}
    54  
    55  		c.customManagedImageID = *image.ID
    56  	}
    57  
    58  	return nil
    59  }
    60  
    61  func (s *resourceResolver) shouldResolveResourceGroup(c *Config) bool {
    62  	return c.VirtualNetworkName != "" && c.VirtualNetworkResourceGroupName == ""
    63  }
    64  
    65  func (s *resourceResolver) shouldResolveManagedImageName(c *Config) bool {
    66  	return c.CustomManagedImageName != ""
    67  }
    68  
    69  func getResourceGroupNameFromId(id string) string {
    70  	// "/subscriptions/3f499422-dd76-4114-8859-86d526c9deb6/resourceGroups/packer-Resource-Group-yylnwsl30j/providers/...
    71  	xs := strings.Split(id, "/")
    72  	return xs[4]
    73  }
    74  
    75  func findManagedImageByName(client *AzureClient, name, resourceGroupName string) (*compute.Image, error) {
    76  	images, err := client.ImagesClient.ListByResourceGroupComplete(context.TODO(), resourceGroupName)
    77  	if err != nil {
    78  		return nil, err
    79  	}
    80  
    81  	for images.NotDone() {
    82  		image := images.Value()
    83  		if strings.EqualFold(name, *image.Name) {
    84  			return &image, nil
    85  		}
    86  		if err = images.Next(); err != nil {
    87  			return nil, err
    88  		}
    89  	}
    90  
    91  	return nil, fmt.Errorf("Cannot find an image named '%s' in the resource group '%s'", name, resourceGroupName)
    92  }
    93  
    94  func findVirtualNetworkResourceGroup(client *AzureClient, name string) (string, error) {
    95  	virtualNetworks, err := client.VirtualNetworksClient.ListAllComplete(context.TODO())
    96  	if err != nil {
    97  		return "", err
    98  	}
    99  
   100  	resourceGroupNames := make([]string, 0)
   101  	for virtualNetworks.NotDone() {
   102  		virtualNetwork := virtualNetworks.Value()
   103  		if strings.EqualFold(name, *virtualNetwork.Name) {
   104  			rgn := getResourceGroupNameFromId(*virtualNetwork.ID)
   105  			resourceGroupNames = append(resourceGroupNames, rgn)
   106  		}
   107  		if err = virtualNetworks.Next(); err != nil {
   108  			return "", err
   109  		}
   110  	}
   111  
   112  	if len(resourceGroupNames) == 0 {
   113  		return "", fmt.Errorf("Cannot find a resource group with a virtual network called %q", name)
   114  	}
   115  
   116  	if len(resourceGroupNames) > 1 {
   117  		return "", fmt.Errorf("Found multiple resource groups with a virtual network called %q, please use virtual_network_subnet_name and virtual_network_resource_group_name to disambiguate", name)
   118  	}
   119  
   120  	return resourceGroupNames[0], nil
   121  }
   122  
   123  func findVirtualNetworkSubnet(client *AzureClient, resourceGroupName string, name string) (string, error) {
   124  	subnets, err := client.SubnetsClient.List(context.TODO(), resourceGroupName, name)
   125  	if err != nil {
   126  		return "", err
   127  	}
   128  
   129  	subnetList := subnets.Values() // only first page of subnets, but only interested in ==0 or >1
   130  
   131  	if len(subnetList) == 0 {
   132  		return "", fmt.Errorf("Cannot find a subnet in the resource group %q associated with the virtual network called %q", resourceGroupName, name)
   133  	}
   134  
   135  	if len(subnetList) > 1 {
   136  		return "", fmt.Errorf("Found multiple subnets in the resource group %q associated with the virtual network called %q, please use virtual_network_subnet_name and virtual_network_resource_group_name to disambiguate", resourceGroupName, name)
   137  	}
   138  
   139  	subnet := subnetList[0]
   140  	return *subnet.Name, nil
   141  }