diff --git a/src/azure-cli/azure/cli/command_modules/acs/_help.py b/src/azure-cli/azure/cli/command_modules/acs/_help.py index a23d9aaeeaf..c260aae1d1d 100644 --- a/src/azure-cli/azure/cli/command_modules/acs/_help.py +++ b/src/azure-cli/azure/cli/command_modules/acs/_help.py @@ -1008,6 +1008,12 @@ - name: --http-proxy-config type: string short-summary: HTTP Proxy configuration for this cluster. + - name: --disable-http-proxy + type: bool + short-summary: Disable HTTP Proxy Configuration on the cluster. + - name: --enable-http-proxy + type: bool + short-summary: Enable HTTP Proxy Configuration on the cluster. - name: --enable-oidc-issuer type: bool short-summary: Enable OIDC issuer. diff --git a/src/azure-cli/azure/cli/command_modules/acs/_params.py b/src/azure-cli/azure/cli/command_modules/acs/_params.py index 75f92e508f9..829b52ac595 100644 --- a/src/azure-cli/azure/cli/command_modules/acs/_params.py +++ b/src/azure-cli/azure/cli/command_modules/acs/_params.py @@ -715,6 +715,8 @@ def load_arguments(self, _): c.argument('disable_image_cleaner', action='store_true', validator=validate_image_cleaner_enable_disable_mutually_exclusive) c.argument('image_cleaner_interval_hours', type=int) c.argument('http_proxy_config') + c.argument('disable_http_proxy', action='store_true') + c.argument('enable_http_proxy', action='store_true') c.argument('custom_ca_trust_certificates', options_list=["--custom-ca-trust-certificates", "--ca-certs"], validator=validate_custom_ca_trust_certificates, help="path to file containing list of new line separated CAs") c.argument('enable_run_command', action='store_true') c.argument('disable_run_command', action='store_true') diff --git a/src/azure-cli/azure/cli/command_modules/acs/custom.py b/src/azure-cli/azure/cli/command_modules/acs/custom.py index 257adc299e8..657b32bd872 100644 --- a/src/azure-cli/azure/cli/command_modules/acs/custom.py +++ b/src/azure-cli/azure/cli/command_modules/acs/custom.py @@ -1146,6 +1146,8 @@ def aks_update( disable_image_cleaner=False, image_cleaner_interval_hours=None, http_proxy_config=None, + disable_http_proxy=False, + enable_http_proxy=False, enable_keda=False, disable_keda=False, enable_vpa=False, diff --git a/src/azure-cli/azure/cli/command_modules/acs/linter_exclusions.yml b/src/azure-cli/azure/cli/command_modules/acs/linter_exclusions.yml index cbe8508c5cf..fdc7da98c3c 100644 --- a/src/azure-cli/azure/cli/command_modules/acs/linter_exclusions.yml +++ b/src/azure-cli/azure/cli/command_modules/acs/linter_exclusions.yml @@ -218,6 +218,12 @@ aks update: disable_container_network_logs: rule_exclusions: - option_length_too_long + disable_http_proxy: + rule_exclusions: + - option_length_too_long + enable_http_proxy: + rule_exclusions: + - option_length_too_long aks nodepool add: parameters: disable_windows_outbound_nat: diff --git a/src/azure-cli/azure/cli/command_modules/acs/managed_cluster_decorator.py b/src/azure-cli/azure/cli/command_modules/acs/managed_cluster_decorator.py index d607ea1f53b..c009e0c72bb 100644 --- a/src/azure-cli/azure/cli/command_modules/acs/managed_cluster_decorator.py +++ b/src/azure-cli/azure/cli/command_modules/acs/managed_cluster_decorator.py @@ -2010,6 +2010,64 @@ def get_http_proxy_config(self) -> Union[Dict, ManagedClusterHTTPProxyConfig, No # this parameter does not need validation return http_proxy_config + def get_disable_http_proxy(self) -> bool: + """Obtain the value of disable_http_proxy. + + This function will verify the parameter by default. If both enable_http_proxy and disable_http_proxy are + specified, raise a MutuallyExclusiveArgumentError. + + :return: bool + """ + return self._get_disable_http_proxy(enable_validation=True) + + def _get_disable_http_proxy(self, enable_validation: bool = False) -> bool: + """Internal function to obtain the value of disable_http_proxy. + + This function supports the option of enable_validation. When enabled, if both enable_http_proxy and + disable_http_proxy are specified, raise a MutuallyExclusiveArgumentError. + + :return: bool + """ + # read the original value passed by the command + disable_http_proxy = self.raw_param.get("disable_http_proxy") + + if enable_validation: + if disable_http_proxy and self._get_enable_http_proxy(enable_validation=False): + raise MutuallyExclusiveArgumentError( + "Cannot specify --enable-http-proxy and --disable-http-proxy at the same time." + ) + + return disable_http_proxy + + def get_enable_http_proxy(self) -> bool: + """Obtain the value of enable_http_proxy. + + This function will verify the parameter by default. If both enable_http_proxy and disable_http_proxy are + specified, raise a MutuallyExclusiveArgumentError. + + :return: bool + """ + return self._get_enable_http_proxy(enable_validation=True) + + def _get_enable_http_proxy(self, enable_validation: bool = False) -> bool: + """Internal function to obtain the value of enable_http_proxy. + + This function supports the option of enable_validation. When enabled, if both enable_http_proxy and + disable_http_proxy are specified, raise a MutuallyExclusiveArgumentError. + + :return: bool + """ + # read the original value passed by the command + enable_http_proxy = self.raw_param.get("enable_http_proxy") + + if enable_validation: + if enable_http_proxy and self._get_disable_http_proxy(enable_validation=False): + raise MutuallyExclusiveArgumentError( + "Cannot specify --enable-http-proxy and --disable-http-proxy at the same time." + ) + + return enable_http_proxy + def get_assignee_from_identity_or_sp_profile(self) -> Tuple[str, bool]: """Helper function to obtain the value of assignee from identity_profile or service_principal_profile. @@ -8332,11 +8390,38 @@ def update_monitoring_profile_flow_logs(self, mc: ManagedCluster) -> ManagedClus def update_http_proxy_config(self, mc: ManagedCluster) -> ManagedCluster: """Set up http proxy config for the ManagedCluster object. + Only updates if --http-proxy-config was explicitly provided, to avoid wiping existing config. + :return: the ManagedCluster object """ self._ensure_mc(mc) - mc.http_proxy_config = self.context.get_http_proxy_config() + http_proxy_config = self.context.get_http_proxy_config() + if http_proxy_config is not None: + mc.http_proxy_config = http_proxy_config + return mc + + def update_http_proxy_enabled(self, mc: ManagedCluster) -> ManagedCluster: + """Update http proxy enabled/disabled state for the ManagedCluster object. + + :return: the ManagedCluster object + """ + self._ensure_mc(mc) + + if self.context.get_disable_http_proxy(): + if mc.http_proxy_config is None: + mc.http_proxy_config = ( + self.models.ManagedClusterHTTPProxyConfig() # pylint: disable=no-member + ) + mc.http_proxy_config.enabled = False + + if self.context.get_enable_http_proxy(): + if mc.http_proxy_config is None: + mc.http_proxy_config = ( + self.models.ManagedClusterHTTPProxyConfig() # pylint: disable=no-member + ) + mc.http_proxy_config.enabled = True + return mc def update_identity(self, mc: ManagedCluster) -> ManagedCluster: @@ -9644,6 +9729,8 @@ def update_mc_profile_default(self) -> ManagedCluster: mc = self.update_identity_profile(mc) # set up http proxy config mc = self.update_http_proxy_config(mc) + # update http proxy enabled/disabled state + mc = self.update_http_proxy_enabled(mc) # update workload autoscaler profile mc = self.update_workload_auto_scaler_profile(mc) # update kubernetes support plan diff --git a/src/azure-cli/azure/cli/command_modules/acs/tests/latest/data/httpproxyconfig.json b/src/azure-cli/azure/cli/command_modules/acs/tests/latest/data/httpproxyconfig.json index 83a4781dd5e..f5bb2335bbc 100644 --- a/src/azure-cli/azure/cli/command_modules/acs/tests/latest/data/httpproxyconfig.json +++ b/src/azure-cli/azure/cli/command_modules/acs/tests/latest/data/httpproxyconfig.json @@ -1,6 +1,6 @@ { "httpProxy": "http://cli-proxy-vm:3128/", - "httpsProxy": "https://cli-proxy-vm:3129/", + "httpsProxy": "http://cli-proxy-vm:3128/", "noProxy": [ "localhost", "127.0.0.1" diff --git a/src/azure-cli/azure/cli/command_modules/acs/tests/latest/data/httpproxyconfig_update.json b/src/azure-cli/azure/cli/command_modules/acs/tests/latest/data/httpproxyconfig_update.json index c94e9fccdb8..1db207fbe56 100644 --- a/src/azure-cli/azure/cli/command_modules/acs/tests/latest/data/httpproxyconfig_update.json +++ b/src/azure-cli/azure/cli/command_modules/acs/tests/latest/data/httpproxyconfig_update.json @@ -1,6 +1,6 @@ { "httpProxy": "http://cli-proxy-vm:3128/", - "httpsProxy": "https://cli-proxy-vm:3129/", + "httpsProxy": "http://cli-proxy-vm:3128/", "noProxy": [ "localhost", "127.0.0.1" diff --git a/src/azure-cli/azure/cli/command_modules/acs/tests/latest/data/setup_proxy.sh b/src/azure-cli/azure/cli/command_modules/acs/tests/latest/data/setup_proxy.sh index 879a0988688..d6cf831c0e5 100644 --- a/src/azure-cli/azure/cli/command_modules/acs/tests/latest/data/setup_proxy.sh +++ b/src/azure-cli/azure/cli/command_modules/acs/tests/latest/data/setup_proxy.sh @@ -7,22 +7,12 @@ echo "setting up ${WORKDIR}" pushd "$WORKDIR" -apt update -y && apt install -y apt-transport-https curl gnupg make gcc < /dev/null - -# add diladele apt key -wget -qO - https://packages.diladele.com/diladele_pub.asc | apt-key add - - -# add new repo -tee /etc/apt/sources.list.d/squid413-ubuntu20.diladele.com.list </dev/null || \ + /usr/libexec/squid/security_file_certgen -c -s /var/lib/squid/ssl_db -M 4MB 2>/dev/null || true chown -R proxy:proxy /var/lib/squid @@ -127,7 +117,7 @@ cp squidc.pem /usr/local/share/ca-certificates/squidc.crt update-ca-certificates sed -i 's~http_access deny all~http_access allow all~' /etc/squid/squid.conf -sed -i "s~http_port 3128~http_port $HOST:3128\nhttps_port $HOST:3129 tls-cert=/etc/squid/squidc.pem tls-key=/etc/squid/squidk.pem~" /etc/squid/squid.conf +sed -i "s~http_port 3128~http_port $HOST:3128~" /etc/squid/squid.conf systemctl restart squid systemctl status squid @@ -135,5 +125,3 @@ systemctl status squid # validation, fails VM creation if commands fail curl -fsSl -o /dev/null -w '%{http_code}\n' -x http://${HOST}:3128/ -I http://www.google.com curl -fsSl -o /dev/null -w '%{http_code}\n' -x http://${HOST}:3128/ -I https://www.google.com -curl -fsSl -o /dev/null -w '%{http_code}\n' -x https://${HOST}:3129/ -I http://www.google.com -curl -fsSl -o /dev/null -w '%{http_code}\n' -x https://${HOST}:3129/ -I https://www.google.com diff --git a/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_aks_commands.py b/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_aks_commands.py index 5f3a81b6556..acb757b1cbc 100644 --- a/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_aks_commands.py +++ b/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_aks_commands.py @@ -609,13 +609,14 @@ def test_aks_api_server_authorized_ip_ranges(self, resource_group, resource_grou self.cmd('aks delete -g {resource_group} -n {name} --yes --no-wait', checks=[self.is_empty()]) @live_only() - @AllowLargeResponse() + @AllowLargeResponse(999999) @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix='clitest', location='westus2') def test_aks_create_and_update_with_http_proxy_config(self, resource_group, resource_group_location): aks_name = self.create_random_name('cliakstest', 16) self.kwargs.update({ 'resource_group': resource_group, 'name': aks_name, + 'location': resource_group_location, 'http_proxy_path': get_test_data_file_path('httpproxyconfig.json').replace('\\', '\\\\'), 'custom_data_path': get_test_data_file_path('setup_proxy.sh').replace('\\', '\\\\'), 'ssh_key_value': self.generate_ssh_keys() @@ -633,7 +634,31 @@ def test_aks_create_and_update_with_http_proxy_config(self, resource_group, reso --vnet-name={name} \ --name proxy-subnet \ --address-prefix 10.42.3.0/24' - # --default-outbound false # disable outbound connection would fail cluster creation + + # NAT gateway gives the proxy VM outbound internet access (no public IP on the VM) + create_nat_pip_cmd = 'network public-ip create \ + --resource-group={resource_group} \ + --name=nat-pip \ + --sku Standard \ + --location={location}' + + create_nat_gw_cmd = 'network nat gateway create \ + --resource-group={resource_group} \ + --name=nat-gw \ + --public-ip-addresses nat-pip \ + --location={location}' + + attach_nat_proxy_cmd = 'network vnet subnet update \ + --resource-group={resource_group} \ + --vnet-name={name} \ + --name proxy-subnet \ + --nat-gateway nat-gw' + + attach_nat_aks_cmd = 'network vnet subnet update \ + --resource-group={resource_group} \ + --vnet-name={name} \ + --name aks-subnet \ + --nat-gateway nat-gw' show_subnet_cmd = 'network vnet subnet show \ --resource-group={resource_group} \ @@ -646,7 +671,7 @@ def test_aks_create_and_update_with_http_proxy_config(self, resource_group, reso create_vm_cmd = 'vm create \ --resource-group={resource_group} \ --name=cli-proxy-vm \ - --image Canonical:0001-com-ubuntu-server-focal:20_04-lts:latest \ + --image Canonical:0001-com-ubuntu-server-jammy:22_04-lts:latest \ --ssh-key-values @{ssh_key_value} \ --public-ip-address "" \ --custom-data {custom_data_path} \ @@ -661,12 +686,25 @@ def test_aks_create_and_update_with_http_proxy_config(self, resource_group, reso self.check('provisioningState', 'Succeeded') ]) + self.cmd(create_nat_pip_cmd) + self.cmd(create_nat_gw_cmd) + self.cmd(attach_nat_proxy_cmd) + self.cmd(attach_nat_aks_cmd) + subnet_output = self.cmd(show_subnet_cmd).get_output_in_json() subnet_id = subnet_output["id"] assert subnet_id is not None self.cmd(create_vm_cmd) + # Wait for cloud-init to finish so Squid is installed and running + # before AKS nodes try to route traffic through the proxy + self.cmd('vm run-command invoke \ + --resource-group={resource_group} \ + --name=cli-proxy-vm \ + --command-id RunShellScript \ + --scripts "cloud-init status --wait"') + self.kwargs.update({ 'vnet_subnet_id': subnet_id, }) @@ -679,7 +717,7 @@ def test_aks_create_and_update_with_http_proxy_config(self, resource_group, reso self.check('httpProxyConfig.httpProxy', 'http://cli-proxy-vm:3128/'), self.check('httpProxyConfig.httpsProxy', - 'https://cli-proxy-vm:3129/'), + 'http://cli-proxy-vm:3128/'), self.check('httpProxyConfig.trustedCa', 'LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUZHekNDQXdPZ0F3SUJBZ0lVT1FvajhDTFpkc2Vscjk3cnZJd3g1T0xEc3V3d0RRWUpLb1pJaHZjTkFRRUwKQlFBd0Z6RVZNQk1HQTFVRUF3d01ZMnhwTFhCeWIzaDVMWFp0TUI0WERUSXlNRE13T0RFMk5EUTBOMW9YRFRNeQpNRE13TlRFMk5EUTBOMW93RnpFVk1CTUdBMVVFQXd3TVkyeHBMWEJ5YjNoNUxYWnRNSUlDSWpBTkJna3Foa2lHCjl3MEJBUUVGQUFPQ0FnOEFNSUlDQ2dLQ0FnRUEvTVB0VjVCVFB0NmNxaTRSZE1sbXIzeUlzYTJ1anpjaHh2NGgKanNDMUR0blJnb3M1UzQxUEgwcmkrM3RUU1ZYMzJ5cndzWStyRDFZUnVwbTZsbUU3R2hVNUkwR2k5b3prU0YwWgpLS2FKaTJveXBVL0ZCK1FQcXpvQ1JzTUV3R0NibUtGVmw4VnVoeW5kWEs0YjRrYmxyOWJsL2V1d2Q3TThTYnZ6CldVam5lRHJRc2lJc3J6UFQ0S0FaTHFjdHpEZTRsbFBUN1lLYTMzaGlFUE9mdldpWitkcWthUUE5UDY0eFhTeW4KZkhYOHVWQUozdUJWSmVHeEQwcGtOSjdqT3J5YVV1SEh1Y1U4UzltSWpuS2pBQjVhUGpMSDV4QXM2bG1iMzEyMgp5KzF0bkVBbVhNNTBEK1VvRWpmUzZIT2I1cmRpcVhHdmMxS2JvS2p6a1BDUnh4MmE3MmN2ZWdVajZtZ0FKTHpnClRoRTFsbGNtVTRpemd4b0lNa1ZwR1RWT0xMbjFWRkt1TmhNWkN2RnZLZ25Lb0F2M0cwRlVuZldFYVJSalNObUQKTFlhTURUNUg5WnQycERJVWpVR1N0Q2w3Z1J6TUVuWXdKTzN5aURwZzQzbzVkUnlzVXlMOUpmRS9OaDdUZzYxOApuOGNKL1c3K1FZYllsanVyYXA4cjdRRlNyb2wzVkNoRkIrT29yNW5pK3ZvaFNBd0pmMFVsTXBHM3hXbXkxVUk0ClRGS2ZGR1JSVHpyUCs3Yk53WDVoSXZJeTVWdGd5YU9xSndUeGhpL0pkeHRPcjJ0QTVyQ1c3K0N0Z1N2emtxTkUKWHlyN3ZrWWdwNlk1TFpneTR0VWpLMEswT1VnVmRqQk9oRHBFenkvRkY4dzFGRVZnSjBxWS9yV2NMa0JIRFQ4Ugp2SmtoaW84Q0F3RUFBYU5mTUYwd0Z3WURWUjBSQkJBd0RvSU1ZMnhwTFhCeWIzaDVMWFp0TUJJR0ExVWRFd0VCCi93UUlNQVlCQWY4Q0FRQXdEd1lEVlIwUEFRSC9CQVVEQXdmbmdEQWRCZ05WSFNVRUZqQVVCZ2dyQmdFRkJRY0QKQWdZSUt3WUJCUVVIQXdFd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dJQkFBb21qQ3lYdmFRT3hnWUs1MHNYTEIyKwp3QWZkc3g1bm5HZGd5Zmc0dXJXMlZtMTVEaEd2STdDL250cTBkWXkyNE4vVWJHN1VEWHZseUxJSkZxMVhQN25mCnBaRzBWQ2paNjlibXhLbTNaOG0wL0F3TXZpOGU5ZWR5OHY5a05CQ3dMR2tIYkE4WW85Q0lpUWdlbGZwcDF2VWgKYm5OQmhhRCtpdTZDZmlDTHdnSmIvaXc3ZW8vQ3lvWnF4K3RqWGFPMnpYdm00cC8rUUlmQU9ndEdRTEZVOGNmWgovZ1VyVHE1Z0ZxMCtQOUd5V3NBVEpGNnE3TDZXWlpqME91VHNlN2Y0Q1NpajZNbk9NTXhBK0pvYWhKejdsc1NpClRKSEl3RXA1ci9SeWhweWVwUXhGWWNVSDVKSmY5cmFoWExXWmkrOVRqeFNNMll5aHhmUlBzaVVFdUdEb2s3OFEKbS9RUGlDaTlKSmIxb2NtVGpBVjh4RFNob2NpdlhPRnlobjZMbjc3dkxqWStBYXZ0V0RoUXRocHVQeHNMdFZ6bQplMFNIMTFkRUxSdGI3NG1xWE9yTzdmdS8rSUJzM0pxTEUvVSt4dXhRdHZHOHZHMXlES0hIU1pxUzJoL1dzNGw0Ck5pQXNoSGdlaFFEUEJjWTl3WVl6ZkJnWnBPVU16ZERmNTB4K0ZTbFk0M1dPSkp6U3VRaDR5WjArM2t5Z3VDRjgKcm5NTFNjZXlTNGNpNExtSi9LQ1N1R2RmNlhWWXo4QkU5Z2pqanBDUDZxeTBVbFJlZldzL2lnL3djSysyYkYxVApuL1l2KzZnWGVDVEhKNzVxRElQbHA3RFJVVWswZmJNajRiSWthb2dXV2s0emYydThteFpMYTBsZVBLTktaTi9tCkdDdkZ3cjNlaSt1LzhjenA1RjdUCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K') ]) @@ -695,10 +733,70 @@ def test_aks_create_and_update_with_http_proxy_config(self, resource_group, reso self.check('httpProxyConfig.httpProxy', 'http://cli-proxy-vm:3128/'), self.check('httpProxyConfig.httpsProxy', - 'https://cli-proxy-vm:3129/'), + 'http://cli-proxy-vm:3128/'), self.check('httpProxyConfig.trustedCa', 'LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUZERENDQXZTZ0F3SUJBZ0lVQlJ3cGs1eTh5ckdrNmtYTjhkSHlMRUNvaHBrd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0VqRVFNQTRHQTFVRUF3d0habTl2TFdKaGNqQWVGdzB5TVRFd01UTXdNekU1TlRoYUZ3MHpNVEV3TVRFdwpNekU1TlRoYU1CSXhFREFPQmdOVkJBTU1CMlp2YnkxaVlYSXdnZ0lpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElDCkR3QXdnZ0lLQW9JQ0FRRFcwRE9sVC9yci9xUEZIUU9lNndBNDkyVGh3VWxZaDhCQkszTW9VWVZLNjEvL2xXekEKeFkrYzlmazlvckUrZXhMSVpwdUg1VnNZR21MNUFyc05sVmNBMkU4MWgwSlBPYUo1eEpiZG40YldpZG9vdXRVVwpXeDNhYUJLSEt0RWdZbUNmTjliWXlZMlNWRWQvNS9HeGh0akVabHJ1aEtRdkZVa3hwR0xKK1JRQ25oNklZakQwCnNpQ0YyTjJhVUJ4RE5KaUdmeHlHSVIrY2p4Vlcrd01md05CQ0l6QVkxMnY4WmpzUXdmUWlhOE5oWEx3M0tuRm0KdzUrcHN2bU1HL1FFUUtZMXNOTnk2dS9DZkI3cmIxQ0EwcjdNNnFsNFMrWHJjZUVRcXpDUWR6NWJueGNYbmFkbwp5MDlhdm5OSGRqbmpvcHNPSkxhd2hzb3RGNWFrL1FLdjYzdU9yVFFlOHlPSWlCZ3JSUzdwejcxbVlhRGNMcXFtCmtmdDVLYnFnMHNZYmo0M09LSm5aZ3crTUtackhoSFJKNi9BcWxOclZML3pFUytHU0ozQ1lSaE5nYXdDQ0Nqd1gKanZYZnkycWFEV2NQbWZaSWVVMVNzdE05THBVRWFQNjJzUVNmb3NEdnZFbUFyUVgwcmd1WGhvZ3pRUFdGWVlEKwo4SUNFYkNFc21hVnN3MzhVUzgzbFlGVCtyTHh3cm5UK1JXSUZ2WFRXbHhCNm5JeWpsOXBhNzlkdU5ocjJxN2RzCjVOU3ZWWHg5UGNqVTQ2VUZ6QnVTbUl0Q0M0Y1NadFRWc3l6ZnpMd2hKbGlqV0czTkp5TnpHUkZQcUpQdTNJUzEKZ3VtKytqdWx4bXZNWm1vM1RqSE5JRm90a0kyd3d3ZUtIcWpYcW9STmwvVnZobE5CaXZRR2gxeGovd0lEQVFBQgpvMW93V0RBU0JnTlZIUkVFQ3pBSmdnZG1iMjh0WW1GeU1CSUdBMVVkRXdFQi93UUlNQVlCQWY4Q0FRQXdEd1lEClZSMFBBUUgvQkFVREF3Zm5nREFkQmdOVkhTVUVGakFVQmdnckJnRUZCUWNEQWdZSUt3WUJCUVVIQXdFd0RRWUoKS29aSWh2Y05BUUVMQlFBRGdnSUJBTDF3RlpTdUw4NTM3aHpUTXhSUWJjcWdEU2F4RUd0ZDJaNTVCcnVWQVloagpxQjR6STd1UVZ2SkNpeXdmQm5BNnZmejh2UDBzdGJJbkVtajh1dS9CSS81NzZqR0tWUWRQSDhqMnQvN1NQWjFKClhBWk9wc1hoVll2RmtpQlhVeW1RMnAvRjFqb2ZRRE1JQ0htdHhRUSthakJQNjBpcnFnVnpsRi95NlQySUgzOHYKbGordndIam52WW5vVmhGNEY0TlE5amp6S3Y1NUhVTk0xUEJKZkFaOTJqeXovczdPMmN2cjhNWlNkT2s5QVk1RQp5RXRlQjBTSjdLS0tUZklBVmVMQzdrRnBHR3FsRkRBNzhPSS9YakNZViswRjk4MHdNOVkxTEVUa3ZMamVSMEFyCnVzZDNIS1Vtd2EwTVEwUTNZNGxma0ZtNjJTclhvcjJURC9WZHpFZWNOTnVmV1VJTVNuaEJDNTVHWjBOTVYvR0QKRXhGZTVWQkhUZEZVNlIwb3JCOVFjVll1Mzk0MEt5NXhkbHNaUHZlMmRJNS9WOXhzY0Zad3cxWWs4K21RK3NVeQp2UVBoL2ZmK0tTQjdVVkdvTVNXUlg3YjFFMGVzZSs4QzZlaVV2OXpDR0VRbkVCcnFIQWxSUDJ2ZzQ0bXFJSnRzCjN2NUt1NW0ySmJoeWNsQVR3VUNQZkN3a2tLRTg0MzZGRitDK0ZUVTJ1OWVpL2t5QTAxYi9zRFl2cWdsS2FWK3MKbEVHRkhjd05Ea2VrS1BFUEZxNkpnZ3R0WlNidE5SMnFadzl3cExIbDVuVlVXdnBGa2hvcW1KVkphK0VBSTQ1LwpqRkh4VG9PMHp1NlBxc1p5SnM2TC84Z3BhbTcwMDV6b0VETVRjcFltMlduMFBKcEg3NE9zUHJVRDVJWVA5ZEt5Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K') ]) + self.kwargs.update( + { + "resource_group": resource_group, + "name": aks_name, + } + ) + + disable_cmd = "aks update --resource-group={resource_group} --name={name} --disable-http-proxy" + + # Retry with backoff: AKS may still hold an internal operation lock + # after the ARM LRO completes (provisioningState=Succeeded). + for attempt in range(10): + try: + self.cmd( + disable_cmd, + checks=[ + self.check("httpProxyConfig.enabled", False), + self.check("httpProxyConfig.httpProxy", "http://cli-proxy-vm:3128/"), + self.check("httpProxyConfig.httpsProxy", "http://cli-proxy-vm:3128/"), + ], + ) + break + except Exception as ex: + if attempt < 9 and 'ResourceExistsError' in type(ex).__name__: + time.sleep(60) + continue + raise + + self.kwargs.update( + { + "resource_group": resource_group, + "name": aks_name, + } + ) + + reenable_cmd = "aks update --resource-group={resource_group} --name={name} --enable-http-proxy" + + for attempt in range(10): + try: + self.cmd( + reenable_cmd, + checks=[ + self.check("httpProxyConfig.enabled", True), + self.check("httpProxyConfig.httpProxy", "http://cli-proxy-vm:3128/"), + self.check("httpProxyConfig.httpsProxy", "http://cli-proxy-vm:3128/"), + ], + ) + break + except Exception as ex: + if attempt < 9 and 'ResourceExistsError' in type(ex).__name__: + time.sleep(60) + continue + raise + + # delete + self.cmd( + "aks delete -g {resource_group} -n {name} --yes --no-wait", + checks=[self.is_empty()], + ) + # role assignment is not properly mocked @live_only() @AllowLargeResponse() diff --git a/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_managed_cluster_decorator.py b/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_managed_cluster_decorator.py index 31c2e471169..bd3832675f9 100644 --- a/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_managed_cluster_decorator.py +++ b/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_managed_cluster_decorator.py @@ -8717,7 +8717,7 @@ def test_set_up_http_proxy_config(self): location="test_location", http_proxy_config={ "httpProxy": "http://cli-proxy-vm:3128/", - "httpsProxy": "https://cli-proxy-vm:3129/", + "httpsProxy": "http://cli-proxy-vm:3128/", "noProxy": ["localhost", "127.0.0.1"], "trustedCa": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUZHekNDQXdPZ0F3SUJBZ0lVT1FvajhDTFpkc2Vscjk3cnZJd3g1T0xEc3V3d0RRWUpLb1pJaHZjTkFRRUwKQlFBd0Z6RVZNQk1HQTFVRUF3d01ZMnhwTFhCeWIzaDVMWFp0TUI0WERUSXlNRE13T0RFMk5EUTBOMW9YRFRNeQpNRE13TlRFMk5EUTBOMW93RnpFVk1CTUdBMVVFQXd3TVkyeHBMWEJ5YjNoNUxYWnRNSUlDSWpBTkJna3Foa2lHCjl3MEJBUUVGQUFPQ0FnOEFNSUlDQ2dLQ0FnRUEvTVB0VjVCVFB0NmNxaTRSZE1sbXIzeUlzYTJ1anpjaHh2NGgKanNDMUR0blJnb3M1UzQxUEgwcmkrM3RUU1ZYMzJ5cndzWStyRDFZUnVwbTZsbUU3R2hVNUkwR2k5b3prU0YwWgpLS2FKaTJveXBVL0ZCK1FQcXpvQ1JzTUV3R0NibUtGVmw4VnVoeW5kWEs0YjRrYmxyOWJsL2V1d2Q3TThTYnZ6CldVam5lRHJRc2lJc3J6UFQ0S0FaTHFjdHpEZTRsbFBUN1lLYTMzaGlFUE9mdldpWitkcWthUUE5UDY0eFhTeW4KZkhYOHVWQUozdUJWSmVHeEQwcGtOSjdqT3J5YVV1SEh1Y1U4UzltSWpuS2pBQjVhUGpMSDV4QXM2bG1iMzEyMgp5KzF0bkVBbVhNNTBEK1VvRWpmUzZIT2I1cmRpcVhHdmMxS2JvS2p6a1BDUnh4MmE3MmN2ZWdVajZtZ0FKTHpnClRoRTFsbGNtVTRpemd4b0lNa1ZwR1RWT0xMbjFWRkt1TmhNWkN2RnZLZ25Lb0F2M0cwRlVuZldFYVJSalNObUQKTFlhTURUNUg5WnQycERJVWpVR1N0Q2w3Z1J6TUVuWXdKTzN5aURwZzQzbzVkUnlzVXlMOUpmRS9OaDdUZzYxOApuOGNKL1c3K1FZYllsanVyYXA4cjdRRlNyb2wzVkNoRkIrT29yNW5pK3ZvaFNBd0pmMFVsTXBHM3hXbXkxVUk0ClRGS2ZGR1JSVHpyUCs3Yk53WDVoSXZJeTVWdGd5YU9xSndUeGhpL0pkeHRPcjJ0QTVyQ1c3K0N0Z1N2emtxTkUKWHlyN3ZrWWdwNlk1TFpneTR0VWpLMEswT1VnVmRqQk9oRHBFenkvRkY4dzFGRVZnSjBxWS9yV2NMa0JIRFQ4Ugp2SmtoaW84Q0F3RUFBYU5mTUYwd0Z3WURWUjBSQkJBd0RvSU1ZMnhwTFhCeWIzaDVMWFp0TUJJR0ExVWRFd0VCCi93UUlNQVlCQWY4Q0FRQXdEd1lEVlIwUEFRSC9CQVVEQXdmbmdEQWRCZ05WSFNVRUZqQVVCZ2dyQmdFRkJRY0QKQWdZSUt3WUJCUVVIQXdFd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dJQkFBb21qQ3lYdmFRT3hnWUs1MHNYTEIyKwp3QWZkc3g1bm5HZGd5Zmc0dXJXMlZtMTVEaEd2STdDL250cTBkWXkyNE4vVWJHN1VEWHZseUxJSkZxMVhQN25mCnBaRzBWQ2paNjlibXhLbTNaOG0wL0F3TXZpOGU5ZWR5OHY5a05CQ3dMR2tIYkE4WW85Q0lpUWdlbGZwcDF2VWgKYm5OQmhhRCtpdTZDZmlDTHdnSmIvaXc3ZW8vQ3lvWnF4K3RqWGFPMnpYdm00cC8rUUlmQU9ndEdRTEZVOGNmWgovZ1VyVHE1Z0ZxMCtQOUd5V3NBVEpGNnE3TDZXWlpqME91VHNlN2Y0Q1NpajZNbk9NTXhBK0pvYWhKejdsc1NpClRKSEl3RXA1ci9SeWhweWVwUXhGWWNVSDVKSmY5cmFoWExXWmkrOVRqeFNNMll5aHhmUlBzaVVFdUdEb2s3OFEKbS9RUGlDaTlKSmIxb2NtVGpBVjh4RFNob2NpdlhPRnlobjZMbjc3dkxqWStBYXZ0V0RoUXRocHVQeHNMdFZ6bQplMFNIMTFkRUxSdGI3NG1xWE9yTzdmdS8rSUJzM0pxTEUvVSt4dXhRdHZHOHZHMXlES0hIU1pxUzJoL1dzNGw0Ck5pQXNoSGdlaFFEUEJjWTl3WVl6ZkJnWnBPVU16ZERmNTB4K0ZTbFk0M1dPSkp6U3VRaDR5WjArM2t5Z3VDRjgKcm5NTFNjZXlTNGNpNExtSi9LQ1N1R2RmNlhWWXo4QkU5Z2pqanBDUDZxeTBVbFJlZldzL2lnL3djSysyYkYxVApuL1l2KzZnWGVDVEhKNzVxRElQbHA3RFJVVWswZmJNajRiSWthb2dXV2s0emYydThteFpMYTBsZVBLTktaTi9tCkdDdkZ3cjNlaSt1LzhjenA1RjdUCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", }, @@ -12637,13 +12637,81 @@ def test_update_http_proxy_config(self): location="test_location", http_proxy_config={ "httpProxy": "http://cli-proxy-vm:3128/", - "httpsProxy": "https://cli-proxy-vm:3129/", + "httpsProxy": "http://cli-proxy-vm:3128/", "noProxy": ["localhost", "127.0.0.1"], "trustedCa": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUZHekNDQXdPZ0F3SUJBZ0lVT1FvajhDTFpkc2Vscjk3cnZJd3g1T0xEc3V3d0RRWUpLb1pJaHZjTkFRRUwKQlFBd0Z6RVZNQk1HQTFVRUF3d01ZMnhwTFhCeWIzaDVMWFp0TUI0WERUSXlNRE13T0RFMk5EUTBOMW9YRFRNeQpNRE13TlRFMk5EUTBOMW93RnpFVk1CTUdBMVVFQXd3TVkyeHBMWEJ5YjNoNUxYWnRNSUlDSWpBTkJna3Foa2lHCjl3MEJBUUVGQUFPQ0FnOEFNSUlDQ2dLQ0FnRUEvTVB0VjVCVFB0NmNxaTRSZE1sbXIzeUlzYTJ1anpjaHh2NGgKanNDMUR0blJnb3M1UzQxUEgwcmkrM3RUU1ZYMzJ5cndzWStyRDFZUnVwbTZsbUU3R2hVNUkwR2k5b3prU0YwWgpLS2FKaTJveXBVL0ZCK1FQcXpvQ1JzTUV3R0NibUtGVmw4VnVoeW5kWEs0YjRrYmxyOWJsL2V1d2Q3TThTYnZ6CldVam5lRHJRc2lJc3J6UFQ0S0FaTHFjdHpEZTRsbFBUN1lLYTMzaGlFUE9mdldpWitkcWthUUE5UDY0eFhTeW4KZkhYOHVWQUozdUJWSmVHeEQwcGtOSjdqT3J5YVV1SEh1Y1U4UzltSWpuS2pBQjVhUGpMSDV4QXM2bG1iMzEyMgp5KzF0bkVBbVhNNTBEK1VvRWpmUzZIT2I1cmRpcVhHdmMxS2JvS2p6a1BDUnh4MmE3MmN2ZWdVajZtZ0FKTHpnClRoRTFsbGNtVTRpemd4b0lNa1ZwR1RWT0xMbjFWRkt1TmhNWkN2RnZLZ25Lb0F2M0cwRlVuZldFYVJSalNObUQKTFlhTURUNUg5WnQycERJVWpVR1N0Q2w3Z1J6TUVuWXdKTzN5aURwZzQzbzVkUnlzVXlMOUpmRS9OaDdUZzYxOApuOGNKL1c3K1FZYllsanVyYXA4cjdRRlNyb2wzVkNoRkIrT29yNW5pK3ZvaFNBd0pmMFVsTXBHM3hXbXkxVUk0ClRGS2ZGR1JSVHpyUCs3Yk53WDVoSXZJeTVWdGd5YU9xSndUeGhpL0pkeHRPcjJ0QTVyQ1c3K0N0Z1N2emtxTkUKWHlyN3ZrWWdwNlk1TFpneTR0VWpLMEswT1VnVmRqQk9oRHBFenkvRkY4dzFGRVZnSjBxWS9yV2NMa0JIRFQ4Ugp2SmtoaW84Q0F3RUFBYU5mTUYwd0Z3WURWUjBSQkJBd0RvSU1ZMnhwTFhCeWIzaDVMWFp0TUJJR0ExVWRFd0VCCi93UUlNQVlCQWY4Q0FRQXdEd1lEVlIwUEFRSC9CQVVEQXdmbmdEQWRCZ05WSFNVRUZqQVVCZ2dyQmdFRkJRY0QKQWdZSUt3WUJCUVVIQXdFd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dJQkFBb21qQ3lYdmFRT3hnWUs1MHNYTEIyKwp3QWZkc3g1bm5HZGd5Zmc0dXJXMlZtMTVEaEd2STdDL250cTBkWXkyNE4vVWJHN1VEWHZseUxJSkZxMVhQN25mCnBaRzBWQ2paNjlibXhLbTNaOG0wL0F3TXZpOGU5ZWR5OHY5a05CQ3dMR2tIYkE4WW85Q0lpUWdlbGZwcDF2VWgKYm5OQmhhRCtpdTZDZmlDTHdnSmIvaXc3ZW8vQ3lvWnF4K3RqWGFPMnpYdm00cC8rUUlmQU9ndEdRTEZVOGNmWgovZ1VyVHE1Z0ZxMCtQOUd5V3NBVEpGNnE3TDZXWlpqME91VHNlN2Y0Q1NpajZNbk9NTXhBK0pvYWhKejdsc1NpClRKSEl3RXA1ci9SeWhweWVwUXhGWWNVSDVKSmY5cmFoWExXWmkrOVRqeFNNMll5aHhmUlBzaVVFdUdEb2s3OFEKbS9RUGlDaTlKSmIxb2NtVGpBVjh4RFNob2NpdlhPRnlobjZMbjc3dkxqWStBYXZ0V0RoUXRocHVQeHNMdFZ6bQplMFNIMTFkRUxSdGI3NG1xWE9yTzdmdS8rSUJzM0pxTEUvVSt4dXhRdHZHOHZHMXlES0hIU1pxUzJoL1dzNGw0Ck5pQXNoSGdlaFFEUEJjWTl3WVl6ZkJnWnBPVU16ZERmNTB4K0ZTbFk0M1dPSkp6U3VRaDR5WjArM2t5Z3VDRjgKcm5NTFNjZXlTNGNpNExtSi9LQ1N1R2RmNlhWWXo4QkU5Z2pqanBDUDZxeTBVbFJlZldzL2lnL3djSysyYkYxVApuL1l2KzZnWGVDVEhKNzVxRElQbHA3RFJVVWswZmJNajRiSWthb2dXV2s0emYydThteFpMYTBsZVBLTktaTi9tCkdDdkZ3cjNlaSt1LzhjenA1RjdUCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", }, ) self.assertEqual(dec_mc_1, ground_truth_mc_1) + # custom value + dec_2 = AKSManagedClusterUpdateDecorator( + self.cmd, + self.client, + { + "disable_http_proxy": True, + }, + ResourceType.MGMT_CONTAINERSERVICE, + ) + + mc_2 = self.models.ManagedCluster( + location="test_location", + http_proxy_config = self.models.ManagedClusterHTTPProxyConfig( + enabled=True, + http_proxy="http://cli-proxy-vm:3128/", + https_proxy="https://cli-proxy-vm:3129/", + ) + ) + dec_2.context.attach_mc(mc_2) + # fail on passing the wrong mc object + with self.assertRaises(CLIInternalError): + dec_2.update_http_proxy_enabled(None) + dec_mc_2 = dec_2.update_http_proxy_enabled(mc_2) + + ground_truth_mc_2 = self.models.ManagedCluster( + location="test_location", + http_proxy_config = self.models.ManagedClusterHTTPProxyConfig( + enabled=False, + http_proxy="http://cli-proxy-vm:3128/", + https_proxy="https://cli-proxy-vm:3129/", + ) + ) + self.assertEqual(dec_mc_2, ground_truth_mc_2) + + # custom value + dec_3 = AKSManagedClusterUpdateDecorator( + self.cmd, + self.client, + { + "enable_http_proxy": True, + }, + ResourceType.MGMT_CONTAINERSERVICE, + ) + + mc_3 = self.models.ManagedCluster( + location="test_location", + http_proxy_config = self.models.ManagedClusterHTTPProxyConfig( + enabled=False, + http_proxy="http://cli-proxy-vm:3128/", + https_proxy="https://cli-proxy-vm:3129/", + ) + ) + dec_3.context.attach_mc(mc_3) + # fail on passing the wrong mc object + with self.assertRaises(CLIInternalError): + dec_3.update_http_proxy_enabled(None) + dec_mc_3 = dec_3.update_http_proxy_enabled(mc_3) + + ground_truth_mc_3 = self.models.ManagedCluster( + location="test_location", + http_proxy_config = self.models.ManagedClusterHTTPProxyConfig( + enabled=True, + http_proxy="http://cli-proxy-vm:3128/", + https_proxy="https://cli-proxy-vm:3129/", + ) + ) + self.assertEqual(dec_mc_3, ground_truth_mc_3) + def test_update_service_mesh_profile(self): dec_1 = AKSManagedClusterUpdateDecorator( self.cmd,