From 0e49b77cfdb169a203e54981a89d55d0b5fe87f3 Mon Sep 17 00:00:00 2001 From: STANIAC Date: Thu, 12 Sep 2024 15:31:09 +0200 Subject: [PATCH] added support for device_id, description and device_id fields in netbox_service. Schema updated for fields, a service can now be assigned to a virtual machine or a device, but not both. Added tests for tags, description and device_id --- netbox/resource_netbox_service.go | 84 ++++++++++++++++-- netbox/resource_netbox_service_test.go | 113 +++++++++++++++++++++++++ 2 files changed, 188 insertions(+), 9 deletions(-) diff --git a/netbox/resource_netbox_service.go b/netbox/resource_netbox_service.go index c4cceed9..a277e61e 100644 --- a/netbox/resource_netbox_service.go +++ b/netbox/resource_netbox_service.go @@ -32,8 +32,9 @@ func resourceNetboxService() *schema.Resource { ValidateFunc: validation.StringLenBetween(1, 100), }, "virtual_machine_id": { - Type: schema.TypeInt, - Required: true, + Type: schema.TypeInt, + Optional: true, + ExactlyOneOf: []string{"virtual_machine_id", "device_id"}, }, "protocol": { Type: schema.TypeString, @@ -55,6 +56,22 @@ func resourceNetboxService() *schema.Resource { Type: schema.TypeInt, }, }, + "description": { + Type: schema.TypeString, + Optional: true, + }, + "tags": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "device_id": { + Type: schema.TypeInt, + Optional: true, + ExactlyOneOf: []string{"virtual_machine_id", "device_id"}, + }, customFieldsKey: customFieldsSchema, }, Importer: &schema.ResourceImporter{ @@ -89,10 +106,24 @@ func resourceNetboxServiceCreate(d *schema.ResourceData, m interface{}) error { } } - dataVirtualMachineID := int64(d.Get("virtual_machine_id").(int)) - data.VirtualMachine = &dataVirtualMachineID + if v, ok := d.GetOk("device_id"); ok { + deviceID := int64(v.(int)) + data.Device = &deviceID + } + + if v, ok := d.GetOk("virtual_machine_id"); ok { + dataVirtualMachineID := int64(v.(int)) + data.VirtualMachine = &dataVirtualMachineID + } + + v, ok := d.GetOk("tags") + tags, _ := getNestedTagListFromResourceDataSet(api, v) + data.Tags = tags + + if v, ok := d.GetOk("description"); ok { + data.Description = v.(string) + } - data.Tags = []*models.NestedTag{} data.Ipaddresses = []int64{} ct, ok := d.GetOk(customFieldsKey) @@ -131,7 +162,28 @@ func resourceNetboxServiceRead(d *schema.ResourceData, m interface{}) error { d.Set("name", res.GetPayload().Name) d.Set("protocol", res.GetPayload().Protocol.Value) d.Set("ports", res.GetPayload().Ports) - d.Set("virtual_machine_id", res.GetPayload().VirtualMachine.ID) + d.Set("description", res.GetPayload().Description) + + if res.GetPayload().VirtualMachine != nil { + d.Set("virtual_machine_id", res.GetPayload().VirtualMachine.ID) + } else { + d.Set("virtual_machine_id", nil) + } + + if res.GetPayload().Device != nil { + d.Set("device_id", res.GetPayload().Device.ID) + } else { + d.Set("device_id", nil) + } + + if tags := res.GetPayload().Tags; tags != nil { + var tagList []interface{} + for _, tag := range tags { + tagName := tag.Name + tagList = append(tagList, *tagName) + } + d.Set("tags", tagList) + } cf := getCustomFields(res.GetPayload().CustomFields) if cf != nil { @@ -166,11 +218,25 @@ func resourceNetboxServiceUpdate(d *schema.ResourceData, m interface{}) error { } } - data.Tags = []*models.NestedTag{} data.Ipaddresses = []int64{} - dataVirtualMachineID := int64(d.Get("virtual_machine_id").(int)) - data.VirtualMachine = &dataVirtualMachineID + v, ok := d.GetOk("tags") + tags, _ := getNestedTagListFromResourceDataSet(api, v) + data.Tags = tags + + if v, ok := d.GetOk("description"); ok { + data.Description = v.(string) + } + + if v, ok := d.GetOk("device_id"); ok { + deviceID := int64(v.(int)) + data.Device = &deviceID + } + + if v, ok := d.GetOk("virtual_machine_id"); ok { + dataVirtualMachineID := int64(v.(int)) + data.VirtualMachine = &dataVirtualMachineID + } cf, ok := d.GetOk(customFieldsKey) if ok { diff --git a/netbox/resource_netbox_service_test.go b/netbox/resource_netbox_service_test.go index 5f346072..634a8b45 100644 --- a/netbox/resource_netbox_service_test.go +++ b/netbox/resource_netbox_service_test.go @@ -138,6 +138,119 @@ func testAccCheckServiceDestroy(s *terraform.State) error { return nil } +func TestAccNetboxService_withDescriptionDeviceID(t *testing.T) { + testSlug := "svc_with_desc_tags_device" + testName := testAccGetTestName(testSlug) + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckServiceDestroy, + Steps: []resource.TestStep{ + { + Config: fmt.Sprintf(` +resource "netbox_service" "test" { + name = "%s" + device_id = netbox_device.test_device.id + ports = [666] + protocol = "tcp" + description = "Test service description" +} + resource "netbox_site" "test_site" { + name = "%[1]s_site" + slug = "%[1]s_site" +} + +resource "netbox_device_role" "test_role" { + name = "%[1]s_role" + slug = "%[1]s_role" + color_hex = "123456" +} + +resource "netbox_manufacturer" "test_manufacturer" { + name = "%[1]s_manufacturer" +} + +resource "netbox_device_type" "test_type" { + model = "%[1]s_type" + manufacturer_id = netbox_manufacturer.test_manufacturer.id +} + +resource "netbox_device" "test_device" { + name = "%[1]s_device" + role_id = netbox_device_role.test_role.id + device_type_id = netbox_device_type.test_type.id + site_id = netbox_site.test_site.id +} +`, testName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("netbox_service.test", "name", testName), + resource.TestCheckResourceAttrPair("netbox_service.test", "device_id", "netbox_device.test_device", "id"), + resource.TestCheckResourceAttr("netbox_service.test", "ports.#", "1"), + resource.TestCheckResourceAttr("netbox_service.test", "ports.0", "666"), + resource.TestCheckResourceAttr("netbox_service.test", "protocol", "tcp"), + resource.TestCheckResourceAttr("netbox_service.test", "description", "Test service description"), + ), + }, + { + ResourceName: "netbox_service.test", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccNetboxService_withDescriptionTagsVirtualMachine(t *testing.T) { + testSlug := "svc_with_desc_tags_device" + testName := testAccGetTestName(testSlug) + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckServiceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccNetboxServiceFullDependencies(testName) + fmt.Sprintf( + ` + resource "netbox_tag" "tag1" { + name = "tag1" + slug = "tag1" + } + resource "netbox_tag" "tag2" { + name = "tag2" + slug = "tag2" + } + resource "netbox_service" "test" { + name = "%s" + virtual_machine_id = netbox_virtual_machine.test.id + ports = [666] + protocol = "tcp" + description = "Test service description" + tags = [netbox_tag.tag1.name, netbox_tag.tag2.name] + } + `, + testName, + ), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("netbox_service.test", "name", testName), + resource.TestCheckResourceAttr("netbox_service.test", "tags.#", "2"), + resource.TestCheckResourceAttr("netbox_service.test", "tags.0", "tag1"), + resource.TestCheckResourceAttr("netbox_service.test", "tags.1", "tag2"), + resource.TestCheckResourceAttrPair("netbox_service.test", "virtual_machine_id", "netbox_virtual_machine.test", "id"), + resource.TestCheckResourceAttr("netbox_service.test", "ports.#", "1"), + resource.TestCheckResourceAttr("netbox_service.test", "ports.0", "666"), + resource.TestCheckResourceAttr("netbox_service.test", "protocol", "tcp"), + resource.TestCheckResourceAttr("netbox_service.test", "description", "Test service description"), + ), + }, + { + ResourceName: "netbox_service.test", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func init() { resource.AddTestSweepers("netbox_service", &resource.Sweeper{ Name: "netbox_service",