Skip to content

Commit

Permalink
Merge pull request #37059 from DrFaust92/app-image-config
Browse files Browse the repository at this point in the history
r/sagemaker_app_image_config - add `code_editor_app_image_config`
  • Loading branch information
ewbankkit authored Apr 24, 2024
2 parents 1180ab8 + 4b59f3d commit 4669a81
Show file tree
Hide file tree
Showing 4 changed files with 241 additions and 6 deletions.
7 changes: 7 additions & 0 deletions .changelog/37059.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
```release-note:enhancement
resource/aws_sagemaker_app_image_config: Add `code_editor_app_image_config` and `jupyter_lab_image_config.jupyter_lab_image_config` arguments
```

```release-note:enhancement
resource/aws_sagemaker_app_image_config: Change `kernel_gateway_image_config.kernel_spec` MaxItems to 5
```
163 changes: 158 additions & 5 deletions internal/service/sagemaker/app_image_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,69 @@ func ResourceAppImageConfig() *schema.Resource {
validation.StringMatch(regexache.MustCompile(`^[0-9A-Za-z](-*[0-9A-Za-z])*$`), "Valid characters are a-z, A-Z, 0-9, and - (hyphen)."),
),
},
"code_editor_app_image_config": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"container_config": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"container_arguments": {
Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"container_entrypoint": {
Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"container_environment_variables": {
Type: schema.TypeMap,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
},
},
},
"file_system_config": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"default_gid": {
Type: schema.TypeInt,
Optional: true,
Default: 100,
ValidateFunc: validation.IntInSlice([]int{0, 100}),
},
"default_uid": {
Type: schema.TypeInt,
Optional: true,
Default: 1000,
ValidateFunc: validation.IntInSlice([]int{0, 1000}),
},
"mount_path": {
Type: schema.TypeString,
Optional: true,
Default: "/home/sagemaker-user",
ValidateFunc: validation.All(
validation.StringLenBetween(1, 1024),
validation.StringMatch(regexache.MustCompile(`^\/.*`), "Must start with `/`."),
),
},
},
},
},
},
},
},
"jupyter_lab_image_config": {
Type: schema.TypeList,
Optional: true,
Expand Down Expand Up @@ -78,6 +141,36 @@ func ResourceAppImageConfig() *schema.Resource {
},
},
},
"file_system_config": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"default_gid": {
Type: schema.TypeInt,
Optional: true,
Default: 100,
ValidateFunc: validation.IntInSlice([]int{0, 100}),
},
"default_uid": {
Type: schema.TypeInt,
Optional: true,
Default: 1000,
ValidateFunc: validation.IntInSlice([]int{0, 1000}),
},
"mount_path": {
Type: schema.TypeString,
Optional: true,
Default: "/home/sagemaker-user",
ValidateFunc: validation.All(
validation.StringLenBetween(1, 1024),
validation.StringMatch(regexache.MustCompile(`^\/.*`), "Must start with `/`."),
),
},
},
},
},
},
},
},
Expand Down Expand Up @@ -120,7 +213,7 @@ func ResourceAppImageConfig() *schema.Resource {
"kernel_spec": {
Type: schema.TypeList,
Required: true,
MaxItems: 1,
MaxItems: 5,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"display_name": {
Expand Down Expand Up @@ -156,6 +249,10 @@ func resourceAppImageConfigCreate(ctx context.Context, d *schema.ResourceData, m
Tags: getTagsIn(ctx),
}

if v, ok := d.GetOk("code_editor_app_image_config"); ok && len(v.([]interface{})) > 0 {
input.CodeEditorAppImageConfig = expandCodeEditorAppImageConfig(v.([]interface{}))
}

if v, ok := d.GetOk("jupyter_lab_image_config"); ok && len(v.([]interface{})) > 0 {
input.JupyterLabAppImageConfig = expandJupyterLabAppImageConfig(v.([]interface{}))
}
Expand Down Expand Up @@ -192,6 +289,10 @@ func resourceAppImageConfigRead(ctx context.Context, d *schema.ResourceData, met
d.Set("app_image_config_name", image.AppImageConfigName)
d.Set("arn", arn)

if err := d.Set("code_editor_app_image_config", flattenCodeEditorAppImageConfig(image.CodeEditorAppImageConfig)); err != nil {
return sdkdiag.AppendErrorf(diags, "setting code_editor_app_image_config: %s", err)
}

if err := d.Set("kernel_gateway_image_config", flattenKernelGatewayImageConfig(image.KernelGatewayImageConfig)); err != nil {
return sdkdiag.AppendErrorf(diags, "setting kernel_gateway_image_config: %s", err)
}
Expand All @@ -212,6 +313,12 @@ func resourceAppImageConfigUpdate(ctx context.Context, d *schema.ResourceData, m
AppImageConfigName: aws.String(d.Id()),
}

if d.HasChange("code_editor_app_image_config") {
if v, ok := d.GetOk("code_editor_app_image_config"); ok && len(v.([]interface{})) > 0 {
input.CodeEditorAppImageConfig = expandCodeEditorAppImageConfig(v.([]interface{}))
}
}

if d.HasChange("kernel_gateway_image_config") {
if v, ok := d.GetOk("kernel_gateway_image_config"); ok && len(v.([]interface{})) > 0 {
input.KernelGatewayImageConfig = expandKernelGatewayImageConfig(v.([]interface{}))
Expand Down Expand Up @@ -266,13 +373,13 @@ func expandKernelGatewayImageConfig(l []interface{}) *sagemaker.KernelGatewayIma
}

if v, ok := m["file_system_config"].([]interface{}); ok && len(v) > 0 {
config.FileSystemConfig = expandKernelGatewayImageConfigFileSystemConfig(v)
config.FileSystemConfig = expandFileSystemConfig(v)
}

return config
}

func expandKernelGatewayImageConfigFileSystemConfig(l []interface{}) *sagemaker.FileSystemConfig {
func expandFileSystemConfig(l []interface{}) *sagemaker.FileSystemConfig {
if len(l) == 0 || l[0] == nil {
return nil
}
Expand Down Expand Up @@ -328,13 +435,13 @@ func flattenKernelGatewayImageConfig(config *sagemaker.KernelGatewayImageConfig)
}

if config.FileSystemConfig != nil {
m["file_system_config"] = flattenKernelGatewayImageConfigFileSystemConfig(config.FileSystemConfig)
m["file_system_config"] = flattenFileSystemConfig(config.FileSystemConfig)
}

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

func flattenKernelGatewayImageConfigFileSystemConfig(config *sagemaker.FileSystemConfig) []map[string]interface{} {
func flattenFileSystemConfig(config *sagemaker.FileSystemConfig) []map[string]interface{} {
if config == nil {
return []map[string]interface{}{}
}
Expand Down Expand Up @@ -366,6 +473,44 @@ func flattenKernelGatewayImageConfigKernelSpecs(kernelSpecs []*sagemaker.KernelS
return res
}

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

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

config := &sagemaker.CodeEditorAppImageConfig{}

if v, ok := m["container_config"].([]interface{}); ok && len(v) > 0 {
config.ContainerConfig = expandContainerConfig(v)
}

if v, ok := m["file_system_config"].([]interface{}); ok && len(v) > 0 {
config.FileSystemConfig = expandFileSystemConfig(v)
}

return config
}

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

m := map[string]interface{}{}

if config.ContainerConfig != nil {
m["container_config"] = flattenContainerConfig(config.ContainerConfig)
}

if config.FileSystemConfig != nil {
m["file_system_config"] = flattenFileSystemConfig(config.FileSystemConfig)
}

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

func expandJupyterLabAppImageConfig(l []interface{}) *sagemaker.JupyterLabAppImageConfig {
if len(l) == 0 || l[0] == nil {
return nil
Expand All @@ -379,6 +524,10 @@ func expandJupyterLabAppImageConfig(l []interface{}) *sagemaker.JupyterLabAppIma
config.ContainerConfig = expandContainerConfig(v)
}

if v, ok := m["file_system_config"].([]interface{}); ok && len(v) > 0 {
config.FileSystemConfig = expandFileSystemConfig(v)
}

return config
}

Expand All @@ -393,6 +542,10 @@ func flattenJupyterLabAppImageConfig(config *sagemaker.JupyterLabAppImageConfig)
m["container_config"] = flattenContainerConfig(config.ContainerConfig)
}

if config.FileSystemConfig != nil {
m["file_system_config"] = flattenFileSystemConfig(config.FileSystemConfig)
}

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

Expand Down
68 changes: 68 additions & 0 deletions internal/service/sagemaker/app_image_config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,56 @@ func TestAccSageMakerAppImageConfig_KernelGatewayImage_fileSystem(t *testing.T)
})
}

func TestAccSageMakerAppImageConfig_CodeEditor(t *testing.T) {
ctx := acctest.Context(t)
var config sagemaker.DescribeAppImageConfigOutput
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
rNameUpdated := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
resourceName := "aws_sagemaker_app_image_config.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(ctx, t) },
ErrorCheck: acctest.ErrorCheck(t, names.SageMakerServiceID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckAppImageDestroyConfig(ctx),
Steps: []resource.TestStep{
{
Config: testAccAppImageConfigConfig_codeEditor(rName, rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckAppImageExistsConfig(ctx, resourceName, &config),
resource.TestCheckResourceAttr(resourceName, "app_image_config_name", rName),
resource.TestCheckResourceAttr(resourceName, "code_editor_app_image_config.#", "1"),
resource.TestCheckResourceAttr(resourceName, "code_editor_app_image_config.0.container_config.#", "1"),
resource.TestCheckResourceAttr(resourceName, "code_editor_app_image_config.0.container_config.0.container_arguments.#", "1"),
resource.TestCheckResourceAttr(resourceName, "code_editor_app_image_config.0.container_config.0.container_arguments.0", rName),
resource.TestCheckResourceAttr(resourceName, "code_editor_app_image_config.0.container_config.0.container_entrypoint.#", "1"),
resource.TestCheckResourceAttr(resourceName, "code_editor_app_image_config.0.container_config.0.container_entrypoint.0", rName),
resource.TestCheckResourceAttr(resourceName, "code_editor_app_image_config.0.container_config.0.container_environment_variables.%", "1"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
{
Config: testAccAppImageConfigConfig_codeEditor(rName, rNameUpdated),
Check: resource.ComposeTestCheckFunc(
testAccCheckAppImageExistsConfig(ctx, resourceName, &config),
resource.TestCheckResourceAttr(resourceName, "app_image_config_name", rName),
resource.TestCheckResourceAttr(resourceName, "code_editor_app_image_config.#", "1"),
resource.TestCheckResourceAttr(resourceName, "code_editor_app_image_config.0.container_config.#", "1"),
resource.TestCheckResourceAttr(resourceName, "code_editor_app_image_config.0.container_config.0.container_arguments.#", "1"),
resource.TestCheckResourceAttr(resourceName, "code_editor_app_image_config.0.container_config.0.container_arguments.0", rNameUpdated),
resource.TestCheckResourceAttr(resourceName, "code_editor_app_image_config.0.container_config.0.container_entrypoint.#", "1"),
resource.TestCheckResourceAttr(resourceName, "code_editor_app_image_config.0.container_config.0.container_entrypoint.0", rNameUpdated),
resource.TestCheckResourceAttr(resourceName, "code_editor_app_image_config.0.container_config.0.container_environment_variables.%", "1"),
),
},
},
})
}

func TestAccSageMakerAppImageConfig_JupyterLab(t *testing.T) {
ctx := acctest.Context(t)
var config sagemaker.DescribeAppImageConfigOutput
Expand Down Expand Up @@ -429,3 +479,21 @@ resource "aws_sagemaker_app_image_config" "test" {
}
`, rName, arg)
}

func testAccAppImageConfigConfig_codeEditor(rName, arg string) string {
return fmt.Sprintf(`
resource "aws_sagemaker_app_image_config" "test" {
app_image_config_name = %[1]q
code_editor_app_image_config {
container_config {
container_arguments = ["%[2]s"]
container_entrypoint = ["%[2]s"]
container_environment_variables = {
%[2]q = %[2]q
}
}
}
}
`, rName, arg)
}
9 changes: 8 additions & 1 deletion website/docs/r/sagemaker_app_image_config.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,20 @@ resource "aws_sagemaker_app_image_config" "test" {
This resource supports the following arguments:

* `app_image_config_name` - (Required) The name of the App Image Config.
* `kernel_gateway_image_config` - (Optional) The JupyterLabAppImageConfig. You can only specify one image kernel in the AppImageConfig API. This kernel is shown to users before the image starts. After the image runs, all kernels are visible in JupyterLab. See [Jupyte rLab Image Config](#jupyter-lab-image-config) details below.
* `code_editor_app_image_config` - (Optional) The CodeEditorAppImageConfig. You can only specify one image kernel in the AppImageConfig API. This kernel is shown to users before the image starts. After the image runs, all kernels are visible in Code Editor. See [Code Editor App Image Config](#code-editor-app-image-config) details below.
* `jupyter_lab_image_config` - (Optional) The JupyterLabAppImageConfig. You can only specify one image kernel in the AppImageConfig API. This kernel is shown to users before the image starts. After the image runs, all kernels are visible in JupyterLab. See [Jupyter Lab Image Config](#jupyter-lab-image-config) details below.
* `kernel_gateway_image_config` - (Optional) The configuration for the file system and kernels in a SageMaker image running as a KernelGateway app. See [Kernel Gateway Image Config](#kernel-gateway-image-config) details below.
* `tags` - (Optional) A map of tags to assign to the resource. If configured with a provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level.

### Code Editor App Image Config

* `container_config` - (Optional) The configuration used to run the application image container. See [Container Config](#container-config) details below.
* `file_system_config` - (Optional) The URL where the Git repository is located. See [File System Config](#file-system-config) details below.

### Jupyter Lab Image Config

* `container_config` - (Optional) The configuration used to run the application image container. See [Container Config](#container-config) details below.
* `file_system_config` - (Optional) The URL where the Git repository is located. See [File System Config](#file-system-config) details below.

#### Container Config

Expand Down

0 comments on commit 4669a81

Please sign in to comment.