Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/aks-preview/HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ If there is no rush to release a new version, please just add a description of t

To release a new version, please select a new version number (usually plus 1 to last patch version, X.Y.Z -> Major.Minor.Patch, more details in `\doc <https://semver.org/>`_), and then add a new section named as the new version number in this file, the content should include the new modifications and everything from the *Pending* section. Finally, update the `VERSION` variable in `setup.py` with this new version number.

0.5.153
++++++
* outbound ip, ipprefix and managed ips in loadbalancerProfile should be mutually exclusive

0.5.152
++++++
* move loadbalancer/natgateway util functions to azure-cli and update reference in aks-preview project.
Expand Down
143 changes: 121 additions & 22 deletions src/aks-preview/azext_aks_preview/_loadbalancer.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,51 +5,150 @@

from types import SimpleNamespace
from knack.log import get_logger
from azure.cli.command_modules.acs._loadbalancer import (
is_load_balancer_profile_provided as _is_load_balancer_profile_provided,
configure_load_balancer_profile as _configure_load_balancer_profile,
from azure.cli.core.azclierror import (
InvalidArgumentValueError,
)


logger = get_logger(__name__)


def update_load_balancer_profile(managed_outbound_ip_count, managed_outbound_ipv6_count, outbound_ips,
outbound_ip_prefixes, outbound_ports, idle_timeout, backend_pool_type, profile, models):
"""parse and update an existing load balancer profile"""
if not (_is_load_balancer_profile_provided(managed_outbound_ip_count, managed_outbound_ipv6_count, outbound_ips,
outbound_ip_prefixes, outbound_ports, idle_timeout) or backend_pool_type):
if not (is_load_balancer_profile_provided(managed_outbound_ip_count, managed_outbound_ipv6_count, outbound_ips,
outbound_ip_prefixes, outbound_ports, idle_timeout) or backend_pool_type):
return profile
if profile is None:
if isinstance(models, SimpleNamespace):
ManagedClusterLoadBalancerProfile = models.ManagedClusterLoadBalancerProfile
else:
ManagedClusterLoadBalancerProfile = models.get("ManagedClusterLoadBalancerProfile")
profile = ManagedClusterLoadBalancerProfile()
return configure_load_balancer_profile(managed_outbound_ip_count, managed_outbound_ipv6_count, outbound_ips,
outbound_ip_prefixes, outbound_ports, idle_timeout, backend_pool_type, profile, models)


def create_load_balancer_profile(managed_outbound_ip_count, managed_outbound_ipv6_count, outbound_ips,
outbound_ip_prefixes, outbound_ports, idle_timeout, backend_pool_type, models):
"""parse and build load balancer profile"""
if not (_is_load_balancer_profile_provided(managed_outbound_ip_count, managed_outbound_ipv6_count, outbound_ips,
outbound_ip_prefixes, outbound_ports, idle_timeout) or backend_pool_type):
if not (is_load_balancer_profile_provided(managed_outbound_ip_count, managed_outbound_ipv6_count, outbound_ips,
outbound_ip_prefixes, outbound_ports, idle_timeout) or backend_pool_type):
return None

if isinstance(models, SimpleNamespace):
ManagedClusterLoadBalancerProfile = models.ManagedClusterLoadBalancerProfile
else:
ManagedClusterLoadBalancerProfile = models.get("ManagedClusterLoadBalancerProfile")
profile = ManagedClusterLoadBalancerProfile()
return configure_load_balancer_profile(managed_outbound_ip_count, managed_outbound_ipv6_count, outbound_ips,
outbound_ip_prefixes, outbound_ports, idle_timeout, backend_pool_type, profile, models)
outbound_ip_prefixes, outbound_ports, idle_timeout, backend_pool_type, None, models)


def configure_load_balancer_profile(managed_outbound_ip_count, managed_outbound_ipv6_count, outbound_ips,
outbound_ip_prefixes, outbound_ports, idle_timeout, backend_pool_type, profile, models):
"""configure a load balancer with customer supplied values"""
if not profile:
if isinstance(models, SimpleNamespace):
ManagedClusterLoadBalancerProfile = models.ManagedClusterLoadBalancerProfile
else:
ManagedClusterLoadBalancerProfile = models.get("ManagedClusterLoadBalancerProfile")
profile = ManagedClusterLoadBalancerProfile()
if any([managed_outbound_ip_count,
managed_outbound_ipv6_count,
outbound_ips,
outbound_ip_prefixes]):
ip_provider = 0

