From 4fcff03dc7343ae09bdd4513f4e44630d6215972 Mon Sep 17 00:00:00 2001 From: Alistair Yan <52126234+1riatsila1@users.noreply.github.com> Date: Fri, 30 Aug 2024 13:24:43 +0200 Subject: [PATCH] feat: agent group assignment (#13) --- .../examples/full-demo/main.tf | 7 + pkg/config-api-provider/provider/provider.go | 1 + .../provider/resources/agent.go | 6 +- .../resources/agent_group_assignment.go | 172 ++++++++++++++++++ .../test/agent_group_assignment_test.go | 158 ++++++++++++++++ pkg/config-api-provider/test/agent_test.go | 8 +- pkg/config-api-provider/test/utils.go | 8 + 7 files changed, 353 insertions(+), 7 deletions(-) create mode 100644 pkg/config-api-provider/provider/resources/agent_group_assignment.go create mode 100644 pkg/config-api-provider/test/agent_group_assignment_test.go diff --git a/pkg/config-api-provider/examples/full-demo/main.tf b/pkg/config-api-provider/examples/full-demo/main.tf index 4580a7d6..bc5fe22b 100644 --- a/pkg/config-api-provider/examples/full-demo/main.tf +++ b/pkg/config-api-provider/examples/full-demo/main.tf @@ -120,11 +120,18 @@ import { id = "uid" } +// Sensor Group Assignment resource "uxi_sensor_group_assignment" "my_uxi_sensor_group_assignment" { sensor_id = uxi_sensor.my_sensor.id group_id = uxi_group.my_group.id } +// Agent Group Assignment +resource "uxi_agent_group_assignment" "my_uxi_agent_group_assignment" { + agent_id = uxi_agent.my_agent.id + group_id = uxi_group.my_group.id +} + # output "group" { # value = uxi_group.group # } diff --git a/pkg/config-api-provider/provider/provider.go b/pkg/config-api-provider/provider/provider.go index 2b225ec1..d93c6e85 100644 --- a/pkg/config-api-provider/provider/provider.go +++ b/pkg/config-api-provider/provider/provider.go @@ -64,5 +64,6 @@ func (p *uxiConfigurationProvider) Resources(_ context.Context) []func() resourc resources.NewWirelessNetworkResource, resources.NewServiceTestResource, resources.NewSensorGroupAssignmentResource, + resources.NewAgentGroupAssignmentResource, } } diff --git a/pkg/config-api-provider/provider/resources/agent.go b/pkg/config-api-provider/provider/resources/agent.go index 2431c033..47f8d131 100644 --- a/pkg/config-api-provider/provider/resources/agent.go +++ b/pkg/config-api-provider/provider/resources/agent.go @@ -97,7 +97,7 @@ func (r *agentResource) Read(ctx context.Context, req resource.ReadRequest, resp return } - response := GetAgent() + response := GetAgent(state.ID.ValueString()) // Update state from client response state.Name = types.StringValue(response.Name) @@ -159,11 +159,11 @@ func (r *agentResource) ImportState(ctx context.Context, req resource.ImportStat } // Get the agent using the configuration-api client -var GetAgent = func() AgentResponseModel { +var GetAgent = func(uid string) AgentResponseModel { // TODO: Query the agent using the client return AgentResponseModel{ - UID: "mock_uid", + UID: uid, Serial: "mock_serial", Name: "mock_name", ModelNumber: "mock_model_number", diff --git a/pkg/config-api-provider/provider/resources/agent_group_assignment.go b/pkg/config-api-provider/provider/resources/agent_group_assignment.go new file mode 100644 index 00000000..57e469e2 --- /dev/null +++ b/pkg/config-api-provider/provider/resources/agent_group_assignment.go @@ -0,0 +1,172 @@ +package resources + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// Ensure the implementation satisfies the expected interfaces. +var ( + _ resource.Resource = &agentGroupAssignmentResource{} + _ resource.ResourceWithConfigure = &agentGroupAssignmentResource{} +) + +type agentGroupAssignmentResourceModel struct { + ID types.String `tfsdk:"id"` + AgentID types.String `tfsdk:"agent_id"` + GroupID types.String `tfsdk:"group_id"` +} + +type AgentGroupAssignmentResponseModel struct { + UID string // + GroupUID string // , + AgentUID string // +} + +type AgentGroupAssignmentRequestModel struct { + GroupUID string // , + AgentUID string // +} + +func NewAgentGroupAssignmentResource() resource.Resource { + return &agentGroupAssignmentResource{} +} + +type agentGroupAssignmentResource struct{} + +func (r *agentGroupAssignmentResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_agent_group_assignment" +} + +func (r *agentGroupAssignmentResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "agent_id": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "group_id": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + }, + } +} + +func (r *agentGroupAssignmentResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { +} + +func (r *agentGroupAssignmentResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + // Retrieve values from plan + var plan agentGroupAssignmentResourceModel + diags := req.Plan.Get(ctx, &plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + // TODO: Call client createAgentGroupAssignment method + agentGroupAssignment := CreateAgentGroupAssignment(AgentGroupAssignmentRequestModel{ + GroupUID: plan.GroupID.ValueString(), + AgentUID: plan.AgentID.ValueString(), + }) + + // Update the state to match the plan + plan.ID = types.StringValue(agentGroupAssignment.UID) + plan.GroupID = types.StringValue(agentGroupAssignment.GroupUID) + plan.AgentID = types.StringValue(agentGroupAssignment.AgentUID) + + // Set state to fully populated data + diags = resp.State.Set(ctx, plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} + +func (r *agentGroupAssignmentResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + // Get current state + var state agentGroupAssignmentResourceModel + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + // TODO: Call client getAgentGroupAssignment method + agentGroupAssignment := GetAgentGroupAssignment(state.ID.ValueString()) + + // Update state from client response + state.GroupID = types.StringValue(agentGroupAssignment.GroupUID) + state.AgentID = types.StringValue(agentGroupAssignment.AgentUID) + + // Set refreshed state + diags = resp.State.Set(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} + +func (r *agentGroupAssignmentResource) Update(ctx context.Context, 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") + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} + +func (r *agentGroupAssignmentResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + // Retrieve values from state + var state agentGroupAssignmentResourceModel + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + // Delete existing agentGroupAssignment using the plan_id +} + +func (r *agentGroupAssignmentResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +var GetAgentGroupAssignment = func(uid string) AgentGroupAssignmentResponseModel { + // TODO: Query the agentGroupAssignment using the client + + return AgentGroupAssignmentResponseModel{ + UID: uid, + GroupUID: "mock_group_uid", + AgentUID: "mock_agent_uid", + } +} + +var CreateAgentGroupAssignment = func(request AgentGroupAssignmentRequestModel) AgentGroupAssignmentResponseModel { + // TODO: Query the agentGroupAssignment using the client + + return AgentGroupAssignmentResponseModel{ + UID: "mock_uid", + GroupUID: "mock_group_uid", + AgentUID: "mock_agent_uid", + } +} diff --git a/pkg/config-api-provider/test/agent_group_assignment_test.go b/pkg/config-api-provider/test/agent_group_assignment_test.go new file mode 100644 index 00000000..c7c0c7c9 --- /dev/null +++ b/pkg/config-api-provider/test/agent_group_assignment_test.go @@ -0,0 +1,158 @@ +package test + +import ( + "testing" + + "github.com/aruba-uxi/configuration-api-terraform-provider/pkg/terraform-provider-configuration/provider/resources" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func TestAgentGroupAssignmentResource(t *testing.T) { + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Creating a agent group assignment + { + PreConfig: func() { + // required for agent import + resources.GetAgent = func(uid string) resources.AgentResponseModel { + return GenerateAgentResponseModel(uid, "") + } + + // required for group create + resources.CreateGroup = func(request resources.GroupCreateRequestModel) resources.GroupResponseModel { + return GenerateGroupResponseModel("group_uid", "", "") + } + resources.GetGroup = func(uid string) resources.GroupResponseModel { + return GenerateGroupResponseModel("group_uid", "", "") + } + + // required for agent group assignment create + agentGroupAssignmentResponse := GenerateAgentGroupAssignmentResponse("agent_group_assignment_uid", "") + resources.CreateAgentGroupAssignment = func(request resources.AgentGroupAssignmentRequestModel) resources.AgentGroupAssignmentResponseModel { + return agentGroupAssignmentResponse + } + resources.GetAgentGroupAssignment = func(uid string) resources.AgentGroupAssignmentResponseModel { + return agentGroupAssignmentResponse + } + }, + + Config: providerConfig + ` + resource "uxi_group" "my_group" { + name = "name" + parent_uid = "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"), + resource.TestCheckResourceAttr("uxi_agent_group_assignment.my_agent_group_assignment", "group_id", "group_uid"), + resource.TestCheckResourceAttr("uxi_agent_group_assignment.my_agent_group_assignment", "id", "agent_group_assignment_uid"), + ), + }, + // ImportState testing + { + ResourceName: "uxi_agent_group_assignment.my_agent_group_assignment", + ImportState: true, + ImportStateVerify: true, + }, + // Update and Read testing + { + PreConfig: func() { + resources.GetAgent = func(uid string) resources.AgentResponseModel { + if uid == "agent_uid" { + return GenerateAgentResponseModel(uid, "") + } else { + return GenerateAgentResponseModel(uid, "_2") + } + } + + // required for creating another group + resources.CreateGroup = func(request resources.GroupCreateRequestModel) resources.GroupResponseModel { + return GenerateGroupResponseModel("group_uid_2", "_2", "_2") + } + resources.GetGroup = func(uid string) resources.GroupResponseModel { + if uid == "group_uid" { + return GenerateGroupResponseModel(uid, "", "") + } else { + return GenerateGroupResponseModel(uid, "_2", "_2") + } + } + + // required for agent group assignment create + resources.GetAgentGroupAssignment = func(uid string) resources.AgentGroupAssignmentResponseModel { + if uid == "agent_group_assignment_uid" { + return GenerateAgentGroupAssignmentResponse(uid, "") + } else { + return GenerateAgentGroupAssignmentResponse(uid, "_2") + } + } + resources.CreateAgentGroupAssignment = func(request resources.AgentGroupAssignmentRequestModel) resources.AgentGroupAssignmentResponseModel { + return GenerateAgentGroupAssignmentResponse("agent_group_assignment_uid_2", "_2") + } + }, + Config: providerConfig + ` + // the original resources + resource "uxi_group" "my_group" { + name = "name" + parent_uid = "parent_uid" + } + + resource "uxi_agent" "my_agent" { + name = "name" + notes = "notes" + pcap_mode = "light" + } + + import { + to = uxi_agent.my_agent + id = "agent_uid" + } + + // the new resources we wanna update the assignment to + resource "uxi_group" "my_group_2" { + name = "name_2" + parent_uid = "parent_uid_2" + } + + resource "uxi_agent" "my_agent_2" { + name = "name_2" + notes = "notes_2" + pcap_mode = "light_2" + } + + import { + to = uxi_agent.my_agent_2 + id = "agent_uid_2" + } + + // the assignment update, updated from agent/group to agent_2/group_2 + resource "uxi_agent_group_assignment" "my_agent_group_assignment" { + agent_id = uxi_agent.my_agent_2.id + group_id = uxi_group.my_group_2.id + }`, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("uxi_agent_group_assignment.my_agent_group_assignment", "agent_id", "agent_uid_2"), + resource.TestCheckResourceAttr("uxi_agent_group_assignment.my_agent_group_assignment", "group_id", "group_uid_2"), + resource.TestCheckResourceAttr("uxi_agent_group_assignment.my_agent_group_assignment", "id", "agent_group_assignment_uid_2"), + ), + }, + // Delete testing automatically occurs in TestCase + }, + }) +} diff --git a/pkg/config-api-provider/test/agent_test.go b/pkg/config-api-provider/test/agent_test.go index cc8ad316..1b0fc462 100644 --- a/pkg/config-api-provider/test/agent_test.go +++ b/pkg/config-api-provider/test/agent_test.go @@ -27,8 +27,8 @@ func TestAgentResource(t *testing.T) { // Importing an agent { PreConfig: func() { - resources.GetAgent = func() resources.AgentResponseModel { - return GenerateAgentResponseModel("uid", "") + resources.GetAgent = func(uid string) resources.AgentResponseModel { + return GenerateAgentResponseModel(uid, "") } }, Config: providerConfig + ` @@ -59,8 +59,8 @@ func TestAgentResource(t *testing.T) { // Update and Read testing { PreConfig: func() { - resources.GetAgent = func() resources.AgentResponseModel { - return GenerateAgentResponseModel("uid", "_2") + resources.GetAgent = func(uid string) resources.AgentResponseModel { + return GenerateAgentResponseModel(uid, "_2") } }, Config: providerConfig + ` diff --git a/pkg/config-api-provider/test/utils.go b/pkg/config-api-provider/test/utils.go index 1a695086..f2ee92c9 100644 --- a/pkg/config-api-provider/test/utils.go +++ b/pkg/config-api-provider/test/utils.go @@ -93,3 +93,11 @@ func GenerateSensorGroupAssignmentResponse(uid string, postfix string) resources SensorUID: "sensor_uid" + postfix, } } + +func GenerateAgentGroupAssignmentResponse(uid string, postfix string) resources.AgentGroupAssignmentResponseModel { + return resources.AgentGroupAssignmentResponseModel{ + UID: "agent_group_assignment_uid" + postfix, + GroupUID: "group_uid" + postfix, + AgentUID: "agent_uid" + postfix, + } +}