diff --git a/aws/resource_aws_sagemaker_notebook_instance.go b/aws/resource_aws_sagemaker_notebook_instance.go index 07533b02507..ea4cad4cc9d 100644 --- a/aws/resource_aws_sagemaker_notebook_instance.go +++ b/aws/resource_aws_sagemaker_notebook_instance.go @@ -27,9 +27,35 @@ func resourceAwsSagemakerNotebookInstance() *schema.Resource { Computed: true, }, + "accelerator_types": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + + "additional_code_repositories": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + + "default_code_repository": { + Type: schema.TypeString, + Optional: true, + // TODO min 1 + }, + + "direct_internet_access": { + // Enum value set: [Enabled, Disabled] + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + "name": { Type: schema.TypeString, - Required: true, + Optional: true, + Computed: true, ForceNew: true, ValidateFunc: validateSagemakerName, }, @@ -37,6 +63,7 @@ func resourceAwsSagemakerNotebookInstance() *schema.Resource { "role_arn": { Type: schema.TypeString, Required: true, + // TODO min length 20 }, "instance_type": { @@ -51,6 +78,7 @@ func resourceAwsSagemakerNotebookInstance() *schema.Resource { }, "security_groups": { + // TODO is type list in API Type: schema.TypeSet, MinItems: 1, Optional: true, @@ -67,9 +95,28 @@ func resourceAwsSagemakerNotebookInstance() *schema.Resource { }, "lifecycle_config_name": { - Type: schema.TypeString, + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateSagemakerName, + }, + + "volume_size": { + Type: schema.TypeInt, Optional: true, ForceNew: true, + Default: 5, + }, + + "status": { + Type: schema.TypeString, + Optional: true, + Default: sagemaker.NotebookInstanceStatusInService, + }, + + // TODO enum + "root_access": { + Type: schema.TypeString, + Optional: true, }, "tags": tagsSchema(), @@ -80,7 +127,12 @@ func resourceAwsSagemakerNotebookInstance() *schema.Resource { func resourceAwsSagemakerNotebookInstanceCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).sagemakerconn - name := d.Get("name").(string) + var name string + if v, ok := d.GetOk("name"); ok { + name = v.(string) + } else { + name = resource.UniqueId() + } createOpts := &sagemaker.CreateNotebookInstanceInput{ SecurityGroupIds: expandStringSet(d.Get("security_groups").(*schema.Set)), @@ -89,47 +141,65 @@ func resourceAwsSagemakerNotebookInstanceCreate(d *schema.ResourceData, meta int InstanceType: aws.String(d.Get("instance_type").(string)), } - if s, ok := d.GetOk("subnet_id"); ok { - createOpts.SubnetId = aws.String(s.(string)) + if v, ok := d.GetOk("accelerator_types"); ok { + acceleratorTypes := expandStringList(v.([]interface{})) + createOpts.AcceleratorTypes = acceleratorTypes + } + + if v, ok := d.GetOk("additional_code_repositories"); ok { + additionalCodeRepositories := expandStringList(v.([]interface{})) + createOpts.AdditionalCodeRepositories = additionalCodeRepositories + } + + if v, ok := d.GetOk("default_code_repository"); ok { + createOpts.DefaultCodeRepository = aws.String(v.(string)) } - if k, ok := d.GetOk("kms_key_id"); ok { - createOpts.KmsKeyId = aws.String(k.(string)) + if v, ok := d.GetOk("direct_internet_access"); ok { + createOpts.DirectInternetAccess = aws.String(v.(string)) + } + + if v, ok := d.GetOk("kms_key_id"); ok { + createOpts.KmsKeyId = aws.String(v.(string)) } if l, ok := d.GetOk("lifecycle_config_name"); ok { createOpts.LifecycleConfigName = aws.String(l.(string)) } + if s, ok := d.GetOk("root_access"); ok { + createOpts.RootAccess = aws.String(s.(string)) + } + + if s, ok := d.GetOk("subnet_id"); ok { + createOpts.SubnetId = aws.String(s.(string)) + } + if v, ok := d.GetOk("tags"); ok { tagsIn := v.(map[string]interface{}) createOpts.Tags = tagsFromMapSagemaker(tagsIn) } - log.Printf("[DEBUG] sagemaker notebook instance create config: %#v", *createOpts) + if v, ok := d.GetOk("volume_size"); ok { + createOpts.VolumeSizeInGB = aws.Int64(int64(v.(int))) + } + + log.Printf("[DEBUG] SageMaker Notebook Instance create config: %#v", *createOpts) _, err := conn.CreateNotebookInstance(createOpts) if err != nil { - return fmt.Errorf("Error creating Sagemaker Notebook Instance: %s", err) + return fmt.Errorf("error creating SageMaker Notebook Instance: %s", err) } d.SetId(name) - log.Printf("[INFO] sagemaker notebook instance ID: %s", d.Id()) + log.Printf("[INFO] SageMaker Notebook Instance ID: %s", d.Id()) - stateConf := &resource.StateChangeConf{ - Pending: []string{ - sagemaker.NotebookInstanceStatusUpdating, - sagemaker.NotebookInstanceStatusPending, - sagemaker.NotebookInstanceStatusStopped, - }, - Target: []string{sagemaker.NotebookInstanceStatusInService}, - Refresh: sagemakerNotebookInstanceStateRefreshFunc(conn, d.Id()), - Timeout: 10 * time.Minute, - } - _, err = stateConf.WaitForState() - if err != nil { - return fmt.Errorf("error waiting for sagemaker notebook instance (%s) to create: %s", d.Id(), err) + describeInput := &sagemaker.DescribeNotebookInstanceInput{ + NotebookInstanceName: aws.String(name), } + if err := conn.WaitUntilNotebookInstanceInService(describeInput); err != nil { + return fmt.Errorf("error waiting for SageMaker Notebook Instance (%s) to be in service: %s", name, err) + } return resourceAwsSagemakerNotebookInstanceRead(d, meta) } @@ -143,40 +213,76 @@ func resourceAwsSagemakerNotebookInstanceRead(d *schema.ResourceData, meta inter if err != nil { if isAWSErr(err, "ValidationException", "RecordNotFound") { d.SetId("") - log.Printf("[WARN] Unable to find sageMaker notebook instance (%s); removing from state", d.Id()) + log.Printf("[WARN] Unable to find SageMaker Notebook Instance (%s); removing from state", d.Id()) return nil } - return fmt.Errorf("error finding sagemaker notebook instance (%s): %s", d.Id(), err) + return fmt.Errorf("error finding SageMaker Notebook Instance (%s): %s", d.Id(), err) + } + if err := d.Set("accelerator_types", notebookInstance.AcceleratorTypes); err != nil { + return fmt.Errorf("error setting accelerator_types for SageMaker Notebook Instance (%s): %s", d.Id(), err) } - if err := d.Set("security_groups", flattenStringList(notebookInstance.SecurityGroups)); err != nil { - return fmt.Errorf("error setting security groups for sagemaker notebook instance (%s): %s", d.Id(), err) + if err := d.Set("additional_code_repositories", notebookInstance.AdditionalCodeRepositories); err != nil { + return fmt.Errorf("error setting additional_code_repositories for SageMaker Notebook Instance (%s): %s", d.Id(), err) + } + + if err := d.Set("default_code_repository", notebookInstance.DefaultCodeRepository); err != nil { + return fmt.Errorf("error setting default_code_repository for SageMaker Notebook Instance (%s): %s", d.Id(), err) + } + + if err := d.Set("direct_internet_access", notebookInstance.DirectInternetAccess); err != nil { + return fmt.Errorf("error setting direct_internet_access for SageMaker Notebook Instance (%s): %s", d.Id(), err) + } + + if err := d.Set("instance_type", notebookInstance.InstanceType); err != nil { + return fmt.Errorf("error setting instance_type for SageMaker Notebook Instance (%s): %s", d.Id(), err) } + if err := d.Set("name", notebookInstance.NotebookInstanceName); err != nil { - return fmt.Errorf("error setting name for sagemaker notebook instance (%s): %s", d.Id(), err) + return fmt.Errorf("error setting name for SageMaker Notebook Instance (%s): %s", d.Id(), err) } + if err := d.Set("role_arn", notebookInstance.RoleArn); err != nil { - return fmt.Errorf("error setting role_arn for sagemaker notebook instance (%s): %s", d.Id(), err) + return fmt.Errorf("error setting role_arn for SageMaker Notebook Instance (%s): %s", d.Id(), err) } - if err := d.Set("instance_type", notebookInstance.InstanceType); err != nil { - return fmt.Errorf("error setting instance_type for sagemaker notebook instance (%s): %s", d.Id(), err) + + if err := d.Set("root_access", notebookInstance.RootAccess); err != nil { + return fmt.Errorf("error setting root_access for sSageMaker Notebook Instance (%s): %s", d.Id(), err) } + + if err := d.Set("security_groups", flattenStringList(notebookInstance.SecurityGroups)); err != nil { + return fmt.Errorf("error setting security groups for SageMaker Notebook Instance (%s): %s", d.Id(), err) + } + if err := d.Set("subnet_id", notebookInstance.SubnetId); err != nil { - return fmt.Errorf("error setting subnet_id for sagemaker notebook instance (%s): %s", d.Id(), err) + return fmt.Errorf("error setting subnet_id for SageMaker Notebook Instance (%s): %s", d.Id(), err) } if err := d.Set("kms_key_id", notebookInstance.KmsKeyId); err != nil { - return fmt.Errorf("error setting kms_key_id for sagemaker notebook instance (%s): %s", d.Id(), err) + return fmt.Errorf("error setting kms_key_id for SageMaker Notebook Instance (%s): %s", d.Id(), err) } if err := d.Set("lifecycle_config_name", notebookInstance.NotebookInstanceLifecycleConfigName); err != nil { - return fmt.Errorf("error setting lifecycle_config_name for sagemaker notebook instance (%s): %s", d.Id(), err) + return fmt.Errorf("error setting lifecycle_config_name for SageMaker Notebook Instance (%s): %s", d.Id(), err) } if err := d.Set("arn", notebookInstance.NotebookInstanceArn); err != nil { - return fmt.Errorf("error setting arn for sagemaker notebook instance (%s): %s", d.Id(), err) + return fmt.Errorf("error setting arn for SageMaker Notebook Instance (%s): %s", d.Id(), err) + } + + if err := d.Set("volume_size", notebookInstance.VolumeSizeInGB); err != nil { + return fmt.Errorf("error setting volume_size for SageMaker Notebook Instance (%s): %s", d.Id(), err) + } + + if err := d.Set("status", notebookInstance.NotebookInstanceStatus); err != nil { + return fmt.Errorf("error setting status for SageMaker Notebook Instance (%s): %s", d.Id(), err) } + + // FailureReason + // NetworkInterfaceId + // Url + tagsOutput, err := conn.ListTags(&sagemaker.ListTagsInput{ ResourceArn: notebookInstance.NotebookInstanceArn, }) @@ -206,18 +312,66 @@ func resourceAwsSagemakerNotebookInstanceUpdate(d *schema.ResourceData, meta int NotebookInstanceName: aws.String(d.Get("name").(string)), } + if d.HasChange("accelerator_types") { + //updateOpts.SetAcceleratorTypes(d.Get("accelerator_types").(string)) + hasChanged = true + // TODO set DisassociateAcceleratorTypes to true + } + + if d.HasChange("additional_code_repositories") { + //updateOpts.SetAdditionalCodeRepositories(d.Get("additional_code_repositories").(string)) + hasChanged = true + } + + if d.HasChange("default_code_repository") { + updateOpts.SetDefaultCodeRepository(d.Get("default_code_repository").(string)) + hasChanged = true + } + if d.HasChange("role_arn") { - updateOpts.RoleArn = aws.String(d.Get("role_arn").(string)) + updateOpts.SetRoleArn(d.Get("role_arn").(string)) hasChanged = true } if d.HasChange("instance_type") { - updateOpts.InstanceType = aws.String(d.Get("instance_type").(string)) + updateOpts.SetInstanceType(d.Get("instance_type").(string)) hasChanged = true } - if hasChanged { + if d.HasChange("lifecycle_config_name") { + updateOpts.SetLifecycleConfigName(d.Get("lifecycle_config_name").(string)) + hasChanged = true + // TODO set flag to true + } + + if d.HasChange("volume_size") { + updateOpts.SetVolumeSizeInGB(d.Get("volume_size").(int64)) + hasChanged = true + } + if d.HasChange("status") { + _, status, _ := sagemakerNotebookInstanceStateRefreshFunc(conn, d.Id())() + if status == sagemaker.NotebookInstanceStatusStopped || + status == sagemaker.NotebookInstanceStatusFailed { + log.Printf("[INFO] Starting Sagemaker Notebook Instance %q", d.Id()) + _, err := conn.StartNotebookInstance(&sagemaker.StartNotebookInstanceInput{ + NotebookInstanceName: aws.String(d.Id()), + }) + if err != nil { + return fmt.Errorf("error starting Sagemaker Notebook Instance (%s): %s", d.Id(), err) + } + + describeInput := &sagemaker.DescribeNotebookInstanceInput{ + NotebookInstanceName: aws.String(d.Id()), + } + + if err := conn.WaitUntilNotebookInstanceInService(describeInput); err != nil { + return fmt.Errorf("error waiting for SageMaker Notebook Instance (%s) to be in service: %s", d.Id(), err) + } + } + } + + if hasChanged { // Stop notebook _, previousStatus, _ := sagemakerNotebookInstanceStateRefreshFunc(conn, d.Id())() if previousStatus != sagemaker.NotebookInstanceStatusStopped { @@ -274,19 +428,12 @@ func resourceAwsSagemakerNotebookInstanceUpdate(d *schema.ResourceData, meta int return err } - stateConf := &resource.StateChangeConf{ - Pending: []string{ - sagemaker.NotebookInstanceStatusUpdating, - sagemaker.NotebookInstanceStatusPending, - sagemaker.NotebookInstanceStatusStopped, - }, - Target: []string{sagemaker.NotebookInstanceStatusInService}, - Refresh: sagemakerNotebookInstanceStateRefreshFunc(conn, d.Id()), - Timeout: 10 * time.Minute, + describeInput := &sagemaker.DescribeNotebookInstanceInput{ + NotebookInstanceName: aws.String(d.Id()), } - _, err = stateConf.WaitForState() - if err != nil { - return fmt.Errorf("error waiting for sagemaker notebook instance (%s) to start after update: %s", d.Id(), err) + + if err := conn.WaitUntilNotebookInstanceInService(describeInput); err != nil { + return fmt.Errorf("error waiting for SageMaker Notebook Instance (%s) to be in service: %s", d.Id(), err) } } } @@ -323,17 +470,12 @@ func resourceAwsSagemakerNotebookInstanceDelete(d *schema.ResourceData, meta int return fmt.Errorf("error trying to delete sagemaker notebook instance (%s): %s", d.Id(), err) } - stateConf := &resource.StateChangeConf{ - Pending: []string{ - sagemaker.NotebookInstanceStatusDeleting, - }, - Target: []string{""}, - Refresh: sagemakerNotebookInstanceStateRefreshFunc(conn, d.Id()), - Timeout: 10 * time.Minute, + describeInput := &sagemaker.DescribeNotebookInstanceInput{ + NotebookInstanceName: aws.String(d.Id()), } - _, err = stateConf.WaitForState() - if err != nil { - return fmt.Errorf("error waiting for sagemaker notebook instance (%s) to delete: %s", d.Id(), err) + + if err := conn.WaitUntilNotebookInstanceDeleted(describeInput); err != nil { + return fmt.Errorf("error waiting for SageMaker Notebook Instance (%s) to be deleted: %s", d.Id(), err) } return nil @@ -359,7 +501,7 @@ func stopSagemakerNotebookInstance(conn *sagemaker.SageMaker, id string) error { } if _, err := conn.StopNotebookInstance(stopOpts); err != nil { - return fmt.Errorf("Error stopping sagemaker notebook instance: %s", err) + return fmt.Errorf("error stopping sagemaker notebook instance: %s", err) } stateConf := &resource.StateChangeConf{ @@ -386,7 +528,7 @@ func sagemakerNotebookInstanceStateRefreshFunc(conn *sagemaker.SageMaker, name s notebook, err := conn.DescribeNotebookInstance(describeNotebookInput) if err != nil { if isAWSErr(err, "ValidationException", "RecordNotFound") { - return 1, "", nil + return "", "", nil } return nil, "", err } diff --git a/aws/resource_aws_sagemaker_notebook_instance_test.go b/aws/resource_aws_sagemaker_notebook_instance_test.go index 337cf28107a..0a12ef646a0 100644 --- a/aws/resource_aws_sagemaker_notebook_instance_test.go +++ b/aws/resource_aws_sagemaker_notebook_instance_test.go @@ -5,31 +5,159 @@ import ( "testing" "time" + "github.com/hashicorp/terraform/helper/acctest" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/sagemaker" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" ) -const sagemakerTestAccSagemakerNotebookInstanceResourceNamePrefix = "terraform-testacc-" - func TestAccAWSSagemakerNotebookInstance_basic(t *testing.T) { var notebook sagemaker.DescribeNotebookInstanceOutput - notebookName := resource.PrefixedUniqueId(sagemakerTestAccSagemakerNotebookInstanceResourceNamePrefix) - var resourceName = "aws_sagemaker_notebook_instance.foo" + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_sagemaker_notebook_instance.foo" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSagemakerNotebookInstanceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSSagemakerNotebookInstanceConfig_Basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSSagemakerNotebookInstanceExists(resourceName, ¬ebook), + + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "instance_type", "ml.t2.medium"), + resource.TestCheckResourceAttrSet(resourceName, "role_arn"), + resource.TestCheckResourceAttrSet(resourceName, "arn"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +//"accelerator_types": { +//Type: schema.TypeList, +//Optional: true, +//Elem: &schema.Schema{Type: schema.TypeString}, +//}, +// +//"additional_code_repositories": { +//Type: schema.TypeList, +//Optional: true, +//Elem: &schema.Schema{Type: schema.TypeString}, +//}, +// +//"default_code_repository": { +//Type: schema.TypeString, +//Optional: true, +//// TODO min 1 +//}, + +func TestAccAWSSagemakerNotebookInstance_DirectInternetAccess(t *testing.T) { + var notebook sagemaker.DescribeNotebookInstanceOutput + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_sagemaker_notebook_instance.foo" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSagemakerNotebookInstanceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSSagemakerNotebookInstanceConfig_DirectInternetAccess(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSSagemakerNotebookInstanceExists(resourceName, ¬ebook), + + resource.TestCheckResourceAttr(resourceName, "direct_internet_access", "Disabled"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSSagemakerNotebookInstance_LifeCycleConfigName(t *testing.T) { + var notebook sagemaker.DescribeNotebookInstanceOutput + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_sagemaker_notebook_instance.foo" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSagemakerNotebookInstanceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSSagemakerNotebookInstanceConfig_LifecycleConfigName(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSSagemakerNotebookInstanceExists(resourceName, ¬ebook), + + resource.TestCheckResourceAttr(resourceName, "lifecycle_config_name", rName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSSagemakerNotebookInstance_volumeSize(t *testing.T) { + var notebook sagemaker.DescribeNotebookInstanceOutput + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_sagemaker_notebook_instance.foo" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSSagemakerNotebookInstanceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSSagemakerNotebookInstanceConfig_VolumeSize(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSSagemakerNotebookInstanceExists(resourceName, ¬ebook), + + resource.TestCheckResourceAttr(resourceName, "volume_size", "7"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSSagemakerNotebookInstance_KmsKeyID(t *testing.T) { + var notebook sagemaker.DescribeNotebookInstanceOutput + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_sagemaker_notebook_instance.foo" + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testAccCheckAWSSagemakerNotebookInstanceDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSSagemakerNotebookInstanceConfig(notebookName), + Config: testAccAWSSagemakerNotebookInstanceConfig_KmsKeyID(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSSagemakerNotebookInstanceExists(resourceName, ¬ebook), - testAccCheckAWSSagemakerNotebookInstanceName(¬ebook, notebookName), - resource.TestCheckResourceAttr( - "aws_sagemaker_notebook_instance.foo", "name", notebookName), + resource.TestCheckResourceAttr(resourceName, "kms_key_id", "7"), ), }, { @@ -41,32 +169,31 @@ func TestAccAWSSagemakerNotebookInstance_basic(t *testing.T) { }) } -func TestAccAWSSagemakerNotebookInstance_update(t *testing.T) { +func TestAccAWSSagemakerNotebookInstance_updateInstanceType(t *testing.T) { var notebook sagemaker.DescribeNotebookInstanceOutput - notebookName := resource.PrefixedUniqueId(sagemakerTestAccSagemakerNotebookInstanceResourceNamePrefix) - var resourceName = "aws_sagemaker_notebook_instance.foo" + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_sagemaker_notebook_instance.foo" + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testAccCheckAWSSagemakerNotebookInstanceDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSSagemakerNotebookInstanceConfig(notebookName), + Config: testAccAWSSagemakerNotebookInstanceConfig_Basic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSSagemakerNotebookInstanceExists(resourceName, ¬ebook), - resource.TestCheckResourceAttr( - "aws_sagemaker_notebook_instance.foo", "instance_type", "ml.t2.medium"), + resource.TestCheckResourceAttr(resourceName, "instance_type", "ml.t2.medium"), ), }, { - Config: testAccAWSSagemakerNotebookInstanceUpdateConfig(notebookName), + Config: testAccAWSSagemakerNotebookInstanceUpdateInstanceConfig(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSSagemakerNotebookInstanceExists("aws_sagemaker_notebook_instance.foo", ¬ebook), + testAccCheckAWSSagemakerNotebookInstanceExists(resourceName, ¬ebook), - resource.TestCheckResourceAttr( - "aws_sagemaker_notebook_instance.foo", "instance_type", "ml.m4.xlarge"), + resource.TestCheckResourceAttr(resourceName, "instance_type", "ml.m4.xlarge"), ), }, { @@ -107,7 +234,8 @@ func TestAccAWSSagemakerNotebookInstance_LifecycleConfigName(t *testing.T) { func TestAccAWSSagemakerNotebookInstance_tags(t *testing.T) { var notebook sagemaker.DescribeNotebookInstanceOutput - notebookName := resource.PrefixedUniqueId(sagemakerTestAccSagemakerNotebookInstanceResourceNamePrefix) + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_sagemaker_notebook_instance.foo" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -115,27 +243,22 @@ func TestAccAWSSagemakerNotebookInstance_tags(t *testing.T) { CheckDestroy: testAccCheckAWSSagemakerNotebookInstanceDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSSagemakerNotebookInstanceTagsConfig(notebookName), + Config: testAccAWSSagemakerNotebookInstanceTagsConfig(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSSagemakerNotebookInstanceExists("aws_sagemaker_notebook_instance.foo", ¬ebook), - testAccCheckAWSSagemakerNotebookInstanceTags(¬ebook, "foo", "bar"), + testAccCheckAWSSagemakerNotebookInstanceExists(resourceName, ¬ebook), - resource.TestCheckResourceAttr( - "aws_sagemaker_notebook_instance.foo", "name", notebookName), - resource.TestCheckResourceAttr("aws_sagemaker_notebook_instance.foo", "tags.%", "1"), - resource.TestCheckResourceAttr("aws_sagemaker_notebook_instance.foo", "tags.foo", "bar"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.foo", "bar"), ), }, { - Config: testAccAWSSagemakerNotebookInstanceTagsUpdateConfig(notebookName), + Config: testAccAWSSagemakerNotebookInstanceTagsUpdateConfig(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSSagemakerNotebookInstanceExists("aws_sagemaker_notebook_instance.foo", ¬ebook), - testAccCheckAWSSagemakerNotebookInstanceTags(¬ebook, "foo", ""), - testAccCheckAWSSagemakerNotebookInstanceTags(¬ebook, "bar", "baz"), + testAccCheckAWSSagemakerNotebookInstanceExists(resourceName, ¬ebook), - resource.TestCheckResourceAttr("aws_sagemaker_notebook_instance.foo", "tags.%", "1"), - resource.TestCheckResourceAttr("aws_sagemaker_notebook_instance.foo", "tags.bar", "baz"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.bar", "baz"), ), }, }, @@ -144,15 +267,16 @@ func TestAccAWSSagemakerNotebookInstance_tags(t *testing.T) { func TestAccAWSSagemakerNotebookInstance_disappears(t *testing.T) { var notebook sagemaker.DescribeNotebookInstanceOutput - notebookName := resource.PrefixedUniqueId(sagemakerTestAccSagemakerNotebookInstanceResourceNamePrefix) - var resourceName = "aws_sagemaker_notebook_instance.foo" + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_sagemaker_notebook_instance.foo" + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, CheckDestroy: testAccCheckAWSSagemakerNotebookInstanceDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSSagemakerNotebookInstanceConfig(notebookName), + Config: testAccAWSSagemakerNotebookInstanceConfig_Basic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSSagemakerNotebookInstanceExists(resourceName, ¬ebook), testAccCheckAWSSagemakerNotebookInstanceDisappears(¬ebook), @@ -176,7 +300,10 @@ func testAccCheckAWSSagemakerNotebookInstanceDestroy(s *terraform.State) error { } notebookInstance, err := conn.DescribeNotebookInstance(describeNotebookInput) if err != nil { - return nil + if isAWSErr(err, "ValidationException", "RecordNotFound") { + return nil + } + return err } if *notebookInstance.NotebookInstanceName == rs.Primary.ID { @@ -191,11 +318,11 @@ func testAccCheckAWSSagemakerNotebookInstanceExists(n string, notebook *sagemake return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] if !ok { - return fmt.Errorf("Not found: %s", n) + return fmt.Errorf("not found: %s", n) } if rs.Primary.ID == "" { - return fmt.Errorf("No sagmaker Notebook Instance ID is set") + return fmt.Errorf("no sagmaker Notebook Instance ID is set") } conn := testAccProvider.Meta().(*AWSClient).sagemakerconn @@ -248,57 +375,10 @@ func testAccCheckAWSSagemakerNotebookInstanceDisappears(instance *sagemaker.Desc } } -func testAccCheckAWSSagemakerNotebookInstanceName(notebook *sagemaker.DescribeNotebookInstanceOutput, expected string) resource.TestCheckFunc { - return func(s *terraform.State) error { - notebookName := notebook.NotebookInstanceName - if *notebookName != expected { - return fmt.Errorf("Bad Notebook Instance name: %s", *notebook.NotebookInstanceName) - } - - return nil - } -} - -func testAccCheckAWSSagemakerNotebookInstanceTags(notebook *sagemaker.DescribeNotebookInstanceOutput, key string, value string) resource.TestCheckFunc { - return func(s *terraform.State) error { - conn := testAccProvider.Meta().(*AWSClient).sagemakerconn - - ts, err := conn.ListTags(&sagemaker.ListTagsInput{ - ResourceArn: notebook.NotebookInstanceArn, - }) - if err != nil { - return fmt.Errorf("Error listing tags: %s", err) - } - - m := tagsToMapSagemaker(ts.Tags) - v, ok := m[key] - if value != "" && !ok { - return fmt.Errorf("Missing tag: %s", key) - } else if value == "" && ok { - return fmt.Errorf("Extra tag: %s", key) - } - if value == "" { - return nil - } - - if v != value { - return fmt.Errorf("%s: bad value: %s", key, v) - } - - return nil - } -} - -func testAccAWSSagemakerNotebookInstanceConfig(notebookName string) string { +func testagemakerNotebookInstanceConfig_Base(rName string) string { return fmt.Sprintf(` -resource "aws_sagemaker_notebook_instance" "foo" { - name = "%s" - role_arn = "${aws_iam_role.foo.arn}" - instance_type = "ml.t2.medium" -} - resource "aws_iam_role" "foo" { - name = "%s" + name = %q path = "/" assume_role_policy = "${data.aws_iam_policy_document.assume_role.json}" } @@ -312,33 +392,39 @@ data "aws_iam_policy_document" "assume_role" { } } } -`, notebookName, notebookName) +`, rName) } -func testAccAWSSagemakerNotebookInstanceUpdateConfig(notebookName string) string { - return fmt.Sprintf(` +func testAccAWSSagemakerNotebookInstanceConfig_Basic(notebookName string) string { + return testagemakerNotebookInstanceConfig_Base(notebookName) + fmt.Sprintf(` resource "aws_sagemaker_notebook_instance" "foo" { - name = "%s" + name = %q role_arn = "${aws_iam_role.foo.arn}" - instance_type = "ml.m4.xlarge" + instance_type = "ml.t2.medium" +} +`, notebookName) } -resource "aws_iam_role" "foo" { +func testAccAWSSagemakerNotebookInstanceConfig_VolumeSize(notebookName string) string { + return testagemakerNotebookInstanceConfig_Base(notebookName) + fmt.Sprintf(` +resource "aws_sagemaker_notebook_instance" "foo" { name = "%s" - path = "/" - assume_role_policy = "${data.aws_iam_policy_document.assume_role.json}" + role_arn = "${aws_iam_role.foo.arn}" + instance_type = "ml.t2.medium" + kms_key_id = "foo" +} +`, notebookName) } -data "aws_iam_policy_document" "assume_role" { - statement { - actions = [ "sts:AssumeRole" ] - principals { - type = "Service" - identifiers = [ "sagemaker.amazonaws.com" ] - } - } +func testAccAWSSagemakerNotebookInstanceConfig_KmsKeyID(notebookName string) string { + return testagemakerNotebookInstanceConfig_Base(notebookName) + fmt.Sprintf(` +resource "aws_sagemaker_notebook_instance" "foo" { + name = "%s" + role_arn = "${aws_iam_role.foo.arn}" + instance_type = "ml.t2.medium" + volume_size = "7" } -`, notebookName, notebookName) +`, notebookName) } func testAccAWSSagemakerNotebookInstanceConfigLifecycleConfigName(rName string) string { @@ -372,60 +458,65 @@ resource "aws_sagemaker_notebook_instance" "test" { `, rName) } -func testAccAWSSagemakerNotebookInstanceTagsConfig(notebookName string) string { - return fmt.Sprintf(` +func testAccAWSSagemakerNotebookInstanceConfig_DirectInternetAccess(notebookName string) string { + return testagemakerNotebookInstanceConfig_Base(notebookName) + fmt.Sprintf(` resource "aws_sagemaker_notebook_instance" "foo" { - name = "%s" + name = %q role_arn = "${aws_iam_role.foo.arn}" instance_type = "ml.t2.medium" - tags = { - foo = "bar" - } + direct_internet_access = "Disabled" +} +`, notebookName) } -resource "aws_iam_role" "foo" { - name = "%s" - path = "/" - assume_role_policy = "${data.aws_iam_policy_document.assume_role.json}" +func testAccAWSSagemakerNotebookInstanceConfig_LifecycleConfigName(notebookName string) string { + return testagemakerNotebookInstanceConfig_Base(notebookName) + fmt.Sprintf(` +resource "aws_sagemaker_notebook_instance" "foo" { + name = %q + role_arn = "${aws_iam_role.foo.arn}" + instance_type = "ml.t2.medium" + lifecycle_config_name = "${aws_sagemaker_lifecycle_config.foo.name}" } -data "aws_iam_policy_document" "assume_role" { - statement { - actions = [ "sts:AssumeRole" ] - principals { - type = "Service" - identifiers = [ "sagemaker.amazonaws.com" ] - } - } +resource "aws_sagemaker_lifecycle_config" "foo" { + name = %q + on_create = "${base64encode("echo foo")}" } `, notebookName, notebookName) } -func testAccAWSSagemakerNotebookInstanceTagsUpdateConfig(notebookName string) string { - return fmt.Sprintf(` +func testAccAWSSagemakerNotebookInstanceUpdateInstanceConfig(notebookName string) string { + return testagemakerNotebookInstanceConfig_Base(notebookName) + fmt.Sprintf(` resource "aws_sagemaker_notebook_instance" "foo" { - name = "%s" + name = %q + role_arn = "${aws_iam_role.foo.arn}" + instance_type = "ml.m4.xlarge" +} +`, notebookName) +} + +func testAccAWSSagemakerNotebookInstanceTagsConfig(notebookName string) string { + return testagemakerNotebookInstanceConfig_Base(notebookName) + fmt.Sprintf(` +resource "aws_sagemaker_notebook_instance" "foo" { + name = %q role_arn = "${aws_iam_role.foo.arn}" instance_type = "ml.t2.medium" tags = { - bar = "baz" + foo = "bar" } } - -resource "aws_iam_role" "foo" { - name = "%s" - path = "/" - assume_role_policy = "${data.aws_iam_policy_document.assume_role.json}" +`, notebookName) } -data "aws_iam_policy_document" "assume_role" { - statement { - actions = [ "sts:AssumeRole" ] - principals { - type = "Service" - identifiers = [ "sagemaker.amazonaws.com" ] - } +func testAccAWSSagemakerNotebookInstanceTagsUpdateConfig(notebookName string) string { + return testagemakerNotebookInstanceConfig_Base(notebookName) + fmt.Sprintf(` +resource "aws_sagemaker_notebook_instance" "foo" { + name = %q + role_arn = "${aws_iam_role.foo.arn}" + instance_type = "ml.t2.medium" + tags = { + bar = "baz" } } -`, notebookName, notebookName) +`, notebookName) } diff --git a/website/docs/r/sagemaker_notebook_instance.html.markdown b/website/docs/r/sagemaker_notebook_instance.html.markdown index 63a03832e96..570e6be8005 100644 --- a/website/docs/r/sagemaker_notebook_instance.html.markdown +++ b/website/docs/r/sagemaker_notebook_instance.html.markdown @@ -1,14 +1,14 @@ --- layout: "aws" -page_title: "AWS: sagemaker_notebook_instance" +page_title: "AWS: aws_sagemaker_notebook_instance" sidebar_current: "docs-aws-resource-sagemaker-notebook-instance" description: |- - Provides a Sagemaker Notebook Instance resource. + Provides a SageMaker Notebook Instance resource. --- # aws_sagemaker_notebook_instance -Provides a Sagemaker Notebook Instance resource. +Provides a SageMaker Notebook Instance resource. ## Example Usage @@ -30,14 +30,19 @@ resource "aws_sagemaker_notebook_instance" "ni" { The following arguments are supported: -* `name` - (Required) The name of the notebook instance (must be unique). -* `role_arn` - (Required) The ARN of the IAM role to be used by the notebook instance which allows SageMaker to call other services on your behalf. * `instance_type` - (Required) The name of ML compute instance type. +* `role_arn` - (Required) The ARN of the IAM role to be used by the notebook instance which allows SageMaker to call other services on your behalf. +* `accelerator_types` - (Optional) +* `additional_code_repositories` - (Optional) +* `default_code_repository` - (Optional) +* `direct_internet_access` - (Optional) +* `name` - (Optional) The name of the notebook instance (must be unique). If omitted, Terraform will assign a random, unique name. +* `kms_key_id` - (Optional) The AWS Key Management Service (AWS KMS) key that Amazon SageMaker uses to encrypt the model artifacts at rest using Amazon S3 server-side encryption. +* `lifecycle_config_name` - (Optional) The name of a lifecycle configuration to associate with the notebook instance. * `subnet_id` - (Optional) The VPC subnet ID. * `security_groups` - (Optional) The associated security groups. -* `kms_key_id` - (Optional) The AWS Key Management Service (AWS KMS) key that Amazon SageMaker uses to encrypt the model artifacts at rest using Amazon S3 server-side encryption. -* `lifecycle_config_name` - (Optional) The name of a lifecycle configuration to associate with the notebook instance. * `tags` - (Optional) A mapping of tags to assign to the resource. +* `volume_size`- (Optional) ## Attributes Reference @@ -48,7 +53,7 @@ The following attributes are exported: ## Import -Sagemaker Notebook Instances can be imported using the `name`, e.g. +SageMaker Notebook Instances can be imported using the `name`, e.g. ``` $ terraform import aws_sagemaker_notebook_instance.test_notebook_instance my-notebook-instance