From ec116ba0c1fcaa237fc0bce5efb1cc3141cf58f0 Mon Sep 17 00:00:00 2001 From: sam Date: Mon, 20 Jan 2025 15:18:36 +0000 Subject: [PATCH] Add partial support for import server Running ``` terraform import hpegl_pc_server.test 126fd201-9e6e-5e31-9ffb-a766265b1fd3,697e8cbf-df7e-570c-a3c7-912d4ce8375a ``` will partially update the state file. Some fields are missing * esx_root_credential_id * ilo_admin_credential_id * server_network these fields are not currently retrievable via the API, so they will be set to `null` in the state. This will lead to an error if a "terraform apply" is subsequently run, unless these fields are manually tweaked. This means that "terraform import" for server resources is only partially supported. --- internal/resources/server/resource.go | 112 ++++++++++++++++++++------ test/server/server_test.go | 67 +++++++++++++++ 2 files changed, 154 insertions(+), 25 deletions(-) diff --git a/internal/resources/server/resource.go b/internal/resources/server/resource.go index 9d44bfc..b258cf7 100644 --- a/internal/resources/server/resource.go +++ b/internal/resources/server/resource.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "path" + "strings" "github.com/HewlettPackard/hpegl-pcbe-terraform-resources/internal/async" "github.com/HewlettPackard/hpegl-pcbe-terraform-resources/internal/client" @@ -247,18 +248,6 @@ func doRead( return } - // If this doesn't match, something is wrong - if *hypervisorClusterID != (*dataP).HypervisorHost.HypervisorClusterId.ValueString() { - (*diagsP).AddError( - "error reading server", - "'hypervisor cluster id' mismatch "+ - (*dataP).HypervisorHost.HypervisorClusterId.ValueString()+ - " != "+*hypervisorClusterID, - ) - - return - } - hypervisorHostIP := server.GetHypervisorHost().GetHypervisorHostIp() if hypervisorHostIP == nil { (*diagsP).AddError( @@ -269,18 +258,6 @@ func doRead( return } - // If this doesn't match, something is wrong - if *hypervisorHostIP != (*dataP).HypervisorHost.HypervisorHostIp.ValueString() { - (*diagsP).AddError( - "error reading server", - "'hypervisor host ip' mismatch "+ - (*dataP).HypervisorHost.HypervisorHostIp.ValueString()+ - " != "+*hypervisorHostIP, - ) - - return - } - hypervisorClusterName := server.GetHypervisorHost().GetHypervisorClusterName() if hypervisorClusterName == nil { (*diagsP).AddError( @@ -330,6 +307,62 @@ func doRead( (*dataP).HypervisorHost = hypervisorHost + iloNetInfo := server.GetIloNetworkInfo() + if iloNetInfo == nil { + (*diagsP).AddError( + "error reading server", + "'ilo network info' is nil", + ) + + return + } + iloGateway := server.GetIloNetworkInfo().GetGateway() + if iloGateway == nil { + (*diagsP).AddError( + "error reading server", + "'ilo network info gateway' is nil", + ) + + return + } + + iloIP := server.GetIloNetworkInfo().GetIloIp() + if iloIP == nil { + (*diagsP).AddError( + "error reading server", + "'ilo network info ip address' is nil", + ) + + return + } + + iloSubnetMask := server.GetIloNetworkInfo().GetSubnetMask() + if iloSubnetMask == nil { + (*diagsP).AddError( + "error reading server", + "'ilo network info subnet mask' is nil", + ) + + return + } + + value = map[string]attr.Value{ + "gateway": types.StringValue(*iloGateway), + "ilo_ip": types.StringValue(*iloIP), + "subnet_mask": types.StringValue(*iloSubnetMask), + } + + iloNetworkInfo, diags := NewIloNetworkInfoValue( + (*dataP).IloNetworkInfo.AttributeTypes(ctx), value, + ) + + (*diagsP).Append(diags...) + if (*diagsP).HasError() { + return + } + + (*dataP).IloNetworkInfo = iloNetworkInfo + if server.GetSerialNumber() == nil { (*diagsP).AddError( "error reading server", @@ -574,10 +607,39 @@ func (r *Resource) Delete( resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) } +// Import only grants access to a single "ID" parameter. Therefore, we have to +// combine the "hci_cluster_uuid" and datastore "id" values into the single +// req.ID string +func parseImportID( + id string, +) (systemID string, clusterID string, error error) { + params := strings.Split(id, ",") + if len(params) != 2 || params[0] == "" || params[1] == "" { + return "", "", errors.New("invalid import ID format") + } + + return params[0], params[1], nil +} + func (r *Resource) ImportState( ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse, ) { - resource.ImportStatePassthroughID(ctx, tfpath.Root("id"), req, resp) + systemID, serverID, err := parseImportID(req.ID) + if err != nil { + resp.Diagnostics.AddError( + "import has invalid server id format", + "Provided import ID \""+req.ID+"\" is invalid. "+ + "Format must be \",\". For example: "+ + "126fd201-9e6e-5e31-9ffb-a766265b1fd3,697e8cbf-df7e-570c-a3c7-912d4ce8375a", + ) + + return + } + + diags := resp.State.SetAttribute(ctx, tfpath.Root("id"), serverID) + resp.Diagnostics.Append(diags...) + diags = resp.State.SetAttribute(ctx, tfpath.Root("system_id"), systemID) + resp.Diagnostics.Append(diags...) } diff --git a/test/server/server_test.go b/test/server/server_test.go index 502a04e..2c78849 100644 --- a/test/server/server_test.go +++ b/test/server/server_test.go @@ -4,6 +4,7 @@ package acceptance import ( "fmt" + "regexp" "testing" "github.com/HewlettPackard/hpegl-pcbe-terraform-resources/internal/provider" @@ -254,6 +255,72 @@ func TestAccServerResource(t *testing.T) { testAccCheckResourceDestroyed("hpegl_pc_server.test"), ), }, + { + // Import + Check: checkFn, + Config: config, + ImportState: true, + ResourceName: "hpegl_pc_server.test", + ImportStateId: "126fd201-9e6e-5e31-9ffb-a766265b1fd3,697e8cbf-df7e-570c-a3c7-912d4ce8375a", + ImportStatePersist: true, + }, + /* + TODO: (API) This test cannot currently pass. + See FF-31582 FF-31587 FF-31581 PC-6095 etc + { + // Check post import state matches the resource config + // e.g. verfies 'name' in state matches 'name' in config + Config: config, + Check: checkFn, + ExpectNonEmptyPlan: false, + }, + */ + }, + }) +} + +func TestAccServerImportBadId(t *testing.T) { + config := providerConfig + ` + resource "hpegl_pc_server" "test" { + system_id = "126fd201-9e6e-5e31-9ffb-a766265b1fd3" + esx_root_credential_id = "cccfcad1-85b7-4162-b16e-f7cadc2c46b5" + ilo_admin_credential_id = "dddfcad1-85b7-4162-b16e-f7cadc2c46b5" + hypervisor_host = { + hypervisor_cluster_id = "acd4daea-e5e3-5f35-8be3-ce4a4b6d946c" + hypervisor_host_ip = "16.182.105.217" + } + ilo_network_info = { + ilo_ip = "16.182.105.216" + gateway = "16.182.104.1" + subnet_mask = "255.255.248.0" + } + server_network = [ + { + esx_ip_address = "10.0.0.88" + data_ip_infos = [ + { + ip_address = "16.182.105.217" + } + ] + } + ] + } + ` + + expected := `import has invalid server id format(.|\n)*698de955-87b5-5fe6-b683-78c3948beede` + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + // Import + ExpectError: regexp.MustCompile(expected), + Config: config, + ImportState: true, + ResourceName: "hpegl_pc_server.test", + // Invalid id (not ",") + ImportStateId: "698de955-87b5-5fe6-b683-78c3948beede", + ImportStatePersist: true, + }, }, }) }