Skip to content

Commit

Permalink
Implement update
Browse files Browse the repository at this point in the history
  • Loading branch information
davidfischer-ch committed Jun 4, 2024
1 parent 165a375 commit 74792ea
Show file tree
Hide file tree
Showing 6 changed files with 351 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ provider "aria" {

### Optional

- `access_token` (String, Sensitive) The access token to use for making API requests. May also be provided via ARIA_ACCESS_TOKEN environment variable.
- `host` (String) The URI to Aria. May also be provided via ARIA_HOST environment variable.
- `insecure` (Boolean) Whether server should be accessed without verifying the TLS certificate. May also be provided via ARIA_INSECURE environment variable.
- `refresh_token` (String, Sensitive) The refresh token to use for making API requests. May also be provided via ARIA_REFRESH_TOKEN environment variable.
38 changes: 38 additions & 0 deletions docs/resources/abx_secret.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "aria_abx_secret Resource - aria"
subcategory: ""
description: |-
ABX secret resource
---

# aria_abx_secret (Resource)

ABX secret resource

## Example Usage

```terraform
resource "aria_abx_secret" "example" {
name = "THIS_IS_MY_SECRET"
value = "1234pass"
}
output "example_secret" {
value = "My secret ${aria_abx_secret.example.name} ID is ${aria_abx_secret.example.id}"
}
```

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

### Required

- `name` (String) Secret name
- `value` (String, Sensitive) Secret value (cannot be enforced since API don't return it)

### Read-Only

- `encrypted` (Boolean) Secret should be always encrypted!
- `id` (String) Secret identifier
- `org_id` (String) Secret organisation ID
8 changes: 8 additions & 0 deletions examples/resources/aria_abx_secret/resource.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
resource "aria_abx_secret" "example" {
name = "THIS_IS_MY_SECRET"
value = "1234pass"
}

output "example_secret" {
value = "My secret ${aria_abx_secret.example.name} ID is ${aria_abx_secret.example.id}"
}
253 changes: 253 additions & 0 deletions internal/provider/abx_secret_resource.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package provider

import (
"context"
"fmt"

"github.com/go-resty/resty/v2"
"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/hashicorp/terraform-plugin-log/tflog"
)

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

func NewABXSecretResource() resource.Resource {
return &ABXSecretResource{}
}

// ABXSecretResource defines the resource implementation.
type ABXSecretResource struct {
client *resty.Client
}

// ABXSecretResourceModel describes the resource data model.
type ABXSecretResourceModel struct {
Id types.String `tfsdk:"id"`
Name types.String `tfsdk:"name"`
Value types.String `tfsdk:"value"`
Encrypted types.Bool `tfsdk:"encrypted"`
OrgId types.String `tfsdk:"org_id"`
}

// ABXSecretResourceAPIModel describes the resource API model.
type ABXSecretResourceAPIModel struct {
Id string `json:"id"`
Name string `json:"name"`
Value string `json:"value"`
Encrypted bool `json:"encrypted"`
OrgId string `json:"orgId"`
CreatedMillis int64 `json:"createdMillis"`
}

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

func (r *ABXSecretResource) Schema(
ctx context.Context,
req resource.SchemaRequest,
resp *resource.SchemaResponse,
) {
resp.Schema = schema.Schema{
MarkdownDescription: "ABX secret resource",
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
MarkdownDescription: "Secret identifier",
Computed: true,
PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()},
},
"name": schema.StringAttribute{
MarkdownDescription: "Secret name",
Required: true,
},
"value": schema.StringAttribute{
MarkdownDescription: "Secret value (cannot be enforced since API don't return it)",
Required: true,
Sensitive: true,
},
"encrypted": schema.BoolAttribute{
MarkdownDescription: "Secret should be always encrypted!",
Computed: true,
},
"org_id": schema.StringAttribute{
MarkdownDescription: "Secret organisation ID",
Computed: true,
PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()},
},
},
}
}

func (r *ABXSecretResource) Configure(
ctx context.Context,
req resource.ConfigureRequest,
resp *resource.ConfigureResponse,
) {
r.client = GetResourceClient(ctx, req, resp)
}

