From 85f2849202065a230527c9826fbb12e290a9300d Mon Sep 17 00:00:00 2001 From: Stuart McLaren Date: Mon, 26 Aug 2024 17:37:27 +0100 Subject: [PATCH] Add initial 'server' resource code Future changes will add API support for creating and deleting servers. Once that support is available we can have full support in the provider for managing servers. For now, we add what we can, mainly reading. --- internal/provider/experimental_resources.go | 2 + internal/resources/server/resource.go | 207 ++++++++++++++++++++ 2 files changed, 209 insertions(+) create mode 100644 internal/resources/server/resource.go diff --git a/internal/provider/experimental_resources.go b/internal/provider/experimental_resources.go index 56dc8dd..153c0e7 100644 --- a/internal/provider/experimental_resources.go +++ b/internal/provider/experimental_resources.go @@ -7,6 +7,7 @@ package provider import ( "context" "github.com/HewlettPackard/hpegl-pcbe-terraform-resources/internal/resources/datastore" + "github.com/HewlettPackard/hpegl-pcbe-terraform-resources/internal/resources/server" "github.com/HewlettPackard/hpegl-pcbe-terraform-resources/internal/resources/hypervisorcluster" "github.com/hashicorp/terraform-plugin-framework/resource" ) @@ -18,5 +19,6 @@ func (p *PCBeProvider) Resources( return []func() resource.Resource{ datastore.NewResource, hypervisorcluster.NewResource, + server.NewResource, } } diff --git a/internal/resources/server/resource.go b/internal/resources/server/resource.go new file mode 100644 index 0000000..2119983 --- /dev/null +++ b/internal/resources/server/resource.go @@ -0,0 +1,207 @@ +package server + +import ( + "context" + + "github.com/HewlettPackard/hpegl-pcbe-terraform-resources/internal/client" + "github.com/HewlettPackard/hpegl-pcbe-terraform-resources/internal/sdk/systems/privatecloudbusiness" + "github.com/hashicorp/terraform-plugin-framework/diag" + tfpath "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 = &Resource{} + _ resource.ResourceWithImportState = &Resource{} +) + +func NewResource() resource.Resource { + return &Resource{} +} + +// Resource defines the resource implementation. +type Resource struct { + client *client.PCBeClient +} + +func (r *Resource) Metadata( + ctx context.Context, + req resource.MetadataRequest, + resp *resource.MetadataResponse, +) { + resp.TypeName = req.ProviderTypeName + "_server" +} + +func (r *Resource) Schema( + ctx context.Context, + req resource.SchemaRequest, + resp *resource.SchemaResponse, +) { + resp.Schema = ServerResourceSchema(ctx) +} + +func (r *Resource) Configure( + ctx context.Context, + req resource.ConfigureRequest, + resp *resource.ConfigureResponse, +) { + if req.ProviderData == nil { + return + } + + r.client = req.ProviderData.(*client.PCBeClient) +} + +func doRead( + ctx context.Context, + client client.PCBeClient, + dataP *ServerModel, + diagsP *diag.Diagnostics, +) { + serverID := (*dataP).Id.ValueString() + systemID := (*dataP).SystemId.ValueString() + + sysClient, _, err := client.NewSysClient(ctx) + if err != nil { + (*diagsP).AddError( + "error reading server", + "unexpected error: "+err.Error(), + ) + + return + } + + grc := &privatecloudbusiness. + V1beta1SystemsItemServersWithServerItemRequestBuilderGetRequestConfiguration{} + server, err := sysClient.PrivateCloudBusiness(). + V1beta1(). + Systems(). + ById(systemID). + Servers(). + ByServerId(serverID). + GetAsWithServerGetResponse(ctx, grc) + if err != nil { + (*diagsP).AddError( + "error reading server", + "get server by ID failed: "+err.Error(), + ) + + return + } + + if server.GetId() == nil { + (*diagsP).AddError( + "error reading server", + "'id' is nil", + ) + + return + } + + if server.GetName() == nil { + (*diagsP).AddError( + "error reading server", + "'name' is nil", + ) + + return + } + + (*dataP).Name = types.StringValue(*(server.GetName())) +} + +func doCreate( + _ context.Context, + _ client.PCBeClient, + dataP *ServerModel, + _ *diag.Diagnostics, +) { + // For now, just set the ID to a known value + (*dataP).Id = types.StringValue("697e8cbf-df7e-570c-a3c7-912d4ce8375a") +} + +// TODO: (API) Implement create when server create API is implemented +func (r *Resource) Create( + ctx context.Context, + req resource.CreateRequest, + resp *resource.CreateResponse, +) { + var data ServerModel + + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + + doCreate(ctx, *r.client, &data, &resp.Diagnostics) + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + + doRead(ctx, *r.client, &data, &resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *Resource) Read( + ctx context.Context, + req resource.ReadRequest, + resp *resource.ReadResponse, +) { + var data ServerModel + + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + + doRead(ctx, *r.client, &data, &resp.Diagnostics) + + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *Resource) Update( + ctx context.Context, + req resource.UpdateRequest, + resp *resource.UpdateResponse, +) { + tflog.Error(ctx, "update server is not implemented") +} + +// TODO: (API) Implement delete when server delete API is implemented +func (r *Resource) Delete( + ctx context.Context, + req resource.DeleteRequest, + resp *resource.DeleteResponse, +) { + var data ServerModel + + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + // For now, just delete the state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *Resource) ImportState( + ctx context.Context, + req resource.ImportStateRequest, + resp *resource.ImportStateResponse, +) { + resource.ImportStatePassthroughID(ctx, tfpath.Root("id"), req, resp) +}