Skip to content

Commit

Permalink
feat: add resource class resource
Browse files Browse the repository at this point in the history
  • Loading branch information
mateuszjenek committed May 24, 2024
1 parent 34c5b09 commit abfb4bb
Show file tree
Hide file tree
Showing 10 changed files with 375 additions and 27 deletions.
50 changes: 50 additions & 0 deletions docs/resources/resource_class.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "humanitec_resource_class Resource - terraform-provider-humanitec"
subcategory: ""
description: |-
DriverDefinition describes the resource driver.
Resource Drivers are code that fulfils the Humanitec Resource Driver Interface.
This interface allows for certain actions to be performed on resources such as creation and destruction.
Humanitec provides numerous Resource Drivers “out of the box”.
It is also possible to use 3rd party Resource Drivers or write your own.
---

# humanitec_resource_class (Resource)

DriverDefinition describes the resource driver.

Resource Drivers are code that fulfils the Humanitec Resource Driver Interface.
This interface allows for certain actions to be performed on resources such as creation and destruction.
Humanitec provides numerous Resource Drivers “out of the box”.
It is also possible to use 3rd party Resource Drivers or write your own.

## Example Usage

```terraform
resource "humanitec_resource_class" "resource_class" {
id = "example"
resource_type = "myssql"
description = "An example resource class"
}
```

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

### Required

- `id` (String) Reflects the class string.
- `resource_type` (String) Defines the resource type this class is applicable for.

### Optional

- `description` (String) A human readable description when this class should be used.

## Import

Import is supported using the following syntax:

```shell
terraform import humanitec_resource_class.example resource_type/class_id
```
1 change: 1 addition & 0 deletions examples/resources/humanitec_resource_class/import.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
terraform import humanitec_resource_class.example resource_type/class_id
5 changes: 5 additions & 0 deletions examples/resources/humanitec_resource_class/resource.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
resource "humanitec_resource_class" "resource_class" {
id = "example"
resource_type = "myssql"
description = "An example resource class"
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ require (
github.com/hashicorp/terraform-plugin-go v0.22.2
github.com/hashicorp/terraform-plugin-log v0.9.0
github.com/hashicorp/terraform-plugin-sdk/v2 v2.33.0
github.com/humanitec/humanitec-go-autogen v0.0.0-20240516095603-93b078e55cd9
github.com/humanitec/humanitec-go-autogen v0.0.0-20240523102513-6cf639116144
github.com/stretchr/testify v1.9.0
sigs.k8s.io/yaml v1.4.0
)
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU
github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/humanitec/humanitec-go-autogen v0.0.0-20240516095603-93b078e55cd9 h1:rW4rt9sN3+4JbltxMkrCkHtqFm7ZInNkjCaY9YhD/u0=
github.com/humanitec/humanitec-go-autogen v0.0.0-20240516095603-93b078e55cd9/go.mod h1:WqItJ/MhAHcjP7LIhIt2/NrgXeXRbLuxvXlin7qY0j4=
github.com/humanitec/humanitec-go-autogen v0.0.0-20240523102513-6cf639116144 h1:h1HURx3DvUaYFxGEFF+iK4eQ4mvKFJtRVnDBHeEZNBM=
github.com/humanitec/humanitec-go-autogen v0.0.0-20240523102513-6cf639116144/go.mod h1:WqItJ/MhAHcjP7LIhIt2/NrgXeXRbLuxvXlin7qY0j4=
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM=
github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
Expand Down
1 change: 1 addition & 0 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ func (p *HumanitecProvider) Resources(ctx context.Context) []func() resource.Res
NewResourcePipeline,
NewResourcePipelineCriteria,
NewResourceRegistry,
NewResourceResourceClass,
NewResourceResourceDriver,
NewResourceRule,
NewResourceSecretStore,
Expand Down
2 changes: 1 addition & 1 deletion internal/provider/resource_environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ func parseEnvironmentResponse(appID string, res *client.EnvironmentResponse, dat
if res.FromDeploy != nil {
fromDeployId = &res.FromDeploy.Id
}

data.FromDeployID = types.StringPointerValue(fromDeployId)
data.AppID = types.StringValue(appID)
data.ID = types.StringValue(res.Id)
Expand Down
247 changes: 247 additions & 0 deletions internal/provider/resource_resource_class.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
package provider

import (
"context"
"fmt"
"strings"

"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/resource/schema/planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/types"

"github.com/humanitec/humanitec-go-autogen"
"github.com/humanitec/humanitec-go-autogen/client"
)

// Ensure provider defined types fully satisfy framework interfaces
var _ resource.Resource = &ResourceResourceClass{}
var _ resource.ResourceWithImportState = &ResourceResourceClass{}

func NewResourceResourceClass() resource.Resource {
return &ResourceResourceClass{}
}

// ResourceResourceClass defines the resource implementation.
type ResourceResourceClass struct {
client *humanitec.Client
orgId string
}

// ResourceClassModel describes the app data model.
type ResourceClassModel struct {
ID types.String `tfsdk:"id"`
ResourceType types.String `tfsdk:"resource_type"`
Description types.String `tfsdk:"description"`
}

func (r *ResourceResourceClass) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_resource_class"
}

func (r *ResourceResourceClass) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
MarkdownDescription: `DriverDefinition describes the resource driver.
Resource Drivers are code that fulfils the Humanitec Resource Driver Interface.
This interface allows for certain actions to be performed on resources such as creation and destruction.
Humanitec provides numerous Resource Drivers “out of the box”.
It is also possible to use 3rd party Resource Drivers or write your own.`,

Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
MarkdownDescription: "Reflects the class string.",
Required: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"resource_type": schema.StringAttribute{
MarkdownDescription: "Defines the resource type this class is applicable for.",
Required: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"description": schema.StringAttribute{
MarkdownDescription: "A human readable description when this class should be used.",
Optional: true,
},
},
}
}