outbound_ip_resources = _get_load_balancer_outbound_ips(outbound_ips, models)
if outbound_ip_resources:
if isinstance(models, SimpleNamespace):
ManagedClusterLoadBalancerProfileOutboundIPs = models.ManagedClusterLoadBalancerProfileOutboundIPs
else:
ManagedClusterLoadBalancerProfileOutboundIPs = models.get(
"ManagedClusterLoadBalancerProfileOutboundIPs"
)
# ips -> i_ps due to track 2 naming issue
profile.outbound_i_ps = ManagedClusterLoadBalancerProfileOutboundIPs(
public_i_ps=outbound_ip_resources
)
profile.managed_outbound_i_ps = None
profile.outbound_ip_prefixes = None
ip_provider += 1
outbound_ip_prefix_resources = _get_load_balancer_outbound_ip_prefixes(outbound_ip_prefixes, models)
if outbound_ip_prefix_resources:
if isinstance(models, SimpleNamespace):
ManagedClusterLoadBalancerProfileOutboundIPPrefixes = (
models.ManagedClusterLoadBalancerProfileOutboundIPPrefixes
)
else:
ManagedClusterLoadBalancerProfileOutboundIPPrefixes = models.get(
"ManagedClusterLoadBalancerProfileOutboundIPPrefixes"
)
profile.outbound_ip_prefixes = ManagedClusterLoadBalancerProfileOutboundIPPrefixes(
public_ip_prefixes=outbound_ip_prefix_resources
)
profile.outbound_i_ps = None
profile.managed_outbound_i_ps = None
ip_provider += 1

profile = _configure_load_balancer_profile(managed_outbound_ip_count, managed_outbound_ipv6_count, outbound_ips,
outbound_ip_prefixes, outbound_ports, idle_timeout, profile, models)
if managed_outbound_ip_count or managed_outbound_ipv6_count:
if profile.managed_outbound_i_ps is None:
if isinstance(models, SimpleNamespace):
ManagedClusterLoadBalancerProfileManagedOutboundIPs = (
models.ManagedClusterLoadBalancerProfileManagedOutboundIPs
)
else:
ManagedClusterLoadBalancerProfileManagedOutboundIPs = models.get(
"ManagedClusterLoadBalancerProfileManagedOutboundIPs"
)
profile.managed_outbound_i_ps = ManagedClusterLoadBalancerProfileManagedOutboundIPs()
if managed_outbound_ip_count:
profile.managed_outbound_i_ps.count = managed_outbound_ip_count
if managed_outbound_ipv6_count:
profile.managed_outbound_i_ps.count_ipv6 = managed_outbound_ipv6_count
profile.outbound_ip_prefixes = None
profile.outbound_i_ps = None
ip_provider += 1
if ip_provider <= 0 or ip_provider > 1:
raise InvalidArgumentValueError("outbound ip/ipprefix and managed ip should be mutual exclusive.")
if outbound_ports:
profile.allocated_outbound_ports = outbound_ports
if idle_timeout:
profile.idle_timeout_in_minutes = idle_timeout
if backend_pool_type:
profile.backend_pool_type = backend_pool_type
return profile


def is_load_balancer_profile_provided(managed_outbound_ip_count, managed_outbound_ipv6_count, outbound_ips, ip_prefixes,
outbound_ports, idle_timeout):
return any([managed_outbound_ip_count,
managed_outbound_ipv6_count,
outbound_ips,
ip_prefixes,
outbound_ports,
idle_timeout])


def _get_load_balancer_outbound_ips(load_balancer_outbound_ips, models):
"""parse load balancer profile outbound IP ids and return an array of references to the outbound IP resources"""
load_balancer_outbound_ip_resources = None
if isinstance(models, SimpleNamespace):
ResourceReference = models.ResourceReference
else:
ResourceReference = models.get("ResourceReference")
if load_balancer_outbound_ips is not None:
if isinstance(load_balancer_outbound_ips, str):
load_balancer_outbound_ip_resources = \
[ResourceReference(id=x.strip())
for x in load_balancer_outbound_ips.split(',')]
else:
load_balancer_outbound_ip_resources = load_balancer_outbound_ips
return load_balancer_outbound_ip_resources


