Skip to content

Commit

Permalink
Merge pull request cloudfoundry#651 from Justin-W/issue-644
Browse files Browse the repository at this point in the history
  • Loading branch information
rkoster authored Jan 27, 2022
2 parents 79b2146 + 0239575 commit 828817f
Show file tree
Hide file tree
Showing 17 changed files with 1,665 additions and 231 deletions.
22 changes: 19 additions & 3 deletions src/bosh_azure_cpi/lib/cloud/azure/models/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,30 @@

module Bosh::AzureCloud
class LoadBalancerConfig
attr_reader :name, :resource_group_name
def initialize(resource_group_name, name)
attr_reader :name, :resource_group_name, :backend_pool_name

def initialize(resource_group_name, name, backend_pool_name = nil)
@resource_group_name = resource_group_name
@name = name
@backend_pool_name = backend_pool_name
end

def to_s
"name: #{@name}, resource_group_name: #{@resource_group_name}, backend_pool_name: #{@backend_pool_name}"
end
end

class ApplicationGatewayConfig
attr_reader :name, :resource_group_name, :backend_pool_name

def initialize(resource_group_name, name, backend_pool_name = nil)
@resource_group_name = resource_group_name
@name = name
@backend_pool_name = backend_pool_name
end

def to_s
"name: #{@name}, resource_group_name: #{@resource_group_name}"
"name: #{@name}, resource_group_name: #{@resource_group_name}, backend_pool_name: #{@backend_pool_name}"
end
end

Expand Down
80 changes: 65 additions & 15 deletions src/bosh_azure_cpi/lib/cloud/azure/models/vm_cloud_props.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ class VMCloudProps
attr_reader :root_disk, :ephemeral_disk, :caching
attr_reader :availability_zone
attr_reader :availability_set
attr_reader :load_balancer
attr_reader :application_gateway
attr_reader :load_balancers
attr_reader :application_gateways
attr_reader :managed_identity
attr_reader :security_group
attr_reader :application_security_groups
Expand All @@ -26,6 +26,8 @@ class VMCloudProps

AVAILABILITY_SET_KEY = 'availability_set'
LOAD_BALANCER_KEY = 'load_balancer'
APPLICATION_GATEWAY_KEY = 'application_gateway'
BACKEND_POOL_NAME_KEY = 'backend_pool_name'
RESOURCE_GROUP_NAME_KEY = 'resource_group_name'
NAME_KEY = 'name'

Expand Down Expand Up @@ -56,8 +58,9 @@ def initialize(vm_properties, global_azure_config)
@availability_set = _parse_availability_set_config(vm_properties, global_azure_config)
cloud_error("Only one of 'availability_zone' and 'availability_set' is allowed to be configured for the VM but you have configured both.") if !@availability_zone.nil? && !@availability_set.name.nil?

@load_balancer = _parse_load_balancer_config(vm_properties, global_azure_config)
@application_gateway = vm_properties['application_gateway']
@load_balancers = _parse_load_balancer_config(vm_properties, global_azure_config)

@application_gateways = _parse_application_gateway_config(vm_properties)

@managed_identity = global_azure_config.default_managed_identity
managed_identity_hash = vm_properties.fetch('managed_identity', nil)
Expand Down Expand Up @@ -99,21 +102,68 @@ def _default_fault_domain_count(global_azure_config)
end
end

# @return [Array<Bosh::AzureCloud::LoadBalancerConfig>,nil]
def _parse_load_balancer_config(vm_properties, global_azure_config)
if vm_properties[LOAD_BALANCER_KEY].is_a?(Hash)
resource_group_name = vm_properties[LOAD_BALANCER_KEY][RESOURCE_GROUP_NAME_KEY] || global_azure_config.resource_group_name
Bosh::AzureCloud::LoadBalancerConfig.new(
resource_group_name,
vm_properties[LOAD_BALANCER_KEY][NAME_KEY]
)
else
Bosh::AzureCloud::LoadBalancerConfig.new(
global_azure_config.resource_group_name,
vm_properties[LOAD_BALANCER_KEY]
)
load_balancer_config = vm_properties[LOAD_BALANCER_KEY]

return nil unless load_balancer_config

