feature: Add optional CodeArtifact login to FrameworkProcessing job script - #4145

Merged
sage-maker merged 19 commits into
aws:masterfrom
akuma12:processing-job-codeartifact-support
Aug 7, 2024
Merged

feature: Add optional CodeArtifact login to FrameworkProcessing job script#4145
sage-maker merged 19 commits into
aws:masterfrom
akuma12:processing-job-codeartifact-support

Conversation

@akuma12

@akuma12akuma12 commented Sep 27, 2023

Copy link
Copy Markdown
Contributor

Issue #, if available:
#4144

Description of changes:
This PR adds an optional codeartifact_repo_arn parameter to the FrameworkProcessor.run() method. Providing this ARN will allow the _generate_framework_script() method to call _get_codeartifact_index(), which will parse the ARN into a CodeArtifact repo URL, retrieve an authentication token, and write an index option into the pip install -r requirements.txt call generated by _generate_framework_script()

If codeartifact_repo_arn is not provided, then _get_codeartifact_index() will not be called and nothing new will be injected into the runproc.sh script.

The _get_codeartifact_index() code is copied from the sagemaker-training-toolkit. All credit to @humanzz for that update.

Testing done:
Validated a PytorchProcessing job both with and without the codeartifact_repo_arn parameter. Downloaded the generated runproc.sh file from S3 and verified that the index option is written to the file if the ARN is provided, and does nothing if it is not.

I could use some advice when it comes to automated testing, however. Since _get_codeartifact_index() interacts with CodeArtifact via Boto3, I was unsure of the best way to handle this. In unit tests, I would typically use moto or patch the Boto3 make_api_call method. With the integration tests, I wasn't sure how I should interact with CodeArtifact, or if I should even add an integration test.

Merge Checklist

Put an x in the boxes that apply. You can also fill these out after creating the PR. If you're unsure about any of them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before merging your pull request.

General

  • I have read the CONTRIBUTING doc
  • I certify that the changes I am introducing will be backward compatible, and I have discussed concerns about this, if any, with the Python SDK team
  • I used the commit message format described in CONTRIBUTING
  • I have passed the region in to all S3 and STS clients that I've initialized as part of this change.
  • I have updated any necessary documentation, including READMEs and API docs (if appropriate)

Tests

  • I have added tests that prove my fix is effective or that my feature works (if appropriate)
  • I have added unit and/or integration tests as appropriate to ensure backward compatibility of the changes
  • I have checked that my tests are not configured for a specific region or account (if appropriate)
  • I have used unique_name_from_base to create resource names in integ tests (if appropriate)

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@akuma12
akuma12 requested a review from a team as a code ownerSeptember 27, 2023 21:12
@akuma12
akuma12 requested review from akrishna1995 and removed request for a teamSeptember 27, 2023 21:12
@akrishna1995akrishna1995 self-assigned this Oct 2, 2023

@akrishna1995akrishna1995 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/bot run all

@akrishna1995akrishna1995 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please get a review from one member in your team. please follow best practices - add Unit tests , integ tests

@sagemaker-bot

Copy link
Copy Markdown
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: sagemaker-python-sdk-unit-tests
  • Commit ID: 53c46b0
  • Result: FAILED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@sagemaker-bot

Copy link
Copy Markdown
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: sagemaker-python-sdk-local-mode-tests
  • Commit ID: 53c46b0
  • Result: SUCCEEDED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@sagemaker-bot

Copy link
Copy Markdown
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: sagemaker-python-sdk-notebook-tests
  • Commit ID: 53c46b0
  • Result: SUCCEEDED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@sagemaker-bot

Copy link
Copy Markdown
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: sagemaker-python-sdk-slow-tests
  • Commit ID: 53c46b0
  • Result: SUCCEEDED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@sagemaker-bot

Copy link
Copy Markdown
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: sagemaker-python-sdk-pr
  • Commit ID: 53c46b0
  • Result: FAILED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@goelakash
goelakashforce-pushed the processing-job-codeartifact-support branch from 53c46b0 to 61190deCompareOctober 9, 2023 23:48
@akuma12

Copy link
Copy Markdown
ContributorAuthor

@akrishna1995 Can you re-run the tests? I added 3 more unit tests around the code and fixed the issues that popped up in the last run.

@akuma12

Copy link
Copy Markdown
ContributorAuthor

Curious if anyone has had a chance to take a look at this. Would appreciate another review.

@mohanasudhan

Copy link
Copy Markdown
Contributor

@akuma12 Can you rebase your change? I had a brief look and it lgtm. It would be good to get all the test successful.

@humanzz

Copy link
Copy Markdown
Contributor

Hi @mohanasudhan and @akuma12,
I've been keeping an eye on this, and recently within my team, a need has arisen for using processing jobs and we'd really benefit from this PR being merged/released.

I pulled this PR, and ran export IGNORE_COVERAGE=- ; tox -e py38 -- -s -vv tests/unit/test_processing.py::test_pytorch_processor_with_required_parameters ; unset IGNORE_COVERAGE to check what's failing and all tests seem to be passing.

the only thing that happened was that it seems like black wanted to reformat the files and resulted in the following changes

