Skip to content

Commit b8d8b63

Browse files
committed
feat: add template data source
1 parent d157e1d commit b8d8b63

File tree

5 files changed

+572
-2
lines changed

5 files changed

+572
-2
lines changed

docs/data-sources/template.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
---
2+
# generated by https://github.com/hashicorp/terraform-plugin-docs
3+
page_title: "coderd_template Data Source - coderd"
4+
subcategory: ""
5+
description: |-
6+
An existing template on the coder deployment
7+
---
8+
9+
# coderd_template (Data Source)
10+
11+
An existing template on the coder deployment
12+
13+
14+
15+
<!-- schema generated by tfplugindocs -->
16+
## Schema
17+
18+
### Optional
19+
20+
- `id` (String) The ID of the template to retrieve. This field will be populated if a template name is supplied.
21+
- `name` (String) The name of the template to retrieve. This field will be populated if an ID is supplied.
22+
- `organization_id` (String) ID of the organization the template is associated with.
23+
24+
### Read-Only
25+
26+
- `active_user_count` (Number) Number of active users using the template.
27+
- `active_version_id` (String) ID of the active version of the template.
28+
- `activity_bump_ms` (Number) Duration to bump the deadline of a workspace when it receives activity.
29+
- `allow_user_autostart` (Boolean) Whether users can autostart workspaces created from the template.
30+
- `allow_user_autostop` (Boolean) Whether users can customize autostop behavior for workspaces created from the template.
31+
- `allow_user_cancel_workspace_jobs` (Boolean) Whether users can cancel jobs in workspaces created from the template.
32+
- `created_at` (Number) Unix timestamp of when the template was created.
33+
- `created_by_user_id` (String) ID of the user who created the template.
34+
- `default_ttl_ms` (Number) Default time-to-live for workspaces created from the template.
35+
- `deprecated` (Boolean) Whether the template is deprecated.
36+
- `deprecation_message` (String) Message to display when the template is deprecated.
37+
- `description` (String) Description of the template.
38+
- `display_name` (String) Display name of the template.
39+
- `failure_ttl_ms` (Number) Automatic cleanup TTL for failed workspace builds.
40+
- `icon` (String) URL of the template's icon.
41+
- `require_active_version` (Boolean) Whether workspaces created from the template must be up-to-datae on the latest active version.
42+
- `time_til_dormant_autodelete_ms` (Number) Duration of inactivity after the workspace becomes dormant before a workspace is automatically deleted.
43+
- `time_til_dormant_ms` (Number) Duration of inactivity before a workspace is considered dormant.
44+
- `updated_at` (Number) Unix timestamp of when the template was last updated.

integration/template-test/main.tf

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ resource "coderd_user" "ethan" {
1717
suspended = false
1818
}
1919

20-
2120
data "coderd_organization" "default" {
2221
is_default = true
2322
}
@@ -64,4 +63,9 @@ resource "coderd_template" "sample" {
6463
]
6564
}
6665
]
67-
}
66+
}
67+
68+
data "coderd_template" "sample" {
69+
organization_id = data.coderd_organization.default.id
70+
name = coderd_template.sample.name
71+
}

internal/provider/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ func (p *CoderdProvider) DataSources(ctx context.Context) []func() datasource.Da
132132
NewGroupDataSource,
133133
NewUserDataSource,
134134
NewOrganizationDataSource,
135+
NewTemplateDataSource,
135136
}
136137
}
137138

