Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate Sagemaker resources to AWS SDK v2 #38835

Merged
merged 54 commits into from
Aug 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
8be510b
feat: regenerate `v2` client
DanielRieske Aug 12, 2024
7dfed18
feat: migrate r/app_image_config to sdkv2
DanielRieske Aug 12, 2024
4ac1f05
feat: migrate r/app to sdkv2
DanielRieske Aug 12, 2024
0612207
feat: migrate r/code_repository to sdkv2
DanielRieske Aug 12, 2024
4c4d21c
feat: migrate r/data_quality_job_definition to sdkv2
DanielRieske Aug 12, 2024
a9952b6
feat: migrate r/device_fleet to sdkv2
DanielRieske Aug 12, 2024
21cfdbe
feat: migrate r/device to sdkv2
DanielRieske Aug 12, 2024
758952a
feat: migrate r/endpoint_configuration to sdkv2
DanielRieske Aug 12, 2024
d29f671
feat: migrate r/domain to sdkv2
DanielRieske Aug 12, 2024
e7865a4
feat: migrate r/endpoint to sdkv2
DanielRieske Aug 12, 2024
0533e00
feat: migrate r/feature_group to sdkv2
DanielRieske Aug 12, 2024
c58d477
feat: migrate r/flow_definition to sdkv2
DanielRieske Aug 12, 2024
c3a3817
chore: generate
DanielRieske Aug 12, 2024
f8792e6
feat: migrate r/human_task_ui to sdkv2
DanielRieske Aug 12, 2024
8379c53
feat: migrate r/image to sdkv2
DanielRieske Aug 12, 2024
5ee46ae
feat: migrate r/image_version to sdkv2
DanielRieske Aug 12, 2024
4cb4573
feat: migrate r/model_package_group_policy to sdkv2
DanielRieske Aug 12, 2024
cc2fcec
feat: migrate r/model_package_group to sdkv2
DanielRieske Aug 12, 2024
81ee623
feat: migrate r/monitoring_schedule to sdkv2
DanielRieske Aug 12, 2024
2ba375c
feat: migrate r/model to sdkv2
DanielRieske Aug 12, 2024
4fac290
feat: migrate r/notebook_instance_lifecycle_configuration to sdkv2
DanielRieske Aug 12, 2024
fe1abb3
feat: migrate r/notebook_instance to sdkv2
DanielRieske Aug 12, 2024
4e2612f
feat: migrate r/pipeline to sdkv2
DanielRieske Aug 12, 2024
0966b54
feat: migrate d/prebuilt_ecr_image to sdkv2
DanielRieske Aug 12, 2024
1ba248a
feat: migrate r/project to sdkv2
DanielRieske Aug 12, 2024
a8a965b
feat: migrate r/servicecatalog_portfolio_status to sdkv2
DanielRieske Aug 12, 2024
432ba64
feat: migrate r/space to sdkv2
DanielRieske Aug 12, 2024
9cc20de
feat: migrate r/studio_lifecycle_config to sdkv2
DanielRieske Aug 12, 2024
d97e64c
feat: migrate r/user_profile to sdkv2
DanielRieske Aug 12, 2024
2d9d861
feat: migrate r/workforce to sdkv2
DanielRieske Aug 12, 2024
67dfd2c
feat: migrate r/workteam to sdkv2
DanielRieske Aug 12, 2024
3913f3c
docs: updated acceptable app_types for r/app
DanielRieske Aug 12, 2024
6837e7d
feat: migrated sweepers
DanielRieske Aug 12, 2024
e416d83
chore: spelling and added test
DanielRieske Aug 12, 2024
71da162
feat: migrate utility functions
DanielRieske Aug 12, 2024
1e88301
chore: add semgrep exemption
DanielRieske Aug 12, 2024
4dc9bb0
fix: changed json normalization replacement for protocol package
DanielRieske Aug 12, 2024
59ad448
fix: `testAccFeatureGroup_offlineConfig_format` added `AmazonSageMake…
DanielRieske Aug 12, 2024
afc448d
chore: added skip to r/device and r/device_fleet tests because they a…
DanielRieske Aug 12, 2024
2e45cbb
Merge branch 'main' into f/migrate-sagemaker-sdkv2
DanielRieske Aug 12, 2024
352e4b6
chore: regenerate client
DanielRieske Aug 12, 2024
fb43f79
feat: cleanup unused exports
DanielRieske Aug 12, 2024
ee5b7f0
chore: construct arn in `testAccDecodeAppID` instead of explicit decl…
DanielRieske Aug 12, 2024
60bdf8a
chore: cleanup
DanielRieske Aug 12, 2024
fe169bf
fix: `TestAccSageMakerDataQualityJobDefinition_endpoint` and `TestAcc…
DanielRieske Aug 13, 2024
2ee3c60
fix: `TestAccSageMakerNotebookInstance_acceleratorTypes` by changing …
DanielRieske Aug 13, 2024
13f30fb
chore: fmt
DanielRieske Aug 13, 2024
8bf277a
fix: constants
DanielRieske Aug 13, 2024
03c3986
chore: use `acctest.Skip` instead of `t.Skip`
DanielRieske Aug 13, 2024
5eba3b8
chore: `tests` to `test`
DanielRieske Aug 13, 2024
4368f27
chore: add deprecation notice for `accelerator_types`, see https://gi…
DanielRieske Aug 13, 2024
8b8212f
Merge branch 'main' into f/migrate-sagemaker-sdkv2
DanielRieske Aug 13, 2024
e3bd57d
Run 'make clean-tidy'.
ewbankkit Aug 13, 2024
119bfe7
Merge branch 'main' into HEAD
ewbankkit Aug 14, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ require (
github.com/aws/aws-sdk-go-v2/service/s3 v1.58.3
github.com/aws/aws-sdk-go-v2/service/s3control v1.46.3
github.com/aws/aws-sdk-go-v2/service/s3outposts v1.26.3
github.com/aws/aws-sdk-go-v2/service/sagemaker v1.152.0
github.com/aws/aws-sdk-go-v2/service/scheduler v1.10.3
github.com/aws/aws-sdk-go-v2/service/schemas v1.26.3
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.32.4
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,8 @@ github.com/aws/aws-sdk-go-v2/service/s3control v1.46.3 h1:3De8/YQpup0mLNKh0G9JHW
github.com/aws/aws-sdk-go-v2/service/s3control v1.46.3/go.mod h1:sUA7DOI2fdRHQQUpvRVfYKTo9P0+UAsWYBHvyqFHcC0=
github.com/aws/aws-sdk-go-v2/service/s3outposts v1.26.3 h1:Hg1FVxD9pelFS8j3ilHJDUe6J/Q/VVwzWaNtN8vyNUQ=
github.com/aws/aws-sdk-go-v2/service/s3outposts v1.26.3/go.mod h1:GVq0lM4BUD3GyiLzlNWXUq9U/H5t+2eytsEDirQSAn4=
github.com/aws/aws-sdk-go-v2/service/sagemaker v1.152.0 h1:y3jRrFbGve0omxt5gDStki51bjYJ6gxhtXr7VFagVv4=
github.com/aws/aws-sdk-go-v2/service/sagemaker v1.152.0/go.mod h1:lDmK3DHWV6Y6hpzeUAaXq4w+ks6fFYXdkjavIe8STCE=
github.com/aws/aws-sdk-go-v2/service/scheduler v1.10.3 h1:gmpU7E0ntMzXr+yQQIXbiiueOewf/1BQ9WgeaXo6BcQ=
github.com/aws/aws-sdk-go-v2/service/scheduler v1.10.3/go.mod h1:jnQp5kPPvEgPmVPm0h/XZPmlx7DQ0pqUiISRO4s6U3s=
github.com/aws/aws-sdk-go-v2/service/schemas v1.26.3 h1:ZJW2OQNpkR8P7URtISmF8twpvz2V0tUN/OgMenlxkao=
Expand Down
6 changes: 3 additions & 3 deletions internal/conns/awsclient_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

169 changes: 123 additions & 46 deletions internal/service/sagemaker/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,18 @@ import (
"strings"

"github.com/YakDriver/regexache"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/arn"
"github.com/aws/aws-sdk-go/service/sagemaker"
"github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/aws/arn"
"github.com/aws/aws-sdk-go-v2/service/sagemaker"
awstypes "github.com/aws/aws-sdk-go-v2/service/sagemaker/types"
"github.com/hashicorp/aws-sdk-go-base/v2/tfawserr"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
"github.com/hashicorp/terraform-provider-aws/internal/conns"
"github.com/hashicorp/terraform-provider-aws/internal/enum"
"github.com/hashicorp/terraform-provider-aws/internal/errs"
"github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag"
tftags "github.com/hashicorp/terraform-provider-aws/internal/tags"
"github.com/hashicorp/terraform-provider-aws/internal/tfresource"
Expand All @@ -27,7 +31,7 @@ import (

// @SDKResource("aws_sagemaker_app", name="App")
// @Tags(identifierAttribute="arn")
func ResourceApp() *schema.Resource {
func resourceApp() *schema.Resource {
return &schema.Resource{
CreateWithoutTimeout: resourceAppCreate,
ReadWithoutTimeout: resourceAppRead,
Expand All @@ -52,10 +56,10 @@ func ResourceApp() *schema.Resource {
),
},
"app_type": {
Type: schema.TypeString,
ForceNew: true,
Required: true,
ValidateFunc: validation.StringInSlice(sagemaker.AppType_Values(), false),
Type: schema.TypeString,
ForceNew: true,
Required: true,
ValidateDiagFunc: enum.Validate[awstypes.AppType](),
},
"domain_id": {
Type: schema.TypeString,
Expand All @@ -71,9 +75,9 @@ func ResourceApp() *schema.Resource {
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
names.AttrInstanceType: {
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.StringInSlice(sagemaker.AppInstanceType_Values(), false),
Type: schema.TypeString,
Optional: true,
ValidateDiagFunc: enum.Validate[awstypes.AppInstanceType](),
},
"lifecycle_config_arn": {
Type: schema.TypeString,
Expand Down Expand Up @@ -120,11 +124,11 @@ func ResourceApp() *schema.Resource {

func resourceAppCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
var diags diag.Diagnostics
conn := meta.(*conns.AWSClient).SageMakerConn(ctx)
conn := meta.(*conns.AWSClient).SageMakerClient(ctx)

input := &sagemaker.CreateAppInput{
AppName: aws.String(d.Get("app_name").(string)),
AppType: aws.String(d.Get("app_type").(string)),
AppType: awstypes.AppType(d.Get("app_type").(string)),
DomainId: aws.String(d.Get("domain_id").(string)),
Tags: getTagsIn(ctx),
}
Expand All @@ -142,20 +146,20 @@ func resourceAppCreate(ctx context.Context, d *schema.ResourceData, meta interfa
}

log.Printf("[DEBUG] SageMaker App create config: %#v", *input)
output, err := conn.CreateAppWithContext(ctx, input)
output, err := conn.CreateApp(ctx, input)
if err != nil {
return sdkdiag.AppendErrorf(diags, "creating SageMaker App: %s", err)
}

appArn := aws.StringValue(output.AppArn)
appArn := aws.ToString(output.AppArn)
domainID, userProfileOrSpaceName, appType, appName, err := decodeAppID(appArn)
if err != nil {
return sdkdiag.AppendErrorf(diags, "creating SageMaker App (%s): %s", appArn, err)
}

d.SetId(appArn)

if _, err := WaitAppInService(ctx, conn, domainID, userProfileOrSpaceName, appType, appName); err != nil {
if _, err := waitAppInService(ctx, conn, domainID, userProfileOrSpaceName, appType, appName); err != nil {
return sdkdiag.AppendErrorf(diags, "create SageMaker App (%s): waiting for completion: %s", d.Id(), err)
}

Expand All @@ -164,27 +168,28 @@ func resourceAppCreate(ctx context.Context, d *schema.ResourceData, meta interfa

func resourceAppRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
var diags diag.Diagnostics
conn := meta.(*conns.AWSClient).SageMakerConn(ctx)
conn := meta.(*conns.AWSClient).SageMakerClient(ctx)

domainID, userProfileOrSpaceName, appType, appName, err := decodeAppID(d.Id())
if err != nil {
return sdkdiag.AppendErrorf(diags, "reading SageMaker App (%s): %s", d.Id(), err)
}

app, err := FindAppByName(ctx, conn, domainID, userProfileOrSpaceName, appType, appName)
app, err := findAppByName(ctx, conn, domainID, userProfileOrSpaceName, appType, appName)

if !d.IsNewResource() && tfresource.NotFound(err) {
d.SetId("")
log.Printf("[WARN] Unable to find SageMaker App (%s); removing from state", d.Id())
return diags
}

if err != nil {
if !d.IsNewResource() && tfresource.NotFound(err) {
d.SetId("")
log.Printf("[WARN] Unable to find SageMaker App (%s); removing from state", d.Id())
return diags
}
return sdkdiag.AppendErrorf(diags, "reading SageMaker App (%s): %s", d.Id(), err)
}

arn := aws.StringValue(app.AppArn)
d.Set("app_name", app.AppName)
d.Set("app_type", app.AppType)
d.Set(names.AttrARN, arn)
d.Set(names.AttrARN, app.AppArn)
d.Set("domain_id", app.DomainId)
d.Set("space_name", app.SpaceName)
d.Set("user_profile_name", app.UserProfileName)
Expand All @@ -206,7 +211,7 @@ func resourceAppUpdate(ctx context.Context, d *schema.ResourceData, meta interfa

func resourceAppDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
var diags diag.Diagnostics
conn := meta.(*conns.AWSClient).SageMakerConn(ctx)
conn := meta.(*conns.AWSClient).SageMakerClient(ctx)

appName := d.Get("app_name").(string)
appType := d.Get("app_type").(string)
Expand All @@ -215,7 +220,7 @@ func resourceAppDelete(ctx context.Context, d *schema.ResourceData, meta interfa

input := &sagemaker.DeleteAppInput{
AppName: aws.String(appName),
AppType: aws.String(appType),
AppType: awstypes.AppType(appType),
DomainId: aws.String(domainID),
}

Expand All @@ -229,26 +234,103 @@ func resourceAppDelete(ctx context.Context, d *schema.ResourceData, meta interfa
userProfileOrSpaceName = v.(string)
}

if _, err := conn.DeleteAppWithContext(ctx, input); err != nil {
if tfawserr.ErrMessageContains(err, "ValidationException", "has already been deleted") ||
tfawserr.ErrMessageContains(err, "ValidationException", "previously failed and was automatically deleted") {
if _, err := conn.DeleteApp(ctx, input); err != nil {
if tfawserr.ErrMessageContains(err, ErrCodeValidationException, "has already been deleted") ||
tfawserr.ErrMessageContains(err, ErrCodeValidationException, "previously failed and was automatically deleted") {
return diags
}

if !tfawserr.ErrCodeEquals(err, sagemaker.ErrCodeResourceNotFound) {
if !errs.IsA[*awstypes.ResourceNotFound](err) {
return sdkdiag.AppendErrorf(diags, "deleting SageMaker App (%s): %s", d.Id(), err)
}
}

if _, err := WaitAppDeleted(ctx, conn, domainID, userProfileOrSpaceName, appType, appName); err != nil {
if !tfawserr.ErrCodeEquals(err, sagemaker.ErrCodeResourceNotFound) {
return sdkdiag.AppendErrorf(diags, "waiting for SageMaker App (%s) to delete: %s", d.Id(), err)
}
if _, err := waitAppDeleted(ctx, conn, domainID, userProfileOrSpaceName, appType, appName); err != nil {
return sdkdiag.AppendErrorf(diags, "waiting for SageMaker App (%s) to delete: %s", d.Id(), err)
}

return diags
}

func findAppByName(ctx context.Context, conn *sagemaker.Client, domainID, userProfileOrSpaceName, appType, appName string) (*sagemaker.DescribeAppOutput, error) {
foundApp, err := listAppsByName(ctx, conn, domainID, userProfileOrSpaceName, appType, appName)

if err != nil {
return nil, err
}

input := &sagemaker.DescribeAppInput{
AppName: aws.String(appName),
AppType: awstypes.AppType(appType),
DomainId: aws.String(domainID),
}
if foundApp.SpaceName != nil {
input.SpaceName = foundApp.SpaceName
}
if foundApp.UserProfileName != nil {
input.UserProfileName = foundApp.UserProfileName
}

output, err := conn.DescribeApp(ctx, input)

if tfawserr.ErrMessageContains(err, ErrCodeValidationException, "RecordNotFound") {
return nil, &retry.NotFoundError{
LastError: err,
LastRequest: input,
}
}

if err != nil {
return nil, err
}

if output == nil {
return nil, tfresource.NewEmptyResultError(input)
}

if state := output.Status; state == awstypes.AppStatusDeleted {
return nil, &retry.NotFoundError{
Message: string(state),
LastRequest: input,
}
}

return output, nil
}

func listAppsByName(ctx context.Context, conn *sagemaker.Client, domainID, userProfileOrSpaceName, appType, appName string) (*awstypes.AppDetails, error) {
input := &sagemaker.ListAppsInput{
DomainIdEquals: aws.String(domainID),
}
var output []awstypes.AppDetails

pages := sagemaker.NewListAppsPaginator(conn, input)
for pages.HasMorePages() {
page, err := pages.NextPage(ctx)

if errs.IsA[*awstypes.ResourceNotFound](err) {
return nil, &retry.NotFoundError{
LastError: err,
LastRequest: input,
}
}

if err != nil {
return nil, err
}

output = append(output, page.Apps...)
}

for _, v := range output {
if aws.ToString(v.AppName) == appName && string(v.AppType) == appType && (aws.ToString(v.SpaceName) == userProfileOrSpaceName || aws.ToString(v.UserProfileName) == userProfileOrSpaceName) {
return &v, nil
}
}

return nil, tfresource.NewEmptyResultError(input)
}

func decodeAppID(id string) (string, string, string, string, error) {
appArn, err := arn.Parse(id)
if err != nil {
Expand All @@ -266,16 +348,11 @@ func decodeAppID(id string) (string, string, string, string, error) {
userProfileOrSpaceName := parts[1]
appType := parts[2]

if appType == "jupyterserver" {
appType = sagemaker.AppTypeJupyterServer
} else if appType == "kernelgateway" {
appType = sagemaker.AppTypeKernelGateway
} else if appType == "tensorboard" {
appType = sagemaker.AppTypeTensorBoard
} else if appType == "rstudioserverpro" {
appType = sagemaker.AppTypeRstudioServerPro
} else if appType == "rsessiongateway" {
appType = sagemaker.AppTypeRsessionGateway
for _, appTypeValue := range awstypes.AppType("").Values() {
if appType == strings.ToLower(string(appTypeValue)) {
appType = string(appTypeValue)
break
}
}

appName := parts[3]
Expand Down
Loading
Loading