@@ -48,3 +48,92 @@ def sagemaker_session_us_east_1():
4848 """Create a SageMaker session in us-east-1 for Nova model tests."""
4949 boto_session = boto3 .Session (region_name = NOVA_REGION )
5050 return Session (boto_session = boto_session )
51+
52+
53+ import time
54+ import logging
55+
56+ logger = logging .getLogger (__name__ )
57+
58+
59+ @pytest .fixture (scope = "module" )
60+ def mlflow_resource_arn ():
61+ """Discover or create an MLflow app for integ tests, clean up if created.
62+
63+ Looks for an existing MLflow app in Created/Updated state. If none exists,
64+ creates one and deletes it after the test module finishes.
65+ """
66+ region = os .environ .get ("AWS_DEFAULT_REGION" , DEFAULT_REGION )
67+ sm_client = boto3 .client ("sagemaker" , region_name = region )
68+ created_arn = None
69+
70+ # Try to find an existing ready app
71+ try :
72+ paginator = sm_client .get_paginator ("list_mlflow_apps" )
73+ for page in paginator .paginate ():
74+ for app in page .get ("MlflowApps" , []):
75+ if app .get ("Status" ) in ("Created" , "Updated" ):
76+ logger .info (f"Using existing MLflow app: { app ['Arn' ]} " )
77+ yield app ["Arn" ]
78+ return
79+ except Exception as e :
80+ logger .warning (f"Failed to list MLflow apps: { e } " )
81+
82+ # No ready app found — create one
83+ logger .info ("No ready MLflow app found. Creating one for integ tests..." )
84+ sts_client = boto3 .client ("sts" , region_name = region )
85+ account_id = sts_client .get_caller_identity ()["Account" ]
86+ app_name = f"integ-test-mlflow-{ int (time .time ())} "
87+ artifact_store_uri = f"s3://sagemaker-{ region } -{ account_id } /mlflow-artifacts"
88+
89+ # Ensure bucket/prefix exists
90+ s3_client = boto3 .client ("s3" , region_name = region )
91+ bucket_name = f"sagemaker-{ region } -{ account_id } "
92+ try :
93+ s3_client .head_bucket (Bucket = bucket_name )
94+ except Exception :
95+ if region == "us-east-1" :
96+ s3_client .create_bucket (Bucket = bucket_name )
97+ else :
98+ s3_client .create_bucket (
99+ Bucket = bucket_name ,
100+ CreateBucketConfiguration = {"LocationConstraint" : region },
101+ )
102+ try :
103+ s3_client .put_object (Bucket = bucket_name , Key = "mlflow-artifacts/" )
104+ except Exception :
105+ pass
106+
107+ # Get execution role
108+ from sagemaker .train .defaults import TrainDefaults
109+ boto_session = boto3 .Session (region_name = region )
110+ sagemaker_session = Session (boto_session = boto_session )
111+ role_arn = TrainDefaults .get_role (role = None , sagemaker_session = sagemaker_session )
112+
113+ resp = sm_client .create_mlflow_app (
114+ Name = app_name ,
115+ ArtifactStoreUri = artifact_store_uri ,
116+ RoleArn = role_arn ,
117+ AccountDefaultStatus = "DISABLED" ,
118+ )
119+ created_arn = resp ["Arn" ]
120+ logger .info (f"Created MLflow app: { created_arn } " )
121+
122+ # Wait for it to become ready
123+ for _ in range (60 ):
124+ desc = sm_client .describe_mlflow_app (Arn = created_arn )
125+ status = desc .get ("Status" )
126+ if status in ("Created" , "Updated" ):
127+ break
128+ if status in ("Failed" , "CreateFailed" , "DeleteFailed" ):
129+ pytest .skip (f"MLflow app creation failed: { desc .get ('FailureReason' )} " )
130+ time .sleep (10 )
131+
132+ yield created_arn
133+
134+ # Cleanup
135+ logger .info (f"Cleaning up MLflow app: { created_arn } " )
136+ try :
137+ sm_client .delete_mlflow_app (Arn = created_arn )
138+ except Exception as e :
139+ logger .warning (f"Failed to delete MLflow app { created_arn } : { e } " )
0 commit comments