hhctl CLI and Terraform provider for Hammerhead

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
VeillaxandClaude Sonnet 5.5 committed 2026-10-27 17:02:15 -06:00
commit bf340cdab4
19 files changed
+2442

No files matched your search

+92
View File
@@ -0,0 +1,92 @@
// Package provider is the Terraform provider for Hammerhead.
package provider
import (
"context"
"os"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/provider"
"github.com/hashicorp/terraform-plugin-framework/provider/schema"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/types"
"git.veillax.com/Veillax/hhctl/client"
)
type hammerheadProvider struct{ version string }
// New returns a provider factory.
func New(version string) func() provider.Provider {
return func() provider.Provider { return &hammerheadProvider{version} }
}
type providerModel struct {
URL types.String `tfsdk:"url"`
Token types.String `tfsdk:"token"`
Org types.String `tfsdk:"org"`
}
func (p *hammerheadProvider) Metadata(_ context.Context, _ provider.MetadataRequest, resp *provider.MetadataResponse) {
resp.TypeName = "hammerhead"
resp.Version = p.version
}
func (p *hammerheadProvider) Schema(_ context.Context, _ provider.SchemaRequest, resp *provider.SchemaResponse) {
resp.Schema = schema.Schema{
Description: "Manage a Hammerhead control plane: sites, ports, domains, edges and alert channels.",
Attributes: map[string]schema.Attribute{
"url": schema.StringAttribute{Optional: true, Description: "Hammerhead address, e.g. https://hammerhead.example.com. Defaults to $HAMMERHEAD_URL."},
"token": schema.StringAttribute{Optional: true, Sensitive: true, Description: "API key (hhk_...). An organization key manages that organization; edges and server alert channels need a server key. Defaults to $HAMMERHEAD_TOKEN."},
"org": schema.StringAttribute{Optional: true, Description: "Organization ID. Optional with an organization key. Defaults to $HAMMERHEAD_ORG."},
},
}
}
func orDefault(v types.String, env string) string {
if !v.IsNull() && !v.IsUnknown() {
return v.ValueString()
}
return os.Getenv(env)
}
func (p *hammerheadProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) {
var cfg providerModel
resp.Diagnostics.Append(req.Config.Get(ctx, &cfg)...)
if resp.Diagnostics.HasError() {
return
}
for _, v := range []struct {
name string
val types.String
}{{"url", cfg.URL}, {"token", cfg.Token}, {"org", cfg.Org}} {
if v.val.IsUnknown() {
resp.Diagnostics.AddAttributeError(path.Root(v.name), "Unknown "+v.name, "The value must be known when the provider is configured.")
}
}
if resp.Diagnostics.HasError() {
return
}
url, token := orDefault(cfg.URL, "HAMMERHEAD_URL"), orDefault(cfg.Token, "HAMMERHEAD_TOKEN")
if url == "" || token == "" {
resp.Diagnostics.AddError("Missing configuration", "Set url and token in the provider block, or HAMMERHEAD_URL and HAMMERHEAD_TOKEN.")
return
}
c := client.New(url, token, orDefault(cfg.Org, "HAMMERHEAD_ORG"))
resp.ResourceData, resp.DataSourceData = c, c
}
func (p *hammerheadProvider) Resources(context.Context) []func() resource.Resource {
return []func() resource.Resource{NewSite, NewPort, NewDomain, NewEdge, NewAlertChannel}
}
func (p *hammerheadProvider) DataSources(context.Context) []func() datasource.DataSource {
return []func() datasource.DataSource{NewEdgeData}
}
// clientFrom pulls the configured client out of ProviderData (nil before Configure ran).
func clientFrom(data any) *client.Client {
c, _ := data.(*client.Client)
return c
}