diff --git a/pkg/config-api-provider/examples/full-demo/main.tf b/pkg/config-api-provider/examples/full-demo/main.tf index bc5fe22b..61ef5a59 100644 --- a/pkg/config-api-provider/examples/full-demo/main.tf +++ b/pkg/config-api-provider/examples/full-demo/main.tf @@ -132,6 +132,12 @@ resource "uxi_agent_group_assignment" "my_uxi_agent_group_assignment" { group_id = uxi_group.my_group.id } +// Network Group Assignment +resource "uxi_network_group_assignment" "my_uxi_network_group_assignment" { + network_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 d93c6e85..0cbc9f26 100644 --- a/pkg/config-api-provider/provider/provider.go +++ b/pkg/config-api-provider/provider/provider.go @@ -65,5 +65,6 @@ func (p *uxiConfigurationProvider) Resources(_ context.Context) []func() resourc resources.NewServiceTestResource, resources.NewSensorGroupAssignmentResource, resources.NewAgentGroupAssignmentResource, + resources.NewNetworkGroupAssignmentResource, } } diff --git a/pkg/config-api-provider/provider/resources/network_group_assignment.go b/pkg/config-api-provider/provider/resources/network_group_assignment.go new file mode 100644 index 00000000..c9d5da90 --- /dev/null +++ b/pkg/config-api-provider/provider/resources/network_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 = &networkGroupAssignmentResource{} + _ resource.ResourceWithConfigure = &networkGroupAssignmentResource{} +) + +type networkGroupAssignmentResourceModel struct { + ID types.String `tfsdk:"id"` + NetworkID types.String `tfsdk:"network_id"` + GroupID types.String `tfsdk:"group_id"` +} + +type NetworkGroupAssignmentResponseModel struct { + UID string // + GroupUID string // , + NetworkUID string // +} + +type NetworkGroupAssignmentRequestModel struct { + GroupUID string // , + NetworkUID string // +} + +func NewNetworkGroupAssignmentResource() resource.Resource { + return &networkGroupAssignmentResource{} +} + +type networkGroupAssignmentResource struct{} + +func (r *networkGroupAssignmentResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_network_group_assignment" +} + +func (r *networkGroupAssignmentResource) 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(), + }, + }, + "network_id": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "group_id": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + }, + } +} + +func (r *networkGroupAssignmentResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { +} + +func (r *networkGroupAssignmentResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + // Retrieve values from plan + var plan networkGroupAssignmentResourceModel + diags := req.Plan.Get(ctx, &plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + // TODO: Call client createNetworkGroupAssignment method + networkGroupAssignment := CreateNetworkGroupAssignment(NetworkGroupAssignmentRequestModel{ + GroupUID: plan.GroupID.ValueString(), + NetworkUID: plan.NetworkID.ValueString(), + }) + + // Update the state to match the plan + plan.ID = types.StringValue(networkGroupAssignment.UID) + plan.GroupID = types.StringValue(networkGroupAssignment.GroupUID) + plan.NetworkID = types.StringValue(networkGroupAssignment.NetworkUID) + + // Set state to fully populated data + diags = resp.State.Set(ctx, plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} + +func (r *networkGroupAssignmentResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + // Get current state + var state networkGroupAssignmentResourceModel + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + // TODO: Call client getNetworkGroupAssignment method + networkGroupAssignment := GetNetworkGroupAssignment(state.ID.ValueString()) + + // Update state from client response + state.GroupID = types.StringValue(networkGroupAssignment.GroupUID) + state.NetworkID = types.StringValue(networkGroupAssignment.NetworkUID) + + // Set refreshed state + diags = resp.State.Set(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} + +func (r *networkGroupAssignmentResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + // Retrieve values from plan + var plan networkGroupAssignmentResourceModel + diags := req.Plan.Get(ctx, &plan) + diags.AddError("operation not supported", "updating a network group assignment is not supported") + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} + +func (r *networkGroupAssignmentResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + // Retrieve values from state + var state networkGroupAssignmentResourceModel + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + // Delete existing networkGroupAssignment using the plan_id +} + +func (r *networkGroupAssignmentResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +var GetNetworkGroupAssignment = func(uid string) NetworkGroupAssignmentResponseModel { + // TODO: Query the networkGroupAssignment using the client + + return NetworkGroupAssignmentResponseModel{ + UID: uid, + GroupUID: "mock_group_uid", + NetworkUID: "mock_network_uid", + } +} + +var CreateNetworkGroupAssignment = func(request NetworkGroupAssignmentRequestModel) NetworkGroupAssignmentResponseModel { + // TODO: Query the networkGroupAssignment using the client + + return NetworkGroupAssignmentResponseModel{ + UID: "mock_uid", + GroupUID: "mock_group_uid", + NetworkUID: "mock_network_uid", + } +} diff --git a/pkg/config-api-provider/provider/resources/wired_network.go b/pkg/config-api-provider/provider/resources/wired_network.go index 2aa775bd..6ddc827a 100644 --- a/pkg/config-api-provider/provider/resources/wired_network.go +++ b/pkg/config-api-provider/provider/resources/wired_network.go @@ -84,7 +84,7 @@ func (r *wiredNetworkResource) Read(ctx context.Context, req resource.ReadReques return } - response := GetWiredNetwork() + response := GetWiredNetwork(state.ID.ValueString()) // Update state from client response state.Alias = types.StringValue(response.Alias) @@ -118,7 +118,7 @@ func (r *wiredNetworkResource) ImportState(ctx context.Context, req resource.Imp } // Get the wiredNetwork using the configuration-api client -var GetWiredNetwork = func() WiredNetworkResponseModel { +var GetWiredNetwork = func(uid string) WiredNetworkResponseModel { // TODO: Query the wiredNetwork using the client return WiredNetworkResponseModel{ diff --git a/pkg/config-api-provider/provider/resources/wireless_network.go b/pkg/config-api-provider/provider/resources/wireless_network.go index 53c5ea3c..716e9a0a 100644 --- a/pkg/config-api-provider/provider/resources/wireless_network.go +++ b/pkg/config-api-provider/provider/resources/wireless_network.go @@ -86,7 +86,7 @@ func (r *wirelessNetworkResource) Read(ctx context.Context, req resource.ReadReq return } - response := GetWirelessNetwork() + response := GetWirelessNetwork(state.ID.ValueString()) // Update state from client response state.Alias = types.StringValue(response.Alias) @@ -120,11 +120,11 @@ func (r *wirelessNetworkResource) ImportState(ctx context.Context, req resource. } // Get the wirelessNetwork using the configuration-api client -var GetWirelessNetwork = func() WirelessNetworkResponseModel { +var GetWirelessNetwork = func(uid string) WirelessNetworkResponseModel { // TODO: Query the wirelessNetwork using the client return WirelessNetworkResponseModel{ - Uid: "mock_uid", + Uid: uid, Ssid: "mock_ssid", DatetimeCreated: "mock_datetime_created", DatetimeUpdated: "mock_datetime_updated", diff --git a/pkg/config-api-provider/test/network_group_assignment_test.go b/pkg/config-api-provider/test/network_group_assignment_test.go new file mode 100644 index 00000000..455f31b1 --- /dev/null +++ b/pkg/config-api-provider/test/network_group_assignment_test.go @@ -0,0 +1,331 @@ +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 TestNetworkGroupAssignmentResource(t *testing.T) { + + // Test Wired Network Group Assignment + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Creating a network group assignment + { + PreConfig: func() { + // required for network import + resources.GetWiredNetwork = func(uid string) resources.WiredNetworkResponseModel { + return GenerateWiredNetworkResponseModel(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 network group assignment create + resources.CreateNetworkGroupAssignment = func(request resources.NetworkGroupAssignmentRequestModel) resources.NetworkGroupAssignmentResponseModel { + return GenerateNetworkGroupAssignmentResponse("network_group_assignment_uid", "") + } + resources.GetNetworkGroupAssignment = func(uid string) resources.NetworkGroupAssignmentResponseModel { + return GenerateNetworkGroupAssignmentResponse("network_group_assignment_uid", "") + } + }, + + Config: providerConfig + ` + resource "uxi_group" "my_group" { + name = "name" + parent_uid = "parent_uid" + } + + resource "uxi_wired_network" "my_network" { + alias = "alias" + } + + import { + to = uxi_wired_network.my_network + id = "network_uid" + } + + resource "uxi_network_group_assignment" "my_network_group_assignment" { + network_id = uxi_wired_network.my_network.id + group_id = uxi_group.my_group.id + }`, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("uxi_network_group_assignment.my_network_group_assignment", "network_id", "network_uid"), + resource.TestCheckResourceAttr("uxi_network_group_assignment.my_network_group_assignment", "group_id", "group_uid"), + resource.TestCheckResourceAttr("uxi_network_group_assignment.my_network_group_assignment", "id", "network_group_assignment_uid"), + ), + }, + // ImportState testing + { + ResourceName: "uxi_network_group_assignment.my_network_group_assignment", + ImportState: true, + ImportStateVerify: true, + }, + // Update and Read testing + { + PreConfig: func() { + resources.GetWiredNetwork = func(uid string) resources.WiredNetworkResponseModel { + if uid == "network_uid" { + return GenerateWiredNetworkResponseModel(uid, "") + } else { + return GenerateWiredNetworkResponseModel(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 network group assignment create + resources.CreateNetworkGroupAssignment = func(request resources.NetworkGroupAssignmentRequestModel) resources.NetworkGroupAssignmentResponseModel { + return GenerateNetworkGroupAssignmentResponse("network_group_assignment_uid_2", "_2") + } + resources.GetNetworkGroupAssignment = func(uid string) resources.NetworkGroupAssignmentResponseModel { + if uid == "network_group_assignment_uid" { + return GenerateNetworkGroupAssignmentResponse(uid, "") + } else { + return GenerateNetworkGroupAssignmentResponse(uid, "_2") + } + } + }, + Config: providerConfig + ` + // the original resources + resource "uxi_group" "my_group" { + name = "name" + parent_uid = "parent_uid" + } + + resource "uxi_wired_network" "my_network" { + alias = "alias" + } + + import { + to = uxi_wired_network.my_network + id = "network_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_wired_network" "my_network_2" { + alias = "alias_2" + } + + import { + to = uxi_wired_network.my_network_2 + id = "network_uid_2" + } + + // the assignment update, updated from network/group to network_2/group_2 + resource "uxi_network_group_assignment" "my_network_group_assignment" { + network_id = uxi_wired_network.my_network_2.id + group_id = uxi_group.my_group_2.id + }`, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("uxi_network_group_assignment.my_network_group_assignment", "network_id", "network_uid_2"), + resource.TestCheckResourceAttr("uxi_network_group_assignment.my_network_group_assignment", "group_id", "group_uid_2"), + resource.TestCheckResourceAttr("uxi_network_group_assignment.my_network_group_assignment", "id", "network_group_assignment_uid_2"), + ), + }, + // Remove networks from state + { + Config: providerConfig + ` + removed { + from = uxi_wired_network.my_network + + lifecycle { + destroy = false + } + } + + removed { + from = uxi_wired_network.my_network_2 + + lifecycle { + destroy = false + } + }`, + }, + // Delete testing automatically occurs in TestCase + }, + }) + + // Test Wireless Network Group Assignment + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Creating a network group assignment + { + PreConfig: func() { + // required for network import + resources.GetWirelessNetwork = func(uid string) resources.WirelessNetworkResponseModel { + return GenerateWirelessNetworkResponseModel(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(uid, "", "") + } + + // required for network group assignment create + resources.CreateNetworkGroupAssignment = func(request resources.NetworkGroupAssignmentRequestModel) resources.NetworkGroupAssignmentResponseModel { + return GenerateNetworkGroupAssignmentResponse("network_group_assignment_uid", "") + } + resources.GetNetworkGroupAssignment = func(uid string) resources.NetworkGroupAssignmentResponseModel { + return GenerateNetworkGroupAssignmentResponse(uid, "") + } + }, + + Config: providerConfig + ` + resource "uxi_group" "my_group" { + name = "name" + parent_uid = "parent_uid" + } + + resource "uxi_wireless_network" "my_network" { + alias = "alias" + } + + import { + to = uxi_wireless_network.my_network + id = "network_uid" + } + + resource "uxi_network_group_assignment" "my_network_group_assignment" { + network_id = uxi_wireless_network.my_network.id + group_id = uxi_group.my_group.id + }`, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("uxi_network_group_assignment.my_network_group_assignment", "network_id", "network_uid"), + resource.TestCheckResourceAttr("uxi_network_group_assignment.my_network_group_assignment", "group_id", "group_uid"), + resource.TestCheckResourceAttr("uxi_network_group_assignment.my_network_group_assignment", "id", "network_group_assignment_uid"), + ), + }, + // ImportState testing + { + ResourceName: "uxi_network_group_assignment.my_network_group_assignment", + ImportState: true, + ImportStateVerify: true, + }, + // Update and Read testing + { + PreConfig: func() { + resources.GetWirelessNetwork = func(uid string) resources.WirelessNetworkResponseModel { + if uid == "network_uid" { + return GenerateWirelessNetworkResponseModel(uid, "") + } else { + return GenerateWirelessNetworkResponseModel(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("group_uid_2", "_2", "_2") + } + } + + // required for network group assignment create + resources.GetNetworkGroupAssignment = func(uid string) resources.NetworkGroupAssignmentResponseModel { + if uid == "network_group_assignment_uid" { + return GenerateNetworkGroupAssignmentResponse("network_group_assignment_uid", "") + } else { + return GenerateNetworkGroupAssignmentResponse("network_group_assignment_uid_2", "_2") + } + } + resources.CreateNetworkGroupAssignment = func(request resources.NetworkGroupAssignmentRequestModel) resources.NetworkGroupAssignmentResponseModel { + return GenerateNetworkGroupAssignmentResponse("network_group_assignment_uid_2", "_2") + } + }, + Config: providerConfig + ` + // the original resources + resource "uxi_group" "my_group" { + name = "name" + parent_uid = "parent_uid" + } + + resource "uxi_wireless_network" "my_network" { + alias = "alias" + } + + import { + to = uxi_wireless_network.my_network + id = "network_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_wireless_network" "my_network_2" { + alias = "alias_2" + } + + import { + to = uxi_wireless_network.my_network_2 + id = "network_uid_2" + } + + // the assignment update, updated from network/group to network_2/group_2 + resource "uxi_network_group_assignment" "my_network_group_assignment" { + network_id = uxi_wireless_network.my_network_2.id + group_id = uxi_group.my_group_2.id + }`, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("uxi_network_group_assignment.my_network_group_assignment", "network_id", "network_uid_2"), + resource.TestCheckResourceAttr("uxi_network_group_assignment.my_network_group_assignment", "group_id", "group_uid_2"), + resource.TestCheckResourceAttr("uxi_network_group_assignment.my_network_group_assignment", "id", "network_group_assignment_uid_2"), + ), + }, + // Remove networks from state + { + Config: providerConfig + ` + removed { + from = uxi_wireless_network.my_network + + lifecycle { + destroy = false + } + } + + removed { + from = uxi_wireless_network.my_network_2 + + lifecycle { + destroy = false + } + }`, + }, + // Delete testing automatically occurs in TestCase + }, + }) +} diff --git a/pkg/config-api-provider/test/utils.go b/pkg/config-api-provider/test/utils.go index f2ee92c9..6bbc2f7b 100644 --- a/pkg/config-api-provider/test/utils.go +++ b/pkg/config-api-provider/test/utils.go @@ -101,3 +101,11 @@ func GenerateAgentGroupAssignmentResponse(uid string, postfix string) resources. AgentUID: "agent_uid" + postfix, } } + +func GenerateNetworkGroupAssignmentResponse(uid string, postfix string) resources.NetworkGroupAssignmentResponseModel { + return resources.NetworkGroupAssignmentResponseModel{ + UID: "network_group_assignment_uid" + postfix, + GroupUID: "group_uid" + postfix, + NetworkUID: "network_uid" + postfix, + } +} diff --git a/pkg/config-api-provider/test/wired_network_test.go b/pkg/config-api-provider/test/wired_network_test.go index 019c277e..20ae89c6 100644 --- a/pkg/config-api-provider/test/wired_network_test.go +++ b/pkg/config-api-provider/test/wired_network_test.go @@ -30,8 +30,8 @@ func TestWiredNetworkResource(t *testing.T) { // Importing a wired_network { PreConfig: func() { - resources.GetWiredNetwork = func() resources.WiredNetworkResponseModel { - return GenerateWiredNetworkResponseModel("uid", "") + resources.GetWiredNetwork = func(uid string) resources.WiredNetworkResponseModel { + return GenerateWiredNetworkResponseModel(uid, "") } }, Config: providerConfig + ` diff --git a/pkg/config-api-provider/test/wireless_network_test.go b/pkg/config-api-provider/test/wireless_network_test.go index fa386edb..d9f79f49 100644 --- a/pkg/config-api-provider/test/wireless_network_test.go +++ b/pkg/config-api-provider/test/wireless_network_test.go @@ -30,8 +30,8 @@ func TestWirelessNetworkResource(t *testing.T) { // Importing a wireless_network { PreConfig: func() { - resources.GetWirelessNetwork = func() resources.WirelessNetworkResponseModel { - return GenerateWirelessNetworkResponseModel("uid", "") + resources.GetWirelessNetwork = func(uid string) resources.WirelessNetworkResponseModel { + return GenerateWirelessNetworkResponseModel(uid, "") } }, Config: providerConfig + `