def _get_load_balancer_outbound_ip_prefixes(load_balancer_outbound_ip_prefixes, models):
"""parse load balancer profile outbound IP prefix ids and return an array \
of references to the outbound IP prefix resources"""
load_balancer_outbound_ip_prefix_resources = None
if isinstance(models, SimpleNamespace):
ResourceReference = models.ResourceReference
else:
ResourceReference = models.get("ResourceReference")
if load_balancer_outbound_ip_prefixes:
if isinstance(load_balancer_outbound_ip_prefixes, str):
load_balancer_outbound_ip_prefix_resources = \
[ResourceReference(id=x.strip())
for x in load_balancer_outbound_ip_prefixes.split(',')]
else:
load_balancer_outbound_ip_prefix_resources = load_balancer_outbound_ip_prefixes
return load_balancer_outbound_ip_prefix_resources
103 changes: 28 additions & 75 deletions src/aks-preview/azext_aks_preview/managed_cluster_decorator.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@
ManagedClusterWorkloadProfileVerticalPodAutoscaler = TypeVar("ManagedClusterWorkloadProfileVerticalPodAutoscaler")
ManagedClusterLoadBalancerProfile = TypeVar("ManagedClusterLoadBalancerProfile")
ServiceMeshProfile = TypeVar("ServiceMeshProfile")
ResourceReference = TypeVar("ResourceReference")


