Skip to content

Commit

Permalink
Add firewall_alias resource
Browse files Browse the repository at this point in the history
  • Loading branch information
browningluke committed Jul 30, 2023
1 parent 54bdae7 commit c45f950
Show file tree
Hide file tree
Showing 4 changed files with 512 additions and 0 deletions.
2 changes: 2 additions & 0 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ func (p *OPNsenseProvider) Resources(ctx context.Context) []func() resource.Reso
// Firewall
service.NewFirewallFilterResource,
service.NewFirewallNATResource,
service.NewFirewallAliasResource,
service.NewFirewallCategoryResource,
}
}
Expand All @@ -140,6 +141,7 @@ func (p *OPNsenseProvider) DataSources(ctx context.Context) []func() datasource.
// Firewall
service.NewFirewallFilterDataSource,
service.NewFirewallNATDataSource,
service.NewFirewallAliasDataSource,
service.NewFirewallCategoryDataSource,
}
}
Expand Down
80 changes: 80 additions & 0 deletions internal/service/firewall_alias_data_source.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package service

import (
"context"
"fmt"
"github.com/browningluke/opnsense-go/pkg/api"
"github.com/browningluke/opnsense-go/pkg/opnsense"
"github.com/hashicorp/terraform-plugin-framework/datasource"
)

// Ensure provider defined types fully satisfy framework interfaces.
var _ datasource.DataSource = &FirewallAliasDataSource{}

func NewFirewallAliasDataSource() datasource.DataSource {
return &FirewallAliasDataSource{}
}

// FirewallAliasDataSource defines the data source implementation.
type FirewallAliasDataSource struct {
client opnsense.Client
}

func (d *FirewallAliasDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_firewall_alias"
}

func (d *FirewallAliasDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = FirewallAliasDataSourceSchema()
}

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

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

d.client = opnsense.NewClient(apiClient)
}

func (d *FirewallAliasDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var data *FirewallAliasResourceModel

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

if resp.Diagnostics.HasError() {
return
}

// Get firewall alias from OPNsense unbound API
resourceStruct, err := d.client.Firewall().GetAlias(ctx, data.Id.ValueString())
if err != nil {
resp.Diagnostics.AddError("Client Error",
fmt.Sprintf("Unable to read firewall alias, got error: %s", err))
return
}

// Convert OPNsense struct to TF schema
resourceModel, err := convertFirewallAliasStructToSchema(resourceStruct)
if err != nil {
resp.Diagnostics.AddError("Client Error",
fmt.Sprintf("Unable to read firewall alias, got error: %s", err))
return
}

// ID cannot be added by convert... func, have to add here
resourceModel.Id = data.Id

// Save updated data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &resourceModel)...)
}
190 changes: 190 additions & 0 deletions internal/service/firewall_alias_resource.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
package service

import (
"context"
"errors"
"fmt"
"github.com/browningluke/opnsense-go/pkg/api"
"github.com/browningluke/opnsense-go/pkg/errs"
"github.com/browningluke/opnsense-go/pkg/opnsense"
"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"
)

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

func NewFirewallAliasResource() resource.Resource {
return &FirewallAliasResource{}
}

// FirewallAliasResource defines the resource implementation.
type FirewallAliasResource struct {
client opnsense.Client
}

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

func (r *FirewallAliasResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = FirewallAliasResourceSchema()
}

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

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

r.client = opnsense.NewClient(apiClient)
}

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

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

if resp.Diagnostics.HasError() {
return
}

// Convert TF schema OPNsense struct
resourceStruct, err := convertFirewallAliasSchemaToStruct(data)
if err != nil {
resp.Diagnostics.AddError("Client Error",
fmt.Sprintf("Unable to parse firwall alias, got error: %s", err))
return
}

// Add firewall alias to unbound
id, err := r.client.Firewall().AddAlias(ctx, resourceStruct)
if err != nil {
if id != "" {
// Tag new resource with ID from OPNsense
data.Id = types.StringValue(id)

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

resp.Diagnostics.AddError("Client Error",
fmt.Sprintf("Unable to create firewall alias, got error: %s", err))
return
}

// Tag new resource with ID from OPNsense
data.Id = types.StringValue(id)

// Write logs using the tflog package
tflog.Trace(ctx, "created a resource")

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

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

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

if resp.Diagnostics.HasError() {
return
}

// Get firewall alias from OPNsense unbound API
resourceStruct, err := r.client.Firewall().GetAlias(ctx, data.Id.ValueString())
if err != nil {
var notFoundError *errs.NotFoundError
if errors.As(err, &notFoundError) {
tflog.Warn(ctx, fmt.Sprintf("firewall alias not present in remote, removing from state"))
resp.State.RemoveResource(ctx)
return
}

resp.Diagnostics.AddError("Client Error",
fmt.Sprintf("Unable to read firewall alias, got error: %s", err))
return
}

// Convert OPNsense struct to TF schema
resourceModel, err := convertFirewallAliasStructToSchema(resourceStruct)
if err != nil {
resp.Diagnostics.AddError("Client Error",
fmt.Sprintf("Unable to read firewall alias, got error: %s", err))
return
}

// ID cannot be added by convert... func, have to add here
resourceModel.Id = data.Id

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

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

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

if resp.Diagnostics.HasError() {
return
}

// Convert TF schema OPNsense struct
resourceStruct, err := convertFirewallAliasSchemaToStruct(data)
if err != nil {
resp.Diagnostics.AddError("Client Error",
fmt.Sprintf("Unable to parse firewall alias, got error: %s", err))
return
}

// Update firewall alias in unbound
err = r.client.Firewall().UpdateAlias(ctx, data.Id.ValueString(), resourceStruct)
if err != nil {
resp.Diagnostics.AddError("Client Error",
fmt.Sprintf("Unable to create firewall alias, got error: %s", err))
return
}

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

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

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

if resp.Diagnostics.HasError() {
return
}

err := r.client.Firewall().DeleteAlias(ctx, data.Id.ValueString())

if err != nil {
resp.Diagnostics.AddError("Client Error",
fmt.Sprintf("Unable to delete firewall alias, got error: %s", err))
return
}
}

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

0 comments on commit c45f950

Please sign in to comment.