mirror of
https://github.com/GlueOps/autoglue.git
synced 2026-02-13 12:50:05 +01:00
feat: sdk migration in progress
This commit is contained in:
78
terraform-provider-autoglue/internal/provider/client.go
Normal file
78
terraform-provider-autoglue/internal/provider/client.go
Normal file
@@ -0,0 +1,78 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/glueops/autoglue-sdk"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
SDK *autoglue.APIClient
|
||||
}
|
||||
|
||||
func NewClient(_ context.Context, cfg providerModel) (*Client, diag.Diagnostics) {
|
||||
var diags diag.Diagnostics
|
||||
|
||||
conf := autoglue.NewConfiguration()
|
||||
conf.Servers = autoglue.ServerConfigurations{{URL: cfg.Addr.ValueString()}}
|
||||
|
||||
// Attach auth headers for *every* request
|
||||
rt := http.DefaultTransport
|
||||
conf.HTTPClient = &http.Client{
|
||||
Transport: headerRoundTripper{
|
||||
under: rt,
|
||||
bearer: strOrEmpty(cfg.Bearer),
|
||||
apiKey: strOrEmpty(cfg.APIKey),
|
||||
orgKey: strOrEmpty(cfg.OrgKey),
|
||||
orgSecret: strOrEmpty(cfg.OrgSecret),
|
||||
orgID: strOrEmpty(cfg.OrgID),
|
||||
},
|
||||
}
|
||||
|
||||
return &Client{SDK: autoglue.NewAPIClient(conf)}, diags
|
||||
}
|
||||
|
||||
type headerRoundTripper struct {
|
||||
under http.RoundTripper
|
||||
bearer string
|
||||
apiKey string
|
||||
orgKey string
|
||||
orgSecret string
|
||||
orgID string
|
||||
}
|
||||
|
||||
func (h headerRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
// Bearer -> Authorization
|
||||
if h.bearer != "" {
|
||||
req.Header.Set("Authorization", "Bearer "+h.bearer)
|
||||
}
|
||||
// User API Key
|
||||
if h.apiKey != "" {
|
||||
req.Header.Set("X-API-KEY", h.apiKey)
|
||||
}
|
||||
// Org key/secret
|
||||
if h.orgKey != "" {
|
||||
req.Header.Set("X-ORG-KEY", h.orgKey)
|
||||
}
|
||||
if h.orgSecret != "" {
|
||||
req.Header.Set("X-ORG-SECRET", h.orgSecret)
|
||||
}
|
||||
// Org selection header (user or key where needed)
|
||||
if h.orgID != "" {
|
||||
req.Header.Set("X-Org-ID", h.orgID)
|
||||
}
|
||||
return h.under.RoundTrip(req)
|
||||
}
|
||||
|
||||
func strOrEmpty(v interface {
|
||||
IsNull() bool
|
||||
IsUnknown() bool
|
||||
ValueString() string
|
||||
}) string {
|
||||
if v.IsNull() || v.IsUnknown() {
|
||||
return ""
|
||||
}
|
||||
return v.ValueString()
|
||||
}
|
||||
99
terraform-provider-autoglue/internal/provider/config.go
Normal file
99
terraform-provider-autoglue/internal/provider/config.go
Normal file
@@ -0,0 +1,99 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/provider"
|
||||
pschema "github.com/hashicorp/terraform-plugin-framework/provider/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
)
|
||||
|
||||
type providerModel struct {
|
||||
Addr types.String `tfsdk:"addr"`
|
||||
Bearer types.String `tfsdk:"bearer"`
|
||||
APIKey types.String `tfsdk:"api_key"`
|
||||
OrgKey types.String `tfsdk:"org_key"`
|
||||
OrgSecret types.String `tfsdk:"org_secret"`
|
||||
OrgID types.String `tfsdk:"org_id"`
|
||||
}
|
||||
|
||||
func providerConfigSchema() map[string]pschema.Attribute {
|
||||
return map[string]pschema.Attribute{
|
||||
"addr": pschema.StringAttribute{
|
||||
Optional: true,
|
||||
Description: "Base URL to the autoglue API (e.g. https://gsot.example.com/api/v1). Defaults to http://localhost:8080/api/v1.",
|
||||
},
|
||||
"bearer": pschema.StringAttribute{
|
||||
Optional: true,
|
||||
Sensitive: true,
|
||||
Description: "Bearer token (user access token).",
|
||||
},
|
||||
"api_key": pschema.StringAttribute{
|
||||
Optional: true,
|
||||
Sensitive: true,
|
||||
Description: "User API key for key-only auth.",
|
||||
},
|
||||
"org_key": pschema.StringAttribute{
|
||||
Optional: true,
|
||||
Sensitive: true,
|
||||
Description: "Org-scoped key for machine auth.",
|
||||
},
|
||||
"org_secret": pschema.StringAttribute{
|
||||
Optional: true,
|
||||
Sensitive: true,
|
||||
Description: "Org-scoped secret for machine auth.",
|
||||
},
|
||||
"org_id": pschema.StringAttribute{
|
||||
Optional: true,
|
||||
Description: "Organization ID (UUID). Required for user/bearer and user API key auth unless single-org membership. Omitted for org key/secret (derived server-side).",
|
||||
Validators: []validator.String{stringvalidator.LengthAtLeast(1)},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func readConfig(ctx context.Context, req provider.ConfigureRequest) (providerModel, diag.Diagnostics) {
|
||||
var cfg providerModel
|
||||
var diags diag.Diagnostics
|
||||
|
||||
req.Config.Get(ctx, &cfg)
|
||||
|
||||
if cfg.Addr.IsNull() || cfg.Addr.IsUnknown() {
|
||||
if v := os.Getenv("AUTOGLUE_ADDR"); v != "" {
|
||||
cfg.Addr = types.StringValue(v)
|
||||
} else {
|
||||
cfg.Addr = types.StringValue("http://localhost:8080/api/v1")
|
||||
}
|
||||
}
|
||||
if cfg.Bearer.IsNull() || cfg.Bearer.IsUnknown() {
|
||||
if v := os.Getenv("AUTOGLUE_TOKEN"); v != "" {
|
||||
cfg.Bearer = types.StringValue(v)
|
||||
}
|
||||
}
|
||||
if cfg.APIKey.IsNull() || cfg.APIKey.IsUnknown() {
|
||||
if v := os.Getenv("AUTOGLUE_API_KEY"); v != "" {
|
||||
cfg.APIKey = types.StringValue(v)
|
||||
}
|
||||
}
|
||||
if cfg.OrgKey.IsNull() || cfg.OrgKey.IsUnknown() {
|
||||
if v := os.Getenv("AUTOGLUE_ORG_KEY"); v != "" {
|
||||
cfg.OrgKey = types.StringValue(v)
|
||||
}
|
||||
}
|
||||
if cfg.OrgSecret.IsNull() || cfg.OrgSecret.IsUnknown() {
|
||||
if v := os.Getenv("AUTOGLUE_ORG_SECRET"); v != "" {
|
||||
cfg.OrgSecret = types.StringValue(v)
|
||||
}
|
||||
}
|
||||
if cfg.OrgID.IsNull() || cfg.OrgID.IsUnknown() {
|
||||
if v := os.Getenv("AUTOGLUE_ORG_ID"); v != "" {
|
||||
cfg.OrgID = types.StringValue(v)
|
||||
} else {
|
||||
cfg.OrgID = types.StringNull()
|
||||
}
|
||||
}
|
||||
return cfg, diags
|
||||
}
|
||||
121
terraform-provider-autoglue/internal/provider/datasource_ssh.go
Normal file
121
terraform-provider-autoglue/internal/provider/datasource_ssh.go
Normal file
@@ -0,0 +1,121 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||
dschema "github.com/hashicorp/terraform-plugin-framework/datasource/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
)
|
||||
|
||||
var _ datasource.DataSource = &SshDataSource{}
|
||||
var _ datasource.DataSourceWithConfigure = &SshDataSource{}
|
||||
|
||||
type SshDataSource struct{ client *Client }
|
||||
|
||||
func NewSshDataSource() datasource.DataSource { return &SshDataSource{} }
|
||||
|
||||
type sshDSModel struct {
|
||||
NameContains types.String `tfsdk:"name_contains"`
|
||||
Fingerprint types.String `tfsdk:"fingerprint"`
|
||||
Keys []sshItem `tfsdk:"keys"`
|
||||
}
|
||||
|
||||
type sshItem struct {
|
||||
ID types.String `tfsdk:"id"`
|
||||
Name types.String `tfsdk:"name"`
|
||||
PublicKey types.String `tfsdk:"public_key"`
|
||||
Fingerprint types.String `tfsdk:"fingerprint"`
|
||||
CreatedAt types.String `tfsdk:"created_at"`
|
||||
UpdatedAt types.String `tfsdk:"updated_at"`
|
||||
}
|
||||
|
||||
func (d *SshDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_ssh_keys"
|
||||
}
|
||||
|
||||
func (d *SshDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
||||
resp.Schema = dschema.Schema{
|
||||
Attributes: map[string]dschema.Attribute{
|
||||
"name_contains": dschema.StringAttribute{
|
||||
Optional: true,
|
||||
Description: "Filter by substring of name (client-side).",
|
||||
},
|
||||
"fingerprint": dschema.StringAttribute{
|
||||
Optional: true,
|
||||
Description: "Filter by exact fingerprint (client-side).",
|
||||
},
|
||||
"keys": dschema.ListNestedAttribute{
|
||||
Computed: true,
|
||||
Description: "SSH keys",
|
||||
NestedObject: dschema.NestedAttributeObject{
|
||||
Attributes: map[string]dschema.Attribute{
|
||||
"id": dschema.StringAttribute{Computed: true},
|
||||
"name": dschema.StringAttribute{Computed: true},
|
||||
"public_key": dschema.StringAttribute{Computed: true},
|
||||
"fingerprint": dschema.StringAttribute{Computed: true},
|
||||
"created_at": dschema.StringAttribute{Computed: true},
|
||||
"updated_at": dschema.StringAttribute{Computed: true},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (d *SshDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, _ *datasource.ConfigureResponse) {
|
||||
if req.ProviderData == nil {
|
||||
return
|
||||
}
|
||||
d.client = req.ProviderData.(*Client)
|
||||
}
|
||||
|
||||
func (d *SshDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
|
||||
if d.client == nil || d.client.SDK == nil {
|
||||
resp.Diagnostics.AddError("Client not configured", "Provider configuration missing")
|
||||
return
|
||||
}
|
||||
|
||||
var conf sshDSModel
|
||||
resp.Diagnostics.Append(req.Config.Get(ctx, &conf)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
items, httpResp, err := d.client.SDK.SshAPI.ListPublicSshKeys(ctx).Execute()
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("List ssh keys failed", fmt.Sprintf("%v", httpErr(err, httpResp)))
|
||||
return
|
||||
}
|
||||
|
||||
nc := strings.ToLower(conf.NameContains.ValueString())
|
||||
fp := conf.Fingerprint.ValueString()
|
||||
out := sshDSModel{NameContains: conf.NameContains, Fingerprint: conf.Fingerprint}
|
||||
out.Keys = make([]sshItem, 0, len(items))
|
||||
|
||||
for _, s := range items {
|
||||
name := ""
|
||||
if s.Name != nil {
|
||||
name = *s.Name
|
||||
}
|
||||
if nc != "" && !strings.Contains(strings.ToLower(name), nc) {
|
||||
continue
|
||||
}
|
||||
if fp != "" && (s.Fingerprint == nil || *s.Fingerprint != fp) {
|
||||
continue
|
||||
}
|
||||
|
||||
out.Keys = append(out.Keys, sshItem{
|
||||
ID: types.StringPointerValue(s.Id),
|
||||
Name: types.StringPointerValue(s.Name),
|
||||
PublicKey: types.StringPointerValue(s.PublicKey),
|
||||
Fingerprint: types.StringPointerValue(s.Fingerprint),
|
||||
CreatedAt: types.StringPointerValue(s.CreatedAt),
|
||||
UpdatedAt: types.StringPointerValue(s.UpdatedAt),
|
||||
})
|
||||
}
|
||||
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &out)...)
|
||||
}
|
||||
20
terraform-provider-autoglue/internal/provider/http_err.go
Normal file
20
terraform-provider-autoglue/internal/provider/http_err.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func httpErr(err error, resp *http.Response) string {
|
||||
if resp == nil {
|
||||
return err.Error()
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
b, _ := io.ReadAll(resp.Body)
|
||||
return fmt.Sprintf("status=%d: %s (body=%s)", resp.StatusCode, resp.Status, string(b))
|
||||
}
|
||||
|
||||
func isNotFound(resp *http.Response) bool {
|
||||
return resp != nil && resp.StatusCode == http.StatusNotFound
|
||||
}
|
||||
14
terraform-provider-autoglue/internal/provider/num.go
Normal file
14
terraform-provider-autoglue/internal/provider/num.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package provider
|
||||
|
||||
import "math"
|
||||
|
||||
func round6(x float64) float64 {
|
||||
return math.Round(x*1e6) / 1e6
|
||||
}
|
||||
|
||||
func f32ptrToTF64(v *float32) float64 {
|
||||
if v == nil {
|
||||
return 0
|
||||
}
|
||||
return round6(float64(*v))
|
||||
}
|
||||
58
terraform-provider-autoglue/internal/provider/provider.go
Normal file
58
terraform-provider-autoglue/internal/provider/provider.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/provider"
|
||||
pschema "github.com/hashicorp/terraform-plugin-framework/provider/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||
)
|
||||
|
||||
var _ provider.Provider = &AutoglueProvider{}
|
||||
|
||||
func New() provider.Provider { return &AutoglueProvider{} }
|
||||
|
||||
type AutoglueProvider struct {
|
||||
version string
|
||||
}
|
||||
|
||||
func (p *AutoglueProvider) Metadata(_ context.Context, _ provider.MetadataRequest, resp *provider.MetadataResponse) {
|
||||
resp.TypeName = "autoglue"
|
||||
resp.Version = p.version
|
||||
}
|
||||
|
||||
func (p *AutoglueProvider) Schema(_ context.Context, _ provider.SchemaRequest, resp *provider.SchemaResponse) {
|
||||
resp.Schema = pschema.Schema{
|
||||
Attributes: providerConfigSchema(),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *AutoglueProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) {
|
||||
cfg, diags := readConfig(ctx, req)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
client, diags := NewClient(ctx, cfg)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
resp.DataSourceData = client
|
||||
resp.ResourceData = client
|
||||
}
|
||||
|
||||
func (p *AutoglueProvider) DataSources(_ context.Context) []func() datasource.DataSource {
|
||||
return []func() datasource.DataSource{
|
||||
NewSshDataSource,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *AutoglueProvider) Resources(_ context.Context) []func() resource.Resource {
|
||||
return []func() resource.Resource{
|
||||
NewSshResource,
|
||||
}
|
||||
}
|
||||
230
terraform-provider-autoglue/internal/provider/resource_ssh.go
Normal file
230
terraform-provider-autoglue/internal/provider/resource_ssh.go
Normal file
@@ -0,0 +1,230 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/glueops/autoglue-sdk"
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/int64validator"
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||
rschema "github.com/hashicorp/terraform-plugin-framework/resource/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
)
|
||||
|
||||
var _ resource.Resource = &SshResource{}
|
||||
var _ resource.ResourceWithConfigure = &SshResource{}
|
||||
var _ resource.ResourceWithImportState = &SshResource{}
|
||||
|
||||
type SshResource struct{ client *Client }
|
||||
|
||||
func NewSshResource() resource.Resource { return &SshResource{} }
|
||||
|
||||
type sshResModel struct {
|
||||
ID types.String `tfsdk:"id"`
|
||||
Name types.String `tfsdk:"name"`
|
||||
Comment types.String `tfsdk:"comment"`
|
||||
Type types.String `tfsdk:"type"`
|
||||
Bits types.Int64 `tfsdk:"bits"`
|
||||
PublicKey types.String `tfsdk:"public_key"`
|
||||
Fingerprint types.String `tfsdk:"fingerprint"`
|
||||
CreatedAt types.String `tfsdk:"created_at"`
|
||||
UpdatedAt types.String `tfsdk:"updated_at"`
|
||||
PrivateKeyPEM types.String `tfsdk:"private_key_pem"` // not populated by resource
|
||||
}
|
||||
|
||||
func (r *SshResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_ssh_key"
|
||||
}
|
||||
|
||||
func (r *SshResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
|
||||
resp.Schema = rschema.Schema{
|
||||
Attributes: map[string]rschema.Attribute{
|
||||
"id": rschema.StringAttribute{
|
||||
Computed: true,
|
||||
Description: "SSH key ID (UUID)",
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.UseStateForUnknown(),
|
||||
},
|
||||
},
|
||||
"name": rschema.StringAttribute{
|
||||
Required: true,
|
||||
Description: "Display name",
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.RequiresReplace(),
|
||||
},
|
||||
},
|
||||
"comment": rschema.StringAttribute{
|
||||
Required: true,
|
||||
Description: "Comment appended to authorized key",
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.RequiresReplace(),
|
||||
},
|
||||
},
|
||||
"type": rschema.StringAttribute{
|
||||
Optional: true,
|
||||
Description: "Key type: rsa or ed25519 (default rsa)",
|
||||
Validators: []validator.String{
|
||||
stringvalidator.OneOf("rsa", "ed25519", ""),
|
||||
},
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.RequiresReplace(),
|
||||
},
|
||||
},
|
||||
"bits": rschema.Int64Attribute{
|
||||
Optional: true,
|
||||
Description: "RSA key size (2048/3072/4096). Ignored for ed25519.",
|
||||
Validators: []validator.Int64{
|
||||
int64validator.OneOf(2048, 3072, 4096),
|
||||
},
|
||||
PlanModifiers: []planmodifier.Int64{
|
||||
int64planmodifier.RequiresReplace(),
|
||||
},
|
||||
},
|
||||
"public_key": rschema.StringAttribute{
|
||||
Computed: true,
|
||||
Description: "OpenSSH authorized key",
|
||||
},
|
||||
"fingerprint": rschema.StringAttribute{
|
||||
Computed: true,
|
||||
Description: "SHA256 fingerprint",
|
||||
},
|
||||
"created_at": rschema.StringAttribute{
|
||||
Computed: true,
|
||||
Description: "Creation time (RFC3339, UTC)",
|
||||
},
|
||||
"updated_at": rschema.StringAttribute{
|
||||
Computed: true,
|
||||
Description: "Update time (RFC3339, UTC)",
|
||||
},
|
||||
"private_key_pem": rschema.StringAttribute{
|
||||
Computed: true,
|
||||
Sensitive: true,
|
||||
Description: "Private key PEM (resource doesn’t reveal; stays empty).",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (r *SshResource) Configure(_ context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) {
|
||||
if req.ProviderData == nil {
|
||||
return
|
||||
}
|
||||
r.client = req.ProviderData.(*Client)
|
||||
}
|
||||
|
||||
func (r *SshResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
|
||||
if r.client == nil || r.client.SDK == nil {
|
||||
resp.Diagnostics.AddError("Client not configured", "Provider configuration missing")
|
||||
return
|
||||
}
|
||||
|
||||
var plan sshResModel
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
body := autoglue.DtoCreateSSHRequest{
|
||||
Name: plan.Name.ValueStringPointer(),
|
||||
Comment: plan.Comment.ValueStringPointer(),
|
||||
}
|
||||
if t := plan.Type.ValueString(); t != "" {
|
||||
body.Type = &t
|
||||
}
|
||||
if !plan.Bits.IsNull() && !plan.Bits.IsUnknown() {
|
||||
b := int32(plan.Bits.ValueInt64())
|
||||
body.Bits = &b
|
||||
}
|
||||
|
||||
created, httpResp, err := r.client.SDK.SshAPI.CreateSSHKey(ctx).Body(body).Execute()
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("Create ssh key failed", fmt.Sprintf("%v", httpErr(err, httpResp)))
|
||||
return
|
||||
}
|
||||
|
||||
state := sshResModel{
|
||||
ID: types.StringPointerValue(created.Id),
|
||||
Name: types.StringPointerValue(created.Name),
|
||||
Comment: plan.Comment,
|
||||
Type: plan.Type,
|
||||
Bits: plan.Bits,
|
||||
PublicKey: types.StringPointerValue(created.PublicKey),
|
||||
Fingerprint: types.StringPointerValue(created.Fingerprint),
|
||||
CreatedAt: types.StringPointerValue(created.CreatedAt),
|
||||
UpdatedAt: types.StringPointerValue(created.UpdatedAt),
|
||||
// PrivateKeyPEM left empty (no reveal on resource)
|
||||
}
|
||||
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
|
||||
}
|
||||
|
||||
func (r *SshResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
|
||||
if r.client == nil || r.client.SDK == nil {
|
||||
resp.Diagnostics.AddError("Client not configured", "Provider configuration missing")
|
||||
return
|
||||
}
|
||||
|
||||
var state sshResModel
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
got, httpResp, err := r.client.SDK.SshAPI.GetSSHKey(ctx, state.ID.ValueString()).Execute()
|
||||
if err != nil {
|
||||
if isNotFound(httpResp) {
|
||||
resp.State.RemoveResource(ctx)
|
||||
return
|
||||
}
|
||||
resp.Diagnostics.AddError("Read ssh key failed", fmt.Sprintf("%v", httpErr(err, httpResp)))
|
||||
return
|
||||
}
|
||||
|
||||
// Map from flat fields on DtoSshRevealResponse
|
||||
state.Name = types.StringPointerValue(got.Name)
|
||||
state.PublicKey = types.StringPointerValue(got.PublicKey)
|
||||
state.Fingerprint = types.StringPointerValue(got.Fingerprint)
|
||||
state.CreatedAt = types.StringPointerValue(got.CreatedAt)
|
||||
state.UpdatedAt = types.StringPointerValue(got.UpdatedAt)
|
||||
// We intentionally do NOT set PrivateKeyPEM here (resource doesn't reveal)
|
||||
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
|
||||
}
|
||||
|
||||
func (r *SshResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
|
||||
// All changes are RequiresReplace; no server-side update.
|
||||
var state sshResModel
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
|
||||
}
|
||||
|
||||
func (r *SshResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
|
||||
if r.client == nil || r.client.SDK == nil {
|
||||
resp.Diagnostics.AddError("Client not configured", "Provider configuration missing")
|
||||
return
|
||||
}
|
||||
|
||||
var state sshResModel
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
_, httpResp, err := r.client.SDK.SshAPI.DeleteSSHKey(ctx, state.ID.ValueString()).Execute()
|
||||
if err != nil && !isNotFound(httpResp) {
|
||||
resp.Diagnostics.AddError("Delete ssh key failed", fmt.Sprintf("%v", httpErr(err, httpResp)))
|
||||
}
|
||||
}
|
||||
|
||||
func (r *SshResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("id"), req.ID)...)
|
||||
}
|
||||
Reference in New Issue
Block a user