Skip to content

Commit

Permalink
feat: Add SSH Connection worker resource (#815)
Browse files Browse the repository at this point in the history
  • Loading branch information
denys-octopus authored Nov 14, 2024
1 parent 94b4ada commit 6b16b17
Show file tree
Hide file tree
Showing 9 changed files with 573 additions and 2 deletions.
71 changes: 71 additions & 0 deletions docs/resources/ssh_connection_worker.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "octopusdeploy_ssh_connection_worker Resource - terraform-provider-octopusdeploy"
subcategory: ""
description: |-
This resource manages a SSH connection worker in Octopus Deploy.
---

# octopusdeploy_ssh_connection_worker (Resource)

This resource manages a SSH connection worker in Octopus Deploy.

## Example Usage

```terraform
resource "octopusdeploy_ssh_connection_worker" "minimum" {
name = "ssh_worker"
machine_policy_id = "machine-policy-1"
worker_pools = ["worker-pools-1"]
account_id = "account-42"
host = "hostname"
port = 22
fingerprint = "SHA256: 12345abc"
dotnet_platform = "linux-x64"
}
resource "octopusdeploy_ssh_connection_worker" "optionals" {
name = "optional_ssh_worker"
machine_policy_id = "machine-policy-1"
worker_pools = ["worker-pools-1", "worker-pools-2"]
account_id = "account-42"
host = "hostname"
port = 22
fingerprint = "SHA256: 12345abc"
dotnet_platform = "linux-x64"
proxy_id = "proxy-31"
is_disabled = true
}
```

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

### Required

- `account_id` (String) Connection account
- `dotnet_platform` (String) NET Core platform of self-contained version of Calamari
- `fingerprint` (String) The host fingerprint to be verified
- `host` (String) The hostname or IP address of the deployment target to connect to
- `machine_policy_id` (String) Select the machine policy
- `name` (String) The name of this resource.
- `port` (Number) The port number of the host to connect to (usually 22)
- `worker_pool_ids` (Set of String) Select at least one worker pool for the worker

### Optional

- `is_disabled` (Boolean) When disabled, worker will not be included in any deployments
- `proxy_id` (String) Specify the connection type for the Tentacle: direct(when not set) or via a proxy server.
- `space_id` (String) The space ID associated with this Listening tentacle worker.

### Read-Only

- `id` (String) The unique ID for this resource.

## Import

Import is supported using the following syntax:

```shell
terraform import [options] octopusdeploy_ssh_connection_worker.<name> <machine-id>
```
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
terraform import [options] octopusdeploy_ssh_connection_worker.<name> <machine-id>
23 changes: 23 additions & 0 deletions examples/resources/octopusdeploy_ssh_connection_worker/resource.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
resource "octopusdeploy_ssh_connection_worker" "minimum" {
name = "ssh_worker"
machine_policy_id = "machine-policy-1"
worker_pools = ["worker-pools-1"]
account_id = "account-42"
host = "hostname"
port = 22
fingerprint = "SHA256: 12345abc"
dotnet_platform = "linux-x64"
}

resource "octopusdeploy_ssh_connection_worker" "optionals" {
name = "optional_ssh_worker"
machine_policy_id = "machine-policy-1"
worker_pools = ["worker-pools-1", "worker-pools-2"]
account_id = "account-42"
host = "hostname"
port = 22
fingerprint = "SHA256: 12345abc"
dotnet_platform = "linux-x64"
proxy_id = "proxy-31"
is_disabled = true
}
2 changes: 0 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -142,5 +142,3 @@ require (
google.golang.org/protobuf v1.34.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)


1 change: 1 addition & 0 deletions octopusdeploy_framework/framework_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ func (p *octopusDeployFrameworkProvider) Resources(ctx context.Context) []func()
NewTenantResource,
NewTentacleCertificateResource,
NewListeningTentacleWorkerResource,
NewSSHConnectionWorkerResource,
NewScriptModuleResource,
NewUserResource,
}
Expand Down
175 changes: 175 additions & 0 deletions octopusdeploy_framework/resource_ssh_connection_worker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
package octopusdeploy_framework

import (
"context"
"fmt"
"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/machines"
"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/workers"
"github.com/OctopusDeploy/terraform-provider-octopusdeploy/internal/errors"
"github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/schemas"
"github.com/OctopusDeploy/terraform-provider-octopusdeploy/octopusdeploy_framework/util"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
)

type sshConnectionWorkerResource struct {
*Config
}

func NewSSHConnectionWorkerResource() resource.Resource {
return &sshConnectionWorkerResource{}
}

var _ resource.ResourceWithImportState = &sshConnectionWorkerResource{}

func (r *sshConnectionWorkerResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = util.GetTypeName("ssh_connection_worker")
}

func (r *sshConnectionWorkerResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schemas.SSHConnectionWorkerSchema{}.GetResourceSchema()
}

func (r *sshConnectionWorkerResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
r.Config = ResourceConfiguration(req, resp)
}