func (r *ABXSecretResource) Create(
ctx context.Context,
req resource.CreateRequest,
resp *resource.CreateResponse,
) {
var secret ABXSecretResourceModel
var secretRaw ABXSecretResourceAPIModel

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

response, err := r.client.R().
SetBody(ABXSecretResourceAPIModel{
Name: secret.Name.ValueString(),
Value: secret.Value.ValueString(),
Encrypted: true,
}).
SetResult(&secretRaw).
Post("abx/api/resources/action-secrets")

err = handleAPIResponse(ctx, response, err, 200)
if err != nil {
resp.Diagnostics.AddError(
"Client error",
fmt.Sprintf("Unable to create secret, got error: %s", err))
return
}

tflog.Debug(ctx, fmt.Sprintf("Secret %s created", secretRaw.Id))

secret.Id = types.StringValue(secretRaw.Id)
secret.Encrypted = types.BoolValue(secretRaw.Encrypted)
secret.OrgId = types.StringValue(secretRaw.OrgId)

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

func (r *ABXSecretResource) Read(
ctx context.Context,
req resource.ReadRequest,
resp *resource.ReadResponse,
) {
var secret ABXSecretResourceModel
var secretRaw ABXSecretResourceAPIModel

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

secretId := secret.Id.ValueString()
response, err := r.client.R().
SetResult(&secretRaw).
Get("abx/api/resources/action-secrets/" + secretId)

// Handle gracefully a resource that has vanished on the platform
// Beware that some APIs respond with HTTP 404 instead of 403 ...
if response.StatusCode() == 404 {
resp.State.RemoveResource(ctx)
return
}

err = handleAPIResponse(ctx, response, err, 200)
if err != nil {
resp.Diagnostics.AddError(
"Client error",
fmt.Sprintf("Unable to read secret %s, got error: %s", secretId, err))
return
}

secret.Name = types.StringValue(secretRaw.Name)
// secret.Value = types.StringValue(secretRaw.Value)
secret.Encrypted = types.BoolValue(secretRaw.Encrypted)
secret.OrgId = types.StringValue(secretRaw.OrgId)

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

func (r *ABXSecretResource) Update(
ctx context.Context,
req resource.UpdateRequest,
resp *resource.UpdateResponse,
) {
var secret ABXSecretResourceModel
var secretRaw ABXSecretResourceAPIModel

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

secretId := secret.Id.ValueString()
response, err := r.client.R().
SetBody(ABXSecretResourceAPIModel{
Name: secret.Name.ValueString(),
Value: secret.Value.ValueString(),
Encrypted: true,
}).
SetResult(&secretRaw).
Put("abx/api/resources/action-secrets/" + secretId)

err = handleAPIResponse(ctx, response, err, 200)
if err != nil {
resp.Diagnostics.AddError(
"Client error",
fmt.Sprintf("Unable to update secret %s, got error: %s", secretId, err))
return
}

tflog.Debug(ctx, fmt.Sprintf("Secret %s update", secretId))

secret.Name = types.StringValue(secretRaw.Name)
// value is returned with the following '*****' awesome :)
secret.Encrypted = types.BoolValue(secretRaw.Encrypted)
secret.OrgId = types.StringValue(secretRaw.OrgId)

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

func (r *ABXSecretResource) Delete(
ctx context.Context,
req resource.DeleteRequest,
resp *resource.DeleteResponse,
) {
var secret ABXSecretResourceModel

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

secretId := secret.Id.ValueString()
if len(secretId) == 0 {
return
}

response, err := r.client.R().Delete("abx/api/resources/action-secrets/" + secretId)
err = handleAPIResponse(ctx, response, err, 200)
if err != nil {
resp.Diagnostics.AddError(
"Client error",
fmt.Sprintf("Unable to delete secret %s, got error: %s", secretId, err))
}
}
50 changes: 50 additions & 0 deletions internal/provider/abx_secret_resource_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package provider

import (
"fmt"

Check failure on line 7 in internal/provider/abx_secret_resource_test.go

View workflow job for this annotation

GitHub Actions / Build

"fmt" imported and not used (typecheck)

Check failure on line 7 in internal/provider/abx_secret_resource_test.go

View workflow job for this annotation

GitHub Actions / Build

"fmt" imported and not used (typecheck)
"testing"

"github.com/hashicorp/terraform-plugin-testing/helper/resource"
)

func TestAccABXSecretResource(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
// Create and Read testing
{
Config: `
resource "aria_abx_secret" "test" {
name = "THIS_IS_A_TEST"
value = "pass1234"
}`,
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttrSet("aria_abx_secret.test", "id"),
resource.TestCheckResourceAttr("aria_abx_secret.test", "name", "THIS_IS_A_TEST"),
resource.TestCheckResourceAttr("aria_abx_secret.test", "value", "pass1234"),
resource.TestCheckResourceAttr("aria_abx_secret.test", "encrypted", "true"),
),
},
// Update and Read testing
{
Config: `
resource "aria_abx_secret" "test" {
name = "THIS_IS_A_TEST"
value = "newvalue"
}`,
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttrSet("aria_abx_secret.test", "id"),
resource.TestCheckResourceAttr("aria_abx_secret.test", "name", "THIS_IS_A_TEST"),
resource.TestCheckResourceAttr("aria_abx_secret.test", "value", "newvalue"),
resource.TestCheckResourceAttr("aria_abx_secret.test", "encrypted", "true"),
),
},
// Delete testing automatically occurs in TestCase
// TODO Check https://developer.hashicorp.com/terraform/plugin/sdkv2/testing/acceptance-tests/testcase#checkdestroy
},
})
}
1 change: 1 addition & 0 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ func (p *AriaProvider) Configure(ctx context.Context, req provider.ConfigureRequ

func (p *AriaProvider) Resources(ctx context.Context) []func() resource.Resource {
return []func() resource.Resource{
NewABXSecretResource,
NewIconResource,
}
}
Expand Down

0 comments on commit 74792ea

Please sign in to comment.