6 Commits

Author SHA1 Message Date
689069921a v0.0.3 2023-10-24 11:54:34 -04:00
dfd2240fff Update Docs 2023-10-24 11:48:15 -04:00
da54a7062b Update Docs 2023-10-24 11:35:23 -04:00
ef06a879b8 Created SSH management 2023-10-24 11:30:16 -04:00
f0986d7d54 Updates 2023-10-22 19:11:13 -04:00
ff5d061fd2 Removed 2023-10-22 19:01:04 -04:00
15 changed files with 454 additions and 343 deletions

108
README.md
View File

@@ -1,64 +1,72 @@
# Terraform Provider Scaffolding (Terraform Plugin Framework) # Virtfusion Terraform Provider
_This template repository is built on the [Terraform Plugin Framework](https://github.com/hashicorp/terraform-plugin-framework). The template repository built on the [Terraform Plugin SDK](https://github.com/hashicorp/terraform-plugin-sdk) can be found at [terraform-provider-scaffolding](https://github.com/hashicorp/terraform-provider-scaffolding). See [Which SDK Should I Use?](https://developer.hashicorp.com/terraform/plugin/framework-benefits) in the Terraform documentation for additional information._ <p style="color: red">NOTE: This is a work in progress and is not yet ready for production use.</p>
This repository is a *template* for a [Terraform](https://www.terraform.io) provider. It is intended as a starting point for creating Terraform providers, containing:
- A resource and a data source (`internal/provider/`), ## Overview
- Examples (`examples/`) and generated documentation (`docs/`),
- Miscellaneous meta files.
These files contain boilerplate code that you will need to edit to create your own Terraform provider. Tutorials for creating Terraform providers can be found on the [HashiCorp Developer](https://developer.hashicorp.com/terraform/tutorials/providers-plugin-framework) platform. _Terraform Plugin Framework specific guides are titled accordingly._ This is a Terraform provider for the Virtfusion API. It allows you to manage your Virtfusion resources using Terraform.
Please see the [GitHub template repository documentation](https://help.github.com/en/github/creating-cloning-and-archiving-repositories/creating-a-repository-from-a-template) for how to create a new repository from this template on GitHub. # What can I do with this provider?
Once you've written your provider, you'll want to [publish it on the Terraform Registry](https://developer.hashicorp.com/terraform/registry/providers/publishing) so that others can use it. Currently, you're able to manage the following resources:
* Create and delete virtual machines
* Create and delete SSH keys
## Requirements # How do I use this provider?
- [Terraform](https://developer.hashicorp.com/terraform/downloads) >= 1.0 Below is an example of how to use this provider to create a virtual machine and an SSH key.
- [Go](https://golang.org/doc/install) >= 1.19
## Building The Provider ```hcl
terraform {
required_providers {
virtfusion = {
source = "ezscale/virtfusion"
}
}
}
1. Clone the repository provider "virtfusion" {
1. Enter the repository directory endpoint = "virtfusion.example.com"
1. Build the provider using the Go `install` command: api_token = ""
}
```shell variable "common" {
go install type = map(string)
default = {
hypervisor_id = 1
package_id = 12
user_id = 1
}
}
# Create a SSH key
resource "virtfusion_ssh" "key1" {
name = "My Test Key"
public_key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKWyBR+dk5M5MMfmH6Ss5QDSgcAvbCYu0DkqgPKH8O5T testkey@example.com"
user_id = var.common["user_id"]
}
# Create a server
resource "virtfusion_server" "node1" {
hypervisor_id = var.common["hypervisor_id"]
package_id = var.common["package_id"]
user_id = var.common["user_id"]
}
# Initialize the server with the OS we want, the SSH key we want, and the hostname we want.
resource "virtfusion_build" "node1" {
server_id = virtfusion_server.node1.id
name = "node1-demo"
hostname = "node1.example.com"
osid = 34
vnc = true
ipv6 = true
ssh_keys = [virtfusion_ssh.key1.id]
email = true
}
``` ```
## Adding Dependencies # How can I contribute?
This provider uses [Go modules](https://github.com/golang/go/wiki/Modules). If you'd like to contribute, please feel free to open a pull request. If you're unsure of what to work on, please check the issues tab for any open issues.
Please see the Go documentation for the most up to date information about using Go modules.
To add a new dependency `github.com/author/dependency` to your Terraform provider:
```shell
go get github.com/author/dependency
go mod tidy
```
Then commit the changes to `go.mod` and `go.sum`.
## Using the provider
Fill this in for each provider
## Developing the Provider
If you wish to work on the provider, you'll first need [Go](http://www.golang.org) installed on your machine (see [Requirements](#requirements) above).
To compile the provider, run `go install`. This will build the provider and put the provider binary in the `$GOPATH/bin` directory.
To generate or update documentation, run `go generate`.
In order to run the full suite of Acceptance tests, run `make testacc`.
*Note:* Acceptance tests create real resources, and often cost money to run.
```shell
make testacc
```

View File

@@ -1,24 +0,0 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "virtfusion_example Data Source - terraform-provider-virtfusion"
subcategory: ""
description: |-
Example data source
---
# virtfusion_example (Data Source)
Example data source
<!-- schema generated by tfplugindocs -->
## Schema
### Optional
- `configurable_attribute` (String) Example configurable attribute
### Read-Only
- `id` (String) Example identifier

View File

@@ -13,22 +13,6 @@ Virtfusion Server Build Resource
## Example Usage ## Example Usage
```terraform ```terraform
resource "virtfusion_server" "node1" {
package_id = 1
user_id = 1
hypervisor_id = 1
ipv4 = 1
storage = 30
memory = 1024
cores = 1
traffic = 1000
inbound_network_speed = 100
outbound_network_speed = 100
storage_profile = 1
network_profile = 1
}
resource "virtfusion_build" "node1" { resource "virtfusion_build" "node1" {
server_id = virtfusion_server.node1.id server_id = virtfusion_server.node1.id
name = "node1-demo" name = "node1-demo"
@@ -36,7 +20,7 @@ resource "virtfusion_build" "node1" {
osid = 1 osid = 1
vnc = true vnc = true
ipv6 = true ipv6 = true
ssh_keys = [1, 2, 3] ssh_keys = [virtfusion_ssh.dummy_key.id]
email = true email = true
} }
``` ```

View File

@@ -26,7 +26,6 @@ resource "virtfusion_server" "node1" {
outbound_network_speed = 100 outbound_network_speed = 100
storage_profile = 1 storage_profile = 1
network_profile = 1 network_profile = 1
name = "test"
} }
``` ```

39
docs/resources/ssh.md Normal file
View File

@@ -0,0 +1,39 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "virtfusion_ssh Resource - terraform-provider-virtfusion"
subcategory: ""
description: |-
Virtfusion SSH Resource
---
# virtfusion_ssh (Resource)
Virtfusion SSH Resource
## Example Usage
```terraform
resource "virtfusion_ssh" "dummy_key" {
# This is what is displayed in the UI on the SSH keys page.
name = "dummy_key"
public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCRM5gzj6BpVbTEZ8XX5meQOC9X+znTMCQbXTfdqm9IP3HY2JbqH+yfCBWSsLpXim6WvsYtfkAhrtrkdmaX66Wn1uo6XvARwi/5D1VRTM94vwoitJb0rne4OorpwGIGCpDIi1iRA/ERIbAIQpw/2PJfm7q+fEj9TS+n/MzYOOmwTaKPEJ8+wHwXbjcSNoBQmEPonafbQKQN5PXe5rwnTNAqJWhGPHqF2t7lvZy+m7Sl7X1vUVlw+7iZzOVm9iDXmUInc8A0kz18l/O+4ELhRxxzjmSX5/KkN0GG7wS7CHlq9MS2741MS6p0ZNMgTT/04RfsY5JXoOa1gCeAdnXQST9ylvBd6hXubV95lRM8AXAhEJFHpa0Xn1gHMJ4F0cjjvmBIDx39QztuYsNJPk8veBBQwhOzhnJ3Zh2IYTQD+Mwu5yUrJzUt7ia8X5fhjbrYlfUgdH+siBbvJRzyXwnZdHArher55U4xPCJO4qRrFr72Jn+WGzkcY53oLnW5K3NnPaYViCJD2BgJZU1YF8oA3RyEG+2GS7Ksqs2nXXlZ1c+RXLUXM0pxDrwqvYrE3Ae+O/PtZ0cqpesyjxDfH/R2cj86jjdEi7S8nhgkumHwkoac8LCJnoAeC9S7sxmI99VBHcNwCazx3ZL2UAI3Ik/DQBZXcCPXw9MfY25SyQwEYftMKw== dummy_key"
# This is the user ID that the key will be associated with.
user_id = 1
}
```
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- `name` (String) Key Name
- `public_key` (String) Public Key
- `user_id` (Number) User ID
### Read-Only
- `id` (Number) SSH Key ID
- `public_key_hash` (String) Public Key Hash

View File

@@ -1,19 +1,3 @@
resource "virtfusion_server" "node1" {
package_id = 1
user_id = 1
hypervisor_id = 1
ipv4 = 1
storage = 30
memory = 1024
cores = 1
traffic = 1000
inbound_network_speed = 100
outbound_network_speed = 100
storage_profile = 1
network_profile = 1
}
resource "virtfusion_build" "node1" { resource "virtfusion_build" "node1" {
server_id = virtfusion_server.node1.id server_id = virtfusion_server.node1.id
name = "node1-demo" name = "node1-demo"
@@ -21,6 +5,6 @@ resource "virtfusion_build" "node1" {
osid = 1 osid = 1
vnc = true vnc = true
ipv6 = true ipv6 = true
ssh_keys = [1, 2, 3] ssh_keys = [virtfusion_ssh.dummy_key.id]
email = true email = true
} }

View File

@@ -11,5 +11,4 @@ resource "virtfusion_server" "node1" {
outbound_network_speed = 100 outbound_network_speed = 100
storage_profile = 1 storage_profile = 1
network_profile = 1 network_profile = 1
name = "test"
} }

View File

@@ -0,0 +1,9 @@
resource "virtfusion_ssh" "dummy_key" {
# This is what is displayed in the UI on the SSH keys page.
name = "dummy_key"
public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCRM5gzj6BpVbTEZ8XX5meQOC9X+znTMCQbXTfdqm9IP3HY2JbqH+yfCBWSsLpXim6WvsYtfkAhrtrkdmaX66Wn1uo6XvARwi/5D1VRTM94vwoitJb0rne4OorpwGIGCpDIi1iRA/ERIbAIQpw/2PJfm7q+fEj9TS+n/MzYOOmwTaKPEJ8+wHwXbjcSNoBQmEPonafbQKQN5PXe5rwnTNAqJWhGPHqF2t7lvZy+m7Sl7X1vUVlw+7iZzOVm9iDXmUInc8A0kz18l/O+4ELhRxxzjmSX5/KkN0GG7wS7CHlq9MS2741MS6p0ZNMgTT/04RfsY5JXoOa1gCeAdnXQST9ylvBd6hXubV95lRM8AXAhEJFHpa0Xn1gHMJ4F0cjjvmBIDx39QztuYsNJPk8veBBQwhOzhnJ3Zh2IYTQD+Mwu5yUrJzUt7ia8X5fhjbrYlfUgdH+siBbvJRzyXwnZdHArher55U4xPCJO4qRrFr72Jn+WGzkcY53oLnW5K3NnPaYViCJD2BgJZU1YF8oA3RyEG+2GS7Ksqs2nXXlZ1c+RXLUXM0pxDrwqvYrE3Ae+O/PtZ0cqpesyjxDfH/R2cj86jjdEi7S8nhgkumHwkoac8LCJnoAeC9S7sxmI99VBHcNwCazx3ZL2UAI3Ik/DQBZXcCPXw9MfY25SyQwEYftMKw== dummy_key"
# This is the user ID that the key will be associated with.
user_id = 1
}

View File

@@ -1,105 +0,0 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package provider
import (
"context"
"fmt"
"net/http"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
)
// Ensure provider defined types fully satisfy framework interfaces.
var _ datasource.DataSource = &ExampleDataSource{}
func NewVirtfusionDataSource() datasource.DataSource {
return &ExampleDataSource{}
}
// ExampleDataSource defines the data source implementation.
type ExampleDataSource struct {
client *http.Client
}
// ExampleDataSourceModel describes the data source data model.
type ExampleDataSourceModel struct {
ConfigurableAttribute types.String `tfsdk:"configurable_attribute"`
Id types.String `tfsdk:"id"`
}
func (d *ExampleDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_example"
}
func (d *ExampleDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = schema.Schema{
// This description is used by the documentation generator and the language server.
MarkdownDescription: "Example data source",
Attributes: map[string]schema.Attribute{
"configurable_attribute": schema.StringAttribute{
MarkdownDescription: "Example configurable attribute",
Optional: true,
},
"id": schema.StringAttribute{
MarkdownDescription: "Example identifier",
Computed: true,
},
},
}
}
func (d *ExampleDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
// Prevent panic if the provider has not been configured.
if req.ProviderData == nil {
return
}
client, ok := req.ProviderData.(*http.Client)
if !ok {
resp.Diagnostics.AddError(
"Unexpected Data Source Configure Type",
fmt.Sprintf("Expected *http.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData),
)
return
}
d.client = client
}
func (d *ExampleDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var data ExampleDataSourceModel
// Read Terraform configuration data into the model
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}
// If applicable, this is a great opportunity to initialize any necessary
// provider client data and make a call using it.
// httpResp, err := d.client.Do(httpReq)
// if err != nil {
// resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read example, got error: %s", err))
// return
// }
// For the purposes of this example code, hardcoding a response value to
// save into the Terraform state.
data.Id = types.StringValue("example-id")
// Write logs using the tflog package
// Documentation: https://terraform.io/plugin/log
tflog.Trace(ctx, "read a data source")
// Save data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}

View File

@@ -1,32 +0,0 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package provider
import (
"testing"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
)
func TestAccExampleDataSource(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
// Read testing
{
Config: testAccExampleDataSourceConfig,
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("data.virtfusion_server.test", "id", "example-id"),
),
},
},
})
}
const testAccExampleDataSourceConfig = `
data "virtfusion_server" "test" {
configurable_attribute = "example"
}
`

View File

@@ -16,11 +16,11 @@ import (
"path" "path"
) )
// Ensure ScaffoldingProvider satisfies various provider interfaces. // Ensure VirtfusionProvider satisfies various provider interfaces.
var _ provider.Provider = &ScaffoldingProvider{} var _ provider.Provider = &VirtfusionProvider{}
// ScaffoldingProvider defines the provider implementation. // VirtfusionProvider defines the provider implementation.
type ScaffoldingProvider struct { type VirtfusionProvider struct {
// version is set to the provider version on release, "dev" when the // version is set to the provider version on release, "dev" when the
// provider is built and ran locally, and "test" when running acceptance // provider is built and ran locally, and "test" when running acceptance
// testing. // testing.
@@ -33,12 +33,12 @@ type ScaffoldingProviderModel struct {
ApiToken types.String `tfsdk:"api_token"` ApiToken types.String `tfsdk:"api_token"`
} }
func (p *ScaffoldingProvider) Metadata(ctx context.Context, req provider.MetadataRequest, resp *provider.MetadataResponse) { func (p *VirtfusionProvider) Metadata(ctx context.Context, req provider.MetadataRequest, resp *provider.MetadataResponse) {
resp.TypeName = "virtfusion" resp.TypeName = "virtfusion"
resp.Version = p.version resp.Version = p.version
} }
func (p *ScaffoldingProvider) Schema(ctx context.Context, req provider.SchemaRequest, resp *provider.SchemaResponse) { func (p *VirtfusionProvider) Schema(ctx context.Context, req provider.SchemaRequest, resp *provider.SchemaResponse) {
resp.Schema = schema.Schema{ resp.Schema = schema.Schema{
Attributes: map[string]schema.Attribute{ Attributes: map[string]schema.Attribute{
"endpoint": schema.StringAttribute{ "endpoint": schema.StringAttribute{
@@ -53,7 +53,7 @@ func (p *ScaffoldingProvider) Schema(ctx context.Context, req provider.SchemaReq
} }
} }
func (p *ScaffoldingProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) { func (p *VirtfusionProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) {
// Check environment variables // Check environment variables
apiToken := os.Getenv("VIRTFUSION_API_TOKEN") apiToken := os.Getenv("VIRTFUSION_API_TOKEN")
endpoint := os.Getenv("VIRTFUSION_ENDPOINT") endpoint := os.Getenv("VIRTFUSION_ENDPOINT")
@@ -112,17 +112,16 @@ func (p *ScaffoldingProvider) Configure(ctx context.Context, req provider.Config
resp.ResourceData = client resp.ResourceData = client
} }
func (p *ScaffoldingProvider) Resources(ctx context.Context) []func() resource.Resource { func (p *VirtfusionProvider) Resources(ctx context.Context) []func() resource.Resource {
return []func() resource.Resource{ return []func() resource.Resource{
NewVirtfusionServerResource, NewVirtfusionServerResource,
NewVirtfusionServerBuildResource, NewVirtfusionServerBuildResource,
NewVirtfusionSSHResource,
} }
} }
func (p *ScaffoldingProvider) DataSources(ctx context.Context) []func() datasource.DataSource { func (p *VirtfusionProvider) DataSources(ctx context.Context) []func() datasource.DataSource {
return []func() datasource.DataSource{ return []func() datasource.DataSource{}
NewVirtfusionDataSource,
}
} }
type CustomTransport struct { type CustomTransport struct {
@@ -141,7 +140,7 @@ func (c *CustomTransport) RoundTrip(req *http.Request) (*http.Response, error) {
func New(version string) func() provider.Provider { func New(version string) func() provider.Provider {
return func() provider.Provider { return func() provider.Provider {
return &ScaffoldingProvider{ return &VirtfusionProvider{
version: version, version: version,
} }
} }

View File

@@ -1,25 +0,0 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package provider
import (
"testing"
"github.com/hashicorp/terraform-plugin-framework/providerserver"
"github.com/hashicorp/terraform-plugin-go/tfprotov6"
)
// testAccProtoV6ProviderFactories are used to instantiate a provider during
// acceptance testing. The factory function will be invoked for every Terraform
// CLI command executed to create a provider server to which the CLI can
// reattach.
var testAccProtoV6ProviderFactories = map[string]func() (tfprotov6.ProviderServer, error){
"scaffolding": providerserver.NewProtocol6WithError(New("test")()),
}
func testAccPreCheck(t *testing.T) {
// You can add code here to run prior to any test case execution, for example assertions
// about the appropriate environment variables being set are common to see in a pre-check
// function.
}

View File

@@ -1,56 +0,0 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package provider
import (
"fmt"
"testing"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
)
func TestAccExampleResource(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
// Create and Read testing
{
Config: testAccExampleResourceConfig("one"),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("virtfusion_server.test", "configurable_attribute", "one"),
resource.TestCheckResourceAttr("virtfusion_server.test", "defaulted", "example value when not configured"),
resource.TestCheckResourceAttr("virtfusion_server.test", "id", "example-id"),
),
},
// ImportState testing
{
ResourceName: "virtfusion_server.test",
ImportState: true,
ImportStateVerify: true,
// This is not normally necessary, but is here because this
// example code does not have an actual upstream service.
// Once the Read method is able to refresh information from
// the upstream service, this can be removed.
ImportStateVerifyIgnore: []string{"configurable_attribute", "defaulted"},
},
// Update and Read testing
{
Config: testAccExampleResourceConfig("two"),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("virtfusion_server.test", "configurable_attribute", "two"),
),
},
// Delete testing automatically occurs in TestCase
},
})
}
func testAccExampleResourceConfig(configurableAttribute string) string {
return fmt.Sprintf(`
resource "virtfusion_server" "test" {
configurable_attribute = %[1]q
}
`, configurableAttribute)
}

View File

@@ -0,0 +1,332 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package provider
import (
"bytes"
"context"
"encoding/json"
"fmt"
"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"
"io"
"net/http"
)
// Ensure provider defined types fully satisfy framework interfaces.
var _ resource.Resource = &VirtfusionSSHResource{}
var _ resource.ResourceWithImportState = &VirtfusionSSHResource{}
func NewVirtfusionSSHResource() resource.Resource {
return &VirtfusionSSHResource{}
}
// VirtfusionSSHResource defines the resource implementation.
type VirtfusionSSHResource struct {
client *http.Client
}
// VirtfusionSSHResourceModel describes the resource data model.
type VirtfusionSSHResourceModel struct {
UserId *int64 `tfsdk:"user_id" json:"userId"`
Name *string `tfsdk:"name" json:"name"`
PublicKey *string `tfsdk:"public_key" json:"publicKey"`
Id types.Int64 `tfsdk:"id" json:"id,omitempty"`
}
func (r *VirtfusionSSHResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_ssh"
}
func (r *VirtfusionSSHResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
// This description is used by the documentation generator and the language server.
MarkdownDescription: "Virtfusion SSH Resource",
Attributes: map[string]schema.Attribute{
"user_id": schema.Int64Attribute{
Description: "User ID",
Required: true,
},
"name": schema.StringAttribute{
Description: "Key Name",
Required: true,
},
"public_key": schema.StringAttribute{
Description: "Public Key",
Required: true,
},
"id": schema.Int64Attribute{
Description: "SSH Key ID",
Computed: true,
},
},
}
}
func (r *VirtfusionSSHResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
// Prevent panic if the provider has not been configured.
if req.ProviderData == nil {
return
}
client, ok := req.ProviderData.(*http.Client)
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 = client
}
func (r *VirtfusionSSHResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var data VirtfusionSSHResourceModel
// Read Terraform plan data into the model
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}
createReq := VirtfusionSSHResourceModel{
UserId: data.UserId,
Name: data.Name,
PublicKey: data.PublicKey,
}
// Convert the model to JSON
jsonReq, err := json.Marshal(createReq)
if err != nil {
resp.Diagnostics.AddError(
"Failed to marshal request body",
fmt.Sprintf("Failed to marshal request body: %s", err.Error()),
)
return
}
httpReq, err := r.client.Post("/ssh_keys", "application/json", bytes.NewBuffer(jsonReq))
if err != nil {
resp.Diagnostics.AddError(
"Request failed",
fmt.Sprintf("Request failed: %s", err.Error()),
)
return
}
defer func(Body io.ReadCloser) {
err := Body.Close()
if err != nil {
resp.Diagnostics.AddError(
"Failed to close response body",
fmt.Sprintf("Failed to close response body: %s", err.Error()),
)
}
}(httpReq.Body)
if httpReq.StatusCode != 201 {
if httpReq.StatusCode == 422 {
responseBody, _ := io.ReadAll(httpReq.Body)
var errorResponse map[string]interface{}
err = json.Unmarshal(responseBody, &errorResponse)
if errors, exists := errorResponse["errors"]; exists {
resp.Diagnostics.AddError(
"Failed to create SSH key",
fmt.Sprintf("Errors from server: %v", errors),
)
return
}
}
resp.Diagnostics.AddError(
"Invalid Request",
fmt.Sprintf("Failed to create SSH key: %s", httpReq.Status),
)
return
}
// Read the response body into the model. The response is expected to be a JSON object with the body of the created
// ssh key within the `data` field. The `data` field is a JSON object with the ssh key data.
responseBody, err := io.ReadAll(httpReq.Body)
type ResponseData struct {
Data struct {
Id int64 `json:"id"`
Name string `json:"name"`
Type string `json:"type"`
CreatedAt string `json:"createdAt"`
} `json:"data"`
}
var responseData ResponseData
// Unmarshal the response body into the model
err = json.Unmarshal(responseBody, &responseData)
if err != nil {
resp.Diagnostics.AddError(
"Failed to unmarshal response body",
fmt.Sprintf("Failed to unmarshal response body: %s", err.Error()),
)
return
}
data.Id = types.Int64Value(responseData.Data.Id)
data.Name = &responseData.Data.Name
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}
func (r *VirtfusionSSHResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
var data VirtfusionSSHResourceModel
// Read Terraform prior state data into the model
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}
httpReq, err := http.NewRequest("GET", fmt.Sprintf("/ssh_keys/%d", data.Id.ValueInt64()), nil)
if err != nil {
resp.Diagnostics.AddError(
"Failed to Create Request",
fmt.Sprintf("Failed to create a new HTTP request: %s", err.Error()),
)
return
}
// If the resource returns a 404, then the resource has been deleted. Return an empty state.
httpResponse, err := r.client.Do(httpReq)
defer func(Body io.ReadCloser) {
err := Body.Close()
if err != nil {
resp.Diagnostics.AddError(
"Failed to close response body",
fmt.Sprintf("Failed to close response body: %s", err.Error()),
)
}
}(httpResponse.Body)
if err != nil {
resp.Diagnostics.AddError(
"Failed to Execute Request",
fmt.Sprintf("Failed to execute HTTP request: %s", err.Error()),
)
return
}
if httpResponse.StatusCode == 404 {
resp.State.RemoveResource(ctx)
return
}
var responseData struct {
Data struct {
Id int64 `json:"id"`
Name string `json:"name"`
Type string `json:"type"`
Enabled bool `json:"enabled"`
CreatedAt string `json:"created"`
UpdatedAt string `json:"updated"`
PublicKeyHash string `json:"publicKey"`
} `json:"data"`
}
err = json.NewDecoder(httpResponse.Body).Decode(&responseData)
if err != nil {
resp.Diagnostics.AddError(
"Failed to decode response body",
fmt.Sprintf("Failed to decode response body: %s", err.Error()),
)
return
}
data.Name = &responseData.Data.Name
// Save updated data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}
func (r *VirtfusionSSHResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
var data VirtfusionSSHResourceModel
// Read Terraform plan data into the model
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}
// Save updated data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}
func (r *VirtfusionSSHResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
var data VirtfusionSSHResourceModel
// Read Terraform prior state data into the model
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}
httpReq, err := http.NewRequest("DELETE", fmt.Sprintf("/ssh_keys/%d", data.Id.ValueInt64()), nil)
if err != nil {
resp.Diagnostics.AddError(
"Failed to Create Request",
fmt.Sprintf("Failed to create a new HTTP request: %s", err.Error()),
)
return
}
// Add any additional headers (Content-Type, etc.)
httpReq.Header.Set("Content-Type", "application/json")
httpResponse, err := r.client.Do(httpReq)
if err != nil {
resp.Diagnostics.AddError(
"Failed to Execute Request",
fmt.Sprintf("Failed to execute HTTP request: %s", err.Error()),
)
return
}
if httpResponse.StatusCode != 204 {
resp.Diagnostics.AddError(
"Failed to Delete Resource",
fmt.Sprintf("Failed to delete resource: %s", httpResponse.Status),
)
return
}
if err != nil {
resp.Diagnostics.AddError(
"Request failed",
fmt.Sprintf("Request failed: %s", err.Error()),
)
return
}
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}
func (r *VirtfusionSSHResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
}

View File

@@ -26,7 +26,7 @@ import (
var ( var (
// these will be set by the goreleaser configuration // these will be set by the goreleaser configuration
// to appropriate values for the compiled binary. // to appropriate values for the compiled binary.
version string = "dev" version string = "0.0.3"
// goreleaser can pass other information to the main package, such as the specific commit // goreleaser can pass other information to the main package, such as the specific commit
// https://goreleaser.com/cookbooks/using-main.version/ // https://goreleaser.com/cookbooks/using-main.version/
@@ -40,7 +40,7 @@ func main() {
opts := providerserver.ServeOpts{ opts := providerserver.ServeOpts{
// TODO: Update this string with the published name of your provider. // TODO: Update this string with the published name of your provider.
Address: "registry.terraform.io/hashicorp/scaffolding", Address: "registry.terraform.io/EZSCALE/virtfusion",
Debug: debug, Debug: debug,
} }