Skip to content

Commit 1f95807

Browse files
authored
feat: add resources_monitoring field to agent (#331)
* add resources_monitoring logic * add resources_monitoring logic * improve testing logic * improve testing logic * gen doc * improved testing and validation * improved testing and validation * improved testing and validation * regenerate documentation with validation * add more test cases * add validation methods * improve test verbose
1 parent c9b396f commit 1f95807

File tree

5 files changed

+479
-15
lines changed

5 files changed

+479
-15
lines changed

Diff for: docs/resources/agent.md

+28
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ resource "kubernetes_pod" "dev" {
7979
- `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))
8080
- `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"`.
8181
- `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).
82+
- `resources_monitoring` (Block Set, Max: 1) The resources monitoring configuration for this agent. (see [below for nested schema](#nestedblock--resources_monitoring))
8283
- `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`.
8384
- `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`.
8485
- `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).
@@ -116,3 +117,30 @@ Optional:
116117
- `display_name` (String) The user-facing name of this value.
117118
- `order` (Number) The order determines the position of agent metadata in the UI presentation. The lowest order is shown first and metadata with equal order are sorted by key (ascending order).
118119
- `timeout` (Number) The maximum time the command is allowed to run in seconds.
120+
121+
122+
<a id="nestedblock--resources_monitoring"></a>
123+
### Nested Schema for `resources_monitoring`
124+
125+
Optional:
126+
127+
- `memory` (Block Set, Max: 1) The memory monitoring configuration for this agent. (see [below for nested schema](#nestedblock--resources_monitoring--memory))
128+
- `volume` (Block Set) The volumes monitoring configuration for this agent. (see [below for nested schema](#nestedblock--resources_monitoring--volume))
129+
130+
<a id="nestedblock--resources_monitoring--memory"></a>
131+
### Nested Schema for `resources_monitoring.memory`
132+
133+
Required:
134+
135+
- `enabled` (Boolean) Enable memory monitoring for this agent.
136+
- `threshold` (Number) The memory usage threshold in percentage at which to trigger an alert. Value should be between 0 and 100.
137+
138+
139+
<a id="nestedblock--resources_monitoring--volume"></a>
140+
### Nested Schema for `resources_monitoring.volume`
141+
142+
Required:
143+
144+
- `enabled` (Boolean) Enable volume monitoring for this agent.
145+
- `path` (String) The path of the volume to monitor.
146+
- `threshold` (Number) The volume usage threshold in percentage at which to trigger an alert. Value should be between 0 and 100.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
provider "coder" {}
2+
3+
data "coder_provisioner" "dev" {}
4+
5+
data "coder_workspace" "dev" {}
6+
7+
resource "coder_agent" "main" {
8+
arch = data.coder_provisioner.dev.arch
9+
os = data.coder_provisioner.dev.os
10+
resources_monitoring {
11+
memory {
12+
enabled = true
13+
threshold = 80
14+
}
15+
volume {
16+
path = "/volume1"
17+
enabled = true
18+
threshold = 80
19+
}
20+
volume {
21+
path = "/volume2"
22+
enabled = true
23+
threshold = 100
24+
}
25+
}
26+
}

Diff for: provider/agent.go

+128-15
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ package provider
33
import (
44
"context"
55
"fmt"
6+
"path/filepath"
67
"reflect"
78
"strings"
89

910
"github.com/google/uuid"
11+
"github.com/hashicorp/go-cty/cty"
1012
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
1113
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
1214
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
@@ -259,31 +261,142 @@ func agentResource() *schema.Resource {
259261
ForceNew: true,
260262
Optional: true,
261263
},
264+
"resources_monitoring": {
265+
Type: schema.TypeSet,
266+
Description: "The resources monitoring configuration for this agent.",
267+
ForceNew: true,
268+
Optional: true,
269+
MaxItems: 1,
270+
Elem: &schema.Resource{
271+
Schema: map[string]*schema.Schema{
272+
"memory": {
273+
Type: schema.TypeSet,
274+
Description: "The memory monitoring configuration for this agent.",
275+
ForceNew: true,
276+
Optional: true,
277+
MaxItems: 1,
278+
Elem: &schema.Resource{
279+
Schema: map[string]*schema.Schema{
280+
"enabled": {
281+
Type: schema.TypeBool,
282+
Description: "Enable memory monitoring for this agent.",
283+
ForceNew: true,
284+
Required: true,
285+
},
286+
"threshold": {
287+
Type: schema.TypeInt,
288+
Description: "The memory usage threshold in percentage at which to trigger an alert. Value should be between 0 and 100.",
289+
ForceNew: true,
290+
Required: true,
291+
ValidateFunc: validation.IntBetween(0, 100),
292+
},
293+
},
294+
},
295+
},
296+
"volume": {
297+
Type: schema.TypeSet,
298+
Description: "The volumes monitoring configuration for this agent.",
299+
ForceNew: true,
300+
Optional: true,
301+
Elem: &schema.Resource{
302+
Schema: map[string]*schema.Schema{
303+
"path": {
304+
Type: schema.TypeString,
305+
Description: "The path of the volume to monitor.",
306+
ForceNew: true,
307+
Required: true,
308+
ValidateDiagFunc: func(i interface{}, s cty.Path) diag.Diagnostics {
309+
path, ok := i.(string)
310+
if !ok {
311+
return diag.Errorf("volume path must be a string")
312+
}
313+
if path == "" {
314+
return diag.Errorf("volume path must not be empty")
315+
}
316+
317+
if !filepath.IsAbs(i.(string)) {
318+
return diag.Errorf("volume path must be an absolute path")
319+
}
320+
321+
return nil
322+
},
323+
},
324+
"enabled": {
325+
Type: schema.TypeBool,
326+
Description: "Enable volume monitoring for this agent.",
327+
ForceNew: true,
328+
Required: true,
329+
},
330+
"threshold": {
331+
Type: schema.TypeInt,
332+
Description: "The volume usage threshold in percentage at which to trigger an alert. Value should be between 0 and 100.",
333+
ForceNew: true,
334+
Required: true,
335+
ValidateFunc: validation.IntBetween(0, 100),
336+
},
337+
},
338+
},
339+
},
340+
},
341+
},
342+
},
262343
},
263344
CustomizeDiff: func(ctx context.Context, rd *schema.ResourceDiff, i any) error {
264-
if !rd.HasChange("metadata") {
265-
return nil
345+
if rd.HasChange("metadata") {
346+
keys := map[string]bool{}
347+
metadata, ok := rd.Get("metadata").([]any)
348+
if !ok {
349+
return xerrors.Errorf("unexpected type %T for metadata, expected []any", rd.Get("metadata"))
350+
}
351+
for _, t := range metadata {
352+
obj, ok := t.(map[string]any)
353+
if !ok {
354+
return xerrors.Errorf("unexpected type %T for metadata, expected map[string]any", t)
355+
}
356+
key, ok := obj["key"].(string)
357+
if !ok {
358+
return xerrors.Errorf("unexpected type %T for metadata key, expected string", obj["key"])
359+
}
360+
if keys[key] {
361+
return xerrors.Errorf("duplicate agent metadata key %q", key)
362+
}
363+
keys[key] = true
364+
}
266365
}
267366

268-
keys := map[string]bool{}
269-
metadata, ok := rd.Get("metadata").([]any)
270-
if !ok {
271-
return xerrors.Errorf("unexpected type %T for metadata, expected []any", rd.Get("metadata"))
272-
}
273-
for _, t := range metadata {
274-
obj, ok := t.(map[string]any)
367+
if rd.HasChange("resources_monitoring") {
368+
monitors, ok := rd.Get("resources_monitoring").(*schema.Set)
275369
if !ok {
276-
return xerrors.Errorf("unexpected type %T for metadata, expected map[string]any", t)
370+
return xerrors.Errorf("unexpected type %T for resources_monitoring.0.volume, expected []any", rd.Get("resources_monitoring.0.volume"))
277371
}
278-
key, ok := obj["key"].(string)
372+
373+
monitor := monitors.List()[0].(map[string]any)
374+
375+
volumes, ok := monitor["volume"].(*schema.Set)
279376
if !ok {
280-
return xerrors.Errorf("unexpected type %T for metadata key, expected string", obj["key"])
377+
return xerrors.Errorf("unexpected type %T for resources_monitoring.0.volume, expected []any", monitor["volume"])
281378
}
282-
if keys[key] {
283-
return xerrors.Errorf("duplicate agent metadata key %q", key)
379+
380+
paths := map[string]bool{}
381+
for _, volume := range volumes.List() {
382+
obj, ok := volume.(map[string]any)
383+
if !ok {
384+
return xerrors.Errorf("unexpected type %T for volume, expected map[string]any", volume)
385+
}
386+
387+
// print path for debug purpose
388+
389+
path, ok := obj["path"].(string)
390+
if !ok {
391+
return xerrors.Errorf("unexpected type %T for volume path, expected string", obj["path"])
392+
}
393+
if paths[path] {
394+
return xerrors.Errorf("duplicate volume path %q", path)
395+
}
396+
paths[path] = true
284397
}
285-
keys[key] = true
286398
}
399+
287400
return nil
288401
},
289402
}

0 commit comments

Comments
 (0)