Skip to content

Commit

Permalink
Merge pull request #5 from aidy/apps
Browse files Browse the repository at this point in the history
Add application and certificate template resources
  • Loading branch information
aidy authored Nov 19, 2024
2 parents b70f84e + c17039f commit eb28ec9
Show file tree
Hide file tree
Showing 8 changed files with 1,076 additions and 0 deletions.
26 changes: 26 additions & 0 deletions docs/data-sources/ca_product.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "tlspc_ca_product Data Source - tlspc"
subcategory: ""
description: |-
---

# tlspc_ca_product (Data Source)





<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `ca_name` (String)
- `product_option` (String)
- `type` (String)

### Read-Only

- `id` (String) The ID of this resource.
26 changes: 26 additions & 0 deletions docs/resources/application.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "tlspc_application Resource - tlspc"
subcategory: ""
description: |-
---

# tlspc_application (Resource)





<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `ca_template_aliases` (Map of String)
- `name` (String)
- `owners` (Set of Map of String)

### Read-Only

- `id` (String) The ID of this resource.
27 changes: 27 additions & 0 deletions docs/resources/certificate_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "tlspc_certificate_template Resource - tlspc"
subcategory: ""
description: |-
---

# tlspc_certificate_template (Resource)





<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `ca_product_id` (String)
- `ca_type` (String)
- `key_reuse` (Boolean)
- `name` (String)

### Read-Only

- `id` (String) The ID of this resource.
286 changes: 286 additions & 0 deletions internal/provider/application_resource.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,286 @@
// Copyright (c) Venafi, Inc.
// SPDX-License-Identifier: MPL-2.0

package provider

import (
"context"
"fmt"
"strings"

"terraform-provider-tlspc/internal/tlspc"

"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
)

var (
_ resource.Resource = &applicationResource{}
_ resource.ResourceWithConfigure = &applicationResource{}
_ resource.ResourceWithImportState = &applicationResource{}
)

type applicationResource struct {
client *tlspc.Client
}

func NewApplicationResource() resource.Resource {
return &applicationResource{}
}

func (r *applicationResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_application"
}

func (r *applicationResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Computed: true,
},
"name": schema.StringAttribute{
Required: true,
},
"owners": schema.SetAttribute{
Required: true,
ElementType: basetypes.MapType{
ElemType: types.StringType,
},
},
"ca_template_aliases": schema.MapAttribute{
Required: true,
ElementType: types.StringType,
},
},
}
}

func (r *applicationResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
if req.ProviderData == nil {
return
}

client, ok := req.ProviderData.(*tlspc.Client)

if !ok {
resp.Diagnostics.AddError(
"Unexpected Data Source Configure Type",
fmt.Sprintf("Expected *tlspc.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData),
)

return
}

r.client = client
}

type applicationResourceModel struct {
ID types.String `tfsdk:"id"`
Name types.String `tfsdk:"name"`
Owners []types.Map `tfsdk:"owners"`
CATemplateAliases types.Map `tfsdk:"ca_template_aliases"`
}

func (r *applicationResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var plan applicationResourceModel
diags := req.Plan.Get(ctx, &plan)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}

owners := []tlspc.OwnerAndType{}
for _, v := range plan.Owners {
m := v.Elements()
// TODO: Work out how you're supposed to get an unquoted string out
kind := strings.Trim(m["type"].String(), `"`)
ownerId := strings.Trim(m["owner"].String(), `"`)
if kind != "USER" && kind != "TEAM" {
resp.Diagnostics.AddError(
"Error creating application",
"Could not create application, unsupported owner type: "+kind,
)
return
}
if ownerId == "" {
resp.Diagnostics.AddError(
"Error creating application",
"Could not create application, undefined owner",
)
return
}
owner := tlspc.OwnerAndType{
ID: ownerId,
Type: kind,
}
owners = append(owners, owner)
}

aliases := map[string]string{}
for k, v := range plan.CATemplateAliases.Elements() {
aliases[k] = strings.Trim(v.String(), `"`)
}

application := tlspc.Application{
Name: plan.Name.ValueString(),
Owners: owners,
CertificateTemplates: aliases,
}
created, err := r.client.CreateApplication(application)
if err != nil {
resp.Diagnostics.AddError(
"Error creating application",
"Could not create application, unexpected error: "+err.Error(),
)
return
}
plan.ID = types.StringValue(created.ID)
diags = resp.State.Set(ctx, plan)
resp.Diagnostics.Append(diags...)
}

func (r *applicationResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
var state applicationResourceModel

diags := req.State.Get(ctx, &state)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}

app, err := r.client.GetApplication(state.ID.ValueString())
if err != nil {
resp.Diagnostics.AddError(
"Error Reading Application",
"Could not read application ID "+state.ID.ValueString()+": "+err.Error(),
)
return
}

state.ID = types.StringValue(app.ID)
state.Name = types.StringValue(app.Name)

owners := []types.Map{}
for _, v := range app.Owners {
owner := map[string]attr.Value{
"type": types.StringValue(v.Type),
"owner": types.StringValue(v.ID),
}
ownermap, diags := types.MapValue(types.StringType, owner)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
owners = append(owners, ownermap)
}
state.Owners = owners

aliases := map[string]attr.Value{}
for k, v := range app.CertificateTemplates {
aliases[k] = types.StringValue(v)
}

aliasmap, diags := types.MapValue(types.StringType, aliases)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}

state.CATemplateAliases = aliasmap

diags = resp.State.Set(ctx, state)
resp.Diagnostics.Append(diags...)
}

func (r *applicationResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
var plan, state applicationResourceModel

diags := req.State.Get(ctx, &state)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
diags = req.Plan.Get(ctx, &plan)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
owners := []tlspc.OwnerAndType{}
for _, v := range plan.Owners {
m := v.Elements()
// TODO: Work out how you're supposed to get an unquoted string out
kind := strings.Trim(m["type"].String(), `"`)
ownerId := strings.Trim(m["owner"].String(), `"`)
if kind != "USER" && kind != "TEAM" {
resp.Diagnostics.AddError(
"Error creating application",
"Could not create application, unsupported owner type: "+kind,
)
return
}
if ownerId == "" {
resp.Diagnostics.AddError(
"Error creating application",
"Could not create application, undefined owner",
)
return
}
owner := tlspc.OwnerAndType{
ID: ownerId,
Type: kind,
}
owners = append(owners, owner)
}

aliases := map[string]string{}
for k, v := range plan.CATemplateAliases.Elements() {
aliases[k] = strings.Trim(v.String(), `"`)
}

application := tlspc.Application{
ID: state.ID.ValueString(),
Name: plan.Name.ValueString(),
Owners: owners,
CertificateTemplates: aliases,
}

updated, err := r.client.UpdateApplication(application)
if err != nil {
resp.Diagnostics.AddError(
"Error updating application",
"Could not update application, unexpected error: "+err.Error(),
)
return
}
plan.ID = types.StringValue(updated.ID)
diags = resp.State.Set(ctx, plan)
resp.Diagnostics.Append(diags...)
}

func (r *applicationResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
var state applicationResourceModel

diags := req.State.Get(ctx, &state)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}

err := r.client.DeleteApplication(state.ID.ValueString())
if err != nil {
resp.Diagnostics.AddError(
"Error Deleting Application",
"Could not delete Application ID "+state.ID.ValueString()+": "+err.Error(),
)
return
}
}

func (r *applicationResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
// Retrieve import ID and save to id attribute
resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
}
Loading

0 comments on commit eb28ec9

Please sign in to comment.