func (r *sshConnectionWorkerResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var data *schemas.SSHConnectionWorkerResourceModel
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}

worker := createSSHConnectionWorkerResource(ctx, data)

tflog.Info(ctx, fmt.Sprintf("creating SSH connection worker: %s", data.Name.ValueString()))

client := r.Config.Client
createdWorker, err := workers.Add(client, worker)
if err != nil {
resp.Diagnostics.AddError("unable to create SSH connection worker", err.Error())
return
}

updateDataFromSSHConnectionWorker(ctx, data, data.SpaceID.ValueString(), createdWorker)

tflog.Info(ctx, fmt.Sprintf("SSH connection worker created (%s)", data.ID))
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}

func (r *sshConnectionWorkerResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
var data *schemas.SSHConnectionWorkerResourceModel
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}

tflog.Info(ctx, fmt.Sprintf("reading SSH connection worker (%s)", data.ID))

client := r.Config.Client
worker, err := workers.GetByID(client, data.SpaceID.ValueString(), data.ID.ValueString())
if err != nil {
if err := errors.ProcessApiErrorV2(ctx, resp, data, err, "SSH connection worker"); err != nil {
resp.Diagnostics.AddError("unable to load SSH connection worker", err.Error())
}
return
}

if worker.Endpoint.GetCommunicationStyle() != "Ssh" {
resp.Diagnostics.AddError("unable to load SSH connection worker", "found resource is not SSH connection worker")
return
}

updateDataFromSSHConnectionWorker(ctx, data, data.SpaceID.ValueString(), worker)

tflog.Info(ctx, fmt.Sprintf("SSH connection worker read (%s)", worker.GetID()))
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}

func (r *sshConnectionWorkerResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
var data, state *schemas.SSHConnectionWorkerResourceModel
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
if resp.Diagnostics.HasError() {
return
}

tflog.Debug(ctx, fmt.Sprintf("updating SSH connection worker '%s'", data.ID.ValueString()))

worker := createSSHConnectionWorkerResource(ctx, data)
worker.ID = state.ID.ValueString()

tflog.Info(ctx, fmt.Sprintf("updating SSH connection worker (%s)", data.ID))

client := r.Config.Client
updatedWorker, err := workers.Update(client, worker)
if err != nil {
resp.Diagnostics.AddError("unable to update SSH connection worker", err.Error())
return
}

updateDataFromSSHConnectionWorker(ctx, data, state.SpaceID.ValueString(), updatedWorker)

tflog.Info(ctx, fmt.Sprintf("SSH connection worker updated (%s)", data.ID))

resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}

func (r *sshConnectionWorkerResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
var data schemas.SSHConnectionWorkerResourceModel

resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}

if err := workers.DeleteByID(r.Config.Client, data.SpaceID.ValueString(), data.ID.ValueString()); err != nil {
resp.Diagnostics.AddError("unable to delete SSH connection worker", err.Error())
return
}
}

func createSSHConnectionWorkerResource(ctx context.Context, data *schemas.SSHConnectionWorkerResourceModel) *machines.Worker {
endpoint := machines.NewSSHEndpoint(data.Host.ValueString(), int(data.Port.ValueInt64()), data.Fingerprint.ValueString())
endpoint.AccountID = data.AccountId.ValueString()
endpoint.DotNetCorePlatform = data.DotnetPlatform.ValueString()
endpoint.ProxyID = data.ProxyID.ValueString()

worker := machines.NewWorker(data.Name.ValueString(), endpoint)
worker.SpaceID = data.SpaceID.ValueString()
worker.IsDisabled = data.IsDisabled.ValueBool()
worker.MachinePolicyID = data.MachinePolicyID.ValueString()

if !data.WorkerPoolIDs.IsNull() {
var workerPools []string
data.WorkerPoolIDs.ElementsAs(ctx, &workerPools, false)
worker.WorkerPoolIDs = workerPools
}

return worker
}

func updateDataFromSSHConnectionWorker(ctx context.Context, data *schemas.SSHConnectionWorkerResourceModel, spaceId string, worker *machines.Worker) {
data.ID = types.StringValue(worker.ID)
data.SpaceID = types.StringValue(spaceId)
data.Name = types.StringValue(worker.Name)
data.IsDisabled = types.BoolValue(worker.IsDisabled)
data.MachinePolicyID = types.StringValue(worker.MachinePolicyID)
data.WorkerPoolIDs, _ = types.SetValueFrom(ctx, types.StringType, worker.WorkerPoolIDs)

endpoint := worker.Endpoint.(*machines.SSHEndpoint)
data.AccountId = types.StringValue(endpoint.AccountID)
data.Host = types.StringValue(endpoint.Host)
data.Port = types.Int64Value(int64(endpoint.Port))
data.Fingerprint = types.StringValue(endpoint.Fingerprint)
if endpoint.ProxyID != "" {
data.ProxyID = types.StringValue(endpoint.ProxyID)
}
}

func (*sshConnectionWorkerResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
}
Loading

0 comments on commit 6b16b17

Please sign in to comment.