cloud_error("Property '#{LOAD_BALANCER_KEY}' must be a String, Hash, or Array.") unless load_balancer_config.is_a?(String) || load_balancer_config.is_a?(Hash) || load_balancer_config.is_a?(Array)

load_balancer_configs = load_balancer_config.is_a?(Array) ? load_balancer_config : [load_balancer_config]
load_balancers = Array(load_balancer_configs).flat_map do |lbc|
if lbc.is_a?(Hash)
load_balancer_names = lbc[NAME_KEY]
resource_group_name = lbc[RESOURCE_GROUP_NAME_KEY]
backend_pool_name = lbc[BACKEND_POOL_NAME_KEY]
else
load_balancer_names = lbc
resource_group_name = nil
backend_pool_name = nil
end
String(load_balancer_names).split(',').map do |load_balancer_name|
Bosh::AzureCloud::LoadBalancerConfig.new(
resource_group_name || global_azure_config.resource_group_name,
load_balancer_name,
backend_pool_name
)
end
end
load_balancers.compact
end

# @return [Array<Bosh::AzureCloud::ApplicationGatewayConfig>,nil]
def _parse_application_gateway_config(vm_properties)
application_gateway_config = vm_properties[APPLICATION_GATEWAY_KEY]

return nil unless application_gateway_config

cloud_error("Property '#{APPLICATION_GATEWAY_KEY}' must be a String, Hash, or Array.") unless application_gateway_config.is_a?(String) || application_gateway_config.is_a?(Hash) || application_gateway_config.is_a?(Array)

application_gateway_configs = application_gateway_config.is_a?(Array) ? application_gateway_config : [application_gateway_config]
application_gateways = Array(application_gateway_configs).flat_map do |agwc|
if agwc.is_a?(Hash)
application_gateway_names = agwc[NAME_KEY]
resource_group_name = agwc[RESOURCE_GROUP_NAME_KEY]
backend_pool_name = agwc[BACKEND_POOL_NAME_KEY]
else
application_gateway_names = agwc
resource_group_name = nil
backend_pool_name = nil
end
String(application_gateway_names).split(',').map do |application_gateway_name|
Bosh::AzureCloud::ApplicationGatewayConfig.new(
# NOTE: It is OK for the resource_group_name to be `nil` here. The nil will be defaulted elsewhere (if needed). And leaving it nil makes the specs simpler.
resource_group_name,
application_gateway_name,
backend_pool_name
)
end
end
application_gateways.compact
end

# @return [Bosh::AzureCloud::AvailabilitySetConfig]
def _parse_availability_set_config(vm_properties, global_azure_config)
if vm_properties[AVAILABILITY_SET_KEY].is_a?(Hash)
platform_update_domain_count = vm_properties[AVAILABILITY_SET_KEY]['platform_update_domain_count'] || _default_update_domain_count(global_azure_config)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ module Bosh::AzureCloud
# VM can have multiple network interfaces attached to it.
# The VM size determines the number of NICs that you can create for a VM, please refer to
# https://azure.microsoft.com/en-us/documentation/articles/virtual-machines-linux-sizes/ for the max number of NICs for different VM size.
# When there are multiple netowrks, you must have and only have 1 primary network specified. @networks[0] will be picked as the primary network.
# When there are multiple networks, you must have and only have 1 primary network specified. @networks[0] will be picked as the primary network.
#

