diff --git a/internal/provider/resources/agent_group_assignment.go b/internal/provider/resources/agent_group_assignment.go index 1d8563ef..e7216a5e 100644 --- a/internal/provider/resources/agent_group_assignment.go +++ b/internal/provider/resources/agent_group_assignment.go @@ -13,7 +13,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/types" ) -// Ensure the implementation satisfies the expected interfaces. var ( _ resource.Resource = &agentGroupAssignmentResource{} _ resource.ResourceWithConfigure = &agentGroupAssignmentResource{} @@ -25,12 +24,6 @@ type agentGroupAssignmentResourceModel struct { GroupID types.String `tfsdk:"group_id"` } -type AgentGroupAssignmentResponseModel struct { - UID string // - GroupUID string // , - AgentUID string // -} - func NewAgentGroupAssignmentResource() resource.Resource { return &agentGroupAssignmentResource{} } @@ -103,7 +96,6 @@ func (r *agentGroupAssignmentResource) Create( req resource.CreateRequest, resp *resource.CreateResponse, ) { - // Retrieve values from plan var plan agentGroupAssignmentResourceModel diags := req.Plan.Get(ctx, &plan) resp.Diagnostics.Append(diags...) @@ -129,12 +121,10 @@ func (r *agentGroupAssignmentResource) Create( return } - // Update the state to match the plan plan.ID = types.StringValue(agentGroupAssignment.Id) plan.GroupID = types.StringValue(agentGroupAssignment.Group.Id) plan.AgentID = types.StringValue(agentGroupAssignment.Agent.Id) - // Set state to fully populated data diags = resp.State.Set(ctx, plan) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { @@ -147,7 +137,6 @@ func (r *agentGroupAssignmentResource) Read( req resource.ReadRequest, resp *resource.ReadResponse, ) { - // Get current state var state agentGroupAssignmentResourceModel diags := req.State.Get(ctx, &state) resp.Diagnostics.Append(diags...) @@ -174,12 +163,10 @@ func (r *agentGroupAssignmentResource) Read( } agentGroupAssignment := agentGroupAssignmentResponse.Items[0] - // Update state from client response state.ID = types.StringValue(agentGroupAssignment.Id) state.GroupID = types.StringValue(agentGroupAssignment.Group.Id) state.AgentID = types.StringValue(agentGroupAssignment.Agent.Id) - // Set refreshed state diags = resp.State.Set(ctx, &state) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { @@ -192,7 +179,6 @@ func (r *agentGroupAssignmentResource) Update( req resource.UpdateRequest, resp *resource.UpdateResponse, ) { - // Retrieve values from plan var plan agentGroupAssignmentResourceModel diags := req.Plan.Get(ctx, &plan) diags.AddError("operation not supported", "updating an agent group assignment is not supported") @@ -207,7 +193,6 @@ func (r *agentGroupAssignmentResource) Delete( req resource.DeleteRequest, resp *resource.DeleteResponse, ) { - // Retrieve values from state var state agentGroupAssignmentResourceModel diags := req.State.Get(ctx, &state) resp.Diagnostics.Append(diags...) @@ -215,7 +200,18 @@ func (r *agentGroupAssignmentResource) Delete( return } - // Delete existing agentGroupAssignment using the plan_id + request := r.client.ConfigurationAPI. + AgentGroupAssignmentDelete(ctx, state.ID.ValueString()) + _, response, err := util.RetryFor429(request.Execute) + errorPresent, errorDetail := util.RaiseForStatus(response, err) + + if errorPresent { + resp.Diagnostics.AddError( + util.GenerateErrorSummary("delete", "uxi_agent_group_assignment"), + errorDetail, + ) + return + } } func (r *agentGroupAssignmentResource) ImportState( diff --git a/test/mocked/resources/agent_group_assignment_test.go b/test/mocked/resources/agent_group_assignment_test.go index bba5316f..40ac2b3e 100644 --- a/test/mocked/resources/agent_group_assignment_test.go +++ b/test/mocked/resources/agent_group_assignment_test.go @@ -1,12 +1,15 @@ package resource_test import ( - "github.com/aruba-uxi/terraform-provider-hpeuxi/test/mocked/provider" - "github.com/aruba-uxi/terraform-provider-hpeuxi/test/mocked/util" + "regexp" "testing" + "github.com/aruba-uxi/terraform-provider-hpeuxi/test/mocked/provider" + "github.com/aruba-uxi/terraform-provider-hpeuxi/test/mocked/util" "github.com/h2non/gock" "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" + "github.com/nbio/st" ) func TestAgentGroupAssignmentResource(t *testing.T) { @@ -127,7 +130,7 @@ func TestAgentGroupAssignmentResource(t *testing.T) { ImportState: true, ImportStateVerify: true, }, - // Update and Read testing + // Update testing { PreConfig: func() { util.MockGetAgent( @@ -166,6 +169,7 @@ func TestAgentGroupAssignmentResource(t *testing.T) { ), 2, ) + util.MockDeleteAgentGroupAssignment("agent_group_assignment_uid", 1) // required for creating another group util.MockPostGroup( @@ -271,7 +275,7 @@ func TestAgentGroupAssignmentResource(t *testing.T) { ), ), }, - // Delete testing automatically occurs in TestCase + // Delete testing { PreConfig: func() { util.MockGetAgent( @@ -310,12 +314,615 @@ func TestAgentGroupAssignmentResource(t *testing.T) { ), 1, ) + util.MockGetAgentGroupAssignment( + "agent_group_assignment_uid", + util.GeneratePaginatedResponse( + []map[string]interface{}{ + util.GenerateAgentGroupAssignmentResponse( + "agent_group_assignment_uid", + "", + ), + }, + ), + 2, + ) + util.MockGetAgentGroupAssignment( + "agent_group_assignment_uid_2", + util.GeneratePaginatedResponse( + []map[string]interface{}{ + util.GenerateAgentGroupAssignmentResponse( + "agent_group_assignment_uid_2", + "_2", + ), + }, + ), + 1, + ) util.MockDeleteGroup("group_uid", 1) util.MockDeleteGroup("group_uid_2", 1) util.MockDeleteAgent("agent_uid", 1) util.MockDeleteAgent("agent_uid_2", 1) + util.MockDeleteAgentGroupAssignment("agent_group_assignment_uid_2", 1) + }, + Config: provider.ProviderConfig, + }, + }, + }) + + mockOAuth.Mock.Disable() +} + +func TestAgentGroupAssignmentResource429Handling(t *testing.T) { + defer gock.Off() + mockOAuth := util.MockOAuth() + var mock429 *gock.Response + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: provider.TestAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Creating a agent group assignment + { + PreConfig: func() { + // required for agent import + util.MockGetAgent( + "agent_uid", + util.GeneratePaginatedResponse( + []map[string]interface{}{ + util.GenerateAgentResponseModel("agent_uid", ""), + }, + ), + 2, + ) + + // required for agent group assignment create + mock429 = gock.New("https://test.api.capenetworks.com"). + Post("/networking-uxi/v1alpha1/agent-group-assignments"). + Reply(429). + SetHeaders(util.RateLimitingHeaders) + util.MockPostGroup( + util.GenerateGroupRequestModel("group_uid", "", ""), + util.StructToMap( + util.GenerateNonRootGroupResponseModel("group_uid", "", ""), + ), + 1, + ) + util.MockGetGroup( + "group_uid", + util.GeneratePaginatedResponse( + []map[string]interface{}{ + util.GenerateNonRootGroupResponseModel("group_uid", "", ""), + }, + ), + 1, + ) + + // required for agent group assignment create + util.MockPostAgentGroupAssignment( + util.GenerateAgentGroupAssignmentRequest("agent_group_assignment_uid", ""), + util.GenerateAgentGroupAssignmentResponse("agent_group_assignment_uid", ""), + 1, + ) + util.MockGetAgentGroupAssignment( + "agent_group_assignment_uid", + util.GeneratePaginatedResponse( + []map[string]interface{}{ + util.GenerateAgentGroupAssignmentResponse( + "agent_group_assignment_uid", + "", + ), + }, + ), + 1, + ) + }, + + Config: provider.ProviderConfig + ` + resource "uxi_group" "my_group" { + name = "name" + parent_group_id = "parent_uid" + } + + resource "uxi_agent" "my_agent" { + name = "name" + notes = "notes" + pcap_mode = "light" + } + + import { + to = uxi_agent.my_agent + id = "agent_uid" + } + + resource "uxi_agent_group_assignment" "my_agent_group_assignment" { + agent_id = uxi_agent.my_agent.id + group_id = uxi_group.my_group.id + }`, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr( + "uxi_agent_group_assignment.my_agent_group_assignment", + "id", + "agent_group_assignment_uid", + ), + func(s *terraform.State) error { + st.Assert(t, mock429.Mock.Request().Counter, 0) + return nil + }, + ), + }, + // ImportState testing + { + PreConfig: func() { + mock429 = gock.New("https://test.api.capenetworks.com"). + Get("/networking-uxi/v1alpha1/agent-group-assignments"). + Reply(429). + SetHeaders(util.RateLimitingHeaders) + util.MockGetAgentGroupAssignment( + "agent_group_assignment_uid", + util.GeneratePaginatedResponse( + []map[string]interface{}{ + util.GenerateAgentGroupAssignmentResponse( + "agent_group_assignment_uid", + "", + ), + }, + ), + 1, + ) + }, + ResourceName: "uxi_agent_group_assignment.my_agent_group_assignment", + ImportState: true, + ImportStateVerify: true, + Check: func(s *terraform.State) error { + st.Assert(t, mock429.Mock.Request().Counter, 0) + return nil + }, + }, + // Delete testing + { + PreConfig: func() { + util.MockGetAgent( + "agent_uid", + util.GeneratePaginatedResponse( + []map[string]interface{}{ + util.GenerateAgentResponseModel("agent_uid", ""), + }, + ), + 1, + ) + util.MockGetGroup( + "group_uid", + util.GeneratePaginatedResponse( + []map[string]interface{}{ + util.GenerateNonRootGroupResponseModel("group_uid", "", ""), + }, + ), + 1, + ) + util.MockGetAgentGroupAssignment( + "agent_group_assignment_uid", + util.GeneratePaginatedResponse( + []map[string]interface{}{ + util.GenerateAgentGroupAssignmentResponse( + "agent_group_assignment_uid", + "", + ), + }, + ), + 1, + ) + util.MockDeleteGroup("group_uid", 1) + util.MockDeleteAgent("agent_uid", 1) + mock429 = gock.New("https://test.api.capenetworks.com"). + Delete("/networking-uxi/v1alpha1/agent-group-assignments/agent_group_assignment_uid"). + Reply(429). + SetHeaders(util.RateLimitingHeaders) + util.MockDeleteAgentGroupAssignment("agent_group_assignment_uid", 1) }, Config: provider.ProviderConfig, + Check: func(s *terraform.State) error { + st.Assert(t, mock429.Mock.Request().Counter, 0) + return nil + }, + }, + }, + }) + + mockOAuth.Mock.Disable() +} + +func TestAgentGroupAssignmentResourceHttpErrorHandling(t *testing.T) { + defer gock.Off() + mockOAuth := util.MockOAuth() + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: provider.TestAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Creating an agent group assignment - errors + { + PreConfig: func() { + util.MockGetAgent( + "agent_uid", + util.GeneratePaginatedResponse( + []map[string]interface{}{ + util.GenerateAgentResponseModel("agent_uid", ""), + }, + ), + 2, + ) + + // required for group create + util.MockPostGroup( + util.GenerateGroupRequestModel("group_uid", "", ""), + util.StructToMap( + util.GenerateNonRootGroupResponseModel("group_uid", "", ""), + ), + 1, + ) + util.MockGetGroup("group_uid", util.GeneratePaginatedResponse( + []map[string]interface{}{ + util.GenerateNonRootGroupResponseModel("group_uid", "", ""), + }, + ), + 2, + ) + + // agent group assignment create + gock.New("https://test.api.capenetworks.com"). + Post("/networking-uxi/v1alpha1/agent-group-assignments"). + Reply(400). + JSON(map[string]interface{}{ + "httpStatusCode": 400, + "errorCode": "HPE_GL_ERROR_BAD_REQUEST", + "message": "Validation error - bad request", + "debugId": "12312-123123-123123-1231212", + }) + }, + + Config: provider.ProviderConfig + ` + resource "uxi_group" "my_group" { + name = "name" + parent_group_id = "parent_uid" + } + + resource "uxi_agent" "my_agent" { + name = "name" + notes = "notes" + pcap_mode = "light" + } + + import { + to = uxi_agent.my_agent + id = "agent_uid" + } + + resource "uxi_agent_group_assignment" "my_agent_group_assignment" { + agent_id = uxi_agent.my_agent.id + group_id = uxi_group.my_group.id + }`, + ExpectError: regexp.MustCompile( + `(?s)Validation error - bad request\s*DebugID: 12312-123123-123123-1231212`, + ), + }, + // read not found error + { + PreConfig: func() { + util.MockGetAgent( + "agent_uid", + util.GeneratePaginatedResponse( + []map[string]interface{}{ + util.GenerateAgentResponseModel("agent_uid", ""), + }, + ), + 2, + ) + + // required for group create + util.MockPostGroup( + util.GenerateGroupRequestModel("group_uid", "", ""), + util.StructToMap( + util.GenerateNonRootGroupResponseModel("group_uid", "", ""), + ), + 1, + ) + util.MockGetGroup("group_uid", util.GeneratePaginatedResponse( + []map[string]interface{}{ + util.GenerateNonRootGroupResponseModel("group_uid", "", ""), + }, + ), + 2, + ) + + // agent group assignment read + util.MockGetAgentGroupAssignment( + "agent_group_assignment_uid", + util.GeneratePaginatedResponse([]map[string]interface{}{}), + 1, + ) + }, + Config: provider.ProviderConfig + ` + resource "uxi_group" "my_group" { + name = "name" + parent_group_id = "parent_uid" + } + + resource "uxi_agent" "my_agent" { + name = "name" + notes = "notes" + pcap_mode = "light" + } + + import { + to = uxi_agent.my_agent + id = "agent_uid" + } + + resource "uxi_agent_group_assignment" "my_agent_group_assignment" { + agent_id = uxi_agent.my_agent.id + group_id = uxi_group.my_group.id + } + + import { + to = uxi_agent_group_assignment.my_agent_group_assignment + id = "agent_group_assignment_uid" + } + `, + ExpectError: regexp.MustCompile(`Error: Cannot import non-existent remote object`), + }, + // Read 5xx error + { + PreConfig: func() { + util.MockGetAgent( + "agent_uid", + util.GeneratePaginatedResponse( + []map[string]interface{}{ + util.GenerateAgentResponseModel("agent_uid", ""), + }, + ), + 2, + ) + + // required for group create + util.MockPostGroup( + util.GenerateGroupRequestModel("group_uid", "", ""), + util.StructToMap( + util.GenerateNonRootGroupResponseModel("group_uid", "", ""), + ), + 1, + ) + util.MockGetGroup("group_uid", util.GeneratePaginatedResponse( + []map[string]interface{}{ + util.GenerateNonRootGroupResponseModel("group_uid", "", ""), + }, + ), + 2, + ) + + // agent group assignment read + gock.New("https://test.api.capenetworks.com"). + Get("/networking-uxi/v1alpha1/agent-group-assignments"). + Reply(500). + JSON(map[string]interface{}{ + "httpStatusCode": 500, + "errorCode": "HPE_GL_ERROR_INTERNAL_SERVER_ERROR", + "message": "Current request cannot be processed due to unknown issue", + "debugId": "12312-123123-123123-1231212", + }) + }, + Config: provider.ProviderConfig + ` + resource "uxi_group" "my_group" { + name = "name" + parent_group_id = "parent_uid" + } + + resource "uxi_agent" "my_agent" { + name = "name" + notes = "notes" + pcap_mode = "light" + } + + import { + to = uxi_agent.my_agent + id = "agent_uid" + } + + resource "uxi_agent_group_assignment" "my_agent_group_assignment" { + agent_id = uxi_agent.my_agent.id + group_id = uxi_group.my_group.id + } + + import { + to = uxi_agent_group_assignment.my_agent_group_assignment + id = "agent_group_assignment_uid" + } + `, + + ExpectError: regexp.MustCompile( + `(?s)Current request cannot be processed due to unknown issue\s*DebugID: 12312-123123-123123-1231212`, + ), + }, + // Actually creating an agent group assignment - for next step + { + PreConfig: func() { + util.MockGetAgent( + "agent_uid", + util.GeneratePaginatedResponse( + []map[string]interface{}{ + util.GenerateAgentResponseModel("agent_uid", ""), + }, + ), + 2, + ) + + // required for group create + util.MockPostGroup( + util.GenerateGroupRequestModel("group_uid", "", ""), + util.StructToMap( + util.GenerateNonRootGroupResponseModel("group_uid", "", ""), + ), + 1, + ) + util.MockGetGroup("group_uid", util.GeneratePaginatedResponse( + []map[string]interface{}{ + util.GenerateNonRootGroupResponseModel("group_uid", "", ""), + }, + ), + 2, + ) + + // required for agent group assignment create + util.MockPostAgentGroupAssignment( + util.GenerateAgentGroupAssignmentRequest( + "agent_group_assignment_uid", + "", + ), + util.GenerateAgentGroupAssignmentResponse( + "agent_group_assignment_uid", + "", + ), + 1, + ) + util.MockGetAgentGroupAssignment( + "agent_group_assignment_uid", + util.GeneratePaginatedResponse( + []map[string]interface{}{ + util.GenerateAgentGroupAssignmentResponse( + "agent_group_assignment_uid", + "", + ), + }, + ), + 1, + ) + }, + + Config: provider.ProviderConfig + ` + resource "uxi_group" "my_group" { + name = "name" + parent_group_id = "parent_uid" + } + + resource "uxi_agent" "my_agent" { + name = "name" + notes = "notes" + pcap_mode = "light" + + } + + import { + to = uxi_agent.my_agent + id = "agent_uid" + } + + resource "uxi_agent_group_assignment" "my_agent_group_assignment" { + agent_id = uxi_agent.my_agent.id + group_id = uxi_group.my_group.id + }`, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr( + "uxi_agent_group_assignment.my_agent_group_assignment", + "agent_id", + "agent_uid", + ), + ), + }, + // Delete agent-group assignment and remove agents from state - errors + { + PreConfig: func() { + util.MockGetAgent( + "agent_uid", + util.GeneratePaginatedResponse( + []map[string]interface{}{ + util.GenerateAgentResponseModel("agent_uid", ""), + }, + ), + 1, + ) + util.MockGetGroup("group_uid", util.GeneratePaginatedResponse( + []map[string]interface{}{ + util.GenerateNonRootGroupResponseModel("group_uid", "", ""), + }, + ), + 1, + ) + util.MockGetAgentGroupAssignment( + "agent_group_assignment_uid", + util.GeneratePaginatedResponse( + []map[string]interface{}{ + util.GenerateAgentGroupAssignmentResponse( + "agent_group_assignment_uid", + "", + ), + }, + ), + 1, + ) + + // agent group assignment create + gock.New("https://test.api.capenetworks.com"). + Delete("/networking-uxi/v1alpha1/agent-group-assignments"). + Reply(403). + JSON(map[string]interface{}{ + "httpStatusCode": 403, + "errorCode": "HPE_GL_ERROR_FORBIDDEN", + "message": "Forbidden - user has insufficient permissions to complete the request", + "debugId": "12312-123123-123123-1231212", + }) + + }, + Config: provider.ProviderConfig + ` + removed { + from = uxi_agent.my_agent + + lifecycle { + destroy = false + } + }`, + ExpectError: regexp.MustCompile( + `(?s)Forbidden - user has insufficient permissions to complete the request\s*DebugID: 12312-123123-123123-1231212`, + ), + }, + // Actually delete agent-group assignment and remove agents from state + { + PreConfig: func() { + util.MockGetAgent( + "agent_uid", + util.GeneratePaginatedResponse( + []map[string]interface{}{ + util.GenerateAgentResponseModel("agent_uid", ""), + }, + ), + 1, + ) + util.MockGetGroup("group_uid", util.GeneratePaginatedResponse( + []map[string]interface{}{ + util.GenerateNonRootGroupResponseModel("group_uid", "", ""), + }, + ), + 1, + ) + util.MockGetAgentGroupAssignment( + "agent_group_assignment_uid", + util.GeneratePaginatedResponse( + []map[string]interface{}{ + util.GenerateAgentGroupAssignmentResponse( + "agent_group_assignment_uid", + "", + ), + }, + ), + 1, + ) + util.MockDeleteGroup("group_uid", 1) + util.MockDeleteAgentGroupAssignment("agent_group_assignment_uid", 1) + }, + Config: provider.ProviderConfig + ` + removed { + from = uxi_agent.my_agent + + lifecycle { + destroy = false + } + }`, }, }, }) diff --git a/test/mocked/util/utils.go b/test/mocked/util/utils.go index a1bb101d..40117b7d 100644 --- a/test/mocked/util/utils.go +++ b/test/mocked/util/utils.go @@ -3,7 +3,6 @@ package util import ( "encoding/json" - "github.com/aruba-uxi/terraform-provider-hpeuxi/internal/provider/resources" "github.com/h2non/gock" ) @@ -173,17 +172,6 @@ func GenerateAgentGroupAssignmentResponse(uid string, postfix string) map[string } } -func GenerateAgentGroupAssignmentResponseMockedModel( - uid string, - postfix string, -) resources.AgentGroupAssignmentResponseModel { - return resources.AgentGroupAssignmentResponseModel{ - UID: uid, - GroupUID: "group_uid" + postfix, - AgentUID: "agent_uid" + postfix, - } -} - func GenerateNetworkGroupAssignmentResponse(uid string, postfix string) map[string]interface{} { return map[string]interface{}{ "id": uid, @@ -401,6 +389,14 @@ func MockPostAgentGroupAssignment( JSON(response) } +func MockDeleteAgentGroupAssignment(uid string, times int) { + gock.New("https://test.api.capenetworks.com"). + Delete("/networking-uxi/v1alpha1/agent-group-assignments/"+uid). + MatchHeader("Authorization", "mock_token"). + Times(times). + Reply(204) +} + func MockGetSensorGroupAssignment(uid string, response map[string]interface{}, times int) { gock.New("https://test.api.capenetworks.com"). Get("/networking-uxi/v1alpha1/sensor-group-assignments").