Skip to content

Commit 3e8547b

Browse files
feat: add coderd_template resource (#35)
1 parent 75f030f commit 3e8547b

File tree

14 files changed

+1406
-4
lines changed

14 files changed

+1406
-4
lines changed

docs/resources/template.md

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
---
2+
# generated by https://github.com/hashicorp/terraform-plugin-docs
3+
page_title: "coderd_template Resource - coderd"
4+
subcategory: ""
5+
description: |-
6+
A Coder template
7+
---
8+
9+
# coderd_template (Resource)
10+
11+
A Coder template
12+
13+
14+
15+
<!-- schema generated by tfplugindocs -->
16+
## Schema
17+
18+
### Required
19+
20+
- `acl` (Attributes) Access control list for the template. (see [below for nested schema](#nestedatt--acl))
21+
- `name` (String) The name of the template.
22+
- `versions` (Attributes List) (see [below for nested schema](#nestedatt--versions))
23+
24+
### Optional
25+
26+
- `allow_user_auto_start` (Boolean)
27+
- `allow_user_auto_stop` (Boolean)
28+
- `description` (String) A description of the template.
29+
- `display_name` (String) The display name of the template. Defaults to the template name.
30+
- `icon` (String) Relative path or external URL that specifes an icon to be displayed in the dashboard.
31+
- `organization_id` (String) The ID of the organization. Defaults to the provider's default organization
32+
33+
### Read-Only
34+
35+
- `id` (String) The ID of the template.
36+
37+
<a id="nestedatt--acl"></a>
38+
### Nested Schema for `acl`
39+
40+
Required:
41+
42+
- `groups` (Attributes Set) (see [below for nested schema](#nestedatt--acl--groups))
43+
- `users` (Attributes Set) (see [below for nested schema](#nestedatt--acl--users))
44+
45+
<a id="nestedatt--acl--groups"></a>
46+
### Nested Schema for `acl.groups`
47+
48+
Required:
49+
50+
- `id` (String)
51+
- `role` (String)
52+
53+
54+
<a id="nestedatt--acl--users"></a>
55+
### Nested Schema for `acl.users`
56+
57+
Required:
58+
59+
- `id` (String)
60+
- `role` (String)
61+
62+
63+
64+
<a id="nestedatt--versions"></a>
65+
### Nested Schema for `versions`
66+
67+
Required:
68+
69+
- `directory` (String) A path to the directory to create the template version from. Changes in the directory contents will trigger the creation of a new template version.
70+
71+
Optional:
72+
73+
- `active` (Boolean) Whether this version is the active version of the template. Only one version can be active at a time.
74+
- `message` (String) A message describing the changes in this version of the template. Messages longer than 72 characters will be truncated.
75+
- `name` (String) The name of the template version. Automatically generated if not provided.
76+
- `provisioner_tags` (Attributes Set) Provisioner tags for the template version. (see [below for nested schema](#nestedatt--versions--provisioner_tags))
77+
- `tf_vars` (Attributes Set) Terraform variables for the template version. (see [below for nested schema](#nestedatt--versions--tf_vars))
78+
79+
Read-Only:
80+
81+
- `directory_hash` (String)
82+
- `id` (String)
83+
84+
<a id="nestedatt--versions--provisioner_tags"></a>
85+
### Nested Schema for `versions.provisioner_tags`
86+
87+
Required:
88+
89+
- `name` (String)
90+
- `value` (String)
91+
92+
93+
<a id="nestedatt--versions--tf_vars"></a>
94+
### Nested Schema for `versions.tf_vars`
95+
96+
Required:
97+
98+
- `name` (String)
99+
- `value` (String)

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ require (
120120
github.com/secure-systems-lab/go-securesystemslib v0.7.0 // indirect
121121
github.com/shopspring/decimal v1.3.1 // indirect
122122
github.com/spaolacci/murmur3 v1.1.0 // indirect
123+
github.com/spf13/afero v1.11.0 // indirect
123124
github.com/spf13/cast v1.6.0 // indirect
124125
github.com/spf13/pflag v1.0.5 // indirect
125126
github.com/tinylib/msgp v1.1.8 // indirect

go.sum

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
cdr.dev/slog v1.6.2-0.20240126064726-20367d4aede6 h1:KHblWIE/KHOwQ6lEbMZt6YpcGve2FEZ1sDtrW1Am5UI=
22
cdr.dev/slog v1.6.2-0.20240126064726-20367d4aede6/go.mod h1:NaoTA7KwopCrnaSb0JXTC0PTp/O/Y83Lndnq0OEV3ZQ=
3-
cloud.google.com/go v0.110.7 h1:rJyC7nWRg2jWGZ4wSJ5nY65GTdYJkg0cd/uXb+ACI6o=
3+
cloud.google.com/go v0.110.10 h1:LXy9GEO+timppncPIAZoOj3l58LIU9k+kn48AN7IO3Y=
44
cloud.google.com/go/compute v1.25.1 h1:ZRpHJedLtTpKgr3RV1Fx23NuaAEN1Zfx9hw1u4aJdjU=
55
cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=
66
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
@@ -81,8 +81,6 @@ github.com/chenzhuoyu/iasm v0.9.0 h1:9fhXjVzq5hUy2gkhhgHl95zG2cEAhw9OSGs8toWWAwo
8181
github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
8282
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
8383
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
84-
github.com/coder/coder/v2 v2.13.0 h1:MlkRGqQcCAdwIkLc9iV8sQfT4jB3EThHopG0jF3BuFE=
85-
github.com/coder/coder/v2 v2.13.0/go.mod h1:Gxc79InMB6b+sncuDUORtFLWi7aKshvis3QrMUhpq5Q=
8684
github.com/coder/coder/v2 v2.13.1 h1:tCd8ljqIAufbVcBr8ODS1QbsrjJbmOIvgDkvdd/JMXc=
8785
github.com/coder/coder/v2 v2.13.1/go.mod h1:Gxc79InMB6b+sncuDUORtFLWi7aKshvis3QrMUhpq5Q=
8886
github.com/coder/pretty v0.0.0-20230908205945-e89ba86370e0 h1:3A0ES21Ke+FxEM8CXx9n47SZOKOpgSE1bbJzlE4qPVs=
@@ -134,6 +132,8 @@ github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
134132
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
135133
github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk=
136134
github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
135+
github.com/go-chi/render v1.0.1 h1:4/5tis2cKaNdnv9zFLfXzcquC9HbeZgCnxGnKrltBS8=
136+
github.com/go-chi/render v1.0.1/go.mod h1:pq4Rr7HbnsdaeHagklXub+p6Wd16Af5l9koip1OvJns=
137137
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
138138
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
139139
github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU=
@@ -390,6 +390,8 @@ github.com/skeema/knownhosts v1.2.2 h1:Iug2P4fLmDw9f41PB6thxUkNUkJzB5i+1/exaj40L
390390
github.com/skeema/knownhosts v1.2.2/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo=
391391
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
392392
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
393+
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
394+
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
393395
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
394396
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
395397
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=

integration/integration_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,45 @@ func TestIntegration(t *testing.T) {
105105
assert.Equal(t, group.QuotaAllowance, 100)
106106
},
107107
},
108+
{
109+
name: "template-test",
110+
preF: func(t testing.TB, c *codersdk.Client) {},
111+
assertF: func(t testing.TB, c *codersdk.Client) {
112+
defaultOrg, err := c.OrganizationByName(ctx, "first-organization")
113+
assert.NoError(t, err)
114+
user, err := c.User(ctx, "ethan")
115+
require.NoError(t, err)
116+
117+
// Check template metadata
118+
templates, err := c.Templates(ctx)
119+
require.NoError(t, err)
120+
require.Len(t, templates, 1)
121+
require.Equal(t, "example-template", templates[0].Name)
122+
require.False(t, templates[0].AllowUserAutostart)
123+
require.False(t, templates[0].AllowUserAutostop)
124+
125+
// Check versions
126+
versions, err := c.TemplateVersionsByTemplate(ctx, codersdk.TemplateVersionsByTemplateRequest{
127+
TemplateID: templates[0].ID,
128+
})
129+
require.NoError(t, err)
130+
require.Len(t, versions, 2)
131+
require.Equal(t, "latest", versions[0].Name)
132+
require.NotEmpty(t, versions[0].ID)
133+
require.Equal(t, templates[0].ID, *versions[0].TemplateID)
134+
require.Equal(t, templates[0].ActiveVersionID, versions[0].ID)
135+
136+
// Check ACL
137+
acl, err := c.TemplateACL(ctx, templates[0].ID)
138+
require.NoError(t, err)
139+
require.Len(t, acl.Groups, 1)
140+
require.Equal(t, codersdk.TemplateRoleUse, acl.Groups[0].Role)
141+
require.Equal(t, defaultOrg.ID, acl.Groups[0].ID)
142+
require.Len(t, acl.Users, 1)
143+
require.Equal(t, codersdk.TemplateRoleAdmin, acl.Users[0].Role)
144+
require.Equal(t, user.ID, acl.Users[0].ID)
145+
},
146+
},
108147
} {
109148
t.Run(tt.name, func(t *testing.T) {
110149
client := StartCoder(ctx, t, tt.name, true)
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
variable "name" {
2+
type = string
3+
}
4+
5+
resource "local_file" "a" {
6+
filename = "${path.module}/a.txt"
7+
content = "hello ${var.name}"
8+
}
9+
10+
output "a" {
11+
value = local_file.a.content
12+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
variable "name" {
2+
type = string
3+
}
4+
5+
resource "local_file" "a" {
6+
filename = "${path.module}/a.txt"
7+
content = "hello ${var.name}"
8+
}
9+
10+
output "a" {
11+
value = local_file.a.content
12+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
name = "world"

integration/template-test/main.tf

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
terraform {
2+
required_providers {
3+
coderd = {
4+
source = "coder/coderd"
5+
version = ">=0.0.0"
6+
}
7+
}
8+
}
9+
10+
resource "coderd_user" "ethan" {
11+
username = "ethan"
12+
name = "Ethan Coolguy"
13+
email = "test@coder.com"
14+
roles = ["owner", "template-admin"]
15+
login_type = "password"
16+
password = "SomeSecurePassword!"
17+
suspended = false
18+
}
19+
20+
21+
data "coderd_organization" "default" {
22+
is_default = true
23+
}
24+
25+
resource "coderd_template" "sample" {
26+
name = "example-template"
27+
allow_user_auto_stop = false
28+
allow_user_auto_start = false
29+
acl = {
30+
groups = [
31+
{
32+
id = data.coderd_organization.default.id
33+
role = "use"
34+
}
35+
]
36+
users = [
37+
{
38+
id = resource.coderd_user.ethan.id
39+
role = "admin"
40+
}
41+
]
42+
}
43+
versions = [
44+
{
45+
name = "latest"
46+
directory = "./example-template"
47+
active = true
48+
tf_vars = [
49+
{
50+
name = "name"
51+
value = "world"
52+
},
53+
]
54+
},
55+
{
56+
name = "legacy"
57+
directory = "./example-template-2"
58+
active = false
59+
tf_vars = [
60+
{
61+
name = "name"
62+
value = "ethan"
63+
},
64+
]
65+
}
66+
]
67+
}

internal/provider/logger.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package provider
2+
3+
import (
4+
"context"
5+
6+
"cdr.dev/slog"
7+
"github.com/hashicorp/terraform-plugin-log/tflog"
8+
)
9+
10+
var _ slog.Sink = &tfLogSink{}
11+
12+
type tfLogSink struct {
13+
tfCtx context.Context
14+
}
15+
16+
func newTFLogSink(tfCtx context.Context) *tfLogSink {
17+
return &tfLogSink{
18+
tfCtx: tfCtx,
19+
}
20+
}
21+
22+
func (s *tfLogSink) LogEntry(ctx context.Context, e slog.SinkEntry) {
23+
var logFn func(ctx context.Context, msg string, additionalFields ...map[string]interface{})
24+
switch e.Level {
25+
case slog.LevelDebug:
26+
logFn = tflog.Debug
27+
case slog.LevelInfo:
28+
logFn = tflog.Info
29+
case slog.LevelWarn:
30+
logFn = tflog.Warn
31+
default:
32+
logFn = tflog.Error
33+
}
34+
logFn(s.tfCtx, e.Message, mapToFields(e.Fields))
35+
}
36+
37+
func (s *tfLogSink) Sync() {}
38+
39+
func mapToFields(m slog.Map) map[string]interface{} {
40+
fields := make(map[string]interface{}, len(m))
41+
for _, v := range m {
42+
fields[v.Name] = v.Value
43+
}
44+
return fields
45+
}

internal/provider/organization_data_source_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ func TestAccOrganizationDataSource(t *testing.T) {
1919
t.Skip("Acceptance tests are disabled.")
2020
}
2121
ctx := context.Background()
22-
client := integration.StartCoder(ctx, t, "group_acc", true)
22+
client := integration.StartCoder(ctx, t, "org_data_acc", true)
2323
firstUser, err := client.User(ctx, codersdk.Me)
2424
require.NoError(t, err)
2525

0 commit comments

Comments
 (0)
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