188 lines
7.3 KiB
Go
188 lines
7.3 KiB
Go
package provider
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
|
dsschema "github.com/hashicorp/terraform-plugin-framework/datasource/schema"
|
|
"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/setdefault"
|
|
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
|
|
"github.com/hashicorp/terraform-plugin-framework/types"
|
|
|
|
"git.veillax.com/Veillax/hhctl/client"
|
|
)
|
|
|
|
var (
|
|
_ resource.Resource = &edgeResource{}
|
|
_ resource.ResourceWithConfigure = &edgeResource{}
|
|
_ resource.ResourceWithImportState = &edgeResource{}
|
|
_ datasource.DataSource = &edgeData{}
|
|
_ datasource.DataSourceWithConfigure = &edgeData{}
|
|
)
|
|
|
|
func NewEdge() resource.Resource { return &edgeResource{} }
|
|
|
|
type edgeResource struct{ c *client.Client }
|
|
|
|
type edgeModel struct {
|
|
ID types.String `tfsdk:"id"`
|
|
Name types.String `tfsdk:"name"`
|
|
TunnelAddr types.String `tfsdk:"tunnel_addr"`
|
|
OrgIDs types.Set `tfsdk:"org_ids"`
|
|
Token types.String `tfsdk:"token"`
|
|
IPs types.List `tfsdk:"ips"`
|
|
}
|
|
|
|
func (r *edgeResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
|
|
resp.TypeName = req.ProviderTypeName + "_edge"
|
|
}
|
|
|
|
func (r *edgeResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
|
|
resp.Schema = schema.Schema{
|
|
Description: "An edge: a public Blacktip node. Needs a server API key.",
|
|
Attributes: map[string]schema.Attribute{
|
|
"id": schema.StringAttribute{Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}},
|
|
"name": schema.StringAttribute{Required: true},
|
|
"tunnel_addr": schema.StringAttribute{Required: true, Description: "The edge's public WireGuard endpoint, host:port (UDP)."},
|
|
"org_ids": schema.SetAttribute{Optional: true, Computed: true, ElementType: types.StringType, Default: setdefault.StaticValue(setOf(nil)),
|
|
Description: "Organizations allowed to use the edge; empty means all."},
|
|
"token": schema.StringAttribute{Computed: true, Sensitive: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()},
|
|
Description: "The token to give the Blacktip program."},
|
|
"ips": schema.ListAttribute{Computed: true, ElementType: types.StringType, Description: "Addresses the edge reported it can bind."},
|
|
},
|
|
}
|
|
}
|
|
|
|
func (r *edgeResource) Configure(_ context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) {
|
|
r.c = clientFrom(req.ProviderData)
|
|
}
|
|
|
|
func ipsList(ctx context.Context, ips []string) types.List {
|
|
if ips == nil {
|
|
ips = []string{}
|
|
}
|
|
l, _ := types.ListValueFrom(ctx, types.StringType, ips)
|
|
return l
|
|
}
|
|
|
|
func (r *edgeResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
|
|
var plan edgeModel
|
|
if resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...); resp.Diagnostics.HasError() {
|
|
return
|
|
}
|
|
e, err := r.c.CreateEdge(client.Edge{Name: plan.Name.ValueString(), TunnelAddr: plan.TunnelAddr.ValueString(), OrgIDs: stringsOf(ctx, plan.OrgIDs)})
|
|
if err != nil {
|
|
resp.Diagnostics.AddError("Creating edge", err.Error())
|
|
return
|
|
}
|
|
plan.ID, plan.Token, plan.IPs = types.StringValue(e.ID), types.StringValue(e.Token), ipsList(ctx, e.IPs)
|
|
resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
|
|
}
|
|
|
|
func (r *edgeResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
|
|
var state edgeModel
|
|
if resp.Diagnostics.Append(req.State.Get(ctx, &state)...); resp.Diagnostics.HasError() {
|
|
return
|
|
}
|
|
e, err := r.c.GetEdge(state.ID.ValueString())
|
|
if client.IsNotFound(err) {
|
|
resp.State.RemoveResource(ctx)
|
|
return
|
|
}
|
|
if err != nil {
|
|
resp.Diagnostics.AddError("Reading edge", err.Error())
|
|
return
|
|
}
|
|
state.Name, state.TunnelAddr, state.OrgIDs, state.IPs = types.StringValue(e.Name), types.StringValue(e.TunnelAddr), setOf(e.OrgIDs), ipsList(ctx, e.IPs)
|
|
if e.Token != "" {
|
|
state.Token = types.StringValue(e.Token)
|
|
}
|
|
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
|
|
}
|
|
|
|
func (r *edgeResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
|
|
var plan, state edgeModel
|
|
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
|
|
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
|
|
if resp.Diagnostics.HasError() {
|
|
return
|
|
}
|
|
e, err := r.c.UpdateEdge(client.Edge{ID: state.ID.ValueString(), Name: plan.Name.ValueString(), TunnelAddr: plan.TunnelAddr.ValueString(), OrgIDs: stringsOf(ctx, plan.OrgIDs)})
|
|
if err != nil {
|
|
resp.Diagnostics.AddError("Updating edge", err.Error())
|
|
return
|
|
}
|
|
plan.ID, plan.Token, plan.IPs = state.ID, state.Token, ipsList(ctx, e.IPs)
|
|
resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
|
|
}
|
|
|
|
func (r *edgeResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
|
|
var state edgeModel
|
|
if resp.Diagnostics.Append(req.State.Get(ctx, &state)...); resp.Diagnostics.HasError() {
|
|
return
|
|
}
|
|
if err := r.c.DeleteEdge(state.ID.ValueString()); err != nil && !client.IsNotFound(err) {
|
|
resp.Diagnostics.AddError("Deleting edge", err.Error())
|
|
}
|
|
}
|
|
|
|
func (r *edgeResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
|
|
importByID(ctx, req, resp)
|
|
}
|
|
|
|
// ---- data source: look an edge up by name, so ports can refer to one created elsewhere ----
|
|
|
|
func NewEdgeData() datasource.DataSource { return &edgeData{} }
|
|
|
|
type edgeData struct{ c *client.Client }
|
|
|
|
type edgeDataModel struct {
|
|
ID types.String `tfsdk:"id"`
|
|
Name types.String `tfsdk:"name"`
|
|
IPs types.List `tfsdk:"ips"`
|
|
Online types.Bool `tfsdk:"online"`
|
|
}
|
|
|
|
func (d *edgeData) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
|
|
resp.TypeName = req.ProviderTypeName + "_edge"
|
|
}
|
|
|
|
func (d *edgeData) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
|
resp.Schema = dsschema.Schema{
|
|
Description: "An existing edge, found by name. Works with an organization key.",
|
|
Attributes: map[string]dsschema.Attribute{
|
|
"id": dsschema.StringAttribute{Computed: true},
|
|
"name": dsschema.StringAttribute{Required: true},
|
|
"ips": dsschema.ListAttribute{Computed: true, ElementType: types.StringType},
|
|
"online": dsschema.BoolAttribute{Computed: true},
|
|
},
|
|
}
|
|
}
|
|
|
|
func (d *edgeData) Configure(_ context.Context, req datasource.ConfigureRequest, _ *datasource.ConfigureResponse) {
|
|
d.c = clientFrom(req.ProviderData)
|
|
}
|
|
|
|
func (d *edgeData) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
|
|
var m edgeDataModel
|
|
if resp.Diagnostics.Append(req.Config.Get(ctx, &m)...); resp.Diagnostics.HasError() {
|
|
return
|
|
}
|
|
edges, err := d.c.Edges()
|
|
if err != nil {
|
|
resp.Diagnostics.AddError("Listing edges", err.Error())
|
|
return
|
|
}
|
|
for _, e := range edges {
|
|
if e.Name == m.Name.ValueString() {
|
|
m.ID, m.IPs, m.Online = types.StringValue(e.ID), ipsList(ctx, e.IPs), types.BoolValue(e.Online)
|
|
resp.Diagnostics.Append(resp.State.Set(ctx, &m)...)
|
|
return
|
|
}
|
|
}
|
|
resp.Diagnostics.AddError("Edge not found", "No edge named "+m.Name.ValueString()+" is visible to this key.")
|
|
}
|