class NetworkConfigurator
Expand Down Expand Up @@ -64,6 +64,7 @@ def initialize(azure_config, spec)
#
unless network.nil?
if network.has_default_gateway?
# make it the first network, so that it is the Primary
@networks.insert(0, network)
else
@networks.push(network)
Expand Down
81 changes: 50 additions & 31 deletions src/bosh_azure_cpi/lib/cloud/azure/restapi/azure_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@ def create_virtual_machine(resource_group_name, vm_params, network_interfaces, a
network_interfaces_params.push(
'id' => network_interface[:id],
'properties' => {
# NOTE: The first NIC is the Primary/Gateway network. See: `Bosh::AzureCloud::NetworkConfigurator.initialize`.
'primary' => index.zero?
}
)
Expand Down Expand Up @@ -1334,13 +1335,13 @@ def delete_public_ip(resource_group_name, name)
# Network/Load Balancer

# Get a load balancer's information
# @param [String,nil] resource_group_name - The load balancer's resource group name.
# @param [String] name - Name of load balancer.
#
# @return [Hash]
#
# @See https://docs.microsoft.com/en-us/rest/api/load-balancer/loadbalancers/get
#

def get_load_balancer_by_name(resource_group_name, name)
url = rest_api_url(REST_API_PROVIDER_NETWORK, REST_API_LOAD_BALANCERS, resource_group_name: resource_group_name, name: name)
_get_load_balancer(url)
Expand All @@ -1355,6 +1356,7 @@ def get_load_balancer_by_name(resource_group_name, name)
#
def _get_load_balancer(url)
load_balancer = nil
# see: https://docs.microsoft.com/en-us/rest/api/load-balancer/load-balancers/get#loadbalancer
result = get_resource_by_id(url)
unless result.nil?
load_balancer = {}
Expand All @@ -1379,6 +1381,7 @@ def _get_load_balancer(url)
load_balancer[:frontend_ip_configurations].push(ip)
end

# see: https://docs.microsoft.com/en-us/rest/api/load-balancer/load-balancers/get#backendaddresspool
backend = properties['backendAddressPools']
load_balancer[:backend_address_pools] = []
backend.each do |backend_ip|
Expand Down Expand Up @@ -1417,8 +1420,8 @@ def _get_load_balancer(url)
# * +:dns_servers - Array. DNS servers.
# * +:network_security_group - Hash. The network security group which the network interface is bound to.
# * +:application_security_groups - Array. The application security groups which the network interface is bound to.
# * +:load_balancer - Hash. The load balancer which the network interface is bound to.
# * +:application_gateway - Hash. The application gateway which the network interface is bound to.
# * +:load_balancers - Array<Hash>. The load balancers which the network interface is bound to. (see: Bosh::AzureCloud::VMManager._get_load_balancers)
# * +:application_gateways - Array<Hash>. The application gateways which the network interface is bound to. (see: Bosh::AzureCloud::VMManager._get_application_gateways)
#
# @return [Boolean]
#
Expand Down Expand Up @@ -1461,26 +1464,26 @@ def create_network_interface(resource_group_name, nic_params)
end
interface['properties']['ipConfigurations'][0]['properties']['applicationSecurityGroups'] = application_security_groups unless application_security_groups.empty?

load_balancer = nic_params[:load_balancer]
unless load_balancer.nil?
backend_pools = load_balancer.collect { |single_load_balancer| {:id => single_load_balancer[:backend_address_pools][0][:id]} }
# see: Bosh::AzureCloud::VMManager._get_load_balancers
load_balancers = nic_params[:load_balancers]
unless load_balancers.nil?
backend_pools = load_balancers.map { |load_balancer| {:id => load_balancer[:backend_address_pools][0][:id]} }
inbound_nat_rules = Array.new
load_balancer.each do |single_load_balancer|
unless single_load_balancer[:frontend_ip_configurations][0][:inbound_nat_rules].nil?
inbound_nat_rules += single_load_balancer[:frontend_ip_configurations][0][:inbound_nat_rules]
load_balancers.each do |load_balancer|
unless load_balancer[:frontend_ip_configurations][0][:inbound_nat_rules].nil?
inbound_nat_rules += load_balancer[:frontend_ip_configurations][0][:inbound_nat_rules]
end
end
interface['properties']['ipConfigurations'][0]['properties']['loadBalancerBackendAddressPools'] = backend_pools
interface['properties']['ipConfigurations'][0]['properties']['loadBalancerInboundNatRules'] = inbound_nat_rules
end

application_gateway = nic_params[:application_gateway]
unless application_gateway.nil?
interface['properties']['ipConfigurations'][0]['properties']['applicationGatewayBackendAddressPools'] = [
{
'id' => application_gateway[:backend_address_pools][0][:id]
}
]
# see: Bosh::AzureCloud::VMManager._get_application_gateways
application_gateways = nic_params[:application_gateways]
unless application_gateways.nil?
# NOTE: backend_address_pools[0] should always be used. (When `application_gateway/backend_pool_name` is specified, the named pool will always be first here.)
backend_pools = application_gateways.map { |application_gateway| {:id => application_gateway[:backend_address_pools][0][:id]} }
interface['properties']['ipConfigurations'][0]['properties']['applicationGatewayBackendAddressPools'] = backend_pools
end

http_put(url, interface)
Expand Down Expand Up @@ -1619,14 +1622,15 @@ def get_network_subnet(url)
# Network/Application Gateway

# Get an application gateway's information
# @param [String,nil] resource_group_name - The application gateway's resource group name.
# @param [String] name - Name of application gateway.
#
# @return [Hash]
#
# @See https://docs.microsoft.com/en-us/rest/api/application-gateway/applicationgateways/get
#
def get_application_gateway_by_name(name)
url = rest_api_url(REST_API_PROVIDER_NETWORK, REST_API_APPLICATION_GATEWAYS, name: name)
def get_application_gateway_by_name(resource_group_name, name)
url = rest_api_url(REST_API_PROVIDER_NETWORK, REST_API_APPLICATION_GATEWAYS, resource_group_name: resource_group_name, name: name)
get_application_gateway(url)
end

Expand All @@ -1639,6 +1643,7 @@ def get_application_gateway_by_name(name)
#
def get_application_gateway(url)
application_gateway = nil
# see: https://docs.microsoft.com/en-us/rest/api/application-gateway/application-gateways/get#applicationgateway
result = get_resource_by_id(url)
unless result.nil?
application_gateway = {}
Expand All @@ -1648,11 +1653,15 @@ def get_application_gateway(url)
application_gateway[:tags] = result['tags']

properties = result['properties']
# see: https://docs.microsoft.com/en-us/rest/api/application-gateway/application-gateways/get#applicationgatewaybackendaddresspool
backend = properties['backendAddressPools']
application_gateway[:backend_address_pools] = []
backend.each do |backend_ip|
ip = {}
ip[:id] = backend_ip['id']
ip[:name] = backend_ip['name']
ip[:id] = backend_ip['id']
ip[:provisioning_state] = backend_ip['properties']['provisioningState']
ip[:backend_ip_configurations] = backend_ip['properties']['backendIPConfigurations']
application_gateway[:backend_address_pools].push(ip)
end
end
Expand Down Expand Up @@ -2057,21 +2066,31 @@ def parse_network_interface(result, recursive: true)
{ id: ip_configuration_properties['publicIPAddress']['id'] }
end
end
unless ip_configuration_properties['loadBalancerBackendAddressPools'].nil?
if recursive
names = _parse_name_from_id(ip_configuration_properties['loadBalancerBackendAddressPools'][0]['id'])
interface[:load_balancer] = get_load_balancer_by_name(names[:resource_group_name], names[:resource_name])
else
interface[:load_balancer] = { id: ip_configuration_properties['loadBalancerBackendAddressPools'][0]['id'] }
load_balancer_backend_pools = ip_configuration_properties['loadBalancerBackendAddressPools']
unless load_balancer_backend_pools.nil?
load_balancers = load_balancer_backend_pools.map do |lb_backend_pool|
if recursive
names = _parse_name_from_id(lb_backend_pool['id'])
load_balancer = get_load_balancer_by_name(names[:resource_group_name], names[:resource_name])
else
load_balancer = { id: lb_backend_pool['id'] }
end
load_balancer
end
interface[:load_balancers] = load_balancers
end
unless ip_configuration_properties['applicationGatewayBackendAddressPools'].nil?
if recursive
names = _parse_name_from_id(ip_configuration_properties['applicationGatewayBackendAddressPools'][0]['id'])
interface[:application_gateway] = get_application_gateway_by_name(names[:resource_name])
else
interface[:application_gateway] = { id: ip_configuration_properties['applicationGatewayBackendAddressPools'][0]['id'] }
application_gateway_backend_pools = ip_configuration_properties['applicationGatewayBackendAddressPools']
unless application_gateway_backend_pools.nil?
application_gateways = application_gateway_backend_pools.map do |agw_backend_pool|
if recursive
names = _parse_name_from_id(agw_backend_pool['id'])
application_gateway = get_application_gateway_by_name(names[:resource_group_name], names[:resource_name])
else
application_gateway = { id: agw_backend_pool['id'] }
end
application_gateway
end
interface[:application_gateways] = application_gateways
end
unless ip_configuration_properties['applicationSecurityGroups'].nil?
asgs_properties = ip_configuration_properties['applicationSecurityGroups']
Expand Down
Loading

0 comments on commit 828817f

Please sign in to comment.