Lines changed: 283 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,283 @@
1+
package provider
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/coder/coder/v2/codersdk"
8+
"github.com/google/uuid"
9+
"github.com/hashicorp/terraform-plugin-framework-validators/datasourcevalidator"
10+
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
11+
"github.com/hashicorp/terraform-plugin-framework/datasource"
12+
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
13+
"github.com/hashicorp/terraform-plugin-framework/path"
14+
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
15+
"github.com/hashicorp/terraform-plugin-framework/types"
16+
)
17+
18+
// Ensure provider defined types fully satisfy framework interfaces.
19+
var _ datasource.DataSource = &TemplateDataSource{}
20+
var _ datasource.DataSourceWithConfigValidators = &TemplateDataSource{}
21+
22+
func NewTemplateDataSource() datasource.DataSource {
23+
return &TemplateDataSource{}
24+
}
25+
26+
// TemplateDataSource defines the data source implementation.
27+
type TemplateDataSource struct {
28+
data *CoderdProviderData
29+
}
30+
31+
// TemplateDataSourceModel describes the data source data model.
32+
type TemplateDataSourceModel struct {
33+
// ((Organization and Name) or ID) must be set
34+
OrganizationID UUID `tfsdk:"organization_id"`
35+
ID UUID `tfsdk:"id"`
36+
Name types.String `tfsdk:"name"`
37+
38+
DisplayName types.String `tfsdk:"display_name"`
39+
// TODO: Provisioner
40+
Description types.String `tfsdk:"description"`
41+
ActiveVersionID UUID `tfsdk:"active_version_id"`
42+
ActiveUserCount types.Int64 `tfsdk:"active_user_count"`
43+
Deprecated types.Bool `tfsdk:"deprecated"`
44+
DeprecationMessage types.String `tfsdk:"deprecation_message"`
45+
Icon types.String `tfsdk:"icon"`
46+
47+
DefaultTTLMillis types.Int64 `tfsdk:"default_ttl_ms"`
48+
ActivityBumpMillis types.Int64 `tfsdk:"activity_bump_ms"`
49+
// TODO: AutostopRequirement
50+
// TODO: AutostartRequirement
51+
52+
AllowUserAutostart types.Bool `tfsdk:"allow_user_autostart"`
53+
AllowUserAutostop types.Bool `tfsdk:"allow_user_autostop"`
54+
AllowUserCancelWorkspaceJobs types.Bool `tfsdk:"allow_user_cancel_workspace_jobs"`
55+
56+
FailureTTLMillis types.Int64 `tfsdk:"failure_ttl_ms"`
57+
TimeTilDormantMillis types.Int64 `tfsdk:"time_til_dormant_ms"`
58+
TimeTilDormantAutoDeleteMillis types.Int64 `tfsdk:"time_til_dormant_autodelete_ms"`
59+
60+
RequireActiveVersion types.Bool `tfsdk:"require_active_version"`
61+
// TODO: MaxPortShareLevel
62+
63+
CreatedByUserID UUID `tfsdk:"created_by_user_id"`
64+
CreatedAt types.Int64 `tfsdk:"created_at"` // Unix timestamp
65+
UpdatedAt types.Int64 `tfsdk:"updated_at"` // Unix timestamp
66+
67+
// TODO: ACL-related stuff
68+
}
69+
70+
func (d *TemplateDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
71+
resp.TypeName = req.ProviderTypeName + "_template"
72+
}
73+
74+
func (d *TemplateDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
75+
resp.Schema = schema.Schema{
76+
MarkdownDescription: "An existing template on the coder deployment",
77+
78+
Attributes: map[string]schema.Attribute{
79+
"organization_id": schema.StringAttribute{
80+
MarkdownDescription: "ID of the organization the template is associated with.",
81+
CustomType: UUIDType,
82+
Optional: true,
83+
Computed: true,
84+
},
85+
"id": schema.StringAttribute{
86+
MarkdownDescription: "The ID of the template to retrieve. This field will be populated if a template name is supplied.",
87+
CustomType: UUIDType,
88+
Optional: true,
89+
Computed: true,
90+
Validators: []validator.String{
91+
stringvalidator.AtLeastOneOf(path.Expressions{
92+
path.MatchRoot("name"),
93+
}...),
94+
},
95+
},
96+
"name": schema.StringAttribute{
97+
MarkdownDescription: "The name of the template to retrieve. This field will be populated if an ID is supplied.",
98+
Optional: true,
99+
Computed: true,
100+
},
101+
102+
"display_name": schema.StringAttribute{
103+
MarkdownDescription: "Display name of the template.",
104+
Computed: true,
105+
},
106+
"description": schema.StringAttribute{
107+
MarkdownDescription: "Description of the template.",
108+
Computed: true,
109+
},
110+
"active_version_id": schema.StringAttribute{
111+
MarkdownDescription: "ID of the active version of the template.",
112+
CustomType: UUIDType,
113+
Computed: true,
114+
},
115+
"active_user_count": schema.Int64Attribute{
116+
MarkdownDescription: "Number of active users using the template.",
117+
Computed: true,
118+
},
119+
"deprecated": schema.BoolAttribute{
120+
MarkdownDescription: "Whether the template is deprecated.",
121+
Computed: true,
122+
},
123+
"deprecation_message": schema.StringAttribute{
124+
MarkdownDescription: "Message to display when the template is deprecated.",
125+
Computed: true,
126+
},
127+
"icon": schema.StringAttribute{
128+
MarkdownDescription: "URL of the template's icon.",
129+
Computed: true,
130+
},
131+
"default_ttl_ms": schema.Int64Attribute{
132+
MarkdownDescription: "Default time-to-live for workspaces created from the template.",
133+
Computed: true,
134+
},
135+
"activity_bump_ms": schema.Int64Attribute{
136+
MarkdownDescription: "Duration to bump the deadline of a workspace when it receives activity.",
137+
Computed: true,
138+
},
139+
"allow_user_autostart": schema.BoolAttribute{
140+
MarkdownDescription: "Whether users can autostart workspaces created from the template.",
141+
Computed: true,
142+
},
143+
"allow_user_autostop": schema.BoolAttribute{
144+
MarkdownDescription: "Whether users can customize autostop behavior for workspaces created from the template.",
145+
Computed: true,
146+
},
147+
"allow_user_cancel_workspace_jobs": schema.BoolAttribute{
148+
MarkdownDescription: "Whether users can cancel jobs in workspaces created from the template.",
149+
Computed: true,
150+
},
151+
"failure_ttl_ms": schema.Int64Attribute{
152+
MarkdownDescription: "Automatic cleanup TTL for failed workspace builds.",
153+
Computed: true,
154+
},
155+
"time_til_dormant_ms": schema.Int64Attribute{
156+
MarkdownDescription: "Duration of inactivity before a workspace is considered dormant.",
157+
Computed: true,
158+
},
159+
"time_til_dormant_autodelete_ms": schema.Int64Attribute{
160+
MarkdownDescription: "Duration of inactivity after the workspace becomes dormant before a workspace is automatically deleted.",
161+
Computed: true,
162+
},
163+
"require_active_version": schema.BoolAttribute{
164+
MarkdownDescription: "Whether workspaces created from the template must be up-to-datae on the latest active version.",
165+
Computed: true,
166+
},
167+
"created_by_user_id": schema.StringAttribute{
168+
MarkdownDescription: "ID of the user who created the template.",
169+
CustomType: UUIDType,
170+
Computed: true,
171+
},
172+
"created_at": schema.Int64Attribute{
173+
MarkdownDescription: "Unix timestamp of when the template was created.",
174+
Computed: true,
175+
},
176+
"updated_at": schema.Int64Attribute{
177+
MarkdownDescription: "Unix timestamp of when the template was last updated.",
178+
Computed: true,
179+
},
180+
},
181+
}
182+
}
183+
184+
func (d *TemplateDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
185+
// Prevent panic if the provider has not been configured.
186+
if req.ProviderData == nil {
187+
return
188+
}
189+
190+
data, ok := req.ProviderData.(*CoderdProviderData)
191+
192+
if !ok {
193+
resp.Diagnostics.AddError(
194+
"Unexpected Data Source Configure Type",
195+
fmt.Sprintf("Expected *CoderdProviderData, got: %T. Please report this issue to the provider developers.", req.ProviderData),
196+
)
197+
198+
return
199+
}
200+
201+
d.data = data
202+
}
203+
204+
func (d *TemplateDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
205+
var data TemplateDataSourceModel
206+
207+
// Read Terraform configuration data into the model
208+
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
209+
210+
if resp.Diagnostics.HasError() {
211+
return
212+
}
213+
214+
client := d.data.Client
215+
216+
var (
217+
template codersdk.Template
218+
err error
219+
)
220+
if data.ID.ValueUUID() != uuid.Nil {
221+
template, err = client.Template(ctx, data.ID.ValueUUID())
222+
} else {
223+
if data.OrganizationID.ValueUUID() == uuid.Nil {
224+
data.OrganizationID = UUIDValue(d.data.DefaultOrganizationID)
225+
}
226+
if data.OrganizationID.ValueUUID() == uuid.Nil {
227+
resp.Diagnostics.AddError("Client Error", "name requires organization_id to be set")
228+
return
229+
}
230+
template, err = client.TemplateByName(ctx, data.OrganizationID.ValueUUID(), data.Name.ValueString())
231+
}
232+
if err != nil {
233+
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to get template, got error: %s", err))
234+
return
235+
}
236+
if !data.ID.IsNull() && template.ID.String() != data.ID.ValueString() {
237+
resp.Diagnostics.AddError("Client Error", "Retrieved Template's ID does not match the provided ID")
238+
return
239+
}
240+
if !data.Name.IsNull() && template.Name != data.Name.ValueString() {
241+
resp.Diagnostics.AddError("Client Error", "Retrieved Template's name does not match the provided name")
242+
return
243+
}
244+
if !data.OrganizationID.IsNull() && template.OrganizationID.String() != data.OrganizationID.ValueString() {
245+
resp.Diagnostics.AddError("Client Error", "Retrieved Template's organization ID does not match the provided organization ID")
246+
return
247+
}
248+
249+
data.OrganizationID = UUIDValue(template.OrganizationID)
250+
data.ID = UUIDValue(template.ID)
251+
data.Name = types.StringValue(template.Name)
252+
data.DisplayName = types.StringValue(template.DisplayName)
253+
data.Description = types.StringValue(template.Description)
254+
data.ActiveVersionID = UUIDValue(template.ActiveVersionID)
255+
data.ActiveUserCount = types.Int64Value(int64(template.ActiveUserCount))
256+
data.Deprecated = types.BoolValue(template.Deprecated)
257+
data.DeprecationMessage = types.StringValue(template.DeprecationMessage)
258+
data.Icon = types.StringValue(template.Icon)
259+
data.DefaultTTLMillis = types.Int64Value(template.DefaultTTLMillis)
260+
data.ActivityBumpMillis = types.Int64Value(template.ActivityBumpMillis)
261+
data.AllowUserAutostart = types.BoolValue(template.AllowUserAutostart)
262+
data.AllowUserAutostop = types.BoolValue(template.AllowUserAutostop)
263+
data.AllowUserCancelWorkspaceJobs = types.BoolValue(template.AllowUserCancelWorkspaceJobs)
264+
data.FailureTTLMillis = types.Int64Value(template.FailureTTLMillis)
265+
data.TimeTilDormantMillis = types.Int64Value(template.TimeTilDormantMillis)
266+
data.TimeTilDormantAutoDeleteMillis = types.Int64Value(template.TimeTilDormantAutoDeleteMillis)
267+
data.RequireActiveVersion = types.BoolValue(template.RequireActiveVersion)
268+
data.CreatedByUserID = UUIDValue(template.CreatedByID)
269+
data.CreatedAt = types.Int64Value(template.CreatedAt.Unix())
270+
data.UpdatedAt = types.Int64Value(template.UpdatedAt.Unix())
271+
272+
// Save data into Terraform state
273+
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
274+
}
275+
276+
func (d *TemplateDataSource) ConfigValidators(context.Context) []datasource.ConfigValidator {
277+
return []datasource.ConfigValidator{
278+
datasourcevalidator.AtLeastOneOf(
279+
path.MatchRoot("id"),
280+
path.MatchRoot("name"),
281+
),
282+
}
283+
}

0 commit comments

Comments
 (0)