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

r/sagemaker_device_fleet - new resource #20058

Merged
merged 11 commits into from
Aug 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions .changelog/20058.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:new-resource
aws_sagemaker_device_fleet
```
29 changes: 29 additions & 0 deletions aws/internal/service/sagemaker/finder/finder.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,35 @@ func ImageVersionByName(conn *sagemaker.SageMaker, name string) (*sagemaker.Desc
return output, nil
}

// DeviceFleetByName returns the Device Fleet corresponding to the specified Device Fleet name.
// Returns nil if no Device Fleet is found.
func DeviceFleetByName(conn *sagemaker.SageMaker, id string) (*sagemaker.DescribeDeviceFleetOutput, error) {
input := &sagemaker.DescribeDeviceFleetInput{
DeviceFleetName: aws.String(id),
}

output, err := conn.DescribeDeviceFleet(input)
Copy link
Contributor

Choose a reason for hiding this comment

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

Looking at https://docs.aws.amazon.com/sagemaker/latest/APIReference/API_DescribeDeviceFleet.html, it looks like the API can return ResourceNotFound if the Device Fleet doesn't exist

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

wasnt able to produce this error from tests

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

moved error check to finder

if tfawserr.ErrMessageContains(err, "ValidationException", "No devicefleet with name") {
return nil, &resource.NotFoundError{
LastError: err,
LastRequest: input,
}
}

if err != nil {
return nil, err
}

if output == nil {
return nil, &resource.NotFoundError{
Message: "Empty result",
LastRequest: input,
}
}

return output, nil
}

// DomainByName returns the domain corresponding to the specified domain id.
// Returns nil if no domain is found.
func DomainByName(conn *sagemaker.SageMaker, domainID string) (*sagemaker.DescribeDomainOutput, error) {
Expand Down
1 change: 1 addition & 0 deletions aws/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -1003,6 +1003,7 @@ func Provider() *schema.Provider {
"aws_sagemaker_app": resourceAwsSagemakerApp(),
"aws_sagemaker_app_image_config": resourceAwsSagemakerAppImageConfig(),
"aws_sagemaker_code_repository": resourceAwsSagemakerCodeRepository(),
"aws_sagemaker_device_fleet": resourceAwsSagemakerDeviceFleet(),
"aws_sagemaker_domain": resourceAwsSagemakerDomain(),
"aws_sagemaker_endpoint": resourceAwsSagemakerEndpoint(),
"aws_sagemaker_endpoint_configuration": resourceAwsSagemakerEndpointConfiguration(),
Expand Down
253 changes: 253 additions & 0 deletions aws/resource_aws_sagemaker_device_fleet.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
package aws

import (
"fmt"
"log"
"regexp"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/sagemaker"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/service/sagemaker/finder"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource"
)

func resourceAwsSagemakerDeviceFleet() *schema.Resource {
return &schema.Resource{
Create: resourceAwsSagemakerDeviceFleetCreate,
Read: resourceAwsSagemakerDeviceFleetRead,
Update: resourceAwsSagemakerDeviceFleetUpdate,
Delete: resourceAwsSagemakerDeviceFleetDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},

Schema: map[string]*schema.Schema{
"arn": {
Type: schema.TypeString,
Computed: true,
},
"description": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.StringLenBetween(1, 800),
},
"device_fleet_name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.All(
validation.StringLenBetween(1, 63),
validation.StringMatch(regexp.MustCompile(`^[a-zA-Z0-9](-*[a-zA-Z0-9]){0,62}$`), "Valid characters are a-z, A-Z, 0-9, and - (hyphen)."),
),
},
"enable_iot_role_alias": {
Type: schema.TypeBool,
Optional: true,
},
"iot_role_alias": {
Type: schema.TypeString,
Computed: true,
},
"output_config": {
Type: schema.TypeList,
Required: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"kms_key_id": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: validateArn,
},
"s3_output_location": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringLenBetween(1, 1024),
},
},
},
},
"role_arn": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validateArn,
},
"tags": tagsSchema(),
"tags_all": tagsSchemaComputed(),
},
CustomizeDiff: SetTagsDiff,
}
}

func resourceAwsSagemakerDeviceFleetCreate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).sagemakerconn
defaultTagsConfig := meta.(*AWSClient).DefaultTagsConfig
tags := defaultTagsConfig.MergeTags(keyvaluetags.New(d.Get("tags").(map[string]interface{})))

name := d.Get("device_fleet_name").(string)
input := &sagemaker.CreateDeviceFleetInput{
DeviceFleetName: aws.String(name),
OutputConfig: expandSagemakerFeatureDeviceFleetOutputConfig(d.Get("output_config").([]interface{})),
EnableIotRoleAlias: aws.Bool(d.Get("enable_iot_role_alias").(bool)),
}

if v, ok := d.GetOk("role_arn"); ok {
input.RoleArn = aws.String(v.(string))
}

if v, ok := d.GetOk("description"); ok {
input.Description = aws.String(v.(string))
}

if len(tags) > 0 {
input.Tags = tags.IgnoreAws().SagemakerTags()
}

_, err := retryOnAwsCode("ValidationException", func() (interface{}, error) {
return conn.CreateDeviceFleet(input)
})
if err != nil {
return fmt.Errorf("error creating SageMaker Device Fleet %s: %w", name, err)
}

d.SetId(name)

return resourceAwsSagemakerDeviceFleetRead(d, meta)
}

func resourceAwsSagemakerDeviceFleetRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).sagemakerconn
defaultTagsConfig := meta.(*AWSClient).DefaultTagsConfig
ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig

deviceFleet, err := finder.DeviceFleetByName(conn, d.Id())
if !d.IsNewResource() && tfresource.NotFound(err) {
log.Printf("[WARN] Unable to find SageMaker Device Fleet (%s); removing from state", d.Id())
d.SetId("")
return nil
}

if err != nil {
return fmt.Errorf("error reading SageMaker Device Fleet (%s): %w", d.Id(), err)
}

arn := aws.StringValue(deviceFleet.DeviceFleetArn)
d.Set("device_fleet_name", deviceFleet.DeviceFleetName)
d.Set("arn", arn)
d.Set("role_arn", deviceFleet.RoleArn)
d.Set("description", deviceFleet.Description)

iotAlias := aws.StringValue(deviceFleet.IotRoleAlias)
d.Set("iot_role_alias", iotAlias)
d.Set("enable_iot_role_alias", len(iotAlias) > 0)

if err := d.Set("output_config", flattenSagemakerFeatureDeviceFleetOutputConfig(deviceFleet.OutputConfig)); err != nil {
return fmt.Errorf("error setting output_config for Sagemaker Device Fleet (%s): %w", d.Id(), err)
}

tags, err := keyvaluetags.SagemakerListTags(conn, arn)

if err != nil {
return fmt.Errorf("error listing tags for SageMaker Device Fleet (%s): %w", d.Id(), err)
}

tags = tags.IgnoreAws().IgnoreConfig(ignoreTagsConfig)

//lintignore:AWSR002
if err := d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil {
return fmt.Errorf("error setting tags: %w", err)
}

if err := d.Set("tags_all", tags.Map()); err != nil {
return fmt.Errorf("error setting tags_all: %w", err)
}

return nil
}

func resourceAwsSagemakerDeviceFleetUpdate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).sagemakerconn

if d.HasChangeExcept("tags_all") {
input := &sagemaker.UpdateDeviceFleetInput{
DeviceFleetName: aws.String(d.Id()),
EnableIotRoleAlias: aws.Bool(d.Get("enable_iot_role_alias").(bool)),
OutputConfig: expandSagemakerFeatureDeviceFleetOutputConfig(d.Get("output_config").([]interface{})),
RoleArn: aws.String(d.Get("role_arn").(string)),
}

if d.HasChange("description") {
input.Description = aws.String(d.Get("description").(string))
}

log.Printf("[DEBUG] sagemaker DeviceFleet update config: %s", input.String())
_, err := conn.UpdateDeviceFleet(input)
if err != nil {
return fmt.Errorf("error updating SageMaker Device Fleet: %w", err)
}
}

if d.HasChange("tags_all") {
o, n := d.GetChange("tags_all")

if err := keyvaluetags.SagemakerUpdateTags(conn, d.Get("arn").(string), o, n); err != nil {
return fmt.Errorf("error updating SageMaker Device Fleet (%s) tags: %w", d.Id(), err)
}
}

return resourceAwsSagemakerDeviceFleetRead(d, meta)
}

func resourceAwsSagemakerDeviceFleetDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).sagemakerconn

input := &sagemaker.DeleteDeviceFleetInput{
DeviceFleetName: aws.String(d.Id()),
}

if _, err := conn.DeleteDeviceFleet(input); err != nil {
if isAWSErr(err, "ValidationException", "DeviceFleet with name") {
return nil
}
return fmt.Errorf("error deleting SageMaker Device Fleet (%s): %w", d.Id(), err)
}

return nil
}

func expandSagemakerFeatureDeviceFleetOutputConfig(l []interface{}) *sagemaker.EdgeOutputConfig {
if len(l) == 0 || l[0] == nil {
return nil
}

m := l[0].(map[string]interface{})

config := &sagemaker.EdgeOutputConfig{
S3OutputLocation: aws.String(m["s3_output_location"].(string)),
}

if v, ok := m["kms_key_id"].(string); ok && v != "" {
config.KmsKeyId = aws.String(m["kms_key_id"].(string))
}

return config
}

func flattenSagemakerFeatureDeviceFleetOutputConfig(config *sagemaker.EdgeOutputConfig) []map[string]interface{} {
if config == nil {
return []map[string]interface{}{}
}

m := map[string]interface{}{
"s3_output_location": aws.StringValue(config.S3OutputLocation),
}

if config.KmsKeyId != nil {
m["kms_key_id"] = aws.StringValue(config.KmsKeyId)
}

return []map[string]interface{}{m}
}
Loading