1010"""
1111import json
1212import os
13+ import time
1314from subprocess import DEVNULL , PIPE , TimeoutExpired , run
1415
1516SKELETON_XML_URL = (
@@ -329,7 +330,7 @@ def test_k8s_single_deploy():
329330 svc_output = kubectl_output ("get svc -o name" )
330331 assert "app-skeleton-python" in svc_output , f"No service found: { svc_output } "
331332
332- # Verify Service type matches the expose type
333+ # Verify Service type and managed-by label
333334 expected_type = EXPECTED_SVC_TYPE .get (EXPOSE_TYPE )
334335 if expected_type :
335336 svc_json = kubectl_output ("get svc -l app.kubernetes.io/component=exapp -o json" )
@@ -341,6 +342,12 @@ def test_k8s_single_deploy():
341342 assert actual_type == expected_type , (
342343 f"Service type mismatch: expected { expected_type } , got { actual_type } "
343344 )
345+ managed_by = item ["metadata" ].get ("labels" , {}).get (
346+ "app.kubernetes.io/managed-by"
347+ )
348+ assert managed_by == "harp" , (
349+ f"Service missing managed-by=harp label, got: { managed_by } "
350+ )
344351 found = True
345352 break
346353 assert found , (
@@ -368,9 +375,14 @@ def test_k8s_single_enable_disable():
368375 # Verify replicas=0
369376 deploy_json = kubectl_output ("get deploy -l app.kubernetes.io/component=exapp -o json" , check = False )
370377 data = json .loads (deploy_json )
371- if data .get ("items" ):
372- replicas = data ["items" ][0 ]["spec" ].get ("replicas" , - 1 )
373- assert replicas == 0 , f"Expected 0 replicas after disable, got { replicas } "
378+ items = data .get ("items" , [])
379+ assert items , "No deployments found after disable — label selector returned empty list"
380+ replicas = items [0 ]["spec" ].get ("replicas" , - 1 )
381+ assert replicas == 0 , f"Expected 0 replicas after disable, got { replicas } "
382+
383+ # Verify AppAPI shows disabled
384+ list_output = occ_output ("app_api:app:list" )
385+ assert "disabled" in list_output , f"Expected 'disabled' in app list after disable: { list_output } "
374386
375387 # Re-enable
376388 r = occ ("app_api:app:enable app-skeleton-python" , check = False , timeout = 300 )
@@ -379,9 +391,14 @@ def test_k8s_single_enable_disable():
379391 # Verify replicas=1
380392 deploy_json = kubectl_output ("get deploy -l app.kubernetes.io/component=exapp -o json" , check = False )
381393 data = json .loads (deploy_json )
382- if data .get ("items" ):
383- replicas = data ["items" ][0 ]["spec" ].get ("replicas" , - 1 )
384- assert replicas == 1 , f"Expected 1 replica after enable, got { replicas } "
394+ items = data .get ("items" , [])
395+ assert items , "No deployments found after enable — label selector returned empty list"
396+ replicas = items [0 ]["spec" ].get ("replicas" , - 1 )
397+ assert replicas == 1 , f"Expected 1 replica after enable, got { replicas } "
398+
399+ # Verify AppAPI shows enabled
400+ list_output = occ_output ("app_api:app:list" )
401+ assert "enabled" in list_output , f"Expected 'enabled' in app list after enable: { list_output } "
385402 print ("OK" )
386403
387404
@@ -429,15 +446,13 @@ def test_k8s_single_deploy_rm_data():
429446 # PVC removal is requested via remove_data=true in the payload to HaRP.
430447 # K8s pvc-protection finalizer may delay actual deletion until pod terminates.
431448 # Wait briefly then check.
432- import time
433449 time .sleep (5 )
434450 pvc_output = kubectl_output ("get pvc -o name" , check = False )
435451 # Note: PVC may still exist with a deletion timestamp (terminating).
436452 # Check that it's either gone or marked for deletion.
437453 if "app-skeleton-python" in pvc_output :
438454 pvc_json = kubectl_output ("get pvc -o json" , check = False )
439- import json as _json
440- data = _json .loads (pvc_json )
455+ data = json .loads (pvc_json )
441456 for item in data .get ("items" , []):
442457 if "app-skeleton-python" in item ["metadata" ]["name" ]:
443458 deletion_ts = item ["metadata" ].get ("deletionTimestamp" )
@@ -543,7 +558,9 @@ def test_k8s_multi_enable_disable():
543558 # Verify all deployments scaled to 0
544559 deploy_json = kubectl_output ("get deploy -l app.kubernetes.io/component=exapp -o json" , check = False )
545560 data = json .loads (deploy_json )
546- for item in data .get ("items" , []):
561+ items = data .get ("items" , [])
562+ assert len (items ) >= 2 , f"Expected 2+ deployments after disable, got { len (items )} "
563+ for item in items :
547564 replicas = item ["spec" ].get ("replicas" , - 1 )
548565 name = item ["metadata" ]["name" ]
549566 assert replicas == 0 , f"Expected 0 replicas for { name } after disable, got { replicas } "
@@ -555,7 +572,9 @@ def test_k8s_multi_enable_disable():
555572 # Verify all deployments scaled to 1
556573 deploy_json = kubectl_output ("get deploy -l app.kubernetes.io/component=exapp -o json" , check = False )
557574 data = json .loads (deploy_json )
558- for item in data .get ("items" , []):
575+ items = data .get ("items" , [])
576+ assert len (items ) >= 2 , f"Expected 2+ deployments after enable, got { len (items )} "
577+ for item in items :
559578 replicas = item ["spec" ].get ("replicas" , - 1 )
560579 name = item ["metadata" ]["name" ]
561580 assert replicas == 1 , f"Expected 1 replica for { name } after enable, got { replicas } "
@@ -586,10 +605,7 @@ def run_multi_role_tests():
586605 return
587606 print ("\n === Group C: K8s Multi-Role Deploy Lifecycle ===" )
588607 test_k8s_multi_deploy ()
589- # Skip enable/disable for multi-role: app-skeleton-python exits after init
590- # (pod enters 'Succeeded' phase), which waitExAppStart treats as failure.
591- # The enable/disable K8s code path (startAllRoles/stopAllRoles) is already
592- # tested in Group B with single-role — same code, just iterates roles.
608+ test_k8s_multi_enable_disable ()
593609 test_k8s_multi_unregister ()
594610 print ("=== Group C: All multi-role tests passed ===\n " )
595611
@@ -637,6 +653,12 @@ def test_k8s_deploy_bad_image():
637653 svc_output = kubectl_output ("get svc -o name" , check = False )
638654 assert "bad-image-test" not in svc_output , f"Leftover service found: { svc_output } "
639655
656+ # Verify cleanup: no leftover PVCs for bad-image-test
657+ pvc_output = kubectl_output ("get pvc -o name" , check = False )
658+ if "bad-image-test" in pvc_output :
659+ print ("WARN: leftover PVC found, cleaning up " , end = "" )
660+ kubectl ("delete pvc --all" , check = False )
661+
640662 # Verify not in AppAPI list
641663 list_output = occ_output ("app_api:app:list" , check = False )
642664 # Force-clean if still registered
0 commit comments