@@ -2,9 +2,13 @@ package toolsdk
2
2
3
3
import (
4
4
"archive/tar"
5
+ "bytes"
5
6
"context"
6
7
"errors"
7
8
"io"
9
+ "mime"
10
+ "path/filepath"
11
+ "strings"
8
12
9
13
"github.com/coder/coder/v2/codersdk"
10
14
"github.com/coder/coder/v2/codersdk/agentsdk"
42
46
CreateTemplateVersion .Generic (),
43
47
CreateTemplate .Generic (),
44
48
GetTemplateVersionLogs .Generic (),
49
+ GetTemplateVersion .Generic (),
50
+ DownloadTarFile .Generic (),
45
51
UpdateTemplateActiveVersion .Generic (),
46
52
DeleteTemplate .Generic (),
47
53
GetWorkspaceAgentLogs .Generic (),
85
91
Name : "coder_get_workspace" ,
86
92
Description : `Get a workspace by ID.
87
93
88
- This returns more data than list_workspaces to reduce token usage.` ,
94
+ This returns more data than list_workspaces to reduce token usage.
95
+
96
+ Workspace health is composed of infrastructure and agents.
97
+ Infrastructure can be provisioned successfully, but the agent
98
+ could fail to connect due to template misconfiguration or
99
+ connection issues. To ensure a workspace is alive, eventually
100
+ the agent should be in a connected state.` ,
89
101
Schema : aisdk.Schema {
90
102
Properties : map [string ]any {
91
103
"workspace_id" : map [string ]any {
@@ -286,6 +298,10 @@ is provisioned correctly and the agent can connect to the control plane.
286
298
"type" : "string" ,
287
299
"description" : "The transition to perform. Must be one of: start, stop, delete" ,
288
300
},
301
+ "template_version_id" : map [string ]any {
302
+ "type" : "string" ,
303
+ "description" : "The template version ID to use for the workspace build. Workspaces do not update template versions automatically." ,
304
+ },
289
305
},
290
306
Required : []string {"workspace_id" , "transition" },
291
307
},
@@ -303,8 +319,16 @@ is provisioned correctly and the agent can connect to the control plane.
303
319
if ! ok {
304
320
return codersdk.WorkspaceBuild {}, errors .New ("transition must be a string" )
305
321
}
322
+ var templateVersionID uuid.UUID
323
+ if args ["template_version_id" ] != nil {
324
+ templateVersionID , err = uuid .Parse (args ["template_version_id" ].(string ))
325
+ if err != nil {
326
+ return codersdk.WorkspaceBuild {}, err
327
+ }
328
+ }
306
329
return client .CreateWorkspaceBuild (ctx , workspaceID , codersdk.CreateWorkspaceBuildRequest {
307
- Transition : codersdk .WorkspaceTransition (rawTransition ),
330
+ Transition : codersdk .WorkspaceTransition (rawTransition ),
331
+ TemplateVersionID : templateVersionID ,
308
332
})
309
333
},
310
334
}
@@ -413,7 +437,7 @@ This resource provides the following fields:
413
437
- init_script: The script to run on provisioned infrastructure to fetch and start the agent.
414
438
- token: Set the environment variable CODER_AGENT_TOKEN to this value to authenticate the agent.
415
439
416
- The agent MUST be installed and started using the init_script.
440
+ The agent MUST be installed and started using the init_script. A utility like curl or wget to fetch the agent binary must exist in the provisioned infrastructure.
417
441
418
442
Expose terminal or HTTP applications running in a workspace with:
419
443
@@ -533,13 +557,20 @@ resource "google_compute_instance" "dev" {
533
557
auto_delete = false
534
558
source = google_compute_disk.root.name
535
559
}
560
+ // In order to use google-instance-identity, a service account *must* be provided.
536
561
service_account {
537
562
email = data.google_compute_default_service_account.default.email
538
563
scopes = ["cloud-platform"]
539
564
}
565
+ # ONLY FOR WINDOWS:
566
+ # metadata = {
567
+ # windows-startup-script-ps1 = coder_agent.main.init_script
568
+ # }
540
569
# The startup script runs as root with no $HOME environment set up, so instead of directly
541
570
# running the agent init script, create a user (with a homedir, default shell and sudo
542
571
# permissions) and execute the init script as that user.
572
+ #
573
+ # The agent MUST be started in here.
543
574
metadata_startup_script = <<EOMETA
544
575
#!/usr/bin/env sh
545
576
set -eux
@@ -877,6 +908,35 @@ Useful for checking whether a workspace builds successfully or not.`,
877
908
},
878
909
}
879
910
911
+ GetTemplateVersion = Tool [codersdk.TemplateVersion ]{
912
+ Tool : aisdk.Tool {
913
+ Name : "coder_get_template_version" ,
914
+ Description : "Get a template version by ID." ,
915
+ Schema : aisdk.Schema {
916
+ Properties : map [string ]any {
917
+ "template_version_id" : map [string ]any {
918
+ "type" : "string" ,
919
+ },
920
+ },
921
+ },
922
+ },
923
+ Handler : func (ctx context.Context , args map [string ]any ) (codersdk.TemplateVersion , error ) {
924
+ client , err := clientFromContext (ctx )
925
+ if err != nil {
926
+ return codersdk.TemplateVersion {}, err
927
+ }
928
+ templateVersionID , err := uuid .Parse (args ["template_version_id" ].(string ))
929
+ if err != nil {
930
+ return codersdk.TemplateVersion {}, err
931
+ }
932
+ templateVersion , err := client .TemplateVersion (ctx , templateVersionID )
933
+ if err != nil {
934
+ return codersdk.TemplateVersion {}, err
935
+ }
936
+ return templateVersion , nil
937
+ },
938
+ }
939
+
880
940
GetTemplateVersionLogs = Tool [[]string ]{
881
941
Tool : aisdk.Tool {
882
942
Name : "coder_get_template_version_logs" ,
@@ -1012,6 +1072,63 @@ Useful for checking whether a workspace builds successfully or not.`,
1012
1072
},
1013
1073
}
1014
1074
1075
+ DownloadTarFile = Tool [map [string ]string ]{
1076
+ Tool : aisdk.Tool {
1077
+ Name : "coder_download_tar_file" ,
1078
+ Description : "Download a tar file and get key/value mapping of file names to file contents." ,
1079
+ Schema : aisdk.Schema {
1080
+ Properties : map [string ]any {
1081
+ "file_id" : map [string ]any {
1082
+ "type" : "string" ,
1083
+ },
1084
+ },
1085
+ },
1086
+ },
1087
+ Handler : func (ctx context.Context , args map [string ]any ) (map [string ]string , error ) {
1088
+ client , err := clientFromContext (ctx )
1089
+ if err != nil {
1090
+ return nil , err
1091
+ }
1092
+ uploadID , err := uuid .Parse (args ["file_id" ].(string ))
1093
+ if err != nil {
1094
+ return nil , err
1095
+ }
1096
+ download , contentType , err := client .Download (ctx , uploadID )
1097
+ if err != nil {
1098
+ return nil , err
1099
+ }
1100
+
1101
+ if contentType != codersdk .ContentTypeTar {
1102
+ return nil , errors .New ("content type is not tar" )
1103
+ }
1104
+
1105
+ tarReader := tar .NewReader (bytes .NewReader (download ))
1106
+ files := make (map [string ]string )
1107
+ for {
1108
+ header , err := tarReader .Next ()
1109
+ if err == io .EOF {
1110
+ break
1111
+ }
1112
+ if err != nil {
1113
+ return nil , err
1114
+ }
1115
+ // if the mime type is not text, skip it.
1116
+ mimeType := mime .TypeByExtension (filepath .Ext (header .Name ))
1117
+ if mimeType != "" && ! strings .HasPrefix (mimeType , "text/" ) {
1118
+ files [header .Name ] = `<content skipped because it's not text - mime type: ` + mimeType + `>`
1119
+ continue
1120
+ }
1121
+ content , err := io .ReadAll (tarReader )
1122
+ if err != nil {
1123
+ return nil , err
1124
+ }
1125
+ files [header .Name ] = string (content )
1126
+ }
1127
+
1128
+ return files , nil
1129
+ },
1130
+ }
1131
+
1015
1132
CreateTemplate = Tool [codersdk.Template ]{
1016
1133
Tool : aisdk.Tool {
1017
1134
Name : "coder_create_template" ,
0 commit comments