1212import stat
1313import subprocess
1414import time
15+ from datetime import datetime
1516from subprocess import PIPE , Popen
1617
1718import oras .client # type: ignore[import-untyped]
1819import psutil
1920import requests
21+ import yaml
2022from azure .cli .core import get_default_cli
2123from azure .cli .core .azclierror import (
2224 ArgumentUsageError ,
@@ -343,7 +345,7 @@ def test_connect_withbundlefeatureflag(self, resource_group):
343345 with self .assertLogs (level = "WARNING" ) as cm :
344346 self .cmd (
345347 "connectedk8s connect -g {rg} -n {name} -l {location} --disable-auto-upgrade --kube-config {kubeconfig} \
346- --kube-context {managed_cluster_name}-admin --config extensionSets.versionManagedExtensions='preview' --yes" ,
348+ --kube-context {managed_cluster_name}-admin --config extensionSets.versionManagedExtensions='preview' --yes" ,
347349 checks = [
348350 self .check ("resourceGroup" , "{rg}" ),
349351 self .check ("name" , "{name}" ),
@@ -495,16 +497,16 @@ def test_update_withbundlefeatureflag(self, resource_group):
495497
496498 self .cmd (
497499 "k8s-extension create --cluster-name {name} --resource-group {rg} --cluster-type connectedClusters \
498- --extension-type microsoft.iotoperations.platform --name azure-iot-operations-platform \
499- --release-train preview --auto-upgrade-minor-version False --config installTrustManager=true \
500- --config installCertManager=true --version 0.7.6 --release-namespace cert-manager --scope cluster"
500+ --extension-type microsoft.iotoperations.platform --name azure-iot-operations-platform \
501+ --release-train preview --auto-upgrade-minor-version False --config installTrustManager=true \
502+ --config installCertManager=true --version 0.7.6 --release-namespace cert-manager --scope cluster"
501503 )
502504
503505 self .cmd (
504506 "k8s-extension create --cluster-name {name} --resource-group {rg} --cluster-type connectedClusters \
505- --extension-type microsoft.azure.secretstore --name azure-secret-store --auto-upgrade-minor-version False \
506- --config rotationPollIntervalInSeconds=120 --config validatingAdmissionPolicies.applyPolicies=false \
507- --scope cluster"
507+ --extension-type microsoft.azure.secretstore --name azure-secret-store --auto-upgrade-minor-version False \
508+ --config rotationPollIntervalInSeconds=120 --config validatingAdmissionPolicies.applyPolicies=false \
509+ --scope cluster"
508510 )
509511
510512 # With bundle extensions installed on the cluster, the bundle feature flag cannot be set to 'disabled'
@@ -521,6 +523,144 @@ def test_update_withbundlefeatureflag(self, resource_group):
521523 )
522524 self .assertEqual (configmap_bundle_feature_flag , "enabled" )
523525
526+ @live_only ()
527+ @ResourceGroupPreparer (
528+ name_prefix = "conk8stest" , location = CONFIG ["location" ], random_name_length = 16
529+ )
530+ def test_upgrade_with_agentupdatevalidator (self , resource_group ):
531+ managed_cluster_name = self .create_random_name (prefix = "test-upgrade" , length = 24 )
532+ kubeconfig = _get_test_data_file (managed_cluster_name + "-config.yaml" )
533+ self .kwargs .update (
534+ {
535+ "rg" : resource_group ,
536+ "name" : self .create_random_name (prefix = "cc-" , length = 12 ),
537+ "kubeconfig" : kubeconfig ,
538+ "managed_cluster_name" : managed_cluster_name ,
539+ "location" : CONFIG ["location" ],
540+ }
541+ )
542+ self .cmd ("aks create -g {rg} -n {managed_cluster_name} --generate-ssh-keys" )
543+ self .cmd (
544+ "aks get-credentials -g {rg} -n {managed_cluster_name} -f {kubeconfig} --admin"
545+ )
546+
547+ self .cmd (
548+ "connectedk8s connect -g {rg} -n {name} -l {location} --disable-auto-upgrade --kube-config {kubeconfig} \
549+ --kube-context {managed_cluster_name}-admin --config extensionSets.versionManagedExtensions='enabled'" ,
550+ checks = [
551+ self .check ("resourceGroup" , "{rg}" ),
552+ self .check ("name" , "{name}" ),
553+ self .check (
554+ "arcAgentryConfigurations[0].settings.versionManagedExtensions" ,
555+ "enabled" ,
556+ ),
557+ ],
558+ )
559+
560+ ns = "azure-arc"
561+ config_dir = os .path .join (
562+ os .path .dirname (__file__ ), "agent_update_validator_test_config"
563+ )
564+ arc_agent_values_path = os .path .join (config_dir , "ArcAgentryValues.json" )
565+ fake_ext_config_path = os .path .join (config_dir , "fake_ext_config.yml" )
566+
567+ with open (fake_ext_config_path ) as file :
568+ data = yaml .safe_load (file )
569+
570+ # Update the lastSyncTime to the current time
571+ current_time = datetime .now ().strftime ("%Y-%m-%dT%H:%M:%S.000Z" )
572+ data ["status" ]["syncStatus" ]["lastSyncTime" ] = current_time
573+
574+ with open (fake_ext_config_path , "w" ) as file :
575+ yaml .dump (data , file , default_flow_style = False )
576+
577+ kubectl_client_location = install_kubectl_client ()
578+
579+ # Create the fake extension config to simulate an extension that depends on the bundle
580+ subprocess .run (
581+ [
582+ kubectl_client_location ,
583+ "apply" ,
584+ "-f" ,
585+ fake_ext_config_path ,
586+ "--namespace" ,
587+ ns ,
588+ "--kubeconfig" ,
589+ kubeconfig ,
590+ "--context" ,
591+ f"{ managed_cluster_name } -admin" ,
592+ ]
593+ )
594+
595+ os .environ ["HELMVALUESPATH" ] = arc_agent_values_path
596+
597+ with self .assertRaisesRegex (
598+ CLIError , "Error: Failed to validate agent update.*?no such host"
599+ ):
600+ self .cmd (
601+ "connectedk8s upgrade -g {rg} -n {name} --agent-version 1.26.0 --kube-config {kubeconfig} \
602+ --kube-context {managed_cluster_name}-admin" ,
603+ )
604+
605+ os .environ ["HELMVALUESPATH" ] = ""
606+
607+ # Remove the finalizers from the fake extension config to allow deletion
608+ subprocess .run (
609+ [
610+ kubectl_client_location ,
611+ "patch" ,
612+ "extensionconfig" ,
613+ "fake-ext-config" ,
614+ "--namespace" ,
615+ ns ,
616+ "--type=json" ,
617+ "-p" ,
618+ '[{"op": "remove", "path": "/metadata/finalizers"}]' ,
619+ "--kubeconfig" ,
620+ kubeconfig ,
621+ "--context" ,
622+ f"{ managed_cluster_name } -admin" ,
623+ ]
624+ )
625+
626+ subprocess .Popen (
627+ [
628+ kubectl_client_location ,
629+ "delete" ,
630+ "extensionconfig" ,
631+ "fake-ext-config" ,
632+ "--namespace" ,
633+ ns ,
634+ "--kubeconfig" ,
635+ kubeconfig ,
636+ "--context" ,
637+ f"{ managed_cluster_name } -admin" ,
638+ ]
639+ )
640+
641+ time .sleep (60 )
642+
643+ cmd_output = subprocess .Popen (
644+ [
645+ kubectl_client_location ,
646+ "get" ,
647+ "extensionconfig" ,
648+ "fake-ext-config" ,
649+ "--namespace" ,
650+ ns ,
651+ "--kubeconfig" ,
652+ kubeconfig ,
653+ "--context" ,
654+ f"{ managed_cluster_name } -admin" ,
655+ ],
656+ stdout = PIPE ,
657+ stderr = PIPE ,
658+ )
659+ _ , error_get_ext_config = cmd_output .communicate ()
660+
661+ # Should fail to get the extension config as it has been deleted
662+ assert cmd_output .returncode != 0
663+
524664 @live_only ()
525665 @ResourceGroupPreparer (
526666 name_prefix = "conk8stest" , location = CONFIG ["location" ], random_name_length = 16
0 commit comments