From 609d6e02fde99191f84d98a48fe820b2c78a0f9e Mon Sep 17 00:00:00 2001 From: Mathias Fredriksson Date: Fri, 2 Jun 2023 13:16:38 +0000 Subject: [PATCH 1/5] Replace `login_before_ready` with `startup_script_behavior` --- docs/resources/agent.md | 3 ++- provider/agent.go | 56 +++++++++++++++++++++++++++++++++++------ provider/agent_test.go | 4 +-- 3 files changed, 52 insertions(+), 11 deletions(-) diff --git a/docs/resources/agent.md b/docs/resources/agent.md index 9ee5c1d0..79937571 100644 --- a/docs/resources/agent.md +++ b/docs/resources/agent.md @@ -50,12 +50,13 @@ resource "kubernetes_pod" "dev" { - `connection_timeout` (Number) Time in seconds until the agent is marked as timed out when a connection with the server cannot be established. A value of zero never marks the agent as timed out. - `dir` (String) The starting directory when a user creates a shell session. Defaults to $HOME. - `env` (Map of String) A mapping of environment variables to set inside the workspace. -- `login_before_ready` (Boolean) This option defines whether or not the user can (by default) login to the workspace before it is ready. Ready means that e.g. the startup_script is done and has exited. When enabled, users may see an incomplete workspace when logging in. +- `login_before_ready` (Boolean, Deprecated) This option defines whether or not the user can (by default) login to the workspace before it is ready. Ready means that e.g. the startup_script is done and has exited. When enabled, users may see an incomplete workspace when logging in. - `metadata` (Block List) Each "metadata" block defines a single item consisting of a key/value pair. This feature is in alpha and may break in future releases. (see [below for nested schema](#nestedblock--metadata)) - `motd_file` (String) The path to a file within the workspace containing a message to display to users when they login via SSH. A typical value would be /etc/motd. - `shutdown_script` (String) A script to run before the agent is stopped. The script should exit when it is done to signal that the workspace can be stopped. - `shutdown_script_timeout` (Number) Time in seconds until the agent lifecycle status is marked as timed out during shutdown, this happens when the shutdown script has not completed (exited) in the given time. - `startup_script` (String) A script to run after the agent starts. The script should exit when it is done to signal that the agent is ready. +- `startup_script_behavior` (String) This option sets the behavior of the startup_script. When set to "blocking", the startup_script must exit before the workspace is ready. When set to "non-blocking", the startup_script may run in the background and the workspace will be ready immediately. Default is "non-blocking", although "blocking" is recommended. - `startup_script_timeout` (Number) Time in seconds until the agent lifecycle status is marked as timed out during start, this happens when the startup script has not completed (exited) in the given time. - `troubleshooting_url` (String) A URL to a document with instructions for troubleshooting problems with the agent. diff --git a/provider/agent.go b/provider/agent.go index c5797081..85baee88 100644 --- a/provider/agent.go +++ b/provider/agent.go @@ -16,23 +16,31 @@ import ( func agentResource() *schema.Resource { return &schema.Resource{ Description: "Use this resource to associate an agent.", - CreateContext: func(c context.Context, resourceData *schema.ResourceData, i interface{}) diag.Diagnostics { + CreateContext: func(ctx context.Context, resourceData *schema.ResourceData, i interface{}) diag.Diagnostics { // This should be a real authentication token! resourceData.SetId(uuid.NewString()) err := resourceData.Set("token", uuid.NewString()) if err != nil { return diag.FromErr(err) } + err = updateStartupScriptBehaviorIfLoginBeforeReady(resourceData) + if err != nil { + return diag.FromErr(err) + } return updateInitScript(resourceData, i) }, - ReadWithoutTimeout: func(c context.Context, resourceData *schema.ResourceData, i interface{}) diag.Diagnostics { + ReadWithoutTimeout: func(ctx context.Context, resourceData *schema.ResourceData, i interface{}) diag.Diagnostics { err := resourceData.Set("token", uuid.NewString()) if err != nil { return diag.FromErr(err) } + err = updateStartupScriptBehaviorIfLoginBeforeReady(resourceData) + if err != nil { + return diag.FromErr(err) + } return updateInitScript(resourceData, i) }, - DeleteContext: func(c context.Context, rd *schema.ResourceData, i interface{}) diag.Diagnostics { + DeleteContext: func(ctx context.Context, resourceData *schema.ResourceData, i interface{}) diag.Diagnostics { return nil }, Schema: map[string]*schema.Schema{ @@ -131,11 +139,27 @@ func agentResource() *schema.Resource { Description: "The path to a file within the workspace containing a message to display to users when they login via SSH. A typical value would be /etc/motd.", }, "login_before_ready": { - Type: schema.TypeBool, - Default: true, // Change default value to false in a future release. - ForceNew: true, - Optional: true, - Description: "This option defines whether or not the user can (by default) login to the workspace before it is ready. Ready means that e.g. the startup_script is done and has exited. When enabled, users may see an incomplete workspace when logging in.", + Type: schema.TypeBool, + Default: true, + ForceNew: true, + Optional: true, + Description: "This option defines whether or not the user can (by default) login to the workspace before it is ready. Ready means that e.g. the startup_script is done and has exited. When enabled, users may see an incomplete workspace when logging in.", + Deprecated: "Configure startup_script_behavior instead. This attribute will be removed in a future version of the provider.", + ConflictsWith: []string{"startup_script_behavior"}, + }, + "startup_script_behavior": { + // Note: Our default value is "non-blocking" but we do not set + // it here because we want to be able to differentiate between + // the user setting this or "login_before_ready". For all + // intents and purposes, until deprecation, setting + // "login_before_ready = false" is equivalent to setting + // "startup_script_behavior = blocking". + Type: schema.TypeString, + ForceNew: true, + Optional: true, + Description: "This option sets the behavior of the startup_script. When set to \"blocking\", the startup_script must exit before the workspace is ready. When set to \"non-blocking\", the startup_script may run in the background and the workspace will be ready immediately. Default is \"non-blocking\", although \"blocking\" is recommended.", + ValidateFunc: validation.StringInSlice([]string{"blocking", "non-blocking"}, false), + ConflictsWith: []string{"login_before_ready"}, }, "metadata": { Type: schema.TypeList, @@ -251,3 +275,19 @@ func updateInitScript(resourceData *schema.ResourceData, i interface{}) diag.Dia } return nil } + +func updateStartupScriptBehaviorIfLoginBeforeReady(resourceData *schema.ResourceData) error { + if rc := resourceData.GetRawConfig(); !rc.IsNull() { + if attr := rc.GetAttr("login_before_ready"); !attr.IsNull() { + behavior := "non-blocking" + if attr.False() { + behavior = "blocking" + } + err := resourceData.Set("startup_script_behavior", behavior) + if err != nil { + return err + } + } + } + return nil +} diff --git a/provider/agent_test.go b/provider/agent_test.go index 2069247b..7e92bcc8 100644 --- a/provider/agent_test.go +++ b/provider/agent_test.go @@ -36,7 +36,7 @@ func TestAgent(t *testing.T) { motd_file = "/etc/motd" shutdown_script = "echo bye bye" shutdown_script_timeout = 120 - login_before_ready = false + startup_script_behavior = "blocking" } `, Check: func(state *terraform.State) error { @@ -58,7 +58,7 @@ func TestAgent(t *testing.T) { "motd_file", "shutdown_script", "shutdown_script_timeout", - "login_before_ready", + "startup_script_behavior", } { value := resource.Primary.Attributes[key] t.Logf("%q = %q", key, value) From 386efce86437881fdccd672fa5a42c8aa3e7d6da Mon Sep 17 00:00:00 2001 From: Mathias Fredriksson Date: Fri, 2 Jun 2023 15:29:47 +0000 Subject: [PATCH 2/5] Add note for future deprecation removal --- provider/agent.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/provider/agent.go b/provider/agent.go index 85baee88..31760906 100644 --- a/provider/agent.go +++ b/provider/agent.go @@ -139,6 +139,8 @@ func agentResource() *schema.Resource { Description: "The path to a file within the workspace containing a message to display to users when they login via SSH. A typical value would be /etc/motd.", }, "login_before_ready": { + // Note: When this is removed, "startup_script_behavior" should + // be set to "non-blocking" by default (instead of empty string). Type: schema.TypeBool, Default: true, ForceNew: true, From b9521bf1e7e23e9d9d5f7af2df6a84d05dd07ec2 Mon Sep 17 00:00:00 2001 From: Mathias Fredriksson Date: Mon, 5 Jun 2023 08:46:07 +0000 Subject: [PATCH 3/5] Remove `updateStartupScriptBehaviorIfLoginBeforeReady` --- provider/agent.go | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/provider/agent.go b/provider/agent.go index 31760906..66fedb84 100644 --- a/provider/agent.go +++ b/provider/agent.go @@ -23,10 +23,6 @@ func agentResource() *schema.Resource { if err != nil { return diag.FromErr(err) } - err = updateStartupScriptBehaviorIfLoginBeforeReady(resourceData) - if err != nil { - return diag.FromErr(err) - } return updateInitScript(resourceData, i) }, ReadWithoutTimeout: func(ctx context.Context, resourceData *schema.ResourceData, i interface{}) diag.Diagnostics { @@ -34,10 +30,6 @@ func agentResource() *schema.Resource { if err != nil { return diag.FromErr(err) } - err = updateStartupScriptBehaviorIfLoginBeforeReady(resourceData) - if err != nil { - return diag.FromErr(err) - } return updateInitScript(resourceData, i) }, DeleteContext: func(ctx context.Context, resourceData *schema.ResourceData, i interface{}) diag.Diagnostics { @@ -277,19 +269,3 @@ func updateInitScript(resourceData *schema.ResourceData, i interface{}) diag.Dia } return nil } - -func updateStartupScriptBehaviorIfLoginBeforeReady(resourceData *schema.ResourceData) error { - if rc := resourceData.GetRawConfig(); !rc.IsNull() { - if attr := rc.GetAttr("login_before_ready"); !attr.IsNull() { - behavior := "non-blocking" - if attr.False() { - behavior = "blocking" - } - err := resourceData.Set("startup_script_behavior", behavior) - if err != nil { - return err - } - } - } - return nil -} From b6f045150250ec9a703821a6d49f04b87f5eea07 Mon Sep 17 00:00:00 2001 From: Mathias Fredriksson Date: Mon, 5 Jun 2023 08:46:24 +0000 Subject: [PATCH 4/5] Fix PR comment --- docs/resources/agent.md | 2 +- provider/agent.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/resources/agent.md b/docs/resources/agent.md index 79937571..f3690194 100644 --- a/docs/resources/agent.md +++ b/docs/resources/agent.md @@ -56,7 +56,7 @@ resource "kubernetes_pod" "dev" { - `shutdown_script` (String) A script to run before the agent is stopped. The script should exit when it is done to signal that the workspace can be stopped. - `shutdown_script_timeout` (Number) Time in seconds until the agent lifecycle status is marked as timed out during shutdown, this happens when the shutdown script has not completed (exited) in the given time. - `startup_script` (String) A script to run after the agent starts. The script should exit when it is done to signal that the agent is ready. -- `startup_script_behavior` (String) This option sets the behavior of the startup_script. When set to "blocking", the startup_script must exit before the workspace is ready. When set to "non-blocking", the startup_script may run in the background and the workspace will be ready immediately. Default is "non-blocking", although "blocking" is recommended. +- `startup_script_behavior` (String) This option sets the behavior of the `startup_script`. When set to "blocking", the startup_script must exit before the workspace is ready. When set to "non-blocking", the startup_script may run in the background and the workspace will be ready immediately. Default is "non-blocking", although "blocking" is recommended. - `startup_script_timeout` (Number) Time in seconds until the agent lifecycle status is marked as timed out during start, this happens when the startup script has not completed (exited) in the given time. - `troubleshooting_url` (String) A URL to a document with instructions for troubleshooting problems with the agent. diff --git a/provider/agent.go b/provider/agent.go index 66fedb84..90dd0525 100644 --- a/provider/agent.go +++ b/provider/agent.go @@ -151,7 +151,7 @@ func agentResource() *schema.Resource { Type: schema.TypeString, ForceNew: true, Optional: true, - Description: "This option sets the behavior of the startup_script. When set to \"blocking\", the startup_script must exit before the workspace is ready. When set to \"non-blocking\", the startup_script may run in the background and the workspace will be ready immediately. Default is \"non-blocking\", although \"blocking\" is recommended.", + Description: "This option sets the behavior of the `startup_script`. When set to \"blocking\", the startup_script must exit before the workspace is ready. When set to \"non-blocking\", the startup_script may run in the background and the workspace will be ready immediately. Default is \"non-blocking\", although \"blocking\" is recommended.", ValidateFunc: validation.StringInSlice([]string{"blocking", "non-blocking"}, false), ConflictsWith: []string{"login_before_ready"}, }, From f763469f1788d5a54ae56701a6cd968354160aff Mon Sep 17 00:00:00 2001 From: Mathias Fredriksson Date: Mon, 5 Jun 2023 08:46:42 +0000 Subject: [PATCH 5/5] Add test cases for startup script behavior --- provider/agent_test.go | 94 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 92 insertions(+), 2 deletions(-) diff --git a/provider/agent_test.go b/provider/agent_test.go index 7e92bcc8..e3d96d1b 100644 --- a/provider/agent_test.go +++ b/provider/agent_test.go @@ -1,6 +1,7 @@ package provider_test import ( + "regexp" "testing" "github.com/coder/terraform-provider-coder/provider" @@ -36,7 +37,6 @@ func TestAgent(t *testing.T) { motd_file = "/etc/motd" shutdown_script = "echo bye bye" shutdown_script_timeout = 120 - startup_script_behavior = "blocking" } `, Check: func(state *terraform.State) error { @@ -58,7 +58,6 @@ func TestAgent(t *testing.T) { "motd_file", "shutdown_script", "shutdown_script_timeout", - "startup_script_behavior", } { value := resource.Primary.Attributes[key] t.Logf("%q = %q", key, value) @@ -71,6 +70,97 @@ func TestAgent(t *testing.T) { }) } +func TestAgent_StartupScriptBehavior(t *testing.T) { + t.Parallel() + + for _, tc := range []struct { + Name string + Config string + ExpectError *regexp.Regexp + Check func(state *terraform.ResourceState) + }{ + { + Name: "blocking", + Config: ` + resource "coder_agent" "new" { + os = "linux" + arch = "amd64" + startup_script_behavior = "blocking" + } + `, + Check: func(state *terraform.ResourceState) { + require.Equal(t, "blocking", state.Primary.Attributes["startup_script_behavior"]) + }, + }, + { + Name: "non-blocking", + Config: ` + resource "coder_agent" "new" { + os = "linux" + arch = "amd64" + startup_script_behavior = "non-blocking" + } + `, + Check: func(state *terraform.ResourceState) { + require.Equal(t, "non-blocking", state.Primary.Attributes["startup_script_behavior"]) + }, + }, + { + Name: "login_before_ready (deprecated)", + Config: ` + resource "coder_agent" "new" { + os = "linux" + arch = "amd64" + login_before_ready = false + } + `, + Check: func(state *terraform.ResourceState) { + require.Equal(t, "false", state.Primary.Attributes["login_before_ready"]) + // startup_script_behavior must be empty, this indicates that + // login_before_ready should be used instead. + require.Equal(t, "", state.Primary.Attributes["startup_script_behavior"]) + }, + }, + { + Name: "no login_before_ready with startup_script_behavior", + Config: ` + resource "coder_agent" "new" { + os = "linux" + arch = "amd64" + login_before_ready = false + startup_script_behavior = "blocking" + } + `, + ExpectError: regexp.MustCompile("conflicts with"), + }, + } { + tc := tc + t.Run(tc.Name, func(t *testing.T) { + t.Parallel() + resource.Test(t, resource.TestCase{ + Providers: map[string]*schema.Provider{ + "coder": provider.New(), + }, + IsUnitTest: true, + Steps: []resource.TestStep{{ + Config: tc.Config, + ExpectError: tc.ExpectError, + Check: func(state *terraform.State) error { + require.Len(t, state.Modules, 1) + require.Len(t, state.Modules[0].Resources, 1) + resource := state.Modules[0].Resources["coder_agent.new"] + require.NotNil(t, resource) + if tc.Check != nil { + tc.Check(resource) + } + return nil + }, + }}, + }) + }) + } +} + func TestAgent_Instance(t *testing.T) { t.Parallel() resource.Test(t, resource.TestCase{ pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy