diff --git a/src/aks-preview/HISTORY.rst b/src/aks-preview/HISTORY.rst index b9ac535326d..3ed0859d346 100644 --- a/src/aks-preview/HISTORY.rst +++ b/src/aks-preview/HISTORY.rst @@ -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 `_), 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. diff --git a/src/aks-preview/azext_aks_preview/_loadbalancer.py b/src/aks-preview/azext_aks_preview/_loadbalancer.py index a4f2557fb73..1516ba2e81a 100644 --- a/src/aks-preview/azext_aks_preview/_loadbalancer.py +++ b/src/aks-preview/azext_aks_preview/_loadbalancer.py @@ -5,25 +5,20 @@ 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) @@ -31,25 +26,129 @@ def update_load_balancer_profile(managed_outbound_ip_count, managed_outbound_ipv 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 diff --git a/src/aks-preview/azext_aks_preview/managed_cluster_decorator.py b/src/aks-preview/azext_aks_preview/managed_cluster_decorator.py index b24a28cf4ef..08dff98cda8 100644 --- a/src/aks-preview/azext_aks_preview/managed_cluster_decorator.py +++ b/src/aks-preview/azext_aks_preview/managed_cluster_decorator.py @@ -109,6 +109,7 @@ ManagedClusterWorkloadProfileVerticalPodAutoscaler = TypeVar("ManagedClusterWorkloadProfileVerticalPodAutoscaler") ManagedClusterLoadBalancerProfile = TypeVar("ManagedClusterLoadBalancerProfile") ServiceMeshProfile = TypeVar("ServiceMeshProfile") +ResourceReference = TypeVar("ResourceReference") # pylint: disable=too-few-public-methods @@ -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[ @@ -634,6 +589,16 @@ 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. @@ -641,37 +606,25 @@ def get_load_balancer_managed_outbound_ipv6_count(self) -> Union[int, None]: :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. diff --git a/src/aks-preview/azext_aks_preview/tests/latest/test_loadbalancer.py b/src/aks-preview/azext_aks_preview/tests/latest/test_loadbalancer.py index 97d767e8f2c..8ea21dc0aba 100644 --- a/src/aks-preview/azext_aks_preview/tests/latest/test_loadbalancer.py +++ b/src/aks-preview/azext_aks_preview/tests/latest/test_loadbalancer.py @@ -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): @@ -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() diff --git a/src/aks-preview/azext_aks_preview/tests/latest/test_managed_cluster_decorator.py b/src/aks-preview/azext_aks_preview/tests/latest/test_managed_cluster_decorator.py index d7fa6a53040..4d8178fad1f 100644 --- a/src/aks-preview/azext_aks_preview/tests/latest/test_managed_cluster_decorator.py +++ b/src/aks-preview/azext_aks_preview/tests/latest/test_managed_cluster_decorator.py @@ -500,7 +500,7 @@ def test_get_load_balancer_managed_outbound_ip_count(self): network_profile = self.models.ContainerServiceNetworkProfile(load_balancer_profile=load_balancer_profile) mc = self.models.ManagedCluster(location="test_location", network_profile=network_profile) ctx_1.attach_mc(mc) - self.assertEqual(ctx_1.get_load_balancer_managed_outbound_ip_count(), 10) + self.assertEqual(ctx_1.get_load_balancer_managed_outbound_ip_count(), None) # custom value ctx_2 = AKSPreviewManagedClusterContext( @@ -529,7 +529,7 @@ def test_get_load_balancer_managed_outbound_ip_count(self): network_profile_2 = self.models.ContainerServiceNetworkProfile(load_balancer_profile=load_balancer_profile_2) mc_2 = self.models.ManagedCluster(location="test_location", network_profile=network_profile_2) ctx_2.attach_mc(mc_2) - self.assertEqual(ctx_2.get_load_balancer_managed_outbound_ip_count(), 10) + self.assertEqual(ctx_2.get_load_balancer_managed_outbound_ip_count(), None) def test_get_load_balancer_managed_outbound_ipv6_count(self): # default @@ -552,7 +552,7 @@ def test_get_load_balancer_managed_outbound_ipv6_count(self): network_profile = self.models.ContainerServiceNetworkProfile(load_balancer_profile=load_balancer_profile) mc = self.models.ManagedCluster(location="test_location", network_profile=network_profile) ctx_1.attach_mc(mc) - self.assertEqual(ctx_1.get_load_balancer_managed_outbound_ipv6_count(), 10) + self.assertEqual(ctx_1.get_load_balancer_managed_outbound_ipv6_count(), None) # custom value ctx_2 = AKSPreviewManagedClusterContext( @@ -590,7 +590,7 @@ def test_get_load_balancer_managed_outbound_ipv6_count(self): network_profile_3 = self.models.ContainerServiceNetworkProfile(load_balancer_profile=load_balancer_profile_3) mc_3 = self.models.ManagedCluster(location="test_location", network_profile=network_profile_3) ctx_3.attach_mc(mc_3) - self.assertEqual(ctx_3.get_load_balancer_managed_outbound_ipv6_count(), 20) + self.assertEqual(ctx_3.get_load_balancer_managed_outbound_ipv6_count(), None) def test_get_load_balancer_backend_pool_type(self): ctx = AKSPreviewManagedClusterContext( diff --git a/src/aks-preview/setup.py b/src/aks-preview/setup.py index 4141bd661f7..60fe2e7211d 100644 --- a/src/aks-preview/setup.py +++ b/src/aks-preview/setup.py @@ -9,7 +9,7 @@ from setuptools import setup, find_packages -VERSION = "0.5.152" +VERSION = "0.5.153" CLASSIFIERS = [ "Development Status :: 4 - Beta",