func (r *ResourceResourceClass) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
// Prevent panic if the provider has not been configured.
if req.ProviderData == nil {
return
}

resdata, ok := req.ProviderData.(*HumanitecData)

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

return
}

r.client = resdata.Client
r.orgId = resdata.OrgID
}

func (r *ResourceResourceClass) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var data *ResourceClassModel

// Read Terraform plan data into the model
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)

if resp.Diagnostics.HasError() {
return
}

id := data.ID.ValueString()
resourceType := data.ResourceType.ValueString()
description := data.Description.ValueString()

httpResp, err := r.client.CreateResourceClassWithResponse(ctx, r.orgId, resourceType, client.ResourceClassRequest{
Id: id,
Description: description,
})
if err != nil {
resp.Diagnostics.AddError(HUM_CLIENT_ERR, fmt.Sprintf("Unable to create resource class, got error: %s", err))
return
}

if httpResp.StatusCode() != 200 {
resp.Diagnostics.AddError(HUM_API_ERR, fmt.Sprintf("Unable to create resource class, unexpected status code: %d, body: %s", httpResp.StatusCode(), httpResp.Body))
return
}

parseResourceClassResponse(httpResp.JSON200, data)

// Save data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}

func (r *ResourceResourceClass) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
var data *ResourceClassModel

// Read Terraform prior state data into the model
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)

if resp.Diagnostics.HasError() {
return
}

id := data.ID.ValueString()
resourceType := data.ResourceType.ValueString()

httpResp, err := r.client.GetResourceClassWithResponse(ctx, r.orgId, resourceType, id)
if err != nil {
resp.Diagnostics.AddError(HUM_CLIENT_ERR, fmt.Sprintf("Unable to read resource class, got error: %s", err))
return
}

if httpResp.StatusCode() != 200 {
resp.Diagnostics.AddError(HUM_API_ERR, fmt.Sprintf("Unable to read resource class, unexpected status code: %d, body: %s", httpResp.StatusCode(), httpResp.Body))
return
}

parseResourceClassResponse(httpResp.JSON200, data)

// Save updated data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}

func (r *ResourceResourceClass) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
var data *ResourceClassModel

// Read Terraform plan data into the model
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}

id := data.ID.ValueString()
resourceType := data.ResourceType.ValueString()
description := data.Description.ValueString()

httpResp, err := r.client.UpdateResourceClassWithResponse(ctx, r.orgId, resourceType, id, client.UpdateResourceClassRequest{
Description: description,
})
if err != nil {
resp.Diagnostics.AddError(HUM_CLIENT_ERR, fmt.Sprintf("Unable to update resource class, got error: %s", err))
return
}

if httpResp.StatusCode() != 200 {
resp.Diagnostics.AddError(HUM_API_ERR, fmt.Sprintf("Unable to update resource class, unexpected status code: %d, body: %s", httpResp.StatusCode(), httpResp.Body))
return
}

parseResourceClassResponse(httpResp.JSON200, data)

// Save updated data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}

func (r *ResourceResourceClass) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
var data *ResourceClassModel

// Read Terraform prior state data into the model
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}

id := data.ID.ValueString()
resourceType := data.ResourceType.ValueString()

httpResp, err := r.client.DeleteResourceClassWithResponse(ctx, r.orgId, resourceType, id)
if err != nil {
resp.Diagnostics.AddError(HUM_CLIENT_ERR, fmt.Sprintf("Unable to delete resource class, got error: %s", err))
return
}

if httpResp.StatusCode() != 204 {
resp.Diagnostics.AddError(HUM_API_ERR, fmt.Sprintf("Unable to delete resource class, unexpected status code: %d, body: %s", httpResp.StatusCode(), httpResp.Body))
return
}
}

func (r *ResourceResourceClass) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
idParts := strings.Split(req.ID, "/")

// ensure idParts elements are not empty
for _, idPart := range idParts {
if idPart == "" {
resp.Diagnostics.AddError(
"Unexpected Import Identifier",
fmt.Sprintf("Expected import identifier with format: resource_type/class_id. Got: %q", req.ID),
)
return
}
}

if len(idParts) == 2 {
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("resource_type"), idParts[0])...)
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("id"), idParts[1])...)
} else {
resp.Diagnostics.AddError(
"Unexpected Import Identifier",
fmt.Sprintf("Expected import identifier with format: resource_type/class_id. Got: %q", req.ID),
)
return
}
}

func parseResourceClassResponse(resp *client.ResourceClassResponse, data *ResourceClassModel) {
data.ID = types.StringValue(resp.Id)
data.ResourceType = types.StringValue(resp.ResourceType)
data.Description = types.StringValue(resp.Description)
}
Loading

0 comments on commit abfb4bb

Please sign in to comment.