# pylint: disable=too-few-public-methods
Expand Down Expand Up @@ -382,52 +383,6 @@ def _get_outbound_type(
raise DecoratorEarlyExitException()
return outbound_type

def get_load_balancer_managed_outbound_ip_count(self) -> Union[int, None]:
"""Obtain the value of load_balancer_managed_outbound_ip_count.

Note: Overwritten in aks-preview to preserve value from `mc` in update mode under certain circumstance.

Note: SDK provides default value 1 and performs the following validation {'maximum': 100, 'minimum': 1}.

:return: int or None
"""
# read the original value passed by the command
load_balancer_managed_outbound_ip_count = self.raw_param.get(
"load_balancer_managed_outbound_ip_count"
)
# In create mode, try to read the property value corresponding to the parameter from the `mc` object.
if self.decorator_mode == DecoratorMode.CREATE:
if (
self.mc and
self.mc.network_profile and
self.mc.network_profile.load_balancer_profile and
self.mc.network_profile.load_balancer_profile.managed_outbound_i_ps and
self.mc.network_profile.load_balancer_profile.managed_outbound_i_ps.count is not None
):
load_balancer_managed_outbound_ip_count = (
self.mc.network_profile.load_balancer_profile.managed_outbound_i_ps.count
)
elif self.decorator_mode == DecoratorMode.UPDATE:
if (
not self.get_load_balancer_outbound_ips() and
not self.get_load_balancer_outbound_ip_prefixes() and
load_balancer_managed_outbound_ip_count is None
):
if (
self.mc and
self.mc.network_profile and
self.mc.network_profile.load_balancer_profile and
self.mc.network_profile.load_balancer_profile.managed_outbound_i_ps and
self.mc.network_profile.load_balancer_profile.managed_outbound_i_ps.count is not None
):
load_balancer_managed_outbound_ip_count = (
self.mc.network_profile.load_balancer_profile.managed_outbound_i_ps.count
)

# this parameter does not need dynamic completion
# this parameter does not need validation
return load_balancer_managed_outbound_ip_count

def _get_pod_cidr_and_service_cidr_and_dns_service_ip_and_docker_bridge_address_and_network_policy(
self, enable_validation: bool = False
) -> Tuple[
Expand Down Expand Up @@ -634,44 +589,42 @@ def get_enable_network_observability(self) -> Optional[bool]:
"""
return self.raw_param.get("enable_network_observability")

def get_load_balancer_managed_outbound_ip_count(self) -> Union[int, None]:
"""Obtain the value of load_balancer_managed_outbound_ip_count.

Note: SDK performs the following validation {'maximum': 100, 'minimum': 1}.

:return: int or None
"""
# read the original value passed by the command
return self.raw_param.get("load_balancer_managed_outbound_ip_count")

def get_load_balancer_managed_outbound_ipv6_count(self) -> Union[int, None]:
"""Obtain the expected count of IPv6 managed outbound IPs.

Note: SDK provides default value 0 and performs the following validation {'maximum': 100, 'minimum': 0}.

:return: int or None
"""
count_ipv6 = self.raw_param.get('load_balancer_managed_outbound_ipv6_count')
return self.raw_param.get('load_balancer_managed_outbound_ipv6_count')

if self.decorator_mode == DecoratorMode.CREATE:
if (
self.mc and
self.mc.network_profile and
self.mc.network_profile.load_balancer_profile and
self.mc.network_profile.load_balancer_profile.managed_outbound_i_ps and
self.mc.network_profile.load_balancer_profile.managed_outbound_i_ps.count_ipv6 is not None
):
count_ipv6 = (
self.mc.network_profile.load_balancer_profile.managed_outbound_i_ps.count_ipv6
)
elif self.decorator_mode == DecoratorMode.UPDATE:
if (
not self.get_load_balancer_outbound_ips() and
not self.get_load_balancer_outbound_ip_prefixes() and
count_ipv6 is None
):
if (
self.mc and
self.mc.network_profile and
self.mc.network_profile.load_balancer_profile and
self.mc.network_profile.load_balancer_profile.managed_outbound_i_ps and
self.mc.network_profile.load_balancer_profile.managed_outbound_i_ps.count_ipv6 is not None
):
count_ipv6 = (
self.mc.network_profile.load_balancer_profile.managed_outbound_i_ps.count_ipv6
)
def get_load_balancer_outbound_ips(self) -> Union[str, List[ResourceReference], None]:
"""Obtain the value of load_balancer_outbound_ips.

Note: SDK performs the following validation {'maximum': 16, 'minimum': 1}.

return count_ipv6
:return: string, list of ResourceReference, or None
"""
# read the original value passed by the command
return self.raw_param.get("load_balancer_outbound_ips")

def get_load_balancer_outbound_ip_prefixes(self) -> Union[str, List[ResourceReference], None]:
"""Obtain the value of load_balancer_outbound_ip_prefixes.

:return: string, list of ResourceReference, or None
"""
# read the original value passed by the command
return self.raw_param.get("load_balancer_outbound_ip_prefixes")

def get_load_balancer_backend_pool_type(self) -> str:
"""Obtain the value of load_balancer_backend_pool_type.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
AKSPreviewManagedClusterModels,
)
from azext_aks_preview.tests.latest.mocks import MockCLI, MockCmd

from azure.cli.core.azclierror import (
InvalidArgumentValueError,
)

class TestLoadBalancer(unittest.TestCase):
def setUp(self):
Expand Down Expand Up @@ -80,6 +82,55 @@ def test_configure_load_balancer_profile(self):
self.assertEqual(p.idle_timeout_in_minutes, 3600)
self.assertEqual(p.backend_pool_type, "nodeIP")

def test_configure_load_balancer_profile_error(self):
managed_outbound_ip_count = 5
managed_outbound_ipv6_count = 3
outbound_ips = "testpip1,testpip2"
outbound_ip_prefixes = None
outbound_ports = 80
idle_timeout = 3600
backend_pool_type = "nodeIP"

# store all the models used by load balancer
ManagedClusterLoadBalancerProfile = (
self.load_balancer_models.ManagedClusterLoadBalancerProfile
)
ManagedClusterLoadBalancerProfileManagedOutboundIPs = (
self.load_balancer_models.ManagedClusterLoadBalancerProfileManagedOutboundIPs
)
ManagedClusterLoadBalancerProfileOutboundIPs = (
self.load_balancer_models.ManagedClusterLoadBalancerProfileOutboundIPs
)
ManagedClusterLoadBalancerProfileOutboundIPPrefixes = (
self.load_balancer_models.ManagedClusterLoadBalancerProfileOutboundIPPrefixes
)

profile = ManagedClusterLoadBalancerProfile()
# ips -> i_ps due to track 2 naming issue
profile.managed_outbound_i_ps = ManagedClusterLoadBalancerProfileManagedOutboundIPs(
count=2
)
# ips -> i_ps due to track 2 naming issue
profile.outbound_i_ps = ManagedClusterLoadBalancerProfileOutboundIPs(
public_i_ps="public_ips"
)
profile.outbound_ip_prefixes = ManagedClusterLoadBalancerProfileOutboundIPPrefixes(
public_ip_prefixes="public_ip_prefixes"
)
err = "outbound ip/ipprefix and managed ip should be mutual exclusive."
with self.assertRaises(InvalidArgumentValueError) as cm:
loadbalancer.configure_load_balancer_profile(
managed_outbound_ip_count,
managed_outbound_ipv6_count,
outbound_ips,
outbound_ip_prefixes,
outbound_ports,
idle_timeout,
backend_pool_type,
profile,
self.load_balancer_models,
)
self.assertEqual(str(cm.exception), err)

if __name__ == '__main__':
unittest.main()
Loading