diff --git a/CHANGELOG.md b/CHANGELOG.md index 84756634b..2f752a8c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ - `Data Sources`: - [ionoscloud_object_storage_accesskey](docs/data-sources/object_storage_accesskey.md) - [ionoscloud_object_storage_region](docs/data-sources/object_storage_region.md) +### Enhancement +- make `mac` optional on `ionoscloud_nic`, `ionoscloud_server`, `ionoscloud_cube_server` and `ionoscloud_vcpu_server` ### Fixes - Refactor `ionoscloud_share` and `ionoscloud_nic` data sources - Remove sleep and delete from `ionoscloud_share` resource diff --git a/docs/resources/nic.md b/docs/resources/nic.md index 5fe956b95..293b1e8ec 100644 --- a/docs/resources/nic.md +++ b/docs/resources/nic.md @@ -58,7 +58,6 @@ resource "ionoscloud_nic" "example" { server_id = ionoscloud_server.example.id lan = ionoscloud_lan.example.id name = "NIC" - lan = 2 dhcp = true firewall_active = true firewall_type = "INGRESS" @@ -175,7 +174,7 @@ This will configure flowlog for accepted ingress traffic and will log it into an - `firewall_active` - (Optional)[Boolean] If this resource is set to true and is nested under a server resource firewall, with open SSH port, resource must be nested under the NIC. - `firewall_type` - (Optional) [String] The type of firewall rules that will be allowed on the NIC. If it is not specified it will take the default value INGRESS - `id` - (Computed) The ID of the NIC. -- `mac` - (Computed) The MAC address of the NIC. +- `mac` - (Optional) The MAC address of the NIC. Can be set on creation only. If not set, one will be assigned automatically by the API. Immutable, update forces re-creation. * `device_number`- (Computed) The Logical Unit Number (LUN) of the storage volume. Null if this NIC was created from CloudAPI and no DCD changes were done on the Datacenter. * `pci_slot`- (Computed) The PCI slot number of the Nic. * `flowlog` - (Optional) Only 1 flow log can be configured. Only the name field can change as part of an update. Flow logs holistically capture network information such as source and destination IP addresses, source and destination ports, number of packets, amount of bytes, the start and end time of the recording, and the type of protocol – and log the extent to which your instances are being accessed. diff --git a/ionoscloud/import_server_test.go b/ionoscloud/import_server_test.go index 0afb0011c..7d133b519 100644 --- a/ionoscloud/import_server_test.go +++ b/ionoscloud/import_server_test.go @@ -50,7 +50,7 @@ func TestAccServerWithLabelsImport(t *testing.T) { ImportStateIdFunc: testAccServerImportStateId, ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"image_password", "ssh_key_path.#", "image_name", "volume.0.user_data", "volume.0.backup_unit_id", "firewallrule_id", "primary_nic", "inline_volume_ids", "primary_ip"}, + ImportStateVerifyIgnore: []string{"image_password", "ssh_key_path.#", "image_name", "volume.0.user_data", "volume.0.backup_unit_id", "firewallrule_id", "primary_nic", "inline_volume_ids", "primary_ip", "allow_replace"}, }, }, }) diff --git a/ionoscloud/resource_cube_server.go b/ionoscloud/resource_cube_server.go index 528ed9139..391f34048 100644 --- a/ionoscloud/resource_cube_server.go +++ b/ionoscloud/resource_cube_server.go @@ -256,7 +256,9 @@ func resourceCubeServer() *schema.Resource { Schema: map[string]*schema.Schema{ "mac": { Type: schema.TypeString, + Optional: true, Computed: true, + ForceNew: true, }, "lan": { Type: schema.TypeInt, @@ -496,7 +498,7 @@ func resourceCubeServerCreate(ctx context.Context, d *schema.ResourceData, meta Properties: &ionoscloud.NicProperties{}, } if _, ok := d.GetOk("nic"); ok { - nic, err = cloudapinic.GetNicFromSchema(d, "nic.0.") + nic, err = cloudapinic.GetNicFromSchemaCreate(d, "nic.0.") if err != nil { diags := diag.FromErr(fmt.Errorf("cube error occurred while getting nic from schema: %w", err)) return diags diff --git a/ionoscloud/resource_cube_server_test.go b/ionoscloud/resource_cube_server_test.go index f9c334848..f9fdb3938 100644 --- a/ionoscloud/resource_cube_server_test.go +++ b/ionoscloud/resource_cube_server_test.go @@ -253,6 +253,7 @@ func TestAccCubeServerResolveImageName(t *testing.T) { resource.TestCheckResourceAttrPair(constant.ServerCubeResource+"."+constant.ServerTestResource, "nic.0.lan", constant.LanResource+"."+constant.LanTestResource, "id"), resource.TestCheckResourceAttr(constant.ServerCubeResource+"."+constant.ServerTestResource, "nic.0.dhcp", "true"), resource.TestCheckResourceAttr(constant.ServerCubeResource+"."+constant.ServerTestResource, "nic.0.firewall_active", "true"), + resource.TestCheckResourceAttr(constant.ServerCubeResource+"."+constant.ServerTestResource, "nic.0.mac", constant.NicMac), resource.TestCheckResourceAttr(constant.ServerCubeResource+"."+constant.ServerTestResource, "nic.0.firewall.0.protocol", "TCP"), resource.TestCheckResourceAttr(constant.ServerCubeResource+"."+constant.ServerTestResource, "nic.0.firewall.0.name", constant.ServerTestResource), resource.TestCheckResourceAttr(constant.ServerCubeResource+"."+constant.ServerTestResource, "nic.0.firewall.0.port_range_start", "22"), @@ -556,6 +557,7 @@ resource ` + constant.ServerCubeResource + ` ` + constant.ServerTestResource + ` lan = ` + constant.LanResource + `.` + constant.LanTestResource + `.id dhcp = true firewall_active = true + mac = "` + constant.NicMac + `" firewall { protocol = "TCP" name = "` + constant.ServerTestResource + `" diff --git a/ionoscloud/resource_nic.go b/ionoscloud/resource_nic.go index d0f7feb8a..71a1f4bf7 100644 --- a/ionoscloud/resource_nic.go +++ b/ionoscloud/resource_nic.go @@ -95,7 +95,9 @@ func resourceNic() *schema.Resource { }, "mac": { Type: schema.TypeString, + Optional: true, Computed: true, + ForceNew: true, }, "device_number": { Type: schema.TypeInt, @@ -179,7 +181,7 @@ func resourceNicCreate(ctx context.Context, d *schema.ResourceData, meta interfa client := meta.(services.SdkBundle).CloudApiClient ns := cloudapinic.Service{Client: client, Meta: meta, D: d} - nic, err := cloudapinic.GetNicFromSchema(d, "") + nic, err := cloudapinic.GetNicFromSchemaCreate(d, "") if err != nil { diags := diag.FromErr(fmt.Errorf("error occurred while getting nic from schema: %w", err)) return diags diff --git a/ionoscloud/resource_nic_test.go b/ionoscloud/resource_nic_test.go index f9d29b312..9bcac4652 100644 --- a/ionoscloud/resource_nic_test.go +++ b/ionoscloud/resource_nic_test.go @@ -35,6 +35,7 @@ func TestAccNicBasic(t *testing.T) { testAccCheckNICExists(constant.FullNicResourceName, &nic), resource.TestCheckResourceAttrSet(constant.FullNicResourceName, "pci_slot"), resource.TestCheckResourceAttr(constant.FullNicResourceName, "name", name), + resource.TestCheckResourceAttr(constant.FullNicResourceName, "mac", "00:0a:95:9d:68:16"), resource.TestCheckResourceAttr(constant.FullNicResourceName, "dhcp", "true"), resource.TestCheckResourceAttr(constant.FullNicResourceName, "dhcpv6", "true"), resource.TestCheckResourceAttrSet(constant.FullNicResourceName, "ipv6_cidr_block"), @@ -55,6 +56,7 @@ func TestAccNicBasic(t *testing.T) { Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrPair(constant.DataSource+"."+dataSourceNicById, "name", constant.FullNicResourceName, "name"), resource.TestCheckResourceAttrPair(constant.DataSource+"."+dataSourceNicById, "dhcp", constant.FullNicResourceName, "dhcp"), + resource.TestCheckResourceAttrPair(constant.DataSource+"."+dataSourceNicById, "mac", constant.FullNicResourceName, "mac"), resource.TestCheckResourceAttrPair(constant.DataSource+"."+dataSourceNicById, "dhcpv6", constant.FullNicResourceName, "dhcpv6"), resource.TestCheckResourceAttrPair(constant.DataSource+"."+dataSourceNicById, "ipv6_cidr_block", constant.FullNicResourceName, "ipv6_cidr_block"), resource.TestCheckResourceAttrPair(constant.DataSource+"."+dataSourceNicById, "firewall_active", constant.FullNicResourceName, "firewall_active"), @@ -106,6 +108,7 @@ func TestAccNicBasic(t *testing.T) { Config: testAccCheckNicConfigUpdate, Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr(constant.FullNicResourceName, "name", "updated"), + resource.TestCheckResourceAttr(constant.FullNicResourceName, "mac", "00:0a:95:9d:68:16"), resource.TestCheckResourceAttr(constant.FullNicResourceName, "dhcp", "false"), resource.TestCheckResourceAttr(constant.FullNicResourceName, "dhcpv6", "false"), resource.TestCheckResourceAttr(constant.FullNicResourceName, "firewall_active", "false"), @@ -244,6 +247,7 @@ const testAccCheckNicConfigBasic = testCreateDataCenterAndServer + ` resource ` + constant.NicResource + ` "database_nic" { datacenter_id = ` + constant.DatacenterResource + `.` + constant.DatacenterTestResource + `.id server_id = ` + constant.ServerResource + `.` + constant.ServerTestResource + `.id + mac = "00:0a:95:9d:68:16" lan = "${ionoscloud_lan.test_lan_2.id}" dhcpv6 = true firewall_active = true @@ -264,6 +268,7 @@ const testAccCheckNicConfigUpdate = testCreateDataCenterAndServer + ` resource ` + constant.NicResource + ` "database_nic" { datacenter_id = ` + constant.DatacenterResource + `.` + constant.DatacenterTestResource + `.id server_id = ` + constant.ServerResource + `.` + constant.ServerTestResource + `.id + mac = "00:0a:95:9d:68:16" lan = "${ionoscloud_lan.test_lan_2.id}" dhcp = false dhcpv6 = false diff --git a/ionoscloud/resource_server.go b/ionoscloud/resource_server.go index 71050e274..bef555a49 100644 --- a/ionoscloud/resource_server.go +++ b/ionoscloud/resource_server.go @@ -353,7 +353,9 @@ func resourceServer() *schema.Resource { }, "mac": { Type: schema.TypeString, + Optional: true, Computed: true, + ForceNew: true, }, "lan": { Type: schema.TypeInt, @@ -524,6 +526,9 @@ func checkServerImmutableFields(_ context.Context, diff *schema.ResourceDiff, _ if diff.HasChange("image_name") { return fmt.Errorf("image_name %s", ImmutableError) } + if diff.HasChange("nic.0.mac") { + return fmt.Errorf("nic mac %s", ImmutableError) + } if diff.HasChange("template_uuid") { return fmt.Errorf("template_uuid %s", ImmutableError) } @@ -630,7 +635,7 @@ func resourceServerCreate(ctx context.Context, d *schema.ResourceData, meta inte if nics.([]interface{}) != nil { for nicIndex := range nics.([]interface{}) { nicPath := fmt.Sprintf("nic.%d.", nicIndex) - nic, err := cloudapinic.GetNicFromSchema(d, nicPath) + nic, err := cloudapinic.GetNicFromSchemaCreate(d, nicPath) if err != nil { diags := diag.FromErr(fmt.Errorf("create error occurred while getting nic from schema: %w", err)) return diags diff --git a/ionoscloud/resource_server_test.go b/ionoscloud/resource_server_test.go index 341fd3bf6..b7a83b675 100644 --- a/ionoscloud/resource_server_test.go +++ b/ionoscloud/resource_server_test.go @@ -317,6 +317,7 @@ func TestAccServerNoBootVolumeBasic(t *testing.T) { resource.TestCheckResourceAttr(constant.ServerResource+"."+constant.ServerTestResource, "nic.0.name", "system"), resource.TestCheckResourceAttr(constant.ServerResource+"."+constant.ServerTestResource, "nic.0.dhcp", "true"), resource.TestCheckResourceAttr(constant.ServerResource+"."+constant.ServerTestResource, "nic.0.firewall_active", "true"), + resource.TestCheckResourceAttr(constant.ServerResource+"."+constant.ServerTestResource, "nic.0.mac", constant.NicMac), resource.TestCheckResourceAttrPair(constant.ServerResource+"."+constant.ServerTestResource, "nic.0.id", constant.ServerResource+"."+constant.ServerTestResource, "primary_nic"), resource.TestCheckResourceAttr(constant.ServerResource+"."+constant.ServerTestResource, "nic.0.firewall_type", "INGRESS"), ), @@ -356,6 +357,7 @@ func TestAccServerBootCdromNoImageAndInlineFwRules(t *testing.T) { resource.TestCheckResourceAttr(constant.ServerResource+"."+constant.ServerTestResource, "volume.0.licence_type", "OTHER"), resource.TestCheckResourceAttrPair(constant.ServerResource+"."+constant.ServerTestResource, "nic.0.lan", constant.LanResource+"."+constant.LanTestResource, "id"), resource.TestCheckResourceAttr(constant.ServerResource+"."+constant.ServerTestResource, "nic.0.dhcp", "true"), + resource.TestCheckResourceAttr(constant.ServerResource+"."+constant.ServerTestResource, "nic.0.mac", constant.NicMac), resource.TestCheckResourceAttr(constant.ServerResource+"."+constant.ServerTestResource, "nic.0.firewall_active", "true"), resource.TestCheckResourceAttr(constant.ServerResource+"."+constant.ServerTestResource, "nic.0.firewall.0.protocol", "TCP"), resource.TestCheckResourceAttr(constant.ServerResource+"."+constant.ServerTestResource, "nic.0.firewall.0.name", constant.ServerTestResource), @@ -1094,6 +1096,7 @@ resource ` + constant.ServerResource + ` ` + constant.ServerTestResource + ` { lan = ` + constant.LanResource + `.` + constant.LanTestResource + `.id dhcp = true firewall_active = true + mac = "` + constant.NicMac + `" firewall { protocol = "TCP" type = "EGRESS" @@ -1901,6 +1904,7 @@ resource ` + constant.ServerResource + ` ` + constant.ServerTestResource + ` { dhcp = true firewall_active = true firewall_type = "INGRESS" + mac = "` + constant.NicMac + `" } } diff --git a/ionoscloud/resource_vcpu_server.go b/ionoscloud/resource_vcpu_server.go index 1fb54876e..e03ba6b3c 100644 --- a/ionoscloud/resource_vcpu_server.go +++ b/ionoscloud/resource_vcpu_server.go @@ -239,7 +239,9 @@ func resourceVCPUServer() *schema.Resource { }, "mac": { Type: schema.TypeString, + Optional: true, Computed: true, + ForceNew: true, }, "lan": { Type: schema.TypeInt, diff --git a/ionoscloud/resource_vcpu_server_test.go b/ionoscloud/resource_vcpu_server_test.go index 4fb066865..64ec79c11 100644 --- a/ionoscloud/resource_vcpu_server_test.go +++ b/ionoscloud/resource_vcpu_server_test.go @@ -102,6 +102,7 @@ func TestAccServerVCPUBasic(t *testing.T) { resource.TestCheckResourceAttr(constant.ServerVCPUResource+"."+constant.ServerTestResource, "nic.0.name", "system"), resource.TestCheckResourceAttr(constant.ServerVCPUResource+"."+constant.ServerTestResource, "nic.0.dhcp", "true"), resource.TestCheckResourceAttr(constant.ServerVCPUResource+"."+constant.ServerTestResource, "nic.0.firewall_active", "true"), + resource.TestCheckResourceAttr(constant.ServerCubeResource+"."+constant.ServerTestResource, "nic.0.mac", constant.NicMac), resource.TestCheckResourceAttrPair(constant.ServerVCPUResource+"."+constant.ServerTestResource, "nic.0.id", constant.ServerVCPUResource+"."+constant.ServerTestResource, "primary_nic"), resource.TestCheckResourceAttr(constant.ServerVCPUResource+"."+constant.ServerTestResource, "nic.0.firewall_type", "BIDIRECTIONAL"), ), diff --git a/ionoscloud/test_constants.go b/ionoscloud/test_constants.go index f38ddc3f0..bec4b5620 100644 --- a/ionoscloud/test_constants.go +++ b/ionoscloud/test_constants.go @@ -1500,7 +1500,8 @@ resource ` + constant.ServerVCPUResource + ` ` + constant.ServerTestResource + ` name = "system" dhcp = true firewall_active = true - firewall_type = "BIDIRECTIONAL" + mac = "` + constant.NicMac + `" + firewall_type = "BIDIRECTIONAL" ips = [""] firewall { protocol = "TCP" diff --git a/services/cloudapi/cloudapinic/nic.go b/services/cloudapi/cloudapinic/nic.go index a44383b69..a72416f71 100644 --- a/services/cloudapi/cloudapinic/nic.go +++ b/services/cloudapi/cloudapinic/nic.go @@ -211,6 +211,20 @@ func GetNicFromSchema(d *schema.ResourceData, path string) (ionoscloud.Nic, erro return nic, nil } +// GetNicFromSchemaCreate - creates a nic object for create operations from the schema +func GetNicFromSchemaCreate(d *schema.ResourceData, path string) (ionoscloud.Nic, error) { + nic, err := GetNicFromSchema(d, path) + if err != nil { + return nic, err + } + if v, ok := d.GetOk(path + "mac"); ok { + vStr := v.(string) + nic.Properties.Mac = &vStr + } + + return nic, nil +} + func NicSetData(d *schema.ResourceData, nic *ionoscloud.Nic) error { if nic == nil { return fmt.Errorf("nic is empty") diff --git a/utils/constant/constants.go b/utils/constant/constants.go index 9b6d5265a..607d6d940 100644 --- a/utils/constant/constants.go +++ b/utils/constant/constants.go @@ -246,6 +246,7 @@ const ( NicResource = "ionoscloud_nic" FullNicResourceName = NicResource + "." + NicTestResourceName NicTestResourceName = "database_nic" + NicMac = "00:0a:95:9d:68:16" ) const (