Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit9d925c0

Browse files
committed
feat: add coderd_template resource
1 parent40c3e02 commit9d925c0

File tree

6 files changed

+323
-0
lines changed

6 files changed

+323
-0
lines changed

‎integration/integration_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,11 @@ func TestIntegration(t *testing.T) {
101101
assert.Equal(t,group.QuotaAllowance,100)
102102
},
103103
},
104+
{
105+
name:"template-test",
106+
preF:func(t testing.TB,c*codersdk.Client) {},
107+
assertF:func(t testing.TB,c*codersdk.Client) {},
108+
},
104109
} {
105110
t.Run(tt.name,func(t*testing.T) {
106111
client:=StartCoder(ctx,t,tt.name)

‎integration/template-test/main.tf

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
terraform {
2+
required_providers {
3+
coderd={
4+
source="coder/coderd"
5+
version=">=0.0.0"
6+
}
7+
}
8+
}
9+
10+
resource"coderd_template""sample" {
11+
name="example-template"
12+
latest={
13+
directory="./example-template"
14+
}
15+
}

‎internal/provider/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ func (p *CoderdProvider) Resources(ctx context.Context) []func() resource.Resour
105105
return []func() resource.Resource{
106106
NewUserResource,
107107
NewGroupResource,
108+
NewTemplateResource,
108109
}
109110
}
110111

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
package provider
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/google/uuid"
8+
"github.com/hashicorp/terraform-plugin-framework/path"
9+
"github.com/hashicorp/terraform-plugin-framework/resource"
10+
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
11+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
12+
"github.com/hashicorp/terraform-plugin-framework/types"
13+
)
14+
15+
// Ensure provider defined types fully satisfy framework interfaces.
16+
var_ resource.Resource=&TemplateResource{}
17+
var_ resource.ResourceWithImportState=&TemplateResource{}
18+
19+
funcNewTemplateResource() resource.Resource {
20+
return&TemplateResource{}
21+
}
22+
23+
// TemplateResource defines the resource implementation.
24+
typeTemplateResourcestruct {
25+
data*CoderdProviderData
26+
}
27+
28+
// TemplateResourceModel describes the resource data model.
29+
typeTemplateResourceModelstruct {
30+
ID types.String`tfsdk:"id"`
31+
32+
Name types.String`tfsdk:"name"`
33+
DisplayName types.String`tfsdk:"display_name"`
34+
Description types.String`tfsdk:"description"`
35+
OrganizationID types.String`tfsdk:"organization_id"`
36+
37+
Latest*TemplateVersion`tfsdk:"latest"`
38+
}
39+
40+
typeTemplateVersionstruct {
41+
Name types.String`tfsdk:"name"`
42+
Message types.String`tfsdk:"message"`
43+
Directory types.String`tfsdk:"directory"`
44+
DirectoryHash types.String`tfsdk:"directory_hash"`
45+
}
46+
47+
func (r*TemplateResource)Metadata(ctx context.Context,req resource.MetadataRequest,resp*resource.MetadataResponse) {
48+
resp.TypeName=req.ProviderTypeName+"_template"
49+
}
50+
51+
func (r*TemplateResource)Schema(ctx context.Context,req resource.SchemaRequest,resp*resource.SchemaResponse) {
52+
resp.Schema= schema.Schema{
53+
MarkdownDescription:"A Coder template",
54+
55+
Attributes:map[string]schema.Attribute{
56+
"id": schema.StringAttribute{
57+
MarkdownDescription:"The ID of the template.",
58+
Computed:true,
59+
},
60+
"name": schema.StringAttribute{
61+
MarkdownDescription:"The name of the template.",
62+
Required:true,
63+
},
64+
"display_name": schema.StringAttribute{
65+
MarkdownDescription:"The display name of the template. Defaults to the template name.",
66+
Optional:true,
67+
},
68+
"description": schema.StringAttribute{
69+
MarkdownDescription:"A description of the template.",
70+
Optional:true,
71+
},
72+
// TODO: Rest of the fields
73+
"organization_id": schema.StringAttribute{
74+
MarkdownDescription:"The ID of the organization. Defaults to the provider's default organization",
75+
Optional:true,
76+
},
77+
"latest": schema.SingleNestedAttribute{
78+
MarkdownDescription:"The latest version of the template.",
79+
Required:true,
80+
Attributes:map[string]schema.Attribute{
81+
"name": schema.StringAttribute{
82+
MarkdownDescription:"The name of the template version. Automatically generated if not provided.",
83+
Optional:true,
84+
},
85+
"message": schema.StringAttribute{
86+
MarkdownDescription:"A message describing the changes in this version of the template. Messages longer than 72 characters will be truncated..",
87+
Optional:true,
88+
},
89+
"directory": schema.StringAttribute{
90+
MarkdownDescription:"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.",
91+
Required:true,
92+
},
93+
"directory_hash": schema.StringAttribute{
94+
Computed:true,
95+
PlanModifiers: []planmodifier.String{
96+
NewDirectoryHashPlanModifier(),
97+
},
98+
},
99+
// TODO: Rest of the fields
100+
},
101+
},
102+
},
103+
}
104+
}
105+
106+
func (r*TemplateResource)Configure(ctx context.Context,req resource.ConfigureRequest,resp*resource.ConfigureResponse) {
107+
// Prevent panic if the provider has not been configured.
108+
ifreq.ProviderData==nil {
109+
return
110+
}
111+
112+
data,ok:=req.ProviderData.(*CoderdProviderData)
113+
114+
if!ok {
115+
resp.Diagnostics.AddError(
116+
"Unexpected Resource Configure Type",
117+
fmt.Sprintf("Expected *CoderdProviderData, got: %T. Please report this issue to the provider developers.",req.ProviderData),
118+
)
119+
120+
return
121+
}
122+
123+
r.data=data
124+
}
125+
126+
func (r*TemplateResource)Create(ctx context.Context,req resource.CreateRequest,resp*resource.CreateResponse) {
127+
vardataTemplateResourceModel
128+
129+
// Read Terraform plan data into the model
130+
resp.Diagnostics.Append(req.Plan.Get(ctx,&data)...)
131+
ifresp.Diagnostics.HasError() {
132+
return
133+
}
134+
135+
// TODO: Placeholder
136+
data.ID=types.StringValue(uuid.New().String())
137+
// client := r.data.Client
138+
// orgID, err := uuid.Parse(data.OrganizationID.ValueString())
139+
// if err != nil {
140+
// resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to parse supplied organization ID as UUID, got error: %s", err))
141+
// return
142+
// }
143+
144+
// Save data into Terraform state
145+
resp.Diagnostics.Append(resp.State.Set(ctx,&data)...)
146+
}
147+
148+
func (r*TemplateResource)Read(ctx context.Context,req resource.ReadRequest,resp*resource.ReadResponse) {
149+
vardataTemplateResourceModel
150+
151+
// Read Terraform prior state data into the model
152+
resp.Diagnostics.Append(req.State.Get(ctx,&data)...)
153+
154+
ifresp.Diagnostics.HasError() {
155+
return
156+
}
157+
158+
// client := r.data.Client
159+
160+
// Save updated data into Terraform state
161+
resp.Diagnostics.Append(resp.State.Set(ctx,&data)...)
162+
}
163+
164+
func (r*TemplateResource)Update(ctx context.Context,req resource.UpdateRequest,resp*resource.UpdateResponse) {
165+
vardataTemplateResourceModel
166+
167+
// Read Terraform plan data into the model
168+
resp.Diagnostics.Append(req.Plan.Get(ctx,&data)...)
169+
170+
ifresp.Diagnostics.HasError() {
171+
return
172+
}
173+
174+
// client := r.data.Client
175+
176+
// Save updated data into Terraform state
177+
resp.Diagnostics.Append(resp.State.Set(ctx,&data)...)
178+
}
179+
180+
func (r*TemplateResource)Delete(ctx context.Context,req resource.DeleteRequest,resp*resource.DeleteResponse) {
181+
vardataTemplateResourceModel
182+
183+
// Read Terraform prior state data into the model
184+
resp.Diagnostics.Append(req.State.Get(ctx,&data)...)
185+
186+
ifresp.Diagnostics.HasError() {
187+
return
188+
}
189+
190+
// client := r.data.Client
191+
}
192+
193+
func (r*TemplateResource)ImportState(ctx context.Context,req resource.ImportStateRequest,resp*resource.ImportStateResponse) {
194+
resource.ImportStatePassthroughID(ctx,path.Root("id"),req,resp)
195+
}
196+
197+
typedirectoryHashPlanModifierstruct{}
198+
199+
funcNewDirectoryHashPlanModifier() planmodifier.String {
200+
return&directoryHashPlanModifier{}
201+
}
202+
203+
// Description implements planmodifier.String.
204+
func (m*directoryHashPlanModifier)Description(context.Context)string {
205+
return"Recomputes the directory hash if the directory has changed."
206+
}
207+
208+
// MarkdownDescription implements planmodifier.String.
209+
func (m*directoryHashPlanModifier)MarkdownDescription(ctx context.Context)string {
210+
returnm.Description(ctx)
211+
}
212+
213+
// PlanModifyString implements planmodifier.String.
214+
func (m*directoryHashPlanModifier)PlanModifyString(ctx context.Context,req planmodifier.StringRequest,resp*planmodifier.StringResponse) {
215+
vardataTemplateResourceModel
216+
resp.Diagnostics.Append(req.Plan.Get(ctx,&data)...)
217+
218+
ifresp.Diagnostics.HasError() {
219+
return
220+
}
221+
222+
hash,err:=computeDirectoryHash(data.Latest.Directory.ValueString())
223+
iferr!=nil {
224+
resp.Diagnostics.AddError("Client Error",fmt.Sprintf("Failed to compute directory hash: %s",err))
225+
return
226+
}
227+
228+
resp.PlanValue=types.StringValue(hash)
229+
}
230+
231+
var_ planmodifier.String=&directoryHashPlanModifier{}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package provider
2+
3+
import (
4+
"strings"
5+
"testing"
6+
"text/template"
7+
8+
"github.com/stretchr/testify/require"
9+
)
10+
11+
funcTestAccTemplateVersionResource(t*testing.T) {}
12+
13+
typetestAccTemplateVersionResourceConfigstruct {
14+
URLstring
15+
Tokenstring
16+
}
17+
18+
func (ctestAccTemplateVersionResourceConfig)String(t*testing.T)string {
19+
t.Helper()
20+
tpl:=`
21+
provider coderd {
22+
url = "{{.URL}}"
23+
token = "{{.Token}}"
24+
}
25+
26+
resource "coderd_template_version" "test" {}
27+
`
28+
29+
funcMap:= template.FuncMap{
30+
"orNull":PrintOrNull(t),
31+
}
32+
33+
buf:= strings.Builder{}
34+
tmpl,err:=template.New("test").Funcs(funcMap).Parse(tpl)
35+
require.NoError(t,err)
36+
37+
err=tmpl.Execute(&buf,c)
38+
require.NoError(t,err)
39+
40+
returnbuf.String()
41+
}

‎internal/provider/util.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
package provider
22

33
import (
4+
"crypto/sha256"
5+
"encoding/hex"
46
"fmt"
7+
"os"
8+
"path/filepath"
59
"testing"
610

711
"github.com/stretchr/testify/require"
@@ -53,3 +57,29 @@ func PrintOrNull(t *testing.T) func(v any) string {
5357
}
5458
}
5559
}
60+
61+
funccomputeDirectoryHash(directorystring) (string,error) {
62+
varfiles []string
63+
err:=filepath.Walk(directory,func(pathstring,info os.FileInfo,errerror)error {
64+
iferr!=nil {
65+
returnerr
66+
}
67+
if!info.IsDir() {
68+
files=append(files,path)
69+
}
70+
returnnil
71+
})
72+
iferr!=nil {
73+
return"",err
74+
}
75+
76+
hash:=sha256.New()
77+
for_,file:=rangefiles {
78+
data,err:=os.ReadFile(file)
79+
iferr!=nil {
80+
return"",err
81+
}
82+
hash.Write(data)
83+
}
84+
returnhex.EncodeToString(hash.Sum(nil)),nil
85+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp