@@ -161,6 +161,137 @@ def test_repack_step_output_config_respects_user_override():
161161 assert kwargs ["output_data_config" ] is mine
162162
163163
164+ def test_repack_step_gets_volume_kms_key_from_model ():
165+ """GH #6536: the repack job's compute volume must be encrypted with the model's KMS
166+ key. This reaches _RepackModelStep via volume_kms_key_id, which applies it to the
167+ job's Compute (ResourceConfig.VolumeKmsKeyId). Without it the runtime repack cannot
168+ use a configured CMK for its volume."""
169+ from sagemaker .serve .model_builder import ModelBuilder
170+ from sagemaker .mlops .workflow import model_step as ms
171+
172+ ps = _pipeline_session ()
173+ builder = Mock (spec = ModelBuilder )
174+ builder .sagemaker_session = ps
175+ builder .model_name = "my-model"
176+ builder .role_arn = "arn:aws:iam::111122223333:role/R"
177+ builder .s3_model_data_url = "s3://orig/model.tar.gz"
178+ builder .entry_point = "inference.py"
179+ builder .source_dir = "/code"
180+ builder .source_code = Mock (requirements = "requirements.txt" )
181+ builder .vpc_config = None
182+ builder .model_kms_key = "arn:aws:kms:us-west-2:111122223333:key/abc"
183+
184+ step_args = _FakeModelStepArgs (builder , {id (builder )})
185+ fake_repack = Mock ()
186+ fake_repack .properties .ModelArtifacts .S3ModelArtifacts = "s3://repacked/model.tar.gz"
187+
188+ with patch ("sagemaker.core.workflow.utilities.validate_step_args_input" ):
189+ with patch .object (ms , "_RepackModelStep" , return_value = fake_repack ) as mock_repack :
190+ ms .ModelStep (name = "step" , step_args = step_args )
191+
192+ _ , kwargs = mock_repack .call_args
193+ assert kwargs ["volume_kms_key_id" ] == "arn:aws:kms:us-west-2:111122223333:key/abc"
194+
195+
196+ def test_repack_step_omits_volume_kms_key_when_model_has_none ():
197+ """With no model KMS key, volume_kms_key_id must not be sent, leaving the repack job's
198+ ResourceConfig.VolumeKmsKeyId unset (legacy behavior preserved)."""
199+ from sagemaker .serve .model_builder import ModelBuilder
200+ from sagemaker .mlops .workflow import model_step as ms
201+
202+ ps = _pipeline_session ()
203+ builder = Mock (spec = ModelBuilder )
204+ builder .sagemaker_session = ps
205+ builder .model_name = "my-model"
206+ builder .role_arn = "arn:aws:iam::111122223333:role/R"
207+ builder .s3_model_data_url = "s3://orig/model.tar.gz"
208+ builder .entry_point = "inference.py"
209+ builder .source_dir = "/code"
210+ builder .source_code = Mock (requirements = "requirements.txt" )
211+ builder .vpc_config = None
212+ builder .model_kms_key = None
213+
214+ step_args = _FakeModelStepArgs (builder , {id (builder )})
215+ fake_repack = Mock ()
216+ fake_repack .properties .ModelArtifacts .S3ModelArtifacts = "s3://repacked/model.tar.gz"
217+
218+ with patch ("sagemaker.core.workflow.utilities.validate_step_args_input" ):
219+ with patch .object (ms , "_RepackModelStep" , return_value = fake_repack ) as mock_repack :
220+ ms .ModelStep (name = "step" , step_args = step_args )
221+
222+ _ , kwargs = mock_repack .call_args
223+ assert "volume_kms_key_id" not in kwargs
224+
225+
226+ def test_repack_step_volume_kms_key_respects_user_override ():
227+ """An explicit volume_kms_key_id in repack_model_step_settings must win over the model
228+ KMS key default."""
229+ from sagemaker .serve .model_builder import ModelBuilder
230+ from sagemaker .mlops .workflow import model_step as ms
231+
232+ ps = _pipeline_session ()
233+ builder = Mock (spec = ModelBuilder )
234+ builder .sagemaker_session = ps
235+ builder .model_name = "my-model"
236+ builder .role_arn = "arn:aws:iam::111122223333:role/R"
237+ builder .s3_model_data_url = "s3://orig/model.tar.gz"
238+ builder .entry_point = "inference.py"
239+ builder .source_dir = "/code"
240+ builder .source_code = Mock (requirements = "requirements.txt" )
241+ builder .vpc_config = None
242+ builder .model_kms_key = "arn:aws:kms:us-west-2:111122223333:key/abc"
243+
244+ step_args = _FakeModelStepArgs (builder , {id (builder )})
245+ fake_repack = Mock ()
246+ fake_repack .properties .ModelArtifacts .S3ModelArtifacts = "s3://repacked/model.tar.gz"
247+
248+ with patch ("sagemaker.core.workflow.utilities.validate_step_args_input" ):
249+ with patch .object (ms , "_RepackModelStep" , return_value = fake_repack ) as mock_repack :
250+ ms .ModelStep (
251+ name = "step" ,
252+ step_args = step_args ,
253+ repack_model_step_settings = {"volume_kms_key_id" : "my-volume-key" },
254+ )
255+
256+ _ , kwargs = mock_repack .call_args
257+ assert kwargs ["volume_kms_key_id" ] == "my-volume-key"
258+
259+
260+ def test_repack_step_volume_kms_key_accepts_pipeline_variable ():
261+ """A pipeline variable supplied as volume_kms_key_id must be forwarded unchanged."""
262+ from sagemaker .serve .model_builder import ModelBuilder
263+ from sagemaker .core .workflow import ParameterString
264+ from sagemaker .mlops .workflow import model_step as ms
265+
266+ ps = _pipeline_session ()
267+ builder = Mock (spec = ModelBuilder )
268+ builder .sagemaker_session = ps
269+ builder .model_name = "my-model"
270+ builder .role_arn = "arn:aws:iam::111122223333:role/R"
271+ builder .s3_model_data_url = "s3://orig/model.tar.gz"
272+ builder .entry_point = "inference.py"
273+ builder .source_dir = "/code"
274+ builder .source_code = Mock (requirements = "requirements.txt" )
275+ builder .vpc_config = None
276+ builder .model_kms_key = None
277+
278+ volume_key = ParameterString (name = "volume-kms" )
279+ step_args = _FakeModelStepArgs (builder , {id (builder )})
280+ fake_repack = Mock ()
281+ fake_repack .properties .ModelArtifacts .S3ModelArtifacts = "s3://repacked/model.tar.gz"
282+
283+ with patch ("sagemaker.core.workflow.utilities.validate_step_args_input" ):
284+ with patch .object (ms , "_RepackModelStep" , return_value = fake_repack ) as mock_repack :
285+ ms .ModelStep (
286+ name = "step" ,
287+ step_args = step_args ,
288+ repack_model_step_settings = {"volume_kms_key_id" : volume_key },
289+ )
290+
291+ _ , kwargs = mock_repack .call_args
292+ assert kwargs ["volume_kms_key_id" ] is volume_key
293+
294+
164295def test_no_repack_step_for_unrecognized_model_type ():
165296 """An object that is neither a core Model nor a ModelBuilder yields no repack step."""
166297 from sagemaker .mlops .workflow import model_step as ms
0 commit comments