From 908ab275adb67a340406c3981205e455ef8c62f7 Mon Sep 17 00:00:00 2001 From: Mathias Fredriksson Date: Fri, 9 Feb 2024 19:37:34 +0200 Subject: [PATCH 1/4] fix: improve coder_script related docs (#191) --- docs/resources/agent.md | 6 +++--- docs/resources/script.md | 10 +++++----- provider/agent.go | 8 ++++---- provider/script.go | 14 +++++++------- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/docs/resources/agent.md b/docs/resources/agent.md index 9b07b2e7..23966105 100644 --- a/docs/resources/agent.md +++ b/docs/resources/agent.md @@ -79,10 +79,10 @@ resource "kubernetes_pod" "dev" { - `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` (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. This option is an alias for defining a "coder_script" resource with "run_on_stop" set to true. - `shutdown_script_timeout` (Number, Deprecated) 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` (String) A script to run after the agent starts. The script should exit when it is done to signal that the agent is ready. This option is an alias for defining a "coder_script" resource with "run_on_start" set to true. +- `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. This option is an alias for defining a "coder_script" resource with "start_blocks_login" set to true (blocking). - `startup_script_timeout` (Number, Deprecated) 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/docs/resources/script.md b/docs/resources/script.md index 51213aac..a16b39f5 100644 --- a/docs/resources/script.md +++ b/docs/resources/script.md @@ -19,17 +19,17 @@ Use this resource to run a script from an agent. - `agent_id` (String) The "id" property of a "coder_agent" resource to associate with. - `display_name` (String) The display name of the script to display logs in the dashboard. -- `script` (String) The script to run. +- `script` (String) The content of the script that will be run. ### Optional - `cron` (String) The cron schedule to run the script on. This is a cron expression. - `icon` (String) A URL to an icon that will display in the dashboard. View built-in icons here: https://github.com/coder/coder/tree/main/site/static/icon. Use a built-in icon with `data.coder_workspace.me.access_url + "/icon/"`. - `log_path` (String) The path of a file to write the logs to. If relative, it will be appended to tmp. -- `run_on_start` (Boolean) This option defines whether or not the script should run when the agent starts. -- `run_on_stop` (Boolean) This option defines whether or not the script should run when the agent stops. -- `start_blocks_login` (Boolean) This option defines whether or not the user can (by default) login to the workspace before this script completes running on start. When enabled, users may see an incomplete workspace when logging in. -- `timeout` (Number) Time in seconds until the agent lifecycle status is marked as timed out, this happens when the script has not completed (exited) in the given time. +- `run_on_start` (Boolean) This option defines whether or not the script should run when the agent starts. The script should exit when it is done to signal that the agent is ready. +- `run_on_stop` (Boolean) This option defines whether or not the script should run when the agent stops. The script should exit when it is done to signal that the workspace can be stopped. +- `start_blocks_login` (Boolean) This option determines whether users can log in immediately or must wait for the workspace to finish running this script upon startup. If not enabled, users may encounter an incomplete workspace when logging in. This option only sets the default, the user can still manually override the behavior. +- `timeout` (Number) Time in seconds that the script is allowed to run. If the script does not complete within this time, the script is terminated and the agent lifecycle status is marked as timed out. A value of zero (default) means no timeout. ### Read-Only diff --git a/provider/agent.go b/provider/agent.go index a24f4c1b..f02462a8 100644 --- a/provider/agent.go +++ b/provider/agent.go @@ -17,7 +17,7 @@ import ( func agentResource() *schema.Resource { return &schema.Resource{ Description: "Use this resource to associate an agent.", - CreateContext: func(ctx context.Context, resourceData *schema.ResourceData, i interface{}) diag.Diagnostics { + CreateContext: func(_ 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()) @@ -121,7 +121,7 @@ func agentResource() *schema.Resource { }, "startup_script": { ForceNew: true, - Description: "A script to run after the agent starts. The script should exit when it is done to signal that the agent is ready.", + Description: `A script to run after the agent starts. The script should exit when it is done to signal that the agent is ready. This option is an alias for defining a "coder_script" resource with "run_on_start" set to true.`, Type: schema.TypeString, Optional: true, }, @@ -138,7 +138,7 @@ func agentResource() *schema.Resource { Type: schema.TypeString, ForceNew: true, Optional: true, - Description: "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.", + Description: `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. This option is an alias for defining a "coder_script" resource with "run_on_stop" set to true.`, }, "shutdown_script_timeout": { Type: schema.TypeInt, @@ -197,7 +197,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. This option is an alias for defining a "coder_script" resource with "start_blocks_login" set to true (blocking).`, ValidateFunc: validation.StringInSlice([]string{"blocking", "non-blocking"}, false), ConflictsWith: []string{"login_before_ready"}, }, diff --git a/provider/script.go b/provider/script.go index 2db4829f..8b548a22 100644 --- a/provider/script.go +++ b/provider/script.go @@ -16,7 +16,7 @@ var ScriptCRONParser = cron.NewParser(cron.Second | cron.Minute | cron.Hour | cr func scriptResource() *schema.Resource { return &schema.Resource{ Description: "Use this resource to run a script from an agent.", - CreateContext: func(ctx context.Context, rd *schema.ResourceData, i interface{}) diag.Diagnostics { + CreateContext: func(_ context.Context, rd *schema.ResourceData, _ interface{}) diag.Diagnostics { rd.SetId(uuid.NewString()) runOnStart, _ := rd.Get("run_on_start").(bool) runOnStop, _ := rd.Get("run_on_stop").(bool) @@ -60,14 +60,14 @@ func scriptResource() *schema.Resource { ForceNew: true, Type: schema.TypeString, Required: true, - Description: "The script to run.", + Description: "The content of the script that will be run.", }, "cron": { ForceNew: true, Type: schema.TypeString, Optional: true, Description: "The cron schedule to run the script on. This is a cron expression.", - ValidateFunc: func(i interface{}, s string) ([]string, []error) { + ValidateFunc: func(i interface{}, _ string) ([]string, []error) { v, ok := i.(string) if !ok { return []string{}, []error{fmt.Errorf("got type %T instead of string", i)} @@ -84,28 +84,28 @@ func scriptResource() *schema.Resource { Default: false, ForceNew: true, Optional: true, - Description: "This option defines whether or not the user can (by default) login to the workspace before this script completes running on start. When enabled, users may see an incomplete workspace when logging in.", + Description: "This option determines whether users can log in immediately or must wait for the workspace to finish running this script upon startup. If not enabled, users may encounter an incomplete workspace when logging in. This option only sets the default, the user can still manually override the behavior.", }, "run_on_start": { Type: schema.TypeBool, Default: false, ForceNew: true, Optional: true, - Description: "This option defines whether or not the script should run when the agent starts.", + Description: "This option defines whether or not the script should run when the agent starts. The script should exit when it is done to signal that the agent is ready.", }, "run_on_stop": { Type: schema.TypeBool, Default: false, ForceNew: true, Optional: true, - Description: "This option defines whether or not the script should run when the agent stops.", + Description: "This option defines whether or not the script should run when the agent stops. The script should exit when it is done to signal that the workspace can be stopped.", }, "timeout": { Type: schema.TypeInt, Default: 0, ForceNew: true, Optional: true, - Description: "Time in seconds until the agent lifecycle status is marked as timed out, this happens when the script has not completed (exited) in the given time.", + Description: "Time in seconds that the script is allowed to run. If the script does not complete within this time, the script is terminated and the agent lifecycle status is marked as timed out. A value of zero (default) means no timeout.", ValidateFunc: validation.IntAtLeast(1), }, }, From 1797a0306aef19c75ef14f1bafdef2cf123df207 Mon Sep 17 00:00:00 2001 From: Mathias Fredriksson Date: Fri, 9 Feb 2024 19:43:03 +0200 Subject: [PATCH 2/4] fix!: add extra validation for start_blocks_login (#192) --- provider/script.go | 4 +++ provider/script_test.go | 61 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/provider/script.go b/provider/script.go index 8b548a22..4a05440e 100644 --- a/provider/script.go +++ b/provider/script.go @@ -19,12 +19,16 @@ func scriptResource() *schema.Resource { CreateContext: func(_ context.Context, rd *schema.ResourceData, _ interface{}) diag.Diagnostics { rd.SetId(uuid.NewString()) runOnStart, _ := rd.Get("run_on_start").(bool) + startBlocksLogin, _ := rd.Get("start_blocks_login").(bool) runOnStop, _ := rd.Get("run_on_stop").(bool) cron, _ := rd.Get("cron").(string) if !runOnStart && !runOnStop && cron == "" { return diag.Errorf("at least one of run_on_start, run_on_stop, or cron must be set") } + if !runOnStart && startBlocksLogin { + return diag.Errorf("start_blocks_login can only be set if run_on_start is true") + } return nil }, ReadContext: schema.NoopContext, diff --git a/provider/script_test.go b/provider/script_test.go index 4ecbacb6..937c6008 100644 --- a/provider/script_test.go +++ b/provider/script_test.go @@ -73,3 +73,64 @@ func TestScriptNeverRuns(t *testing.T) { }}, }) } + +func TestScriptStartBlocksLoginRequiresRunOnStart(t *testing.T) { + t.Parallel() + + resource.Test(t, resource.TestCase{ + Providers: map[string]*schema.Provider{ + "coder": provider.New(), + }, + IsUnitTest: true, + Steps: []resource.TestStep{{ + Config: ` + provider "coder" { + } + resource "coder_script" "example" { + agent_id = "" + display_name = "Hey" + script = "Wow" + run_on_stop = true + start_blocks_login = true + } + `, + ExpectError: regexp.MustCompile(`start_blocks_login can only be set if run_on_start is true`), + }}, + }) + resource.Test(t, resource.TestCase{ + Providers: map[string]*schema.Provider{ + "coder": provider.New(), + }, + IsUnitTest: true, + Steps: []resource.TestStep{{ + Config: ` + provider "coder" { + } + resource "coder_script" "example" { + agent_id = "" + display_name = "Hey" + script = "Wow" + start_blocks_login = true + run_on_start = true + } + `, + Check: func(state *terraform.State) error { + require.Len(t, state.Modules, 1) + require.Len(t, state.Modules[0].Resources, 1) + script := state.Modules[0].Resources["coder_script.example"] + require.NotNil(t, script) + t.Logf("script attributes: %#v", script.Primary.Attributes) + for key, expected := range map[string]string{ + "agent_id": "", + "display_name": "Hey", + "script": "Wow", + "start_blocks_login": "true", + "run_on_start": "true", + } { + require.Equal(t, expected, script.Primary.Attributes[key]) + } + return nil + }, + }}, + }) +} From 99ba884053346eb75a5f5e662ee774b126c5dda9 Mon Sep 17 00:00:00 2001 From: Kayla Washburn-Love Date: Mon, 12 Feb 2024 15:46:35 -0700 Subject: [PATCH 3/4] feat: add `optional` property to `coder_external_auth` (#185) --- docs/data-sources/external_auth.md | 4 ++++ provider/externalauth.go | 7 +++++- provider/externalauth_test.go | 34 ++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/docs/data-sources/external_auth.md b/docs/data-sources/external_auth.md index b875b874..af4df43b 100644 --- a/docs/data-sources/external_auth.md +++ b/docs/data-sources/external_auth.md @@ -19,6 +19,10 @@ Use this data source to require users to authenticate with an external service p - `id` (String) The ID of a configured external auth provider set up in your Coder deployment. +### Optional + +- `optional` (Boolean) Authenticating with the external auth provider is not required, and can be skipped by users when creating or updating workspaces + ### Read-Only - `access_token` (String) The access token returned by the external auth provider. This can be used to pre-authenticate command-line tools. diff --git a/provider/externalauth.go b/provider/externalauth.go index 89ab5ecc..31dadd66 100644 --- a/provider/externalauth.go +++ b/provider/externalauth.go @@ -32,8 +32,13 @@ func externalAuthDataSource() *schema.Resource { }, "access_token": { Type: schema.TypeString, - Computed: true, Description: "The access token returned by the external auth provider. This can be used to pre-authenticate command-line tools.", + Computed: true, + }, + "optional": { + Type: schema.TypeBool, + Description: "Authenticating with the external auth provider is not required, and can be skipped by users when creating or updating workspaces", + Optional: true, }, }, } diff --git a/provider/externalauth_test.go b/provider/externalauth_test.go index a320684b..826f0a91 100644 --- a/provider/externalauth_test.go +++ b/provider/externalauth_test.go @@ -36,6 +36,40 @@ func TestExternalAuth(t *testing.T) { attribs := resource.Primary.Attributes require.Equal(t, "github", attribs["id"]) + require.Equal(t, "", attribs["optional"]) + + return nil + }, + }}, + }) +} + +func TestOptionalExternalAuth(t *testing.T) { + t.Parallel() + + resource.Test(t, resource.TestCase{ + Providers: map[string]*schema.Provider{ + "coder": provider.New(), + }, + IsUnitTest: true, + Steps: []resource.TestStep{{ + Config: ` + provider "coder" { + } + data "coder_external_auth" "github" { + id = "github" + optional = true + } + `, + 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["data.coder_external_auth.github"] + require.NotNil(t, resource) + + attribs := resource.Primary.Attributes + require.Equal(t, "github", attribs["id"]) + require.Equal(t, "true", attribs["optional"]) return nil }, From 7815596401d6e69aebb4ceefe1e84369cb63c4ac Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Tue, 13 Feb 2024 11:43:12 +0100 Subject: [PATCH 4/4] feat: Add `order` to `coder_agent` (#193) --- docs/resources/agent.md | 1 + provider/agent.go | 6 ++++++ provider/agent_test.go | 2 ++ 3 files changed, 9 insertions(+) diff --git a/docs/resources/agent.md b/docs/resources/agent.md index 23966105..68963947 100644 --- a/docs/resources/agent.md +++ b/docs/resources/agent.md @@ -79,6 +79,7 @@ resource "kubernetes_pod" "dev" { - `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. +- `order` (Number) The order determines the position of agents in the UI presentation. The lowest order is shown first and agents with equal order are sorted by name (ascending order). - `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. This option is an alias for defining a "coder_script" resource with "run_on_stop" set to true. - `shutdown_script_timeout` (Number, Deprecated) 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. This option is an alias for defining a "coder_script" resource with "run_on_start" set to true. diff --git a/provider/agent.go b/provider/agent.go index f02462a8..7b197870 100644 --- a/provider/agent.go +++ b/provider/agent.go @@ -297,6 +297,12 @@ func agentResource() *schema.Resource { }, }, }, + "order": { + Type: schema.TypeInt, + Description: "The order determines the position of agents in the UI presentation. The lowest order is shown first and agents with equal order are sorted by name (ascending order).", + ForceNew: true, + Optional: true, + }, }, } } diff --git a/provider/agent_test.go b/provider/agent_test.go index f70a7f90..491e59f9 100644 --- a/provider/agent_test.go +++ b/provider/agent_test.go @@ -39,6 +39,7 @@ func TestAgent(t *testing.T) { motd_file = "/etc/motd" shutdown_script = "echo bye bye" shutdown_script_timeout = 120 + order = 4 } `, Check: func(state *terraform.State) error { @@ -60,6 +61,7 @@ func TestAgent(t *testing.T) { "motd_file", "shutdown_script", "shutdown_script_timeout", + "order", } { value := resource.Primary.Attributes[key] t.Logf("%q = %q", key, value)