diff --git a/src/sagemaker/processing.py b/src/sagemaker/processing.py
index b4a063ba..59b8c980 100644
--- a/src/sagemaker/processing.py
+++ b/src/sagemaker/processing.py
@@ -1852,7 +1852,7 @@ class FrameworkProcessor(ScriptProcessor):
# `arn:${Partition}:codeartifact:${Region}:${Account}:repository/${Domain}/${Repository}`
https://docs.aws.amazon.com/codeartifact/latest/ug/python-configure-pip.html
https://docs.aws.amazon.com/service-authorization/latest/reference/list_awscodeartifact.html#awscodeartifact-resources-for-iam-policies
- +
Args:
codeartifact_repo_arn: arn of the codeartifact repository
codeartifact_client: boto3 client for codeartifact (used for testing)
@@ -1882,9 +1882,13 @@ class FrameworkProcessor(ScriptProcessor):
)
try:
if not codeartifact_client:
- codeartifact_client = self.sagemaker_session.boto_session.client("codeartifact", region_name=region)
- - auth_token_response = codeartifact_client.get_authorization_token(domain=domain, domainOwner=owner)
+ codeartifact_client = self.sagemaker_session.boto_session.client(
+ "codeartifact", region_name=region
+ )
+
+ auth_token_response = codeartifact_client.get_authorization_token(
+ domain=domain, domainOwner=owner
+ )
token = auth_token_response["authorizationToken"]
endpoint_response = codeartifact_client.get_repository_endpoint(
domain=domain, domainOwner=owner, repository=repository, format="pypi"
diff --git a/tests/unit/test_processing.py b/tests/unit/test_processing.py
index fb55e2fe..3663b35b 100644
--- a/tests/unit/test_processing.py
+++ b/tests/unit/test_processing.py
@@ -1107,27 +1107,33 @@ def test_pyspark_processor_configuration_path_pipeline_config(
@patch("sagemaker.workflow.utilities._pipeline_config", MOCKED_PIPELINE_CONFIG)
def test_get_codeartifact_index(pipeline_session):
- codeartifact_repo_arn = "arn:aws:codeartifact:us-west-2:012345678901:repository/test-domain/test-repository"
+ codeartifact_repo_arn = (
+ "arn:aws:codeartifact:us-west-2:012345678901:repository/test-domain/test-repository"
+ )
codeartifact_url = "test-domain-012345678901.d.codeartifact.us-west-2.amazonaws.com/pypi/test-repository/simple/"
- client = boto3.client('codeartifact', region_name=REGION)
+ client = boto3.client("codeartifact", region_name=REGION)
stubber = Stubber(client)
- +
get_auth_token_response = {
"authorizationToken": "mocked_token",
- "expiration": datetime.datetime(2045, 1, 1, 0, 0, 0)
+ "expiration": datetime.datetime(2045, 1, 1, 0, 0, 0),
}
auth_token_expected_params = {"domain": "test-domain", "domainOwner": "012345678901"}
- stubber.add_response("get_authorization_token", get_auth_token_response, auth_token_expected_params)
+ stubber.add_response(
+ "get_authorization_token", get_auth_token_response, auth_token_expected_params
+ )
get_repo_endpoint_response = {"repositoryEndpoint": f"https://{codeartifact_url}"}
repo_endpoint_expected_params = {
"domain": "test-domain",
"domainOwner": "012345678901",
"repository": "test-repository",
- "format": "pypi"
+ "format": "pypi",
}
- stubber.add_response("get_repository_endpoint", get_repo_endpoint_response, repo_endpoint_expected_params)
+ stubber.add_response(
+ "get_repository_endpoint", get_repo_endpoint_response, repo_endpoint_expected_params
+ )
processor = PyTorchProcessor(
role=ROLE,
@@ -1139,8 +1145,10 @@ def test_get_codeartifact_index(pipeline_session):
)
with stubber:
- codeartifact_index = processor._get_codeartifact_index(codeartifact_repo_arn=codeartifact_repo_arn, codeartifact_client=client)
- + codeartifact_index = processor._get_codeartifact_index(
+ codeartifact_repo_arn=codeartifact_repo_arn, codeartifact_client=client
+ )
+
assert codeartifact_index == f"https://aws:mocked_token@{codeartifact_url}"
@@ -1149,24 +1157,28 @@ def test_get_codeartifact_index_bad_repo_arn(pipeline_session):
codeartifact_repo_arn = "arn:aws:codeartifact:us-west-2:012345678901:repository/test-domain"
codeartifact_url = "test-domain-012345678901.d.codeartifact.us-west-2.amazonaws.com/pypi/test-repository/simple/"
- client = boto3.client('codeartifact', region_name=REGION)
+ client = boto3.client("codeartifact", region_name=REGION)
stubber = Stubber(client)
- +
get_auth_token_response = {
"authorizationToken": "mocked_token",
- "expiration": datetime.datetime(2045, 1, 1, 0, 0, 0)
+ "expiration": datetime.datetime(2045, 1, 1, 0, 0, 0),
}
auth_token_expected_params = {"domain": "test-domain", "domainOwner": "012345678901"}
- stubber.add_response("get_authorization_token", get_auth_token_response, auth_token_expected_params)
+ stubber.add_response(
+ "get_authorization_token", get_auth_token_response, auth_token_expected_params
+ )
get_repo_endpoint_response = {"repositoryEndpoint": f"https://{codeartifact_url}"}
repo_endpoint_expected_params = {
"domain": "test-domain",
"domainOwner": "012345678901",
"repository": "test-repository",
- "format": "pypi"
+ "format": "pypi",
}
- stubber.add_response("get_repository_endpoint", get_repo_endpoint_response, repo_endpoint_expected_params)
+ stubber.add_response(
+ "get_repository_endpoint", get_repo_endpoint_response, repo_endpoint_expected_params
+ )
processor = PyTorchProcessor(
role=ROLE,
@@ -1179,32 +1191,42 @@ def test_get_codeartifact_index_bad_repo_arn(pipeline_session):
with stubber:
with pytest.raises(ValueError):
- processor._get_codeartifact_index(codeartifact_repo_arn=codeartifact_repo_arn, codeartifact_client=client)
+ processor._get_codeartifact_index(
+ codeartifact_repo_arn=codeartifact_repo_arn, codeartifact_client=client
+ )
@patch("sagemaker.workflow.utilities._pipeline_config", MOCKED_PIPELINE_CONFIG)
def test_get_codeartifact_index_client_error(pipeline_session):
- codeartifact_repo_arn = "arn:aws:codeartifact:us-west-2:012345678901:repository/test-domain/test-repository"
+ codeartifact_repo_arn = (
+ "arn:aws:codeartifact:us-west-2:012345678901:repository/test-domain/test-repository"
+ )
codeartifact_url = "test-domain-012345678901.d.codeartifact.us-west-2.amazonaws.com/pypi/test-repository/simple/"
- client = boto3.client('codeartifact', region_name=REGION)
+ client = boto3.client("codeartifact", region_name=REGION)
stubber = Stubber(client)
- +
get_auth_token_response = {
"authorizationToken": "mocked_token",
- "expiration": datetime.datetime(2045, 1, 1, 0, 0, 0)
+ "expiration": datetime.datetime(2045, 1, 1, 0, 0, 0),
}
auth_token_expected_params = {"domain": "test-domain", "domainOwner": "012345678901"}
- stubber.add_client_error("get_authorization_token", service_error_code="404", expected_params=auth_token_expected_params)
+ stubber.add_client_error(
+ "get_authorization_token",
+ service_error_code="404",
+ expected_params=auth_token_expected_params,
+ )
get_repo_endpoint_response = {"repositoryEndpoint": f"https://{codeartifact_url}"}
repo_endpoint_expected_params = {
"domain": "test-domain",
"domainOwner": "012345678901",
"repository": "test-repository",
- "format": "pypi"
+ "format": "pypi",
}
- stubber.add_response("get_repository_endpoint", get_repo_endpoint_response, repo_endpoint_expected_params)
+ stubber.add_response(
+ "get_repository_endpoint", get_repo_endpoint_response, repo_endpoint_expected_params
+ )
processor = PyTorchProcessor(
role=ROLE,
@@ -1217,7 +1239,9 @@ def test_get_codeartifact_index_client_error(pipeline_session):
with stubber:
with pytest.raises(RuntimeError):
- processor._get_codeartifact_index(codeartifact_repo_arn=codeartifact_repo_arn, codeartifact_client=client)
+ processor._get_codeartifact_index(
+ codeartifact_repo_arn=codeartifact_repo_arn, codeartifact_client=client
+ )
def _get_script_processor(sagemaker_session):

With no access to the build logs, I wonder if that is the only issue?

@humanzz

Copy link
Copy Markdown
Contributor

One more thought, my teammate @Stacy-D, has started looking into using processing jobs, and has been experimenting with a different approach for setting up CodeArtifact.

Rather than uploading a script that has the hardcoded index in pip install -r requirements.txt {index_option}, she's written a script leveraging ** aws cli** (it's an assumption, but we confirmed it on pytorch containers) for configuring pip using

aws codeartifact login --tool pip --repository "$CODEARTIFACT_REPO" --domain "$CODEARTIFACT_DOMAIN" --domain-owner "${CODEARTIFACT_OWNER}" --region "${CODEARTIFACT_REGION}"

The script then looks something along the lines of

#!/bin/bash
cd /opt/ml/processing/input/code/
tar -xzf sourcedir.tar.gz
# Exit on any error. SageMaker uses error code to mark failed job.
set -e
aws codeartifact login --tool pip --repository "$CODEARTIFACT_REPO" --domain "$CODEARTIFACT_DOMAIN" --domain-owner "${CODEARTIFACT_OWNER}" --region "${CODEARTIFACT_REGION}"
if [[ -f 'requirements.txt' ]]; then
# Some py3 containers has typing, which may breaks pip install
pip uninstall --yes typing
pip install -r requirements.txt
fi
python "$THE_SCRIPT" "$@"

To make CA optional, the aws codeartifact login would need to be wrapped in an if condition, for the environment variables to be set.

@akuma12

Copy link
Copy Markdown
ContributorAuthor

I had almost forgotten about this. Thank you @humanzz for your feedback. I'll look into that script change and see if I can modify the code to make use of that.

@codecov

codecovBot commented Mar 15, 2024

Copy link
Copy Markdown

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 87.44%. Comparing base (31190c4) to head (cfe8139).

Current head cfe8139 differs from pull request most recent head f9deaa5

Please upload reports for the commit f9deaa5 to get more accurate results.

Additional details and impacted files
@@ Coverage Diff @@## master #4145 +/- ##
==========================================
+ Coverage 86.70% 87.44% +0.74% 
==========================================
Files 409 389 -20 Lines 39067 36904 -2163 ==========================================
- Hits 33872 32272 -1600 + Misses 5195 4632 -563 

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@akuma12

Copy link
Copy Markdown
ContributorAuthor

@humanzz The process using the AWS CLI is muuuuuch simpler. I don't have to rely on boto3, and I confirmed that the PyTorch training images have the AWS CLI installed. Updated the code and added some additional unit tests.

@akuma12

Copy link
Copy Markdown
ContributorAuthor

@mohanasudhan Looks like all tests and lints are passing now, if you could take one last look.

@akuma12

Copy link
Copy Markdown
ContributorAuthor

@akrishna1995 I'd love to get a final review on this, if possible. Thanks!

@akuma12

Copy link
Copy Markdown
ContributorAuthor

@akrishna1995 Could I get a final review on this PR? I'd really like to get my internal projects off of my fork so I can start getting the more recent updates to sagemaker-python-sdk. Thank you!

@sage-maker

sage-maker commented Aug 7, 2024

Copy link
Copy Markdown
Contributor

@akuma12
On-call here taking a look at this PR. I started approval workflow for tests. Are all code changes done here?

@akuma12

Copy link
Copy Markdown
ContributorAuthor

@sage-maker
Thank you!
Yup, everything is tested and ready to go.

@sage-maker
sage-maker merged commit 502e051 into aws:masterAug 7, 2024
@akuma12
akuma12 deleted the processing-job-codeartifact-support branch August 8, 2024 14:51
@akuma12
akuma12 restored the processing-job-codeartifact-support branch August 8, 2024 17:52
Evan-W-ang added a commit to Evan-W-ang/sagemaker-python-sdk that referenced this pull request Jun 8, 2026
…cript (aws#4145)
* feature: Add optional CodeArtifact login to FrameworkProcessing job script
* Add unit test for _get_codeartifact_index
* Fixed docstring
* Convert CodeArtifact integration to simply generate an AWS CLI command to log into CodeArtifact
* Fix lint issues
* More lint fixes
* Lint fix
* Yet Another Lint Fix
* Black fix
---------
Co-authored-by: sage-maker <parknate@amazon.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@akuma12@sagemaker-bot@mohanasudhan@humanzz@sage-maker@akrishna1995
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

feature: Add optional CodeArtifact login to FrameworkProcessing job script - #4145

Merged
sage-maker merged 19 commits into
aws:masterfrom
akuma12:processing-job-codeartifact-support
Aug 7, 2024
Merged

feature: Add optional CodeArtifact login to FrameworkProcessing job script#4145
sage-maker merged 19 commits into
aws:masterfrom
akuma12:processing-job-codeartifact-support

Conversation

@akuma12

@akuma12akuma12 commented Sep 27, 2023

Copy link
Copy Markdown
Contributor

Issue #, if available:
#4144

Description of changes:
This PR adds an optional codeartifact_repo_arn parameter to the FrameworkProcessor.run() method. Providing this ARN will allow the _generate_framework_script() method to call _get_codeartifact_index(), which will parse the ARN into a CodeArtifact repo URL, retrieve an authentication token, and write an index option into the pip install -r requirements.txt call generated by _generate_framework_script()

If codeartifact_repo_arn is not provided, then _get_codeartifact_index() will not be called and nothing new will be injected into the runproc.sh script.

The _get_codeartifact_index() code is copied from the sagemaker-training-toolkit. All credit to @humanzz for that update.

Testing done:
Validated a PytorchProcessing job both with and without the codeartifact_repo_arn parameter. Downloaded the generated runproc.sh file from S3 and verified that the index option is written to the file if the ARN is provided, and does nothing if it is not.

I could use some advice when it comes to automated testing, however. Since _get_codeartifact_index() interacts with CodeArtifact via Boto3, I was unsure of the best way to handle this. In unit tests, I would typically use moto or patch the Boto3 make_api_call method. With the integration tests, I wasn't sure how I should interact with CodeArtifact, or if I should even add an integration test.

Merge Checklist

Put an x in the boxes that apply. You can also fill these out after creating the PR. If you're unsure about any of them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before merging your pull request.

General

  • I have read the CONTRIBUTING doc
  • I certify that the changes I am introducing will be backward compatible, and I have discussed concerns about this, if any, with the Python SDK team
  • I used the commit message format described in CONTRIBUTING
  • I have passed the region in to all S3 and STS clients that I've initialized as part of this change.
  • I have updated any necessary documentation, including READMEs and API docs (if appropriate)

Tests

  • I have added tests that prove my fix is effective or that my feature works (if appropriate)
  • I have added unit and/or integration tests as appropriate to ensure backward compatibility of the changes
  • I have checked that my tests are not configured for a specific region or account (if appropriate)
  • I have used unique_name_from_base to create resource names in integ tests (if appropriate)

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@akuma12
akuma12 requested a review from a team as a code ownerSeptember 27, 2023 21:12
@akuma12
akuma12 requested review from akrishna1995 and removed request for a teamSeptember 27, 2023 21:12
@akrishna1995akrishna1995 self-assigned this Oct 2, 2023

@akrishna1995akrishna1995 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/bot run all

@akrishna1995akrishna1995 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please get a review from one member in your team. please follow best practices - add Unit tests , integ tests

@sagemaker-bot

Copy link
Copy Markdown
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: sagemaker-python-sdk-unit-tests
  • Commit ID: 53c46b0
  • Result: FAILED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@sagemaker-bot

Copy link
Copy Markdown
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: sagemaker-python-sdk-local-mode-tests
  • Commit ID: 53c46b0
  • Result: SUCCEEDED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@sagemaker-bot

Copy link
Copy Markdown
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: sagemaker-python-sdk-notebook-tests
  • Commit ID: 53c46b0
  • Result: SUCCEEDED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@sagemaker-bot

Copy link
Copy Markdown
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: sagemaker-python-sdk-slow-tests
  • Commit ID: 53c46b0
  • Result: SUCCEEDED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@sagemaker-bot

Copy link
Copy Markdown
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: sagemaker-python-sdk-pr
  • Commit ID: 53c46b0
  • Result: FAILED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@goelakash
goelakashforce-pushed the processing-job-codeartifact-support branch from 53c46b0 to 61190deCompareOctober 9, 2023 23:48
@akuma12

Copy link
Copy Markdown
ContributorAuthor

@akrishna1995 Can you re-run the tests? I added 3 more unit tests around the code and fixed the issues that popped up in the last run.

@akuma12

Copy link
Copy Markdown
ContributorAuthor

Curious if anyone has had a chance to take a look at this. Would appreciate another review.

@mohanasudhan

Copy link
Copy Markdown
Contributor

@akuma12 Can you rebase your change? I had a brief look and it lgtm. It would be good to get all the test successful.

@humanzz

Copy link
Copy Markdown
Contributor

Hi @mohanasudhan and @akuma12,
I've been keeping an eye on this, and recently within my team, a need has arisen for using processing jobs and we'd really benefit from this PR being merged/released.

I pulled this PR, and ran export IGNORE_COVERAGE=- ; tox -e py38 -- -s -vv tests/unit/test_processing.py::test_pytorch_processor_with_required_parameters ; unset IGNORE_COVERAGE to check what's failing and all tests seem to be passing.

the only thing that happened was that it seems like black wanted to reformat the files and resulted in the following changes

diff --git a/src/sagemaker/processing.py b/src/sagemaker/processing.py
index b4a063ba..59b8c980 100644
--- a/src/sagemaker/processing.py
+++ b/src/sagemaker/processing.py
@@ -1852,7 +1852,7 @@ class FrameworkProcessor(ScriptProcessor):
# `arn:${Partition}:codeartifact:${Region}:${Account}:repository/${Domain}/${Repository}`
https://docs.aws.amazon.com/codeartifact/latest/ug/python-configure-pip.html
https://docs.aws.amazon.com/service-authorization/latest/reference/list_awscodeartifact.html#awscodeartifact-resources-for-iam-policies
- +
Args:
codeartifact_repo_arn: arn of the codeartifact repository
codeartifact_client: boto3 client for codeartifact (used for testing)
@@ -1882,9 +1882,13 @@ class FrameworkProcessor(ScriptProcessor):
)
try:
if not codeartifact_client:
- codeartifact_client = self.sagemaker_session.boto_session.client("codeartifact", region_name=region)
- - auth_token_response = codeartifact_client.get_authorization_token(domain=domain, domainOwner=owner)
+ codeartifact_client = self.sagemaker_session.boto_session.client(
+ "codeartifact", region_name=region
+ )
+
+ auth_token_response = codeartifact_client.get_authorization_token(
+ domain=domain, domainOwner=owner
+ )
token = auth_token_response["authorizationToken"]
endpoint_response = codeartifact_client.get_repository_endpoint(
domain=domain, domainOwner=owner, repository=repository, format="pypi"
diff --git a/tests/unit/test_processing.py b/tests/unit/test_processing.py
index fb55e2fe..3663b35b 100644
--- a/tests/unit/test_processing.py
+++ b/tests/unit/test_processing.py
@@ -1107,27 +1107,33 @@ def test_pyspark_processor_configuration_path_pipeline_config(
@patch("sagemaker.workflow.utilities._pipeline_config", MOCKED_PIPELINE_CONFIG)
def test_get_codeartifact_index(pipeline_session):
- codeartifact_repo_arn = "arn:aws:codeartifact:us-west-2:012345678901:repository/test-domain/test-repository"
+ codeartifact_repo_arn = (
+ "arn:aws:codeartifact:us-west-2:012345678901:repository/test-domain/test-repository"
+ )
codeartifact_url = "test-domain-012345678901.d.codeartifact.us-west-2.amazonaws.com/pypi/test-repository/simple/"
- client = boto3.client('codeartifact', region_name=REGION)
+ client = boto3.client("codeartifact", region_name=REGION)
stubber = Stubber(client)
- +
get_auth_token_response = {
"authorizationToken": "mocked_token",
- "expiration": datetime.datetime(2045, 1, 1, 0, 0, 0)
+ "expiration": datetime.datetime(2045, 1, 1, 0, 0, 0),
}
auth_token_expected_params = {"domain": "test-domain", "domainOwner": "012345678901"}
- stubber.add_response("get_authorization_token", get_auth_token_response, auth_token_expected_params)
+ stubber.add_response(
+ "get_authorization_token", get_auth_token_response, auth_token_expected_params
+ )
get_repo_endpoint_response = {"repositoryEndpoint": f"https://{codeartifact_url}"}
repo_endpoint_expected_params = {
"domain": "test-domain",
"domainOwner": "012345678901",
"repository": "test-repository",
- "format": "pypi"
+ "format": "pypi",
}
- stubber.add_response("get_repository_endpoint", get_repo_endpoint_response, repo_endpoint_expected_params)
+ stubber.add_response(
+ "get_repository_endpoint", get_repo_endpoint_response, repo_endpoint_expected_params
+ )
processor = PyTorchProcessor(
role=ROLE,
@@ -1139,8 +1145,10 @@ def test_get_codeartifact_index(pipeline_session):
)
with stubber:
- codeartifact_index = processor._get_codeartifact_index(codeartifact_repo_arn=codeartifact_repo_arn, codeartifact_client=client)
- + codeartifact_index = processor._get_codeartifact_index(
+ codeartifact_repo_arn=codeartifact_repo_arn, codeartifact_client=client
+ )
+
assert codeartifact_index == f"https://aws:mocked_token@{codeartifact_url}"
@@ -1149,24 +1157,28 @@ def test_get_codeartifact_index_bad_repo_arn(pipeline_session):
codeartifact_repo_arn = "arn:aws:codeartifact:us-west-2:012345678901:repository/test-domain"
codeartifact_url = "test-domain-012345678901.d.codeartifact.us-west-2.amazonaws.com/pypi/test-repository/simple/"
- client = boto3.client('codeartifact', region_name=REGION)
+ client = boto3.client("codeartifact", region_name=REGION)
stubber = Stubber(client)
- +
get_auth_token_response = {
"authorizationToken": "mocked_token",
- "expiration": datetime.datetime(2045, 1, 1, 0, 0, 0)
+ "expiration": datetime.datetime(2045, 1, 1, 0, 0, 0),
}
auth_token_expected_params = {"domain": "test-domain", "domainOwner": "012345678901"}
- stubber.add_response("get_authorization_token", get_auth_token_response, auth_token_expected_params)
+ stubber.add_response(
+ "get_authorization_token", get_auth_token_response, auth_token_expected_params
+ )
get_repo_endpoint_response = {"repositoryEndpoint": f"https://{codeartifact_url}"}
repo_endpoint_expected_params = {
"domain": "test-domain",
"domainOwner": "012345678901",
"repository": "test-repository",
- "format": "pypi"
+ "format": "pypi",
}
- stubber.add_response("get_repository_endpoint", get_repo_endpoint_response, repo_endpoint_expected_params)
+ stubber.add_response(
+ "get_repository_endpoint", get_repo_endpoint_response, repo_endpoint_expected_params
+ )
processor = PyTorchProcessor(
role=ROLE,
@@ -1179,32 +1191,42 @@ def test_get_codeartifact_index_bad_repo_arn(pipeline_session):
with stubber:
with pytest.raises(ValueError):
- processor._get_codeartifact_index(codeartifact_repo_arn=codeartifact_repo_arn, codeartifact_client=client)
+ processor._get_codeartifact_index(
+ codeartifact_repo_arn=codeartifact_repo_arn, codeartifact_client=client
+ )
@patch("sagemaker.workflow.utilities._pipeline_config", MOCKED_PIPELINE_CONFIG)
def test_get_codeartifact_index_client_error(pipeline_session):
- codeartifact_repo_arn = "arn:aws:codeartifact:us-west-2:012345678901:repository/test-domain/test-repository"
+ codeartifact_repo_arn = (
+ "arn:aws:codeartifact:us-west-2:012345678901:repository/test-domain/test-repository"
+ )
codeartifact_url = "test-domain-012345678901.d.codeartifact.us-west-2.amazonaws.com/pypi/test-repository/simple/"
- client = boto3.client('codeartifact', region_name=REGION)
+ client = boto3.client("codeartifact", region_name=REGION)
stubber = Stubber(client)
- +
get_auth_token_response = {
"authorizationToken": "mocked_token",
- "expiration": datetime.datetime(2045, 1, 1, 0, 0, 0)
+ "expiration": datetime.datetime(2045, 1, 1, 0, 0, 0),
}
auth_token_expected_params = {"domain": "test-domain", "domainOwner": "012345678901"}
- stubber.add_client_error("get_authorization_token", service_error_code="404", expected_params=auth_token_expected_params)
+ stubber.add_client_error(
+ "get_authorization_token",
+ service_error_code="404",
+ expected_params=auth_token_expected_params,
+ )
get_repo_endpoint_response = {"repositoryEndpoint": f"https://{codeartifact_url}"}
repo_endpoint_expected_params = {
"domain": "test-domain",
"domainOwner": "012345678901",
"repository": "test-repository",
- "format": "pypi"
+ "format": "pypi",
}
- stubber.add_response("get_repository_endpoint", get_repo_endpoint_response, repo_endpoint_expected_params)
+ stubber.add_response(
+ "get_repository_endpoint", get_repo_endpoint_response, repo_endpoint_expected_params
+ )
processor = PyTorchProcessor(
role=ROLE,
@@ -1217,7 +1239,9 @@ def test_get_codeartifact_index_client_error(pipeline_session):
with stubber:
with pytest.raises(RuntimeError):
- processor._get_codeartifact_index(codeartifact_repo_arn=codeartifact_repo_arn, codeartifact_client=client)
+ processor._get_codeartifact_index(
+ codeartifact_repo_arn=codeartifact_repo_arn, codeartifact_client=client
+ )
def _get_script_processor(sagemaker_session):

With no access to the build logs, I wonder if that is the only issue?

@humanzz

Copy link
Copy Markdown
Contributor

One more thought, my teammate @Stacy-D, has started looking into using processing jobs, and has been experimenting with a different approach for setting up CodeArtifact.

Rather than uploading a script that has the hardcoded index in pip install -r requirements.txt {index_option}, she's written a script leveraging ** aws cli** (it's an assumption, but we confirmed it on pytorch containers) for configuring pip using

aws codeartifact login --tool pip --repository "$CODEARTIFACT_REPO" --domain "$CODEARTIFACT_DOMAIN" --domain-owner "${CODEARTIFACT_OWNER}" --region "${CODEARTIFACT_REGION}"

The script then looks something along the lines of

#!/bin/bash
cd /opt/ml/processing/input/code/
tar -xzf sourcedir.tar.gz
# Exit on any error. SageMaker uses error code to mark failed job.
set -e
aws codeartifact login --tool pip --repository "$CODEARTIFACT_REPO" --domain "$CODEARTIFACT_DOMAIN" --domain-owner "${CODEARTIFACT_OWNER}" --region "${CODEARTIFACT_REGION}"
if [[ -f 'requirements.txt' ]]; then
# Some py3 containers has typing, which may breaks pip install
pip uninstall --yes typing
pip install -r requirements.txt
fi
python "$THE_SCRIPT" "$@"

To make CA optional, the aws codeartifact login would need to be wrapped in an if condition, for the environment variables to be set.

@akuma12

Copy link
Copy Markdown
ContributorAuthor

I had almost forgotten about this. Thank you @humanzz for your feedback. I'll look into that script change and see if I can modify the code to make use of that.

@codecov

codecovBot commented Mar 15, 2024

Copy link
Copy Markdown

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 87.44%. Comparing base (31190c4) to head (cfe8139).

Current head cfe8139 differs from pull request most recent head f9deaa5

Please upload reports for the commit f9deaa5 to get more accurate results.

Additional details and impacted files
@@ Coverage Diff @@## master #4145 +/- ##
==========================================
+ Coverage 86.70% 87.44% +0.74% 
==========================================
Files 409 389 -20 Lines 39067 36904 -2163 ==========================================
- Hits 33872 32272 -1600 + Misses 5195 4632 -563 

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@akuma12

Copy link
Copy Markdown
ContributorAuthor

@humanzz The process using the AWS CLI is muuuuuch simpler. I don't have to rely on boto3, and I confirmed that the PyTorch training images have the AWS CLI installed. Updated the code and added some additional unit tests.

@akuma12

Copy link
Copy Markdown
ContributorAuthor

@mohanasudhan Looks like all tests and lints are passing now, if you could take one last look.

@akuma12

Copy link
Copy Markdown
ContributorAuthor

@akrishna1995 I'd love to get a final review on this, if possible. Thanks!

@akuma12

Copy link
Copy Markdown
ContributorAuthor

@akrishna1995 Could I get a final review on this PR? I'd really like to get my internal projects off of my fork so I can start getting the more recent updates to sagemaker-python-sdk. Thank you!

@sage-maker

sage-maker commented Aug 7, 2024

Copy link
Copy Markdown
Contributor

@akuma12
On-call here taking a look at this PR. I started approval workflow for tests. Are all code changes done here?

@akuma12

Copy link
Copy Markdown
ContributorAuthor

@sage-maker
Thank you!
Yup, everything is tested and ready to go.

@sage-maker
sage-maker merged commit 502e051 into aws:masterAug 7, 2024
@akuma12
akuma12 deleted the processing-job-codeartifact-support branch August 8, 2024 14:51
@akuma12
akuma12 restored the processing-job-codeartifact-support branch August 8, 2024 17:52
Evan-W-ang added a commit to Evan-W-ang/sagemaker-python-sdk that referenced this pull request Jun 8, 2026
…cript (aws#4145)
* feature: Add optional CodeArtifact login to FrameworkProcessing job script
* Add unit test for _get_codeartifact_index
* Fixed docstring
* Convert CodeArtifact integration to simply generate an AWS CLI command to log into CodeArtifact
* Fix lint issues
* More lint fixes
* Lint fix
* Yet Another Lint Fix
* Black fix
---------
Co-authored-by: sage-maker <parknate@amazon.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@akuma12@sagemaker-bot@mohanasudhan@humanzz@sage-maker@akrishna1995
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feature: Add optional CodeArtifact login to FrameworkProcessing job script - #4145

Merged
sage-maker merged 19 commits into
aws:masterfrom
akuma12:processing-job-codeartifact-support
Aug 7, 2024
Merged

feature: Add optional CodeArtifact login to FrameworkProcessing job script#4145
sage-maker merged 19 commits into
aws:masterfrom
akuma12:processing-job-codeartifact-support

Conversation

@akuma12

@akuma12akuma12 commented Sep 27, 2023

Copy link
Copy Markdown
Contributor

Issue #, if available:
#4144

Description of changes:
This PR adds an optional codeartifact_repo_arn parameter to the FrameworkProcessor.run() method. Providing this ARN will allow the _generate_framework_script() method to call _get_codeartifact_index(), which will parse the ARN into a CodeArtifact repo URL, retrieve an authentication token, and write an index option into the pip install -r requirements.txt call generated by _generate_framework_script()

If codeartifact_repo_arn is not provided, then _get_codeartifact_index() will not be called and nothing new will be injected into the runproc.sh script.

The _get_codeartifact_index() code is copied from the sagemaker-training-toolkit. All credit to @humanzz for that update.

Testing done:
Validated a PytorchProcessing job both with and without the codeartifact_repo_arn parameter. Downloaded the generated runproc.sh file from S3 and verified that the index option is written to the file if the ARN is provided, and does nothing if it is not.

I could use some advice when it comes to automated testing, however. Since _get_codeartifact_index() interacts with CodeArtifact via Boto3, I was unsure of the best way to handle this. In unit tests, I would typically use moto or patch the Boto3 make_api_call method. With the integration tests, I wasn't sure how I should interact with CodeArtifact, or if I should even add an integration test.

Merge Checklist

Put an x in the boxes that apply. You can also fill these out after creating the PR. If you're unsure about any of them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before merging your pull request.

General

  • I have read the CONTRIBUTING doc
  • I certify that the changes I am introducing will be backward compatible, and I have discussed concerns about this, if any, with the Python SDK team
  • I used the commit message format described in CONTRIBUTING
  • I have passed the region in to all S3 and STS clients that I've initialized as part of this change.
  • I have updated any necessary documentation, including READMEs and API docs (if appropriate)

Tests

  • I have added tests that prove my fix is effective or that my feature works (if appropriate)
  • I have added unit and/or integration tests as appropriate to ensure backward compatibility of the changes
  • I have checked that my tests are not configured for a specific region or account (if appropriate)
  • I have used unique_name_from_base to create resource names in integ tests (if appropriate)

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@akuma12
akuma12 requested a review from a team as a code ownerSeptember 27, 2023 21:12
@akuma12
akuma12 requested review from akrishna1995 and removed request for a teamSeptember 27, 2023 21:12
@akrishna1995akrishna1995 self-assigned this Oct 2, 2023

@akrishna1995akrishna1995 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/bot run all

@akrishna1995akrishna1995 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please get a review from one member in your team. please follow best practices - add Unit tests , integ tests

@sagemaker-bot

Copy link
Copy Markdown
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: sagemaker-python-sdk-unit-tests
  • Commit ID: 53c46b0
  • Result: FAILED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@sagemaker-bot

Copy link
Copy Markdown
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: sagemaker-python-sdk-local-mode-tests
  • Commit ID: 53c46b0
  • Result: SUCCEEDED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@sagemaker-bot

Copy link
Copy Markdown
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: sagemaker-python-sdk-notebook-tests
  • Commit ID: 53c46b0
  • Result: SUCCEEDED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@sagemaker-bot

Copy link
Copy Markdown
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: sagemaker-python-sdk-slow-tests
  • Commit ID: 53c46b0
  • Result: SUCCEEDED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@sagemaker-bot

Copy link
Copy Markdown
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: sagemaker-python-sdk-pr
  • Commit ID: 53c46b0
  • Result: FAILED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@goelakash
goelakashforce-pushed the processing-job-codeartifact-support branch from 53c46b0 to 61190deCompareOctober 9, 2023 23:48
@akuma12

Copy link
Copy Markdown
ContributorAuthor

@akrishna1995 Can you re-run the tests? I added 3 more unit tests around the code and fixed the issues that popped up in the last run.

@akuma12

Copy link
Copy Markdown
ContributorAuthor

Curious if anyone has had a chance to take a look at this. Would appreciate another review.

@mohanasudhan

Copy link
Copy Markdown
Contributor

@akuma12 Can you rebase your change? I had a brief look and it lgtm. It would be good to get all the test successful.

@humanzz

Copy link
Copy Markdown
Contributor

Hi @mohanasudhan and @akuma12,
I've been keeping an eye on this, and recently within my team, a need has arisen for using processing jobs and we'd really benefit from this PR being merged/released.

I pulled this PR, and ran export IGNORE_COVERAGE=- ; tox -e py38 -- -s -vv tests/unit/test_processing.py::test_pytorch_processor_with_required_parameters ; unset IGNORE_COVERAGE to check what's failing and all tests seem to be passing.

the only thing that happened was that it seems like black wanted to reformat the files and resulted in the following changes

diff --git a/src/sagemaker/processing.py b/src/sagemaker/processing.py
index b4a063ba..59b8c980 100644
--- a/src/sagemaker/processing.py
+++ b/src/sagemaker/processing.py
@@ -1852,7 +1852,7 @@ class FrameworkProcessor(ScriptProcessor):
# `arn:${Partition}:codeartifact:${Region}:${Account}:repository/${Domain}/${Repository}`
https://docs.aws.amazon.com/codeartifact/latest/ug/python-configure-pip.html
https://docs.aws.amazon.com/service-authorization/latest/reference/list_awscodeartifact.html#awscodeartifact-resources-for-iam-policies
- +
Args:
codeartifact_repo_arn: arn of the codeartifact repository
codeartifact_client: boto3 client for codeartifact (used for testing)
@@ -1882,9 +1882,13 @@ class FrameworkProcessor(ScriptProcessor):
)
try:
if not codeartifact_client:
- codeartifact_client = self.sagemaker_session.boto_session.client("codeartifact", region_name=region)
- - auth_token_response = codeartifact_client.get_authorization_token(domain=domain, domainOwner=owner)
+ codeartifact_client = self.sagemaker_session.boto_session.client(
+ "codeartifact", region_name=region
+ )
+
+ auth_token_response = codeartifact_client.get_authorization_token(
+ domain=domain, domainOwner=owner
+ )
token = auth_token_response["authorizationToken"]
endpoint_response = codeartifact_client.get_repository_endpoint(
domain=domain, domainOwner=owner, repository=repository, format="pypi"
diff --git a/tests/unit/test_processing.py b/tests/unit/test_processing.py
index fb55e2fe..3663b35b 100644
--- a/tests/unit/test_processing.py
+++ b/tests/unit/test_processing.py
@@ -1107,27 +1107,33 @@ def test_pyspark_processor_configuration_path_pipeline_config(
@patch("sagemaker.workflow.utilities._pipeline_config", MOCKED_PIPELINE_CONFIG)
def test_get_codeartifact_index(pipeline_session):
- codeartifact_repo_arn = "arn:aws:codeartifact:us-west-2:012345678901:repository/test-domain/test-repository"
+ codeartifact_repo_arn = (
+ "arn:aws:codeartifact:us-west-2:012345678901:repository/test-domain/test-repository"
+ )
codeartifact_url = "test-domain-012345678901.d.codeartifact.us-west-2.amazonaws.com/pypi/test-repository/simple/"
- client = boto3.client('codeartifact', region_name=REGION)
+ client = boto3.client("codeartifact", region_name=REGION)
stubber = Stubber(client)
- +
get_auth_token_response = {
"authorizationToken": "mocked_token",
- "expiration": datetime.datetime(2045, 1, 1, 0, 0, 0)
+ "expiration": datetime.datetime(2045, 1, 1, 0, 0, 0),
}
auth_token_expected_params = {"domain": "test-domain", "domainOwner": "012345678901"}
- stubber.add_response("get_authorization_token", get_auth_token_response, auth_token_expected_params)
+ stubber.add_response(
+ "get_authorization_token", get_auth_token_response, auth_token_expected_params
+ )
get_repo_endpoint_response = {"repositoryEndpoint": f"https://{codeartifact_url}"}
repo_endpoint_expected_params = {
"domain": "test-domain",
"domainOwner": "012345678901",
"repository": "test-repository",
- "format": "pypi"
+ "format": "pypi",
}
- stubber.add_response("get_repository_endpoint", get_repo_endpoint_response, repo_endpoint_expected_params)
+ stubber.add_response(
+ "get_repository_endpoint", get_repo_endpoint_response, repo_endpoint_expected_params
+ )
processor = PyTorchProcessor(
role=ROLE,
@@ -1139,8 +1145,10 @@ def test_get_codeartifact_index(pipeline_session):
)
with stubber:
- codeartifact_index = processor._get_codeartifact_index(codeartifact_repo_arn=codeartifact_repo_arn, codeartifact_client=client)
- + codeartifact_index = processor._get_codeartifact_index(
+ codeartifact_repo_arn=codeartifact_repo_arn, codeartifact_client=client
+ )
+
assert codeartifact_index == f"https://aws:mocked_token@{codeartifact_url}"
@@ -1149,24 +1157,28 @@ def test_get_codeartifact_index_bad_repo_arn(pipeline_session):
codeartifact_repo_arn = "arn:aws:codeartifact:us-west-2:012345678901:repository/test-domain"
codeartifact_url = "test-domain-012345678901.d.codeartifact.us-west-2.amazonaws.com/pypi/test-repository/simple/"
- client = boto3.client('codeartifact', region_name=REGION)
+ client = boto3.client("codeartifact", region_name=REGION)
stubber = Stubber(client)
- +
get_auth_token_response = {
"authorizationToken": "mocked_token",
- "expiration": datetime.datetime(2045, 1, 1, 0, 0, 0)
+ "expiration": datetime.datetime(2045, 1, 1, 0, 0, 0),
}
auth_token_expected_params = {"domain": "test-domain", "domainOwner": "012345678901"}
- stubber.add_response("get_authorization_token", get_auth_token_response, auth_token_expected_params)
+ stubber.add_response(
+ "get_authorization_token", get_auth_token_response, auth_token_expected_params
+ )
get_repo_endpoint_response = {"repositoryEndpoint": f"https://{codeartifact_url}"}
repo_endpoint_expected_params = {
"domain": "test-domain",
"domainOwner": "012345678901",
"repository": "test-repository",
- "format": "pypi"
+ "format": "pypi",
}
- stubber.add_response("get_repository_endpoint", get_repo_endpoint_response, repo_endpoint_expected_params)
+ stubber.add_response(
+ "get_repository_endpoint", get_repo_endpoint_response, repo_endpoint_expected_params
+ )
processor = PyTorchProcessor(
role=ROLE,
@@ -1179,32 +1191,42 @@ def test_get_codeartifact_index_bad_repo_arn(pipeline_session):
with stubber:
with pytest.raises(ValueError):
- processor._get_codeartifact_index(codeartifact_repo_arn=codeartifact_repo_arn, codeartifact_client=client)
+ processor._get_codeartifact_index(
+ codeartifact_repo_arn=codeartifact_repo_arn, codeartifact_client=client
+ )
@patch("sagemaker.workflow.utilities._pipeline_config", MOCKED_PIPELINE_CONFIG)
def test_get_codeartifact_index_client_error(pipeline_session):
- codeartifact_repo_arn = "arn:aws:codeartifact:us-west-2:012345678901:repository/test-domain/test-repository"
+ codeartifact_repo_arn = (
+ "arn:aws:codeartifact:us-west-2:012345678901:repository/test-domain/test-repository"
+ )
codeartifact_url = "test-domain-012345678901.d.codeartifact.us-west-2.amazonaws.com/pypi/test-repository/simple/"
- client = boto3.client('codeartifact', region_name=REGION)
+ client = boto3.client("codeartifact", region_name=REGION)
stubber = Stubber(client)
- +
get_auth_token_response = {
"authorizationToken": "mocked_token",
- "expiration": datetime.datetime(2045, 1, 1, 0, 0, 0)
+ "expiration": datetime.datetime(2045, 1, 1, 0, 0, 0),
}
auth_token_expected_params = {"domain": "test-domain", "domainOwner": "012345678901"}
- stubber.add_client_error("get_authorization_token", service_error_code="404", expected_params=auth_token_expected_params)
+ stubber.add_client_error(
+ "get_authorization_token",
+ service_error_code="404",
+ expected_params=auth_token_expected_params,
+ )
get_repo_endpoint_response = {"repositoryEndpoint": f"https://{codeartifact_url}"}
repo_endpoint_expected_params = {
"domain": "test-domain",
"domainOwner": "012345678901",
"repository": "test-repository",
- "format": "pypi"
+ "format": "pypi",
}
- stubber.add_response("get_repository_endpoint", get_repo_endpoint_response, repo_endpoint_expected_params)
+ stubber.add_response(
+ "get_repository_endpoint", get_repo_endpoint_response, repo_endpoint_expected_params
+ )
processor = PyTorchProcessor(
role=ROLE,
@@ -1217,7 +1239,9 @@ def test_get_codeartifact_index_client_error(pipeline_session):
with stubber:
with pytest.raises(RuntimeError):
- processor._get_codeartifact_index(codeartifact_repo_arn=codeartifact_repo_arn, codeartifact_client=client)
+ processor._get_codeartifact_index(
+ codeartifact_repo_arn=codeartifact_repo_arn, codeartifact_client=client
+ )
def _get_script_processor(sagemaker_session):

With no access to the build logs, I wonder if that is the only issue?

@humanzz

Copy link
Copy Markdown
Contributor

One more thought, my teammate @Stacy-D, has started looking into using processing jobs, and has been experimenting with a different approach for setting up CodeArtifact.

Rather than uploading a script that has the hardcoded index in pip install -r requirements.txt {index_option}, she's written a script leveraging ** aws cli** (it's an assumption, but we confirmed it on pytorch containers) for configuring pip using

aws codeartifact login --tool pip --repository "$CODEARTIFACT_REPO" --domain "$CODEARTIFACT_DOMAIN" --domain-owner "${CODEARTIFACT_OWNER}" --region "${CODEARTIFACT_REGION}"

The script then looks something along the lines of

#!/bin/bash
cd /opt/ml/processing/input/code/
tar -xzf sourcedir.tar.gz
# Exit on any error. SageMaker uses error code to mark failed job.
set -e
aws codeartifact login --tool pip --repository "$CODEARTIFACT_REPO" --domain "$CODEARTIFACT_DOMAIN" --domain-owner "${CODEARTIFACT_OWNER}" --region "${CODEARTIFACT_REGION}"
if [[ -f 'requirements.txt' ]]; then
# Some py3 containers has typing, which may breaks pip install
pip uninstall --yes typing
pip install -r requirements.txt
fi
python "$THE_SCRIPT" "$@"

To make CA optional, the aws codeartifact login would need to be wrapped in an if condition, for the environment variables to be set.

@akuma12

Copy link
Copy Markdown
ContributorAuthor

I had almost forgotten about this. Thank you @humanzz for your feedback. I'll look into that script change and see if I can modify the code to make use of that.

@codecov

codecovBot commented Mar 15, 2024

Copy link
Copy Markdown

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 87.44%. Comparing base (31190c4) to head (cfe8139).

Current head cfe8139 differs from pull request most recent head f9deaa5

Please upload reports for the commit f9deaa5 to get more accurate results.

Additional details and impacted files
@@ Coverage Diff @@## master #4145 +/- ##
==========================================
+ Coverage 86.70% 87.44% +0.74% 
==========================================
Files 409 389 -20 Lines 39067 36904 -2163 ==========================================
- Hits 33872 32272 -1600 + Misses 5195 4632 -563 

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@akuma12

Copy link
Copy Markdown
ContributorAuthor

@humanzz The process using the AWS CLI is muuuuuch simpler. I don't have to rely on boto3, and I confirmed that the PyTorch training images have the AWS CLI installed. Updated the code and added some additional unit tests.

@akuma12

Copy link
Copy Markdown
ContributorAuthor

@mohanasudhan Looks like all tests and lints are passing now, if you could take one last look.

@akuma12

Copy link
Copy Markdown
ContributorAuthor

@akrishna1995 I'd love to get a final review on this, if possible. Thanks!

@akuma12

Copy link
Copy Markdown
ContributorAuthor

@akrishna1995 Could I get a final review on this PR? I'd really like to get my internal projects off of my fork so I can start getting the more recent updates to sagemaker-python-sdk. Thank you!

@sage-maker

sage-maker commented Aug 7, 2024

Copy link
Copy Markdown
Contributor

@akuma12
On-call here taking a look at this PR. I started approval workflow for tests. Are all code changes done here?

@akuma12

Copy link
Copy Markdown
ContributorAuthor

@sage-maker
Thank you!
Yup, everything is tested and ready to go.

@sage-maker
sage-maker merged commit 502e051 into aws:masterAug 7, 2024
@akuma12
akuma12 deleted the processing-job-codeartifact-support branch August 8, 2024 14:51
@akuma12
akuma12 restored the processing-job-codeartifact-support branch August 8, 2024 17:52
Evan-W-ang added a commit to Evan-W-ang/sagemaker-python-sdk that referenced this pull request Jun 8, 2026
…cript (aws#4145)
* feature: Add optional CodeArtifact login to FrameworkProcessing job script
* Add unit test for _get_codeartifact_index
* Fixed docstring
* Convert CodeArtifact integration to simply generate an AWS CLI command to log into CodeArtifact
* Fix lint issues
* More lint fixes
* Lint fix
* Yet Another Lint Fix
* Black fix
---------
Co-authored-by: sage-maker <parknate@amazon.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@akuma12@sagemaker-bot@mohanasudhan@humanzz@sage-maker@akrishna1995
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feature: Add optional CodeArtifact login to FrameworkProcessing job script - #4145

Merged
sage-maker merged 19 commits into
aws:masterfrom
akuma12:processing-job-codeartifact-support
Aug 7, 2024
Merged

feature: Add optional CodeArtifact login to FrameworkProcessing job script#4145
sage-maker merged 19 commits into
aws:masterfrom
akuma12:processing-job-codeartifact-support

Conversation

@akuma12

@akuma12akuma12 commented Sep 27, 2023

Copy link
Copy Markdown
Contributor

Issue #, if available:
#4144

Description of changes:
This PR adds an optional codeartifact_repo_arn parameter to the FrameworkProcessor.run() method. Providing this ARN will allow the _generate_framework_script() method to call _get_codeartifact_index(), which will parse the ARN into a CodeArtifact repo URL, retrieve an authentication token, and write an index option into the pip install -r requirements.txt call generated by _generate_framework_script()

If codeartifact_repo_arn is not provided, then _get_codeartifact_index() will not be called and nothing new will be injected into the runproc.sh script.

The _get_codeartifact_index() code is copied from the sagemaker-training-toolkit. All credit to @humanzz for that update.

Testing done:
Validated a PytorchProcessing job both with and without the codeartifact_repo_arn parameter. Downloaded the generated runproc.sh file from S3 and verified that the index option is written to the file if the ARN is provided, and does nothing if it is not.

I could use some advice when it comes to automated testing, however. Since _get_codeartifact_index() interacts with CodeArtifact via Boto3, I was unsure of the best way to handle this. In unit tests, I would typically use moto or patch the Boto3 make_api_call method. With the integration tests, I wasn't sure how I should interact with CodeArtifact, or if I should even add an integration test.

Merge Checklist

Put an x in the boxes that apply. You can also fill these out after creating the PR. If you're unsure about any of them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before merging your pull request.

General

  • I have read the CONTRIBUTING doc
  • I certify that the changes I am introducing will be backward compatible, and I have discussed concerns about this, if any, with the Python SDK team
  • I used the commit message format described in CONTRIBUTING
  • I have passed the region in to all S3 and STS clients that I've initialized as part of this change.
  • I have updated any necessary documentation, including READMEs and API docs (if appropriate)

Tests

  • I have added tests that prove my fix is effective or that my feature works (if appropriate)
  • I have added unit and/or integration tests as appropriate to ensure backward compatibility of the changes
  • I have checked that my tests are not configured for a specific region or account (if appropriate)
  • I have used unique_name_from_base to create resource names in integ tests (if appropriate)

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@akuma12
akuma12 requested a review from a team as a code ownerSeptember 27, 2023 21:12
@akuma12
akuma12 requested review from akrishna1995 and removed request for a teamSeptember 27, 2023 21:12
@akrishna1995akrishna1995 self-assigned this Oct 2, 2023

@akrishna1995akrishna1995 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/bot run all

@akrishna1995akrishna1995 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please get a review from one member in your team. please follow best practices - add Unit tests , integ tests

@sagemaker-bot

Copy link
Copy Markdown
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: sagemaker-python-sdk-unit-tests
  • Commit ID: 53c46b0
  • Result: FAILED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@sagemaker-bot

Copy link
Copy Markdown
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: sagemaker-python-sdk-local-mode-tests
  • Commit ID: 53c46b0
  • Result: SUCCEEDED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@sagemaker-bot

Copy link
Copy Markdown
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: sagemaker-python-sdk-notebook-tests
  • Commit ID: 53c46b0
  • Result: SUCCEEDED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@sagemaker-bot

Copy link
Copy Markdown
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: sagemaker-python-sdk-slow-tests
  • Commit ID: 53c46b0
  • Result: SUCCEEDED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@sagemaker-bot

Copy link
Copy Markdown
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: sagemaker-python-sdk-pr
  • Commit ID: 53c46b0
  • Result: FAILED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@goelakash
goelakashforce-pushed the processing-job-codeartifact-support branch from 53c46b0 to 61190deCompareOctober 9, 2023 23:48
@akuma12

Copy link
Copy Markdown
ContributorAuthor

@akrishna1995 Can you re-run the tests? I added 3 more unit tests around the code and fixed the issues that popped up in the last run.

@akuma12

Copy link
Copy Markdown
ContributorAuthor

Curious if anyone has had a chance to take a look at this. Would appreciate another review.

@mohanasudhan

Copy link
Copy Markdown
Contributor

@akuma12 Can you rebase your change? I had a brief look and it lgtm. It would be good to get all the test successful.

@humanzz

Copy link
Copy Markdown
Contributor

Hi @mohanasudhan and @akuma12,
I've been keeping an eye on this, and recently within my team, a need has arisen for using processing jobs and we'd really benefit from this PR being merged/released.

I pulled this PR, and ran export IGNORE_COVERAGE=- ; tox -e py38 -- -s -vv tests/unit/test_processing.py::test_pytorch_processor_with_required_parameters ; unset IGNORE_COVERAGE to check what's failing and all tests seem to be passing.

the only thing that happened was that it seems like black wanted to reformat the files and resulted in the following changes

diff --git a/src/sagemaker/processing.py b/src/sagemaker/processing.py
index b4a063ba..59b8c980 100644
--- a/src/sagemaker/processing.py
+++ b/src/sagemaker/processing.py
@@ -1852,7 +1852,7 @@ class FrameworkProcessor(ScriptProcessor):
# `arn:${Partition}:codeartifact:${Region}:${Account}:repository/${Domain}/${Repository}`
https://docs.aws.amazon.com/codeartifact/latest/ug/python-configure-pip.html
https://docs.aws.amazon.com/service-authorization/latest/reference/list_awscodeartifact.html#awscodeartifact-resources-for-iam-policies
- +
Args:
codeartifact_repo_arn: arn of the codeartifact repository
codeartifact_client: boto3 client for codeartifact (used for testing)
@@ -1882,9 +1882,13 @@ class FrameworkProcessor(ScriptProcessor):
)
try:
if not codeartifact_client:
- codeartifact_client = self.sagemaker_session.boto_session.client("codeartifact", region_name=region)
- - auth_token_response = codeartifact_client.get_authorization_token(domain=domain, domainOwner=owner)
+ codeartifact_client = self.sagemaker_session.boto_session.client(
+ "codeartifact", region_name=region
+ )
+
+ auth_token_response = codeartifact_client.get_authorization_token(
+ domain=domain, domainOwner=owner
+ )
token = auth_token_response["authorizationToken"]
endpoint_response = codeartifact_client.get_repository_endpoint(
domain=domain, domainOwner=owner, repository=repository, format="pypi"
diff --git a/tests/unit/test_processing.py b/tests/unit/test_processing.py
index fb55e2fe..3663b35b 100644
--- a/tests/unit/test_processing.py
+++ b/tests/unit/test_processing.py
@@ -1107,27 +1107,33 @@ def test_pyspark_processor_configuration_path_pipeline_config(
@patch("sagemaker.workflow.utilities._pipeline_config", MOCKED_PIPELINE_CONFIG)
def test_get_codeartifact_index(pipeline_session):
- codeartifact_repo_arn = "arn:aws:codeartifact:us-west-2:012345678901:repository/test-domain/test-repository"
+ codeartifact_repo_arn = (
+ "arn:aws:codeartifact:us-west-2:012345678901:repository/test-domain/test-repository"
+ )
codeartifact_url = "test-domain-012345678901.d.codeartifact.us-west-2.amazonaws.com/pypi/test-repository/simple/"
- client = boto3.client('codeartifact', region_name=REGION)
+ client = boto3.client("codeartifact", region_name=REGION)
stubber = Stubber(client)
- +
get_auth_token_response = {
"authorizationToken": "mocked_token",
- "expiration": datetime.datetime(2045, 1, 1, 0, 0, 0)
+ "expiration": datetime.datetime(2045, 1, 1, 0, 0, 0),
}
auth_token_expected_params = {"domain": "test-domain", "domainOwner": "012345678901"}
- stubber.add_response("get_authorization_token", get_auth_token_response, auth_token_expected_params)
+ stubber.add_response(
+ "get_authorization_token", get_auth_token_response, auth_token_expected_params
+ )
get_repo_endpoint_response = {"repositoryEndpoint": f"https://{codeartifact_url}"}
repo_endpoint_expected_params = {
"domain": "test-domain",
"domainOwner": "012345678901",
"repository": "test-repository",
- "format": "pypi"
+ "format": "pypi",
}
- stubber.add_response("get_repository_endpoint", get_repo_endpoint_response, repo_endpoint_expected_params)
+ stubber.add_response(
+ "get_repository_endpoint", get_repo_endpoint_response, repo_endpoint_expected_params
+ )
processor = PyTorchProcessor(
role=ROLE,
@@ -1139,8 +1145,10 @@ def test_get_codeartifact_index(pipeline_session):
)
with stubber:
- codeartifact_index = processor._get_codeartifact_index(codeartifact_repo_arn=codeartifact_repo_arn, codeartifact_client=client)
- + codeartifact_index = processor._get_codeartifact_index(
+ codeartifact_repo_arn=codeartifact_repo_arn, codeartifact_client=client
+ )
+
assert codeartifact_index == f"https://aws:mocked_token@{codeartifact_url}"
@@ -1149,24 +1157,28 @@ def test_get_codeartifact_index_bad_repo_arn(pipeline_session):
codeartifact_repo_arn = "arn:aws:codeartifact:us-west-2:012345678901:repository/test-domain"
codeartifact_url = "test-domain-012345678901.d.codeartifact.us-west-2.amazonaws.com/pypi/test-repository/simple/"
- client = boto3.client('codeartifact', region_name=REGION)
+ client = boto3.client("codeartifact", region_name=REGION)
stubber = Stubber(client)
- +
get_auth_token_response = {
"authorizationToken": "mocked_token",
- "expiration": datetime.datetime(2045, 1, 1, 0, 0, 0)
+ "expiration": datetime.datetime(2045, 1, 1, 0, 0, 0),
}
auth_token_expected_params = {"domain": "test-domain", "domainOwner": "012345678901"}
- stubber.add_response("get_authorization_token", get_auth_token_response, auth_token_expected_params)
+ stubber.add_response(
+ "get_authorization_token", get_auth_token_response, auth_token_expected_params
+ )
get_repo_endpoint_response = {"repositoryEndpoint": f"https://{codeartifact_url}"}
repo_endpoint_expected_params = {
"domain": "test-domain",
"domainOwner": "012345678901",
"repository": "test-repository",
- "format": "pypi"
+ "format": "pypi",
}
- stubber.add_response("get_repository_endpoint", get_repo_endpoint_response, repo_endpoint_expected_params)
+ stubber.add_response(
+ "get_repository_endpoint", get_repo_endpoint_response, repo_endpoint_expected_params
+ )
processor = PyTorchProcessor(
role=ROLE,
@@ -1179,32 +1191,42 @@ def test_get_codeartifact_index_bad_repo_arn(pipeline_session):
with stubber:
with pytest.raises(ValueError):
- processor._get_codeartifact_index(codeartifact_repo_arn=codeartifact_repo_arn, codeartifact_client=client)
+ processor._get_codeartifact_index(
+ codeartifact_repo_arn=codeartifact_repo_arn, codeartifact_client=client
+ )
@patch("sagemaker.workflow.utilities._pipeline_config", MOCKED_PIPELINE_CONFIG)
def test_get_codeartifact_index_client_error(pipeline_session):
- codeartifact_repo_arn = "arn:aws:codeartifact:us-west-2:012345678901:repository/test-domain/test-repository"
+ codeartifact_repo_arn = (
+ "arn:aws:codeartifact:us-west-2:012345678901:repository/test-domain/test-repository"
+ )
codeartifact_url = "test-domain-012345678901.d.codeartifact.us-west-2.amazonaws.com/pypi/test-repository/simple/"
- client = boto3.client('codeartifact', region_name=REGION)
+ client = boto3.client("codeartifact", region_name=REGION)
stubber = Stubber(client)
- +
get_auth_token_response = {
"authorizationToken": "mocked_token",
- "expiration": datetime.datetime(2045, 1, 1, 0, 0, 0)
+ "expiration": datetime.datetime(2045, 1, 1, 0, 0, 0),
}
auth_token_expected_params = {"domain": "test-domain", "domainOwner": "012345678901"}
- stubber.add_client_error("get_authorization_token", service_error_code="404", expected_params=auth_token_expected_params)
+ stubber.add_client_error(
+ "get_authorization_token",
+ service_error_code="404",
+ expected_params=auth_token_expected_params,
+ )
get_repo_endpoint_response = {"repositoryEndpoint": f"https://{codeartifact_url}"}
repo_endpoint_expected_params = {
"domain": "test-domain",
"domainOwner": "012345678901",
"repository": "test-repository",
- "format": "pypi"
+ "format": "pypi",
}
- stubber.add_response("get_repository_endpoint", get_repo_endpoint_response, repo_endpoint_expected_params)
+ stubber.add_response(
+ "get_repository_endpoint", get_repo_endpoint_response, repo_endpoint_expected_params
+ )
processor = PyTorchProcessor(
role=ROLE,
@@ -1217,7 +1239,9 @@ def test_get_codeartifact_index_client_error(pipeline_session):
with stubber:
with pytest.raises(RuntimeError):
- processor._get_codeartifact_index(codeartifact_repo_arn=codeartifact_repo_arn, codeartifact_client=client)
+ processor._get_codeartifact_index(
+ codeartifact_repo_arn=codeartifact_repo_arn, codeartifact_client=client
+ )
def _get_script_processor(sagemaker_session):

With no access to the build logs, I wonder if that is the only issue?

@humanzz

Copy link
Copy Markdown
Contributor

One more thought, my teammate @Stacy-D, has started looking into using processing jobs, and has been experimenting with a different approach for setting up CodeArtifact.

Rather than uploading a script that has the hardcoded index in pip install -r requirements.txt {index_option}, she's written a script leveraging ** aws cli** (it's an assumption, but we confirmed it on pytorch containers) for configuring pip using

aws codeartifact login --tool pip --repository "$CODEARTIFACT_REPO" --domain "$CODEARTIFACT_DOMAIN" --domain-owner "${CODEARTIFACT_OWNER}" --region "${CODEARTIFACT_REGION}"

The script then looks something along the lines of

#!/bin/bash
cd /opt/ml/processing/input/code/
tar -xzf sourcedir.tar.gz
# Exit on any error. SageMaker uses error code to mark failed job.
set -e
aws codeartifact login --tool pip --repository "$CODEARTIFACT_REPO" --domain "$CODEARTIFACT_DOMAIN" --domain-owner "${CODEARTIFACT_OWNER}" --region "${CODEARTIFACT_REGION}"
if [[ -f 'requirements.txt' ]]; then
# Some py3 containers has typing, which may breaks pip install
pip uninstall --yes typing
pip install -r requirements.txt
fi
python "$THE_SCRIPT" "$@"

To make CA optional, the aws codeartifact login would need to be wrapped in an if condition, for the environment variables to be set.

@akuma12

Copy link
Copy Markdown
ContributorAuthor

I had almost forgotten about this. Thank you @humanzz for your feedback. I'll look into that script change and see if I can modify the code to make use of that.

@codecov

codecovBot commented Mar 15, 2024

Copy link
Copy Markdown

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 87.44%. Comparing base (31190c4) to head (cfe8139).

Current head cfe8139 differs from pull request most recent head f9deaa5

Please upload reports for the commit f9deaa5 to get more accurate results.

Additional details and impacted files
@@ Coverage Diff @@## master #4145 +/- ##
==========================================
+ Coverage 86.70% 87.44% +0.74% 
==========================================
Files 409 389 -20 Lines 39067 36904 -2163 ==========================================
- Hits 33872 32272 -1600 + Misses 5195 4632 -563 

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@akuma12

Copy link
Copy Markdown
ContributorAuthor

@humanzz The process using the AWS CLI is muuuuuch simpler. I don't have to rely on boto3, and I confirmed that the PyTorch training images have the AWS CLI installed. Updated the code and added some additional unit tests.

@akuma12

Copy link
Copy Markdown
ContributorAuthor

@mohanasudhan Looks like all tests and lints are passing now, if you could take one last look.

@akuma12

Copy link
Copy Markdown
ContributorAuthor

@akrishna1995 I'd love to get a final review on this, if possible. Thanks!

@akuma12

Copy link
Copy Markdown
ContributorAuthor

@akrishna1995 Could I get a final review on this PR? I'd really like to get my internal projects off of my fork so I can start getting the more recent updates to sagemaker-python-sdk. Thank you!

@sage-maker

sage-maker commented Aug 7, 2024

Copy link
Copy Markdown
Contributor

@akuma12
On-call here taking a look at this PR. I started approval workflow for tests. Are all code changes done here?

@akuma12

Copy link
Copy Markdown
ContributorAuthor

@sage-maker
Thank you!
Yup, everything is tested and ready to go.

@sage-maker
sage-maker merged commit 502e051 into aws:masterAug 7, 2024
@akuma12
akuma12 deleted the processing-job-codeartifact-support branch August 8, 2024 14:51
@akuma12
akuma12 restored the processing-job-codeartifact-support branch August 8, 2024 17:52
Evan-W-ang added a commit to Evan-W-ang/sagemaker-python-sdk that referenced this pull request Jun 8, 2026
…cript (aws#4145)
* feature: Add optional CodeArtifact login to FrameworkProcessing job script
* Add unit test for _get_codeartifact_index
* Fixed docstring
* Convert CodeArtifact integration to simply generate an AWS CLI command to log into CodeArtifact
* Fix lint issues
* More lint fixes
* Lint fix
* Yet Another Lint Fix
* Black fix
---------
Co-authored-by: sage-maker <parknate@amazon.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@akuma12@sagemaker-bot@mohanasudhan@humanzz@sage-maker@akrishna1995
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

feature: Add optional CodeArtifact login to FrameworkProcessing job script - #4145

Merged
sage-maker merged 19 commits into
aws:masterfrom
akuma12:processing-job-codeartifact-support
Aug 7, 2024
Merged

feature: Add optional CodeArtifact login to FrameworkProcessing job script#4145
sage-maker merged 19 commits into
aws:masterfrom
akuma12:processing-job-codeartifact-support

Conversation

@akuma12

@akuma12akuma12 commented Sep 27, 2023

Copy link
Copy Markdown
Contributor

Issue #, if available:
#4144

Description of changes:
This PR adds an optional codeartifact_repo_arn parameter to the FrameworkProcessor.run() method. Providing this ARN will allow the _generate_framework_script() method to call _get_codeartifact_index(), which will parse the ARN into a CodeArtifact repo URL, retrieve an authentication token, and write an index option into the pip install -r requirements.txt call generated by _generate_framework_script()

If codeartifact_repo_arn is not provided, then _get_codeartifact_index() will not be called and nothing new will be injected into the runproc.sh script.

The _get_codeartifact_index() code is copied from the sagemaker-training-toolkit. All credit to @humanzz for that update.

Testing done:
Validated a PytorchProcessing job both with and without the codeartifact_repo_arn parameter. Downloaded the generated runproc.sh file from S3 and verified that the index option is written to the file if the ARN is provided, and does nothing if it is not.

I could use some advice when it comes to automated testing, however. Since _get_codeartifact_index() interacts with CodeArtifact via Boto3, I was unsure of the best way to handle this. In unit tests, I would typically use moto or patch the Boto3 make_api_call method. With the integration tests, I wasn't sure how I should interact with CodeArtifact, or if I should even add an integration test.

Merge Checklist

Put an x in the boxes that apply. You can also fill these out after creating the PR. If you're unsure about any of them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before merging your pull request.

General

  • I have read the CONTRIBUTING doc
  • I certify that the changes I am introducing will be backward compatible, and I have discussed concerns about this, if any, with the Python SDK team
  • I used the commit message format described in CONTRIBUTING
  • I have passed the region in to all S3 and STS clients that I've initialized as part of this change.
  • I have updated any necessary documentation, including READMEs and API docs (if appropriate)

Tests

  • I have added tests that prove my fix is effective or that my feature works (if appropriate)
  • I have added unit and/or integration tests as appropriate to ensure backward compatibility of the changes
  • I have checked that my tests are not configured for a specific region or account (if appropriate)
  • I have used unique_name_from_base to create resource names in integ tests (if appropriate)

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@akuma12
akuma12 requested a review from a team as a code ownerSeptember 27, 2023 21:12
@akuma12
akuma12 requested review from akrishna1995 and removed request for a teamSeptember 27, 2023 21:12
@akrishna1995akrishna1995 self-assigned this Oct 2, 2023

@akrishna1995akrishna1995 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/bot run all

@akrishna1995akrishna1995 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please get a review from one member in your team. please follow best practices - add Unit tests , integ tests

@sagemaker-bot

Copy link
Copy Markdown
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: sagemaker-python-sdk-unit-tests
  • Commit ID: 53c46b0
  • Result: FAILED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@sagemaker-bot

Copy link
Copy Markdown
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: sagemaker-python-sdk-local-mode-tests
  • Commit ID: 53c46b0
  • Result: SUCCEEDED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@sagemaker-bot

Copy link
Copy Markdown
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: sagemaker-python-sdk-notebook-tests
  • Commit ID: 53c46b0
  • Result: SUCCEEDED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@sagemaker-bot

Copy link
Copy Markdown
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: sagemaker-python-sdk-slow-tests
  • Commit ID: 53c46b0
  • Result: SUCCEEDED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@sagemaker-bot

Copy link
Copy Markdown
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: sagemaker-python-sdk-pr
  • Commit ID: 53c46b0
  • Result: FAILED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@goelakash
goelakashforce-pushed the processing-job-codeartifact-support branch from 53c46b0 to 61190deCompareOctober 9, 2023 23:48
@akuma12

Copy link
Copy Markdown
ContributorAuthor

@akrishna1995 Can you re-run the tests? I added 3 more unit tests around the code and fixed the issues that popped up in the last run.

@akuma12

Copy link
Copy Markdown
ContributorAuthor

Curious if anyone has had a chance to take a look at this. Would appreciate another review.

@mohanasudhan

Copy link
Copy Markdown
Contributor

@akuma12 Can you rebase your change? I had a brief look and it lgtm. It would be good to get all the test successful.

@humanzz

Copy link
Copy Markdown
Contributor

Hi @mohanasudhan and @akuma12,
I've been keeping an eye on this, and recently within my team, a need has arisen for using processing jobs and we'd really benefit from this PR being merged/released.

I pulled this PR, and ran export IGNORE_COVERAGE=- ; tox -e py38 -- -s -vv tests/unit/test_processing.py::test_pytorch_processor_with_required_parameters ; unset IGNORE_COVERAGE to check what's failing and all tests seem to be passing.

the only thing that happened was that it seems like black wanted to reformat the files and resulted in the following changes

diff --git a/src/sagemaker/processing.py b/src/sagemaker/processing.py
index b4a063ba..59b8c980 100644
--- a/src/sagemaker/processing.py
+++ b/src/sagemaker/processing.py
@@ -1852,7 +1852,7 @@ class FrameworkProcessor(ScriptProcessor):
# `arn:${Partition}:codeartifact:${Region}:${Account}:repository/${Domain}/${Repository}`
https://docs.aws.amazon.com/codeartifact/latest/ug/python-configure-pip.html
https://docs.aws.amazon.com/service-authorization/latest/reference/list_awscodeartifact.html#awscodeartifact-resources-for-iam-policies
- +
Args:
codeartifact_repo_arn: arn of the codeartifact repository
codeartifact_client: boto3 client for codeartifact (used for testing)
@@ -1882,9 +1882,13 @@ class FrameworkProcessor(ScriptProcessor):
)
try:
if not codeartifact_client:
- codeartifact_client = self.sagemaker_session.boto_session.client("codeartifact", region_name=region)
- - auth_token_response = codeartifact_client.get_authorization_token(domain=domain, domainOwner=owner)
+ codeartifact_client = self.sagemaker_session.boto_session.client(
+ "codeartifact", region_name=region
+ )
+
+ auth_token_response = codeartifact_client.get_authorization_token(
+ domain=domain, domainOwner=owner
+ )
token = auth_token_response["authorizationToken"]
endpoint_response = codeartifact_client.get_repository_endpoint(
domain=domain, domainOwner=owner, repository=repository, format="pypi"
diff --git a/tests/unit/test_processing.py b/tests/unit/test_processing.py
index fb55e2fe..3663b35b 100644
--- a/tests/unit/test_processing.py
+++ b/tests/unit/test_processing.py
@@ -1107,27 +1107,33 @@ def test_pyspark_processor_configuration_path_pipeline_config(
@patch("sagemaker.workflow.utilities._pipeline_config", MOCKED_PIPELINE_CONFIG)
def test_get_codeartifact_index(pipeline_session):
- codeartifact_repo_arn = "arn:aws:codeartifact:us-west-2:012345678901:repository/test-domain/test-repository"
+ codeartifact_repo_arn = (
+ "arn:aws:codeartifact:us-west-2:012345678901:repository/test-domain/test-repository"
+ )
codeartifact_url = "test-domain-012345678901.d.codeartifact.us-west-2.amazonaws.com/pypi/test-repository/simple/"
- client = boto3.client('codeartifact', region_name=REGION)
+ client = boto3.client("codeartifact", region_name=REGION)
stubber = Stubber(client)
- +
get_auth_token_response = {
"authorizationToken": "mocked_token",
- "expiration": datetime.datetime(2045, 1, 1, 0, 0, 0)
+ "expiration": datetime.datetime(2045, 1, 1, 0, 0, 0),
}
auth_token_expected_params = {"domain": "test-domain", "domainOwner": "012345678901"}
- stubber.add_response("get_authorization_token", get_auth_token_response, auth_token_expected_params)
+ stubber.add_response(
+ "get_authorization_token", get_auth_token_response, auth_token_expected_params
+ )
get_repo_endpoint_response = {"repositoryEndpoint": f"https://{codeartifact_url}"}
repo_endpoint_expected_params = {
"domain": "test-domain",
"domainOwner": "012345678901",
"repository": "test-repository",
- "format": "pypi"
+ "format": "pypi",
}
- stubber.add_response("get_repository_endpoint", get_repo_endpoint_response, repo_endpoint_expected_params)
+ stubber.add_response(
+ "get_repository_endpoint", get_repo_endpoint_response, repo_endpoint_expected_params
+ )
processor = PyTorchProcessor(
role=ROLE,
@@ -1139,8 +1145,10 @@ def test_get_codeartifact_index(pipeline_session):
)
with stubber:
- codeartifact_index = processor._get_codeartifact_index(codeartifact_repo_arn=codeartifact_repo_arn, codeartifact_client=client)
- + codeartifact_index = processor._get_codeartifact_index(
+ codeartifact_repo_arn=codeartifact_repo_arn, codeartifact_client=client
+ )
+
assert codeartifact_index == f"https://aws:mocked_token@{codeartifact_url}"
@@ -1149,24 +1157,28 @@ def test_get_codeartifact_index_bad_repo_arn(pipeline_session):
codeartifact_repo_arn = "arn:aws:codeartifact:us-west-2:012345678901:repository/test-domain"
codeartifact_url = "test-domain-012345678901.d.codeartifact.us-west-2.amazonaws.com/pypi/test-repository/simple/"
- client = boto3.client('codeartifact', region_name=REGION)
+ client = boto3.client("codeartifact", region_name=REGION)
stubber = Stubber(client)
- +
get_auth_token_response = {
"authorizationToken": "mocked_token",
- "expiration": datetime.datetime(2045, 1, 1, 0, 0, 0)
+ "expiration": datetime.datetime(2045, 1, 1, 0, 0, 0),
}
auth_token_expected_params = {"domain": "test-domain", "domainOwner": "012345678901"}
- stubber.add_response("get_authorization_token", get_auth_token_response, auth_token_expected_params)
+ stubber.add_response(
+ "get_authorization_token", get_auth_token_response, auth_token_expected_params
+ )
get_repo_endpoint_response = {"repositoryEndpoint": f"https://{codeartifact_url}"}
repo_endpoint_expected_params = {
"domain": "test-domain",
"domainOwner": "012345678901",
"repository": "test-repository",
- "format": "pypi"
+ "format": "pypi",
}
- stubber.add_response("get_repository_endpoint", get_repo_endpoint_response, repo_endpoint_expected_params)
+ stubber.add_response(
+ "get_repository_endpoint", get_repo_endpoint_response, repo_endpoint_expected_params
+ )
processor = PyTorchProcessor(
role=ROLE,
@@ -1179,32 +1191,42 @@ def test_get_codeartifact_index_bad_repo_arn(pipeline_session):
with stubber:
with pytest.raises(ValueError):
- processor._get_codeartifact_index(codeartifact_repo_arn=codeartifact_repo_arn, codeartifact_client=client)
+ processor._get_codeartifact_index(
+ codeartifact_repo_arn=codeartifact_repo_arn, codeartifact_client=client
+ )
@patch("sagemaker.workflow.utilities._pipeline_config", MOCKED_PIPELINE_CONFIG)
def test_get_codeartifact_index_client_error(pipeline_session):
- codeartifact_repo_arn = "arn:aws:codeartifact:us-west-2:012345678901:repository/test-domain/test-repository"
+ codeartifact_repo_arn = (
+ "arn:aws:codeartifact:us-west-2:012345678901:repository/test-domain/test-repository"
+ )
codeartifact_url = "test-domain-012345678901.d.codeartifact.us-west-2.amazonaws.com/pypi/test-repository/simple/"
- client = boto3.client('codeartifact', region_name=REGION)
+ client = boto3.client("codeartifact", region_name=REGION)
stubber = Stubber(client)
- +
get_auth_token_response = {
"authorizationToken": "mocked_token",
- "expiration": datetime.datetime(2045, 1, 1, 0, 0, 0)
+ "expiration": datetime.datetime(2045, 1, 1, 0, 0, 0),
}
auth_token_expected_params = {"domain": "test-domain", "domainOwner": "012345678901"}
- stubber.add_client_error("get_authorization_token", service_error_code="404", expected_params=auth_token_expected_params)
+ stubber.add_client_error(
+ "get_authorization_token",
+ service_error_code="404",
+ expected_params=auth_token_expected_params,
+ )
get_repo_endpoint_response = {"repositoryEndpoint": f"https://{codeartifact_url}"}
repo_endpoint_expected_params = {
"domain": "test-domain",
"domainOwner": "012345678901",
"repository": "test-repository",
- "format": "pypi"
+ "format": "pypi",
}
- stubber.add_response("get_repository_endpoint", get_repo_endpoint_response, repo_endpoint_expected_params)
+ stubber.add_response(
+ "get_repository_endpoint", get_repo_endpoint_response, repo_endpoint_expected_params
+ )
processor = PyTorchProcessor(
role=ROLE,
@@ -1217,7 +1239,9 @@ def test_get_codeartifact_index_client_error(pipeline_session):
with stubber:
with pytest.raises(RuntimeError):
- processor._get_codeartifact_index(codeartifact_repo_arn=codeartifact_repo_arn, codeartifact_client=client)
+ processor._get_codeartifact_index(
+ codeartifact_repo_arn=codeartifact_repo_arn, codeartifact_client=client
+ )
def _get_script_processor(sagemaker_session):

With no access to the build logs, I wonder if that is the only issue?

@humanzz

Copy link
Copy Markdown
Contributor

One more thought, my teammate @Stacy-D, has started looking into using processing jobs, and has been experimenting with a different approach for setting up CodeArtifact.

Rather than uploading a script that has the hardcoded index in pip install -r requirements.txt {index_option}, she's written a script leveraging ** aws cli** (it's an assumption, but we confirmed it on pytorch containers) for configuring pip using

aws codeartifact login --tool pip --repository "$CODEARTIFACT_REPO" --domain "$CODEARTIFACT_DOMAIN" --domain-owner "${CODEARTIFACT_OWNER}" --region "${CODEARTIFACT_REGION}"

The script then looks something along the lines of

#!/bin/bash
cd /opt/ml/processing/input/code/
tar -xzf sourcedir.tar.gz
# Exit on any error. SageMaker uses error code to mark failed job.
set -e
aws codeartifact login --tool pip --repository "$CODEARTIFACT_REPO" --domain "$CODEARTIFACT_DOMAIN" --domain-owner "${CODEARTIFACT_OWNER}" --region "${CODEARTIFACT_REGION}"
if [[ -f 'requirements.txt' ]]; then
# Some py3 containers has typing, which may breaks pip install
pip uninstall --yes typing
pip install -r requirements.txt
fi
python "$THE_SCRIPT" "$@"

To make CA optional, the aws codeartifact login would need to be wrapped in an if condition, for the environment variables to be set.

@akuma12

Copy link
Copy Markdown
ContributorAuthor

I had almost forgotten about this. Thank you @humanzz for your feedback. I'll look into that script change and see if I can modify the code to make use of that.

@codecov

codecovBot commented Mar 15, 2024

Copy link
Copy Markdown

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 87.44%. Comparing base (31190c4) to head (cfe8139).

Current head cfe8139 differs from pull request most recent head f9deaa5

Please upload reports for the commit f9deaa5 to get more accurate results.

Additional details and impacted files
@@ Coverage Diff @@## master #4145 +/- ##
==========================================
+ Coverage 86.70% 87.44% +0.74% 
==========================================
Files 409 389 -20 Lines 39067 36904 -2163 ==========================================
- Hits 33872 32272 -1600 + Misses 5195 4632 -563 

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@akuma12

Copy link
Copy Markdown
ContributorAuthor

@humanzz The process using the AWS CLI is muuuuuch simpler. I don't have to rely on boto3, and I confirmed that the PyTorch training images have the AWS CLI installed. Updated the code and added some additional unit tests.

@akuma12

Copy link
Copy Markdown
ContributorAuthor

@mohanasudhan Looks like all tests and lints are passing now, if you could take one last look.

@akuma12

Copy link
Copy Markdown
ContributorAuthor

@akrishna1995 I'd love to get a final review on this, if possible. Thanks!

@akuma12

Copy link
Copy Markdown
ContributorAuthor

@akrishna1995 Could I get a final review on this PR? I'd really like to get my internal projects off of my fork so I can start getting the more recent updates to sagemaker-python-sdk. Thank you!

@sage-maker

sage-maker commented Aug 7, 2024

Copy link
Copy Markdown
Contributor

@akuma12
On-call here taking a look at this PR. I started approval workflow for tests. Are all code changes done here?

@akuma12

Copy link
Copy Markdown
ContributorAuthor

@sage-maker
Thank you!
Yup, everything is tested and ready to go.

@sage-maker
sage-maker merged commit 502e051 into aws:masterAug 7, 2024
@akuma12
akuma12 deleted the processing-job-codeartifact-support branch August 8, 2024 14:51
@akuma12
akuma12 restored the processing-job-codeartifact-support branch August 8, 2024 17:52
Evan-W-ang added a commit to Evan-W-ang/sagemaker-python-sdk that referenced this pull request Jun 8, 2026
…cript (aws#4145)
* feature: Add optional CodeArtifact login to FrameworkProcessing job script
* Add unit test for _get_codeartifact_index
* Fixed docstring
* Convert CodeArtifact integration to simply generate an AWS CLI command to log into CodeArtifact
* Fix lint issues
* More lint fixes
* Lint fix
* Yet Another Lint Fix
* Black fix
---------
Co-authored-by: sage-maker <parknate@amazon.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@akuma12@sagemaker-bot@mohanasudhan@humanzz@sage-maker@akrishna1995
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feature: Add optional CodeArtifact login to FrameworkProcessing job script - #4145

Merged
sage-maker merged 19 commits into
aws:masterfrom
akuma12:processing-job-codeartifact-support
Aug 7, 2024
Merged

feature: Add optional CodeArtifact login to FrameworkProcessing job script#4145
sage-maker merged 19 commits into
aws:masterfrom
akuma12:processing-job-codeartifact-support

Conversation

@akuma12

@akuma12akuma12 commented Sep 27, 2023

Copy link
Copy Markdown
Contributor

Issue #, if available:
#4144

Description of changes:
This PR adds an optional codeartifact_repo_arn parameter to the FrameworkProcessor.run() method. Providing this ARN will allow the _generate_framework_script() method to call _get_codeartifact_index(), which will parse the ARN into a CodeArtifact repo URL, retrieve an authentication token, and write an index option into the pip install -r requirements.txt call generated by _generate_framework_script()

If codeartifact_repo_arn is not provided, then _get_codeartifact_index() will not be called and nothing new will be injected into the runproc.sh script.

The _get_codeartifact_index() code is copied from the sagemaker-training-toolkit. All credit to @humanzz for that update.

Testing done:
Validated a PytorchProcessing job both with and without the codeartifact_repo_arn parameter. Downloaded the generated runproc.sh file from S3 and verified that the index option is written to the file if the ARN is provided, and does nothing if it is not.

I could use some advice when it comes to automated testing, however. Since _get_codeartifact_index() interacts with CodeArtifact via Boto3, I was unsure of the best way to handle this. In unit tests, I would typically use moto or patch the Boto3 make_api_call method. With the integration tests, I wasn't sure how I should interact with CodeArtifact, or if I should even add an integration test.

Merge Checklist

Put an x in the boxes that apply. You can also fill these out after creating the PR. If you're unsure about any of them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before merging your pull request.

General

  • I have read the CONTRIBUTING doc
  • I certify that the changes I am introducing will be backward compatible, and I have discussed concerns about this, if any, with the Python SDK team
  • I used the commit message format described in CONTRIBUTING
  • I have passed the region in to all S3 and STS clients that I've initialized as part of this change.
  • I have updated any necessary documentation, including READMEs and API docs (if appropriate)

Tests

  • I have added tests that prove my fix is effective or that my feature works (if appropriate)
  • I have added unit and/or integration tests as appropriate to ensure backward compatibility of the changes
  • I have checked that my tests are not configured for a specific region or account (if appropriate)
  • I have used unique_name_from_base to create resource names in integ tests (if appropriate)

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@akuma12
akuma12 requested a review from a team as a code ownerSeptember 27, 2023 21:12
@akuma12
akuma12 requested review from akrishna1995 and removed request for a teamSeptember 27, 2023 21:12
@akrishna1995akrishna1995 self-assigned this Oct 2, 2023

@akrishna1995akrishna1995 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/bot run all

@akrishna1995akrishna1995 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please get a review from one member in your team. please follow best practices - add Unit tests , integ tests

@sagemaker-bot

Copy link
Copy Markdown
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: sagemaker-python-sdk-unit-tests
  • Commit ID: 53c46b0
  • Result: FAILED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@sagemaker-bot

Copy link
Copy Markdown
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: sagemaker-python-sdk-local-mode-tests
  • Commit ID: 53c46b0
  • Result: SUCCEEDED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@sagemaker-bot

Copy link
Copy Markdown
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: sagemaker-python-sdk-notebook-tests
  • Commit ID: 53c46b0
  • Result: SUCCEEDED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@sagemaker-bot

Copy link
Copy Markdown
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: sagemaker-python-sdk-slow-tests
  • Commit ID: 53c46b0
  • Result: SUCCEEDED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@sagemaker-bot

Copy link
Copy Markdown
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: sagemaker-python-sdk-pr
  • Commit ID: 53c46b0
  • Result: FAILED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@goelakash
goelakashforce-pushed the processing-job-codeartifact-support branch from 53c46b0 to 61190deCompareOctober 9, 2023 23:48
@akuma12

Copy link
Copy Markdown
ContributorAuthor

@akrishna1995 Can you re-run the tests? I added 3 more unit tests around the code and fixed the issues that popped up in the last run.

@akuma12

Copy link
Copy Markdown
ContributorAuthor

Curious if anyone has had a chance to take a look at this. Would appreciate another review.

@mohanasudhan

Copy link
Copy Markdown
Contributor

@akuma12 Can you rebase your change? I had a brief look and it lgtm. It would be good to get all the test successful.

@humanzz

Copy link
Copy Markdown
Contributor

Hi @mohanasudhan and @akuma12,
I've been keeping an eye on this, and recently within my team, a need has arisen for using processing jobs and we'd really benefit from this PR being merged/released.

I pulled this PR, and ran export IGNORE_COVERAGE=- ; tox -e py38 -- -s -vv tests/unit/test_processing.py::test_pytorch_processor_with_required_parameters ; unset IGNORE_COVERAGE to check what's failing and all tests seem to be passing.

the only thing that happened was that it seems like black wanted to reformat the files and resulted in the following changes

diff --git a/src/sagemaker/processing.py b/src/sagemaker/processing.py
index b4a063ba..59b8c980 100644
--- a/src/sagemaker/processing.py
+++ b/src/sagemaker/processing.py
@@ -1852,7 +1852,7 @@ class FrameworkProcessor(ScriptProcessor):
# `arn:${Partition}:codeartifact:${Region}:${Account}:repository/${Domain}/${Repository}`
https://docs.aws.amazon.com/codeartifact/latest/ug/python-configure-pip.html
https://docs.aws.amazon.com/service-authorization/latest/reference/list_awscodeartifact.html#awscodeartifact-resources-for-iam-policies
- +
Args:
codeartifact_repo_arn: arn of the codeartifact repository
codeartifact_client: boto3 client for codeartifact (used for testing)
@@ -1882,9 +1882,13 @@ class FrameworkProcessor(ScriptProcessor):
)
try:
if not codeartifact_client:
- codeartifact_client = self.sagemaker_session.boto_session.client("codeartifact", region_name=region)
- - auth_token_response = codeartifact_client.get_authorization_token(domain=domain, domainOwner=owner)
+ codeartifact_client = self.sagemaker_session.boto_session.client(
+ "codeartifact", region_name=region
+ )
+
+ auth_token_response = codeartifact_client.get_authorization_token(
+ domain=domain, domainOwner=owner
+ )
token = auth_token_response["authorizationToken"]
endpoint_response = codeartifact_client.get_repository_endpoint(
domain=domain, domainOwner=owner, repository=repository, format="pypi"
diff --git a/tests/unit/test_processing.py b/tests/unit/test_processing.py
index fb55e2fe..3663b35b 100644
--- a/tests/unit/test_processing.py
+++ b/tests/unit/test_processing.py
@@ -1107,27 +1107,33 @@ def test_pyspark_processor_configuration_path_pipeline_config(
@patch("sagemaker.workflow.utilities._pipeline_config", MOCKED_PIPELINE_CONFIG)
def test_get_codeartifact_index(pipeline_session):
- codeartifact_repo_arn = "arn:aws:codeartifact:us-west-2:012345678901:repository/test-domain/test-repository"
+ codeartifact_repo_arn = (
+ "arn:aws:codeartifact:us-west-2:012345678901:repository/test-domain/test-repository"
+ )
codeartifact_url = "test-domain-012345678901.d.codeartifact.us-west-2.amazonaws.com/pypi/test-repository/simple/"
- client = boto3.client('codeartifact', region_name=REGION)
+ client = boto3.client("codeartifact", region_name=REGION)
stubber = Stubber(client)
- +
get_auth_token_response = {
"authorizationToken": "mocked_token",
- "expiration": datetime.datetime(2045, 1, 1, 0, 0, 0)
+ "expiration": datetime.datetime(2045, 1, 1, 0, 0, 0),
}
auth_token_expected_params = {"domain": "test-domain", "domainOwner": "012345678901"}
- stubber.add_response("get_authorization_token", get_auth_token_response, auth_token_expected_params)
+ stubber.add_response(
+ "get_authorization_token", get_auth_token_response, auth_token_expected_params
+ )
get_repo_endpoint_response = {"repositoryEndpoint": f"https://{codeartifact_url}"}
repo_endpoint_expected_params = {
"domain": "test-domain",
"domainOwner": "012345678901",
"repository": "test-repository",
- "format": "pypi"
+ "format": "pypi",
}
- stubber.add_response("get_repository_endpoint", get_repo_endpoint_response, repo_endpoint_expected_params)
+ stubber.add_response(
+ "get_repository_endpoint", get_repo_endpoint_response, repo_endpoint_expected_params
+ )
processor = PyTorchProcessor(
role=ROLE,
@@ -1139,8 +1145,10 @@ def test_get_codeartifact_index(pipeline_session):
)
with stubber:
- codeartifact_index = processor._get_codeartifact_index(codeartifact_repo_arn=codeartifact_repo_arn, codeartifact_client=client)
- + codeartifact_index = processor._get_codeartifact_index(
+ codeartifact_repo_arn=codeartifact_repo_arn, codeartifact_client=client
+ )
+
assert codeartifact_index == f"https://aws:mocked_token@{codeartifact_url}"
@@ -1149,24 +1157,28 @@ def test_get_codeartifact_index_bad_repo_arn(pipeline_session):
codeartifact_repo_arn = "arn:aws:codeartifact:us-west-2:012345678901:repository/test-domain"
codeartifact_url = "test-domain-012345678901.d.codeartifact.us-west-2.amazonaws.com/pypi/test-repository/simple/"
- client = boto3.client('codeartifact', region_name=REGION)
+ client = boto3.client("codeartifact", region_name=REGION)
stubber = Stubber(client)
- +
get_auth_token_response = {
"authorizationToken": "mocked_token",
- "expiration": datetime.datetime(2045, 1, 1, 0, 0, 0)
+ "expiration": datetime.datetime(2045, 1, 1, 0, 0, 0),
}
auth_token_expected_params = {"domain": "test-domain", "domainOwner": "012345678901"}
- stubber.add_response("get_authorization_token", get_auth_token_response, auth_token_expected_params)
+ stubber.add_response(
+ "get_authorization_token", get_auth_token_response, auth_token_expected_params
+ )
get_repo_endpoint_response = {"repositoryEndpoint": f"https://{codeartifact_url}"}
repo_endpoint_expected_params = {
"domain": "test-domain",
"domainOwner": "012345678901",
"repository": "test-repository",
- "format": "pypi"
+ "format": "pypi",
}
- stubber.add_response("get_repository_endpoint", get_repo_endpoint_response, repo_endpoint_expected_params)
+ stubber.add_response(
+ "get_repository_endpoint", get_repo_endpoint_response, repo_endpoint_expected_params
+ )
processor = PyTorchProcessor(
role=ROLE,
@@ -1179,32 +1191,42 @@ def test_get_codeartifact_index_bad_repo_arn(pipeline_session):
with stubber:
with pytest.raises(ValueError):
- processor._get_codeartifact_index(codeartifact_repo_arn=codeartifact_repo_arn, codeartifact_client=client)
+ processor._get_codeartifact_index(
+ codeartifact_repo_arn=codeartifact_repo_arn, codeartifact_client=client
+ )
@patch("sagemaker.workflow.utilities._pipeline_config", MOCKED_PIPELINE_CONFIG)
def test_get_codeartifact_index_client_error(pipeline_session):
- codeartifact_repo_arn = "arn:aws:codeartifact:us-west-2:012345678901:repository/test-domain/test-repository"
+ codeartifact_repo_arn = (
+ "arn:aws:codeartifact:us-west-2:012345678901:repository/test-domain/test-repository"
+ )
codeartifact_url = "test-domain-012345678901.d.codeartifact.us-west-2.amazonaws.com/pypi/test-repository/simple/"
- client = boto3.client('codeartifact', region_name=REGION)
+ client = boto3.client("codeartifact", region_name=REGION)
stubber = Stubber(client)
- +
get_auth_token_response = {
"authorizationToken": "mocked_token",
- "expiration": datetime.datetime(2045, 1, 1, 0, 0, 0)
+ "expiration": datetime.datetime(2045, 1, 1, 0, 0, 0),
}
auth_token_expected_params = {"domain": "test-domain", "domainOwner": "012345678901"}
- stubber.add_client_error("get_authorization_token", service_error_code="404", expected_params=auth_token_expected_params)
+ stubber.add_client_error(
+ "get_authorization_token",
+ service_error_code="404",
+ expected_params=auth_token_expected_params,
+ )
get_repo_endpoint_response = {"repositoryEndpoint": f"https://{codeartifact_url}"}
repo_endpoint_expected_params = {
"domain": "test-domain",
"domainOwner": "012345678901",
"repository": "test-repository",
- "format": "pypi"
+ "format": "pypi",
}
- stubber.add_response("get_repository_endpoint", get_repo_endpoint_response, repo_endpoint_expected_params)
+ stubber.add_response(
+ "get_repository_endpoint", get_repo_endpoint_response, repo_endpoint_expected_params
+ )
processor = PyTorchProcessor(
role=ROLE,
@@ -1217,7 +1239,9 @@ def test_get_codeartifact_index_client_error(pipeline_session):
with stubber:
with pytest.raises(RuntimeError):
- processor._get_codeartifact_index(codeartifact_repo_arn=codeartifact_repo_arn, codeartifact_client=client)
+ processor._get_codeartifact_index(
+ codeartifact_repo_arn=codeartifact_repo_arn, codeartifact_client=client
+ )
def _get_script_processor(sagemaker_session):

With no access to the build logs, I wonder if that is the only issue?

@humanzz

Copy link
Copy Markdown
Contributor

One more thought, my teammate @Stacy-D, has started looking into using processing jobs, and has been experimenting with a different approach for setting up CodeArtifact.

Rather than uploading a script that has the hardcoded index in pip install -r requirements.txt {index_option}, she's written a script leveraging ** aws cli** (it's an assumption, but we confirmed it on pytorch containers) for configuring pip using

aws codeartifact login --tool pip --repository "$CODEARTIFACT_REPO" --domain "$CODEARTIFACT_DOMAIN" --domain-owner "${CODEARTIFACT_OWNER}" --region "${CODEARTIFACT_REGION}"

The script then looks something along the lines of

#!/bin/bash
cd /opt/ml/processing/input/code/
tar -xzf sourcedir.tar.gz
# Exit on any error. SageMaker uses error code to mark failed job.
set -e
aws codeartifact login --tool pip --repository "$CODEARTIFACT_REPO" --domain "$CODEARTIFACT_DOMAIN" --domain-owner "${CODEARTIFACT_OWNER}" --region "${CODEARTIFACT_REGION}"
if [[ -f 'requirements.txt' ]]; then
# Some py3 containers has typing, which may breaks pip install
pip uninstall --yes typing
pip install -r requirements.txt
fi
python "$THE_SCRIPT" "$@"

To make CA optional, the aws codeartifact login would need to be wrapped in an if condition, for the environment variables to be set.

@akuma12

Copy link
Copy Markdown
ContributorAuthor

I had almost forgotten about this. Thank you @humanzz for your feedback. I'll look into that script change and see if I can modify the code to make use of that.

@codecov

codecovBot commented Mar 15, 2024

Copy link
Copy Markdown

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 87.44%. Comparing base (31190c4) to head (cfe8139).

Current head cfe8139 differs from pull request most recent head f9deaa5

Please upload reports for the commit f9deaa5 to get more accurate results.

Additional details and impacted files
@@ Coverage Diff @@## master #4145 +/- ##
==========================================
+ Coverage 86.70% 87.44% +0.74% 
==========================================
Files 409 389 -20 Lines 39067 36904 -2163 ==========================================
- Hits 33872 32272 -1600 + Misses 5195 4632 -563 

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@akuma12

Copy link
Copy Markdown
ContributorAuthor

@humanzz The process using the AWS CLI is muuuuuch simpler. I don't have to rely on boto3, and I confirmed that the PyTorch training images have the AWS CLI installed. Updated the code and added some additional unit tests.

@akuma12

Copy link
Copy Markdown
ContributorAuthor

@mohanasudhan Looks like all tests and lints are passing now, if you could take one last look.

@akuma12

Copy link
Copy Markdown
ContributorAuthor

@akrishna1995 I'd love to get a final review on this, if possible. Thanks!

@akuma12

Copy link
Copy Markdown
ContributorAuthor

@akrishna1995 Could I get a final review on this PR? I'd really like to get my internal projects off of my fork so I can start getting the more recent updates to sagemaker-python-sdk. Thank you!

@sage-maker

sage-maker commented Aug 7, 2024

Copy link
Copy Markdown
Contributor

@akuma12
On-call here taking a look at this PR. I started approval workflow for tests. Are all code changes done here?

@akuma12

Copy link
Copy Markdown
ContributorAuthor

@sage-maker
Thank you!
Yup, everything is tested and ready to go.

@sage-maker
sage-maker merged commit 502e051 into aws:masterAug 7, 2024
@akuma12
akuma12 deleted the processing-job-codeartifact-support branch August 8, 2024 14:51
@akuma12
akuma12 restored the processing-job-codeartifact-support branch August 8, 2024 17:52
Evan-W-ang added a commit to Evan-W-ang/sagemaker-python-sdk that referenced this pull request Jun 8, 2026
…cript (aws#4145)
* feature: Add optional CodeArtifact login to FrameworkProcessing job script
* Add unit test for _get_codeartifact_index
* Fixed docstring
* Convert CodeArtifact integration to simply generate an AWS CLI command to log into CodeArtifact
* Fix lint issues
* More lint fixes
* Lint fix
* Yet Another Lint Fix
* Black fix
---------
Co-authored-by: sage-maker <parknate@amazon.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@akuma12@sagemaker-bot@mohanasudhan@humanzz@sage-maker@akrishna1995
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feature: Add optional CodeArtifact login to FrameworkProcessing job script - #4145

Merged
sage-maker merged 19 commits into
aws:masterfrom
akuma12:processing-job-codeartifact-support
Aug 7, 2024
Merged

feature: Add optional CodeArtifact login to FrameworkProcessing job script#4145
sage-maker merged 19 commits into
aws:masterfrom
akuma12:processing-job-codeartifact-support

Conversation

@akuma12

@akuma12akuma12 commented Sep 27, 2023

Copy link
Copy Markdown
Contributor

Issue #, if available:
#4144

Description of changes:
This PR adds an optional codeartifact_repo_arn parameter to the FrameworkProcessor.run() method. Providing this ARN will allow the _generate_framework_script() method to call _get_codeartifact_index(), which will parse the ARN into a CodeArtifact repo URL, retrieve an authentication token, and write an index option into the pip install -r requirements.txt call generated by _generate_framework_script()

If codeartifact_repo_arn is not provided, then _get_codeartifact_index() will not be called and nothing new will be injected into the runproc.sh script.

The _get_codeartifact_index() code is copied from the sagemaker-training-toolkit. All credit to @humanzz for that update.

Testing done:
Validated a PytorchProcessing job both with and without the codeartifact_repo_arn parameter. Downloaded the generated runproc.sh file from S3 and verified that the index option is written to the file if the ARN is provided, and does nothing if it is not.

I could use some advice when it comes to automated testing, however. Since _get_codeartifact_index() interacts with CodeArtifact via Boto3, I was unsure of the best way to handle this. In unit tests, I would typically use moto or patch the Boto3 make_api_call method. With the integration tests, I wasn't sure how I should interact with CodeArtifact, or if I should even add an integration test.

Merge Checklist

Put an x in the boxes that apply. You can also fill these out after creating the PR. If you're unsure about any of them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before merging your pull request.

General

  • I have read the CONTRIBUTING doc
  • I certify that the changes I am introducing will be backward compatible, and I have discussed concerns about this, if any, with the Python SDK team
  • I used the commit message format described in CONTRIBUTING
  • I have passed the region in to all S3 and STS clients that I've initialized as part of this change.
  • I have updated any necessary documentation, including READMEs and API docs (if appropriate)

Tests

  • I have added tests that prove my fix is effective or that my feature works (if appropriate)
  • I have added unit and/or integration tests as appropriate to ensure backward compatibility of the changes
  • I have checked that my tests are not configured for a specific region or account (if appropriate)
  • I have used unique_name_from_base to create resource names in integ tests (if appropriate)

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@akuma12
akuma12 requested a review from a team as a code ownerSeptember 27, 2023 21:12
@akuma12
akuma12 requested review from akrishna1995 and removed request for a teamSeptember 27, 2023 21:12
@akrishna1995akrishna1995 self-assigned this Oct 2, 2023

@akrishna1995akrishna1995 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/bot run all

@akrishna1995akrishna1995 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please get a review from one member in your team. please follow best practices - add Unit tests , integ tests

@sagemaker-bot

Copy link
Copy Markdown
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: sagemaker-python-sdk-unit-tests
  • Commit ID: 53c46b0
  • Result: FAILED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@sagemaker-bot

Copy link
Copy Markdown
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: sagemaker-python-sdk-local-mode-tests
  • Commit ID: 53c46b0
  • Result: SUCCEEDED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@sagemaker-bot

Copy link
Copy Markdown
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: sagemaker-python-sdk-notebook-tests
  • Commit ID: 53c46b0
  • Result: SUCCEEDED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@sagemaker-bot

Copy link
Copy Markdown
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: sagemaker-python-sdk-slow-tests
  • Commit ID: 53c46b0
  • Result: SUCCEEDED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@sagemaker-bot

Copy link
Copy Markdown
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: sagemaker-python-sdk-pr
  • Commit ID: 53c46b0
  • Result: FAILED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@goelakash
goelakashforce-pushed the processing-job-codeartifact-support branch from 53c46b0 to 61190deCompareOctober 9, 2023 23:48
@akuma12

Copy link
Copy Markdown
ContributorAuthor

@akrishna1995 Can you re-run the tests? I added 3 more unit tests around the code and fixed the issues that popped up in the last run.

@akuma12

Copy link
Copy Markdown
ContributorAuthor

Curious if anyone has had a chance to take a look at this. Would appreciate another review.

@mohanasudhan

Copy link
Copy Markdown
Contributor

@akuma12 Can you rebase your change? I had a brief look and it lgtm. It would be good to get all the test successful.

@humanzz

Copy link
Copy Markdown
Contributor

Hi @mohanasudhan and @akuma12,
I've been keeping an eye on this, and recently within my team, a need has arisen for using processing jobs and we'd really benefit from this PR being merged/released.

I pulled this PR, and ran export IGNORE_COVERAGE=- ; tox -e py38 -- -s -vv tests/unit/test_processing.py::test_pytorch_processor_with_required_parameters ; unset IGNORE_COVERAGE to check what's failing and all tests seem to be passing.

the only thing that happened was that it seems like black wanted to reformat the files and resulted in the following changes

diff --git a/src/sagemaker/processing.py b/src/sagemaker/processing.py
index b4a063ba..59b8c980 100644
--- a/src/sagemaker/processing.py
+++ b/src/sagemaker/processing.py
@@ -1852,7 +1852,7 @@ class FrameworkProcessor(ScriptProcessor):
# `arn:${Partition}:codeartifact:${Region}:${Account}:repository/${Domain}/${Repository}`
https://docs.aws.amazon.com/codeartifact/latest/ug/python-configure-pip.html
https://docs.aws.amazon.com/service-authorization/latest/reference/list_awscodeartifact.html#awscodeartifact-resources-for-iam-policies
- +
Args:
codeartifact_repo_arn: arn of the codeartifact repository
codeartifact_client: boto3 client for codeartifact (used for testing)
@@ -1882,9 +1882,13 @@ class FrameworkProcessor(ScriptProcessor):
)
try:
if not codeartifact_client:
- codeartifact_client = self.sagemaker_session.boto_session.client("codeartifact", region_name=region)
- - auth_token_response = codeartifact_client.get_authorization_token(domain=domain, domainOwner=owner)
+ codeartifact_client = self.sagemaker_session.boto_session.client(
+ "codeartifact", region_name=region
+ )
+
+ auth_token_response = codeartifact_client.get_authorization_token(
+ domain=domain, domainOwner=owner
+ )
token = auth_token_response["authorizationToken"]
endpoint_response = codeartifact_client.get_repository_endpoint(
domain=domain, domainOwner=owner, repository=repository, format="pypi"
diff --git a/tests/unit/test_processing.py b/tests/unit/test_processing.py
index fb55e2fe..3663b35b 100644
--- a/tests/unit/test_processing.py
+++ b/tests/unit/test_processing.py
@@ -1107,27 +1107,33 @@ def test_pyspark_processor_configuration_path_pipeline_config(
@patch("sagemaker.workflow.utilities._pipeline_config", MOCKED_PIPELINE_CONFIG)
def test_get_codeartifact_index(pipeline_session):
- codeartifact_repo_arn = "arn:aws:codeartifact:us-west-2:012345678901:repository/test-domain/test-repository"
+ codeartifact_repo_arn = (
+ "arn:aws:codeartifact:us-west-2:012345678901:repository/test-domain/test-repository"
+ )
codeartifact_url = "test-domain-012345678901.d.codeartifact.us-west-2.amazonaws.com/pypi/test-repository/simple/"
- client = boto3.client('codeartifact', region_name=REGION)
+ client = boto3.client("codeartifact", region_name=REGION)
stubber = Stubber(client)
- +
get_auth_token_response = {
"authorizationToken": "mocked_token",
- "expiration": datetime.datetime(2045, 1, 1, 0, 0, 0)
+ "expiration": datetime.datetime(2045, 1, 1, 0, 0, 0),
}
auth_token_expected_params = {"domain": "test-domain", "domainOwner": "012345678901"}
- stubber.add_response("get_authorization_token", get_auth_token_response, auth_token_expected_params)
+ stubber.add_response(
+ "get_authorization_token", get_auth_token_response, auth_token_expected_params
+ )
get_repo_endpoint_response = {"repositoryEndpoint": f"https://{codeartifact_url}"}
repo_endpoint_expected_params = {
"domain": "test-domain",
"domainOwner": "012345678901",
"repository": "test-repository",
- "format": "pypi"
+ "format": "pypi",
}
- stubber.add_response("get_repository_endpoint", get_repo_endpoint_response, repo_endpoint_expected_params)
+ stubber.add_response(
+ "get_repository_endpoint", get_repo_endpoint_response, repo_endpoint_expected_params
+ )
processor = PyTorchProcessor(
role=ROLE,
@@ -1139,8 +1145,10 @@ def test_get_codeartifact_index(pipeline_session):
)
with stubber:
- codeartifact_index = processor._get_codeartifact_index(codeartifact_repo_arn=codeartifact_repo_arn, codeartifact_client=client)
- + codeartifact_index = processor._get_codeartifact_index(
+ codeartifact_repo_arn=codeartifact_repo_arn, codeartifact_client=client
+ )
+
assert codeartifact_index == f"https://aws:mocked_token@{codeartifact_url}"
@@ -1149,24 +1157,28 @@ def test_get_codeartifact_index_bad_repo_arn(pipeline_session):
codeartifact_repo_arn = "arn:aws:codeartifact:us-west-2:012345678901:repository/test-domain"
codeartifact_url = "test-domain-012345678901.d.codeartifact.us-west-2.amazonaws.com/pypi/test-repository/simple/"
- client = boto3.client('codeartifact', region_name=REGION)
+ client = boto3.client("codeartifact", region_name=REGION)
stubber = Stubber(client)
- +
get_auth_token_response = {
"authorizationToken": "mocked_token",
- "expiration": datetime.datetime(2045, 1, 1, 0, 0, 0)
+ "expiration": datetime.datetime(2045, 1, 1, 0, 0, 0),
}
auth_token_expected_params = {"domain": "test-domain", "domainOwner": "012345678901"}
- stubber.add_response("get_authorization_token", get_auth_token_response, auth_token_expected_params)
+ stubber.add_response(
+ "get_authorization_token", get_auth_token_response, auth_token_expected_params
+ )
get_repo_endpoint_response = {"repositoryEndpoint": f"https://{codeartifact_url}"}
repo_endpoint_expected_params = {
"domain": "test-domain",
"domainOwner": "012345678901",
"repository": "test-repository",
- "format": "pypi"
+ "format": "pypi",
}
- stubber.add_response("get_repository_endpoint", get_repo_endpoint_response, repo_endpoint_expected_params)
+ stubber.add_response(
+ "get_repository_endpoint", get_repo_endpoint_response, repo_endpoint_expected_params
+ )
processor = PyTorchProcessor(
role=ROLE,
@@ -1179,32 +1191,42 @@ def test_get_codeartifact_index_bad_repo_arn(pipeline_session):
with stubber:
with pytest.raises(ValueError):
- processor._get_codeartifact_index(codeartifact_repo_arn=codeartifact_repo_arn, codeartifact_client=client)
+ processor._get_codeartifact_index(
+ codeartifact_repo_arn=codeartifact_repo_arn, codeartifact_client=client
+ )
@patch("sagemaker.workflow.utilities._pipeline_config", MOCKED_PIPELINE_CONFIG)
def test_get_codeartifact_index_client_error(pipeline_session):
- codeartifact_repo_arn = "arn:aws:codeartifact:us-west-2:012345678901:repository/test-domain/test-repository"
+ codeartifact_repo_arn = (
+ "arn:aws:codeartifact:us-west-2:012345678901:repository/test-domain/test-repository"
+ )
codeartifact_url = "test-domain-012345678901.d.codeartifact.us-west-2.amazonaws.com/pypi/test-repository/simple/"
- client = boto3.client('codeartifact', region_name=REGION)
+ client = boto3.client("codeartifact", region_name=REGION)
stubber = Stubber(client)
- +
get_auth_token_response = {
"authorizationToken": "mocked_token",
- "expiration": datetime.datetime(2045, 1, 1, 0, 0, 0)
+ "expiration": datetime.datetime(2045, 1, 1, 0, 0, 0),
}
auth_token_expected_params = {"domain": "test-domain", "domainOwner": "012345678901"}
- stubber.add_client_error("get_authorization_token", service_error_code="404", expected_params=auth_token_expected_params)
+ stubber.add_client_error(
+ "get_authorization_token",
+ service_error_code="404",
+ expected_params=auth_token_expected_params,
+ )
get_repo_endpoint_response = {"repositoryEndpoint": f"https://{codeartifact_url}"}
repo_endpoint_expected_params = {
"domain": "test-domain",
"domainOwner": "012345678901",
"repository": "test-repository",
- "format": "pypi"
+ "format": "pypi",
}
- stubber.add_response("get_repository_endpoint", get_repo_endpoint_response, repo_endpoint_expected_params)
+ stubber.add_response(
+ "get_repository_endpoint", get_repo_endpoint_response, repo_endpoint_expected_params
+ )
processor = PyTorchProcessor(
role=ROLE,
@@ -1217,7 +1239,9 @@ def test_get_codeartifact_index_client_error(pipeline_session):
with stubber:
with pytest.raises(RuntimeError):
- processor._get_codeartifact_index(codeartifact_repo_arn=codeartifact_repo_arn, codeartifact_client=client)
+ processor._get_codeartifact_index(
+ codeartifact_repo_arn=codeartifact_repo_arn, codeartifact_client=client
+ )
def _get_script_processor(sagemaker_session):

With no access to the build logs, I wonder if that is the only issue?

@humanzz

Copy link
Copy Markdown
Contributor

One more thought, my teammate @Stacy-D, has started looking into using processing jobs, and has been experimenting with a different approach for setting up CodeArtifact.

Rather than uploading a script that has the hardcoded index in pip install -r requirements.txt {index_option}, she's written a script leveraging ** aws cli** (it's an assumption, but we confirmed it on pytorch containers) for configuring pip using

aws codeartifact login --tool pip --repository "$CODEARTIFACT_REPO" --domain "$CODEARTIFACT_DOMAIN" --domain-owner "${CODEARTIFACT_OWNER}" --region "${CODEARTIFACT_REGION}"

The script then looks something along the lines of

#!/bin/bash
cd /opt/ml/processing/input/code/
tar -xzf sourcedir.tar.gz
# Exit on any error. SageMaker uses error code to mark failed job.
set -e
aws codeartifact login --tool pip --repository "$CODEARTIFACT_REPO" --domain "$CODEARTIFACT_DOMAIN" --domain-owner "${CODEARTIFACT_OWNER}" --region "${CODEARTIFACT_REGION}"
if [[ -f 'requirements.txt' ]]; then
# Some py3 containers has typing, which may breaks pip install
pip uninstall --yes typing
pip install -r requirements.txt
fi
python "$THE_SCRIPT" "$@"

To make CA optional, the aws codeartifact login would need to be wrapped in an if condition, for the environment variables to be set.

@akuma12

Copy link
Copy Markdown
ContributorAuthor

I had almost forgotten about this. Thank you @humanzz for your feedback. I'll look into that script change and see if I can modify the code to make use of that.

@codecov

codecovBot commented Mar 15, 2024

Copy link
Copy Markdown

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 87.44%. Comparing base (31190c4) to head (cfe8139).

Current head cfe8139 differs from pull request most recent head f9deaa5

Please upload reports for the commit f9deaa5 to get more accurate results.

Additional details and impacted files
@@ Coverage Diff @@## master #4145 +/- ##
==========================================
+ Coverage 86.70% 87.44% +0.74% 
==========================================
Files 409 389 -20 Lines 39067 36904 -2163 ==========================================
- Hits 33872 32272 -1600 + Misses 5195 4632 -563 

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@akuma12

Copy link
Copy Markdown
ContributorAuthor

@humanzz The process using the AWS CLI is muuuuuch simpler. I don't have to rely on boto3, and I confirmed that the PyTorch training images have the AWS CLI installed. Updated the code and added some additional unit tests.

@akuma12

Copy link
Copy Markdown
ContributorAuthor

@mohanasudhan Looks like all tests and lints are passing now, if you could take one last look.

@akuma12

Copy link
Copy Markdown
ContributorAuthor

@akrishna1995 I'd love to get a final review on this, if possible. Thanks!

@akuma12

Copy link
Copy Markdown
ContributorAuthor

@akrishna1995 Could I get a final review on this PR? I'd really like to get my internal projects off of my fork so I can start getting the more recent updates to sagemaker-python-sdk. Thank you!

@sage-maker

sage-maker commented Aug 7, 2024

Copy link
Copy Markdown
Contributor

@akuma12
On-call here taking a look at this PR. I started approval workflow for tests. Are all code changes done here?

@akuma12

Copy link
Copy Markdown
ContributorAuthor

@sage-maker
Thank you!
Yup, everything is tested and ready to go.

@sage-maker
sage-maker merged commit 502e051 into aws:masterAug 7, 2024
@akuma12
akuma12 deleted the processing-job-codeartifact-support branch August 8, 2024 14:51
@akuma12
akuma12 restored the processing-job-codeartifact-support branch August 8, 2024 17:52
Evan-W-ang added a commit to Evan-W-ang/sagemaker-python-sdk that referenced this pull request Jun 8, 2026
…cript (aws#4145)
* feature: Add optional CodeArtifact login to FrameworkProcessing job script
* Add unit test for _get_codeartifact_index
* Fixed docstring
* Convert CodeArtifact integration to simply generate an AWS CLI command to log into CodeArtifact
* Fix lint issues
* More lint fixes
* Lint fix
* Yet Another Lint Fix
* Black fix
---------
Co-authored-by: sage-maker <parknate@amazon.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@akuma12@sagemaker-bot@mohanasudhan@humanzz@sage-maker@akrishna1995
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

feature: Add optional CodeArtifact login to FrameworkProcessing job script - #4145

Merged
sage-maker merged 19 commits into
aws:masterfrom
akuma12:processing-job-codeartifact-support
Aug 7, 2024
Merged

feature: Add optional CodeArtifact login to FrameworkProcessing job script#4145
sage-maker merged 19 commits into
aws:masterfrom
akuma12:processing-job-codeartifact-support

Conversation

@akuma12

@akuma12akuma12 commented Sep 27, 2023

Copy link
Copy Markdown
Contributor

Issue #, if available:
#4144

Description of changes:
This PR adds an optional codeartifact_repo_arn parameter to the FrameworkProcessor.run() method. Providing this ARN will allow the _generate_framework_script() method to call _get_codeartifact_index(), which will parse the ARN into a CodeArtifact repo URL, retrieve an authentication token, and write an index option into the pip install -r requirements.txt call generated by _generate_framework_script()

If codeartifact_repo_arn is not provided, then _get_codeartifact_index() will not be called and nothing new will be injected into the runproc.sh script.

The _get_codeartifact_index() code is copied from the sagemaker-training-toolkit. All credit to @humanzz for that update.

Testing done:
Validated a PytorchProcessing job both with and without the codeartifact_repo_arn parameter. Downloaded the generated runproc.sh file from S3 and verified that the index option is written to the file if the ARN is provided, and does nothing if it is not.

I could use some advice when it comes to automated testing, however. Since _get_codeartifact_index() interacts with CodeArtifact via Boto3, I was unsure of the best way to handle this. In unit tests, I would typically use moto or patch the Boto3 make_api_call method. With the integration tests, I wasn't sure how I should interact with CodeArtifact, or if I should even add an integration test.

Merge Checklist

Put an x in the boxes that apply. You can also fill these out after creating the PR. If you're unsure about any of them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before merging your pull request.

General

  • I have read the CONTRIBUTING doc
  • I certify that the changes I am introducing will be backward compatible, and I have discussed concerns about this, if any, with the Python SDK team
  • I used the commit message format described in CONTRIBUTING
  • I have passed the region in to all S3 and STS clients that I've initialized as part of this change.
  • I have updated any necessary documentation, including READMEs and API docs (if appropriate)

Tests

  • I have added tests that prove my fix is effective or that my feature works (if appropriate)
  • I have added unit and/or integration tests as appropriate to ensure backward compatibility of the changes
  • I have checked that my tests are not configured for a specific region or account (if appropriate)
  • I have used unique_name_from_base to create resource names in integ tests (if appropriate)

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@akuma12
akuma12 requested a review from a team as a code ownerSeptember 27, 2023 21:12
@akuma12
akuma12 requested review from akrishna1995 and removed request for a teamSeptember 27, 2023 21:12
@akrishna1995akrishna1995 self-assigned this Oct 2, 2023

@akrishna1995akrishna1995 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/bot run all

@akrishna1995akrishna1995 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please get a review from one member in your team. please follow best practices - add Unit tests , integ tests

@sagemaker-bot

Copy link
Copy Markdown
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: sagemaker-python-sdk-unit-tests
  • Commit ID: 53c46b0
  • Result: FAILED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@sagemaker-bot

Copy link
Copy Markdown
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: sagemaker-python-sdk-local-mode-tests
  • Commit ID: 53c46b0
  • Result: SUCCEEDED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@sagemaker-bot

Copy link
Copy Markdown
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: sagemaker-python-sdk-notebook-tests
  • Commit ID: 53c46b0
  • Result: SUCCEEDED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@sagemaker-bot

Copy link
Copy Markdown
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: sagemaker-python-sdk-slow-tests
  • Commit ID: 53c46b0
  • Result: SUCCEEDED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@sagemaker-bot

Copy link
Copy Markdown
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: sagemaker-python-sdk-pr
  • Commit ID: 53c46b0
  • Result: FAILED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@goelakash
goelakashforce-pushed the processing-job-codeartifact-support branch from 53c46b0 to 61190deCompareOctober 9, 2023 23:48
@akuma12

Copy link
Copy Markdown
ContributorAuthor

@akrishna1995 Can you re-run the tests? I added 3 more unit tests around the code and fixed the issues that popped up in the last run.

@akuma12

Copy link
Copy Markdown
ContributorAuthor

Curious if anyone has had a chance to take a look at this. Would appreciate another review.

@mohanasudhan

Copy link
Copy Markdown
Contributor

@akuma12 Can you rebase your change? I had a brief look and it lgtm. It would be good to get all the test successful.

@humanzz

Copy link
Copy Markdown
Contributor

Hi @mohanasudhan and @akuma12,
I've been keeping an eye on this, and recently within my team, a need has arisen for using processing jobs and we'd really benefit from this PR being merged/released.

I pulled this PR, and ran export IGNORE_COVERAGE=- ; tox -e py38 -- -s -vv tests/unit/test_processing.py::test_pytorch_processor_with_required_parameters ; unset IGNORE_COVERAGE to check what's failing and all tests seem to be passing.

the only thing that happened was that it seems like black wanted to reformat the files and resulted in the following changes

diff --git a/src/sagemaker/processing.py b/src/sagemaker/processing.py
index b4a063ba..59b8c980 100644
--- a/src/sagemaker/processing.py
+++ b/src/sagemaker/processing.py
@@ -1852,7 +1852,7 @@ class FrameworkProcessor(ScriptProcessor):
# `arn:${Partition}:codeartifact:${Region}:${Account}:repository/${Domain}/${Repository}`
https://docs.aws.amazon.com/codeartifact/latest/ug/python-configure-pip.html
https://docs.aws.amazon.com/service-authorization/latest/reference/list_awscodeartifact.html#awscodeartifact-resources-for-iam-policies
- +
Args:
codeartifact_repo_arn: arn of the codeartifact repository
codeartifact_client: boto3 client for codeartifact (used for testing)
@@ -1882,9 +1882,13 @@ class FrameworkProcessor(ScriptProcessor):
)
try:
if not codeartifact_client:
- codeartifact_client = self.sagemaker_session.boto_session.client("codeartifact", region_name=region)
- - auth_token_response = codeartifact_client.get_authorization_token(domain=domain, domainOwner=owner)
+ codeartifact_client = self.sagemaker_session.boto_session.client(
+ "codeartifact", region_name=region
+ )
+
+ auth_token_response = codeartifact_client.get_authorization_token(
+ domain=domain, domainOwner=owner
+ )
token = auth_token_response["authorizationToken"]
endpoint_response = codeartifact_client.get_repository_endpoint(
domain=domain, domainOwner=owner, repository=repository, format="pypi"
diff --git a/tests/unit/test_processing.py b/tests/unit/test_processing.py
index fb55e2fe..3663b35b 100644
--- a/tests/unit/test_processing.py
+++ b/tests/unit/test_processing.py
@@ -1107,27 +1107,33 @@ def test_pyspark_processor_configuration_path_pipeline_config(
@patch("sagemaker.workflow.utilities._pipeline_config", MOCKED_PIPELINE_CONFIG)
def test_get_codeartifact_index(pipeline_session):
- codeartifact_repo_arn = "arn:aws:codeartifact:us-west-2:012345678901:repository/test-domain/test-repository"
+ codeartifact_repo_arn = (
+ "arn:aws:codeartifact:us-west-2:012345678901:repository/test-domain/test-repository"
+ )
codeartifact_url = "test-domain-012345678901.d.codeartifact.us-west-2.amazonaws.com/pypi/test-repository/simple/"
- client = boto3.client('codeartifact', region_name=REGION)
+ client = boto3.client("codeartifact", region_name=REGION)
stubber = Stubber(client)
- +
get_auth_token_response = {
"authorizationToken": "mocked_token",
- "expiration": datetime.datetime(2045, 1, 1, 0, 0, 0)
+ "expiration": datetime.datetime(2045, 1, 1, 0, 0, 0),
}
auth_token_expected_params = {"domain": "test-domain", "domainOwner": "012345678901"}
- stubber.add_response("get_authorization_token", get_auth_token_response, auth_token_expected_params)
+ stubber.add_response(
+ "get_authorization_token", get_auth_token_response, auth_token_expected_params
+ )
get_repo_endpoint_response = {"repositoryEndpoint": f"https://{codeartifact_url}"}
repo_endpoint_expected_params = {
"domain": "test-domain",
"domainOwner": "012345678901",
"repository": "test-repository",
- "format": "pypi"
+ "format": "pypi",
}
- stubber.add_response("get_repository_endpoint", get_repo_endpoint_response, repo_endpoint_expected_params)
+ stubber.add_response(
+ "get_repository_endpoint", get_repo_endpoint_response, repo_endpoint_expected_params
+ )
processor = PyTorchProcessor(
role=ROLE,
@@ -1139,8 +1145,10 @@ def test_get_codeartifact_index(pipeline_session):
)
with stubber:
- codeartifact_index = processor._get_codeartifact_index(codeartifact_repo_arn=codeartifact_repo_arn, codeartifact_client=client)
- + codeartifact_index = processor._get_codeartifact_index(
+ codeartifact_repo_arn=codeartifact_repo_arn, codeartifact_client=client
+ )
+
assert codeartifact_index == f"https://aws:mocked_token@{codeartifact_url}"
@@ -1149,24 +1157,28 @@ def test_get_codeartifact_index_bad_repo_arn(pipeline_session):
codeartifact_repo_arn = "arn:aws:codeartifact:us-west-2:012345678901:repository/test-domain"
codeartifact_url = "test-domain-012345678901.d.codeartifact.us-west-2.amazonaws.com/pypi/test-repository/simple/"
- client = boto3.client('codeartifact', region_name=REGION)
+ client = boto3.client("codeartifact", region_name=REGION)
stubber = Stubber(client)
- +
get_auth_token_response = {
"authorizationToken": "mocked_token",
- "expiration": datetime.datetime(2045, 1, 1, 0, 0, 0)
+ "expiration": datetime.datetime(2045, 1, 1, 0, 0, 0),
}
auth_token_expected_params = {"domain": "test-domain", "domainOwner": "012345678901"}
- stubber.add_response("get_authorization_token", get_auth_token_response, auth_token_expected_params)
+ stubber.add_response(
+ "get_authorization_token", get_auth_token_response, auth_token_expected_params
+ )
get_repo_endpoint_response = {"repositoryEndpoint": f"https://{codeartifact_url}"}
repo_endpoint_expected_params = {
"domain": "test-domain",
"domainOwner": "012345678901",
"repository": "test-repository",
- "format": "pypi"
+ "format": "pypi",
}
- stubber.add_response("get_repository_endpoint", get_repo_endpoint_response, repo_endpoint_expected_params)
+ stubber.add_response(
+ "get_repository_endpoint", get_repo_endpoint_response, repo_endpoint_expected_params
+ )
processor = PyTorchProcessor(
role=ROLE,
@@ -1179,32 +1191,42 @@ def test_get_codeartifact_index_bad_repo_arn(pipeline_session):
with stubber:
with pytest.raises(ValueError):
- processor._get_codeartifact_index(codeartifact_repo_arn=codeartifact_repo_arn, codeartifact_client=client)
+ processor._get_codeartifact_index(
+ codeartifact_repo_arn=codeartifact_repo_arn, codeartifact_client=client
+ )
@patch("sagemaker.workflow.utilities._pipeline_config", MOCKED_PIPELINE_CONFIG)
def test_get_codeartifact_index_client_error(pipeline_session):
- codeartifact_repo_arn = "arn:aws:codeartifact:us-west-2:012345678901:repository/test-domain/test-repository"
+ codeartifact_repo_arn = (
+ "arn:aws:codeartifact:us-west-2:012345678901:repository/test-domain/test-repository"
+ )
codeartifact_url = "test-domain-012345678901.d.codeartifact.us-west-2.amazonaws.com/pypi/test-repository/simple/"
- client = boto3.client('codeartifact', region_name=REGION)
+ client = boto3.client("codeartifact", region_name=REGION)
stubber = Stubber(client)
- +
get_auth_token_response = {
"authorizationToken": "mocked_token",
- "expiration": datetime.datetime(2045, 1, 1, 0, 0, 0)
+ "expiration": datetime.datetime(2045, 1, 1, 0, 0, 0),
}
auth_token_expected_params = {"domain": "test-domain", "domainOwner": "012345678901"}
- stubber.add_client_error("get_authorization_token", service_error_code="404", expected_params=auth_token_expected_params)
+ stubber.add_client_error(
+ "get_authorization_token",
+ service_error_code="404",
+ expected_params=auth_token_expected_params,
+ )
get_repo_endpoint_response = {"repositoryEndpoint": f"https://{codeartifact_url}"}
repo_endpoint_expected_params = {
"domain": "test-domain",
"domainOwner": "012345678901",
"repository": "test-repository",
- "format": "pypi"
+ "format": "pypi",
}
- stubber.add_response("get_repository_endpoint", get_repo_endpoint_response, repo_endpoint_expected_params)
+ stubber.add_response(
+ "get_repository_endpoint", get_repo_endpoint_response, repo_endpoint_expected_params
+ )
processor = PyTorchProcessor(
role=ROLE,
@@ -1217,7 +1239,9 @@ def test_get_codeartifact_index_client_error(pipeline_session):
with stubber:
with pytest.raises(RuntimeError):
- processor._get_codeartifact_index(codeartifact_repo_arn=codeartifact_repo_arn, codeartifact_client=client)
+ processor._get_codeartifact_index(
+ codeartifact_repo_arn=codeartifact_repo_arn, codeartifact_client=client
+ )
def _get_script_processor(sagemaker_session):

With no access to the build logs, I wonder if that is the only issue?

@humanzz

Copy link
Copy Markdown
Contributor

One more thought, my teammate @Stacy-D, has started looking into using processing jobs, and has been experimenting with a different approach for setting up CodeArtifact.

Rather than uploading a script that has the hardcoded index in pip install -r requirements.txt {index_option}, she's written a script leveraging ** aws cli** (it's an assumption, but we confirmed it on pytorch containers) for configuring pip using

aws codeartifact login --tool pip --repository "$CODEARTIFACT_REPO" --domain "$CODEARTIFACT_DOMAIN" --domain-owner "${CODEARTIFACT_OWNER}" --region "${CODEARTIFACT_REGION}"

The script then looks something along the lines of

#!/bin/bash
cd /opt/ml/processing/input/code/
tar -xzf sourcedir.tar.gz
# Exit on any error. SageMaker uses error code to mark failed job.
set -e
aws codeartifact login --tool pip --repository "$CODEARTIFACT_REPO" --domain "$CODEARTIFACT_DOMAIN" --domain-owner "${CODEARTIFACT_OWNER}" --region "${CODEARTIFACT_REGION}"
if [[ -f 'requirements.txt' ]]; then
# Some py3 containers has typing, which may breaks pip install
pip uninstall --yes typing
pip install -r requirements.txt
fi
python "$THE_SCRIPT" "$@"

To make CA optional, the aws codeartifact login would need to be wrapped in an if condition, for the environment variables to be set.

@akuma12

Copy link
Copy Markdown
ContributorAuthor

I had almost forgotten about this. Thank you @humanzz for your feedback. I'll look into that script change and see if I can modify the code to make use of that.

@codecov

codecovBot commented Mar 15, 2024

Copy link
Copy Markdown

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 87.44%. Comparing base (31190c4) to head (cfe8139).

Current head cfe8139 differs from pull request most recent head f9deaa5

Please upload reports for the commit f9deaa5 to get more accurate results.

Additional details and impacted files
@@ Coverage Diff @@## master #4145 +/- ##
==========================================
+ Coverage 86.70% 87.44% +0.74% 
==========================================
Files 409 389 -20 Lines 39067 36904 -2163 ==========================================
- Hits 33872 32272 -1600 + Misses 5195 4632 -563 

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@akuma12

Copy link
Copy Markdown
ContributorAuthor

@humanzz The process using the AWS CLI is muuuuuch simpler. I don't have to rely on boto3, and I confirmed that the PyTorch training images have the AWS CLI installed. Updated the code and added some additional unit tests.

@akuma12

Copy link
Copy Markdown
ContributorAuthor

@mohanasudhan Looks like all tests and lints are passing now, if you could take one last look.

@akuma12

Copy link
Copy Markdown
ContributorAuthor

@akrishna1995 I'd love to get a final review on this, if possible. Thanks!

@akuma12

Copy link
Copy Markdown
ContributorAuthor

@akrishna1995 Could I get a final review on this PR? I'd really like to get my internal projects off of my fork so I can start getting the more recent updates to sagemaker-python-sdk. Thank you!

@sage-maker

sage-maker commented Aug 7, 2024

Copy link
Copy Markdown
Contributor

@akuma12
On-call here taking a look at this PR. I started approval workflow for tests. Are all code changes done here?

@akuma12

Copy link
Copy Markdown
ContributorAuthor

@sage-maker
Thank you!
Yup, everything is tested and ready to go.

@sage-maker
sage-maker merged commit 502e051 into aws:masterAug 7, 2024
@akuma12
akuma12 deleted the processing-job-codeartifact-support branch August 8, 2024 14:51
@akuma12
akuma12 restored the processing-job-codeartifact-support branch August 8, 2024 17:52
Evan-W-ang added a commit to Evan-W-ang/sagemaker-python-sdk that referenced this pull request Jun 8, 2026
…cript (aws#4145)
* feature: Add optional CodeArtifact login to FrameworkProcessing job script
* Add unit test for _get_codeartifact_index
* Fixed docstring
* Convert CodeArtifact integration to simply generate an AWS CLI command to log into CodeArtifact
* Fix lint issues
* More lint fixes
* Lint fix
* Yet Another Lint Fix
* Black fix
---------
Co-authored-by: sage-maker <parknate@amazon.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@akuma12@sagemaker-bot@mohanasudhan@humanzz@sage-maker@akrishna1995