Skip to content

Commit c17039f

Browse files
committed
Add application resource
1 parent e25826e commit c17039f

File tree

4 files changed

+439
-0
lines changed

4 files changed

+439
-0
lines changed

docs/resources/application.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
---
2+
# generated by https://github.com/hashicorp/terraform-plugin-docs
3+
page_title: "tlspc_application Resource - tlspc"
4+
subcategory: ""
5+
description: |-
6+
7+
---
8+
9+
# tlspc_application (Resource)
10+
11+
12+
13+
14+
15+
<!-- schema generated by tfplugindocs -->
16+
## Schema
17+
18+
### Required
19+
20+
- `ca_template_aliases` (Map of String)
21+
- `name` (String)
22+
- `owners` (Set of Map of String)
23+
24+
### Read-Only
25+
26+
- `id` (String) The ID of this resource.
Lines changed: 286 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,286 @@
1+
// Copyright (c) Venafi, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package provider
5+
6+
import (
7+
"context"
8+
"fmt"
9+
"strings"
10+
11+
"terraform-provider-tlspc/internal/tlspc"
12+
13+
"github.com/hashicorp/terraform-plugin-framework/attr"
14+
"github.com/hashicorp/terraform-plugin-framework/path"
15+
"github.com/hashicorp/terraform-plugin-framework/resource"
16+
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
17+
"github.com/hashicorp/terraform-plugin-framework/types"
18+
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
19+
)
20+
21+
var (
22+
_ resource.Resource = &applicationResource{}
23+
_ resource.ResourceWithConfigure = &applicationResource{}
24+
_ resource.ResourceWithImportState = &applicationResource{}
25+
)
26+
27+
type applicationResource struct {
28+
client *tlspc.Client
29+
}
30+
31+
func NewApplicationResource() resource.Resource {
32+
return &applicationResource{}
33+
}
34+
35+
func (r *applicationResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
36+
resp.TypeName = req.ProviderTypeName + "_application"
37+
}
38+
39+
func (r *applicationResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
40+
resp.Schema = schema.Schema{
41+
Attributes: map[string]schema.Attribute{
42+
"id": schema.StringAttribute{
43+
Computed: true,
44+
},
45+
"name": schema.StringAttribute{
46+
Required: true,
47+
},
48+
"owners": schema.SetAttribute{
49+
Required: true,
50+
ElementType: basetypes.MapType{
51+
ElemType: types.StringType,
52+
},
53+
},
54+
"ca_template_aliases": schema.MapAttribute{
55+
Required: true,
56+
ElementType: types.StringType,
57+
},
58+
},
59+
}
60+
}
61+
62+
func (r *applicationResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
63+
if req.ProviderData == nil {
64+
return
65+
}
66+
67+
client, ok := req.ProviderData.(*tlspc.Client)
68+
69+
if !ok {
70+
resp.Diagnostics.AddError(
71+
"Unexpected Data Source Configure Type",
72+
fmt.Sprintf("Expected *tlspc.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData),
73+
)
74+
75+
return
76+
}
77+
78+
r.client = client
79+
}
80+
81+
type applicationResourceModel struct {
82+
ID types.String `tfsdk:"id"`
83+
Name types.String `tfsdk:"name"`
84+
Owners []types.Map `tfsdk:"owners"`
85+
CATemplateAliases types.Map `tfsdk:"ca_template_aliases"`
86+
}
87+
88+
func (r *applicationResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
89+
var plan applicationResourceModel
90+
diags := req.Plan.Get(ctx, &plan)
91+
resp.Diagnostics.Append(diags...)
92+
if resp.Diagnostics.HasError() {
93+
return
94+
}
95+
96+
owners := []tlspc.OwnerAndType{}
97+
for _, v := range plan.Owners {
98+
m := v.Elements()
99+
// TODO: Work out how you're supposed to get an unquoted string out
100+
kind := strings.Trim(m["type"].String(), `"`)
101+
ownerId := strings.Trim(m["owner"].String(), `"`)
102+
if kind != "USER" && kind != "TEAM" {
103+
resp.Diagnostics.AddError(
104+
"Error creating application",
105+
"Could not create application, unsupported owner type: "+kind,
106+
)
107+
return
108+
}
109+
if ownerId == "" {
110+
resp.Diagnostics.AddError(
111+
"Error creating application",
112+
"Could not create application, undefined owner",
113+
)
114+
return
115+
}
116+
owner := tlspc.OwnerAndType{
117+
ID: ownerId,
118+
Type: kind,
119+
}
120+
owners = append(owners, owner)
121+
}
122+
123+
aliases := map[string]string{}
124+
for k, v := range plan.CATemplateAliases.Elements() {
125+
aliases[k] = strings.Trim(v.String(), `"`)
126+
}
127+
128+
application := tlspc.Application{
129+
Name: plan.Name.ValueString(),
130+
Owners: owners,
131+
CertificateTemplates: aliases,
132+
}
133+
created, err := r.client.CreateApplication(application)
134+
if err != nil {
135+
resp.Diagnostics.AddError(
136+
"Error creating application",
137+
"Could not create application, unexpected error: "+err.Error(),
138+
)
139+
return
140+
}
141+
plan.ID = types.StringValue(created.ID)
142+
diags = resp.State.Set(ctx, plan)
143+
resp.Diagnostics.Append(diags...)
144+
}
145+
146+
func (r *applicationResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
147+
var state applicationResourceModel
148+
149+
diags := req.State.Get(ctx, &state)
150+
resp.Diagnostics.Append(diags...)
151+
if resp.Diagnostics.HasError() {
152+
return
153+
}
154+
155+
app, err := r.client.GetApplication(state.ID.ValueString())
156+
if err != nil {
157+
resp.Diagnostics.AddError(
158+
"Error Reading Application",
159+
"Could not read application ID "+state.ID.ValueString()+": "+err.Error(),
160+
)
161+
return
162+
}
163+
164+
state.ID = types.StringValue(app.ID)
165+
state.Name = types.StringValue(app.Name)
166+
167+
owners := []types.Map{}
168+
for _, v := range app.Owners {
169+
owner := map[string]attr.Value{
170+
"type": types.StringValue(v.Type),
171+
"owner": types.StringValue(v.ID),
172+
}
173+
ownermap, diags := types.MapValue(types.StringType, owner)
174+
resp.Diagnostics.Append(diags...)
175+
if resp.Diagnostics.HasError() {
176+
return
177+
}
178+
owners = append(owners, ownermap)
179+
}
180+
state.Owners = owners
181+
182+
aliases := map[string]attr.Value{}
183+
for k, v := range app.CertificateTemplates {
184+
aliases[k] = types.StringValue(v)
185+
}
186+
187+
aliasmap, diags := types.MapValue(types.StringType, aliases)
188+
resp.Diagnostics.Append(diags...)
189+
if resp.Diagnostics.HasError() {
190+
return
191+
}
192+
193+
state.CATemplateAliases = aliasmap
194+
195+
diags = resp.State.Set(ctx, state)
196+
resp.Diagnostics.Append(diags...)
197+
}
198+
199+
func (r *applicationResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
200+
var plan, state applicationResourceModel
201+
202+
diags := req.State.Get(ctx, &state)
203+
resp.Diagnostics.Append(diags...)
204+
if resp.Diagnostics.HasError() {
205+
return
206+
}
207+
diags = req.Plan.Get(ctx, &plan)
208+
resp.Diagnostics.Append(diags...)
209+
if resp.Diagnostics.HasError() {
210+
return
211+
}
212+
owners := []tlspc.OwnerAndType{}
213+
for _, v := range plan.Owners {
214+
m := v.Elements()
215+
// TODO: Work out how you're supposed to get an unquoted string out
216+
kind := strings.Trim(m["type"].String(), `"`)
217+
ownerId := strings.Trim(m["owner"].String(), `"`)
218+
if kind != "USER" && kind != "TEAM" {
219+
resp.Diagnostics.AddError(
220+
"Error creating application",
221+
"Could not create application, unsupported owner type: "+kind,
222+
)
223+
return
224+
}
225+
if ownerId == "" {
226+
resp.Diagnostics.AddError(
227+
"Error creating application",
228+
"Could not create application, undefined owner",
229+
)
230+
return
231+
}
232+
owner := tlspc.OwnerAndType{
233+
ID: ownerId,
234+
Type: kind,
235+
}
236+
owners = append(owners, owner)
237+
}
238+
239+
aliases := map[string]string{}
240+
for k, v := range plan.CATemplateAliases.Elements() {
241+
aliases[k] = strings.Trim(v.String(), `"`)
242+
}
243+
244+
application := tlspc.Application{
245+
ID: state.ID.ValueString(),
246+
Name: plan.Name.ValueString(),
247+
Owners: owners,
248+
CertificateTemplates: aliases,
249+
}
250+
251+
updated, err := r.client.UpdateApplication(application)
252+
if err != nil {
253+
resp.Diagnostics.AddError(
254+
"Error updating application",
255+
"Could not update application, unexpected error: "+err.Error(),
256+
)
257+
return
258+
}
259+
plan.ID = types.StringValue(updated.ID)
260+
diags = resp.State.Set(ctx, plan)
261+
resp.Diagnostics.Append(diags...)
262+
}
263+
264+
func (r *applicationResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
265+
var state applicationResourceModel
266+
267+
diags := req.State.Get(ctx, &state)
268+
resp.Diagnostics.Append(diags...)
269+
if resp.Diagnostics.HasError() {
270+
return
271+
}
272+
273+
err := r.client.DeleteApplication(state.ID.ValueString())
274+
if err != nil {
275+
resp.Diagnostics.AddError(
276+
"Error Deleting Application",
277+
"Could not delete Application ID "+state.ID.ValueString()+": "+err.Error(),
278+
)
279+
return
280+
}
281+
}
282+
283+
func (r *applicationResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
284+
// Retrieve import ID and save to id attribute
285+
resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
286+
}

internal/provider/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ func (p *tlspcProvider) Resources(ctx context.Context) []func() resource.Resourc
8787
NewRegistryAccountResource,
8888
NewPluginResource,
8989
NewCertificateTemplateResource,
90+
NewApplicationResource,
9091
}
9192
}
9293

0 commit comments

Comments
 (0)