feat(iaas): min/max acc tests (#811)

* feat(iaas): security group min/max acc test

* feat(iaas): image min/max acc test

* feat(iaas): KeyPair min/max acc test

* feat(iaas): Network area min/max acc test
- fix: wrong atLeast-validator for `minimum_prefix_length`

* feat(iaas): Network min/max acc test

* feat(iaas): Volume min/max acc test
- fix: volume update doesn't work if no name was defined
- fix: volume.server_id can not be set
- fix: error message volume.size returns value but was null

* feat(iaas): Network interfaces min/max acc test

* feat(iaas): Affinity groups acc test

* feat(iaas): Server min/max acc test
- stackit_server_volume_attach
- stackit_server_network_interface_attach
- stackit_server_service_account_attach

* fix(iaas): acc test
- image: fix read of Config.VirtioScsi
- keypair: add missing RequiresReplace() for name
- server: add missing UserData read in datasource and resource

* feat(iaas): public ip acc test
- fix: when a nic is assigned to a public ip, the field network_interface_id leads to recreation
This commit is contained in:
Marcel Jacek 2025-05-09 08:49:04 +02:00 committed by GitHub
parent b5f955124a
commit c7c64a5806
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 3494 additions and 995 deletions

View file

@ -38,13 +38,13 @@ resource "stackit_volume" "example" {
- `labels` (Map of String) Labels are key-value string pairs which can be attached to a resource container
- `name` (String) The name of the volume.
- `performance_class` (String) The performance class of the volume. Possible values are documented in [Service plans BlockStorage](https://docs.stackit.cloud/stackit/en/service-plans-blockstorage-75137974.html#ServiceplansBlockStorage-CurrentlyavailableServicePlans%28performanceclasses%29)
- `server_id` (String) The server ID of the server to which the volume is attached to.
- `size` (Number) The size of the volume in GB. It can only be updated to a larger value than the current size. Either `size` or `source` must be provided
- `source` (Attributes) The source of the volume. It can be either a volume, an image, a snapshot or a backup. Either `size` or `source` must be provided (see [below for nested schema](#nestedatt--source))
### Read-Only
- `id` (String) Terraform's internal resource ID. It is structured as "`project_id`,`volume_id`".
- `server_id` (String) The server ID of the server to which the volume is attached to.
- `volume_id` (String) The volume ID.
<a id="nestedatt--source"></a>

File diff suppressed because it is too large Load diff

View file

@ -309,7 +309,7 @@ func mapDataSourceFields(ctx context.Context, imageResp *iaas.Image, model *Data
configModel.SecureBoot = types.BoolPointerValue(imageResp.Config.SecureBoot)
configModel.UEFI = types.BoolPointerValue(imageResp.Config.Uefi)
configModel.VideoModel = types.StringPointerValue(imageResp.Config.GetVideoModel())
configModel.VirtioScsi = types.BoolPointerValue(imageResp.Config.VirtioScsi)
configModel.VirtioScsi = types.BoolPointerValue(iaas.PtrBool(imageResp.Config.GetVirtioScsi()))
configObject, diags = types.ObjectValue(configTypes, map[string]attr.Value{
"boot_menu": configModel.BootMenu,

View file

@ -649,7 +649,7 @@ func mapFields(ctx context.Context, imageResp *iaas.Image, model *Model) error {
configModel.SecureBoot = types.BoolPointerValue(imageResp.Config.SecureBoot)
configModel.UEFI = types.BoolPointerValue(imageResp.Config.Uefi)
configModel.VideoModel = types.StringPointerValue(imageResp.Config.GetVideoModel())
configModel.VirtioScsi = types.BoolPointerValue(imageResp.Config.VirtioScsi)
configModel.VirtioScsi = types.BoolPointerValue(iaas.PtrBool(imageResp.Config.GetVirtioScsi()))
configObject, diags = types.ObjectValue(configTypes, map[string]attr.Value{
"boot_menu": configModel.BootMenu,

View file

@ -107,6 +107,7 @@ func (r *keyPairResource) Schema(_ context.Context, _ resource.SchemaRequest, re
Description: "The name of the SSH key pair.",
Required: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
stringplanmodifier.UseStateForUnknown(),
},
},

View file

@ -228,7 +228,7 @@ func (r *networkAreaResource) Schema(_ context.Context, _ resource.SchemaRequest
Optional: true,
Computed: true,
Validators: []validator.Int64{
int64validator.AtLeast(22),
int64validator.AtLeast(8),
int64validator.AtMost(29),
},
Default: int64default.StaticInt64(24),

View file

@ -140,6 +140,7 @@ func (r *publicIpResource) Schema(_ context.Context, _ resource.SchemaRequest, r
"network_interface_id": schema.StringAttribute{
Description: "Associates the public IP with a network interface or a virtual IP (ID). If you are using this resource with a Kubernetes Load Balancer or any other resource which associates a network interface implicitly, use the lifecycle `ignore_changes` property in this field to prevent unintentional removal of the network interface due to drift in the Terraform state",
Optional: true,
Computed: true,
Validators: []validator.String{
validate.UUID(),
validate.NoSeparator(),

View file

@ -323,6 +323,10 @@ func mapDataSourceFields(ctx context.Context, serverResp *iaas.Server, model *Da
model.BootVolume = types.ObjectNull(bootVolumeDataTypes)
}
if serverResp.UserData != nil && len(*serverResp.UserData) > 0 {
model.UserData = types.StringValue(string(*serverResp.UserData))
}
model.AvailabilityZone = types.StringPointerValue(serverResp.AvailabilityZone)
model.ServerId = types.StringValue(serverId)
model.MachineType = types.StringPointerValue(serverResp.MachineType)

View file

@ -960,6 +960,10 @@ func mapFields(ctx context.Context, serverResp *iaas.Server, model *Model) error
if serverResp.Status != nil && *serverResp.Status != wait.ServerDeallocatedStatus {
model.AvailabilityZone = types.StringPointerValue(serverResp.AvailabilityZone)
}
if serverResp.UserData != nil && len(*serverResp.UserData) > 0 {
model.UserData = types.StringValue(string(*serverResp.UserData))
}
model.Name = types.StringPointerValue(serverResp.Name)
model.Labels = labels
model.ImageId = types.StringPointerValue(serverResp.ImageId)

View file

@ -0,0 +1,9 @@
variable "project_id" {}
variable "name" {}
variable "policy" {}
resource "stackit_affinity_group" "affinity_group" {
project_id = var.project_id
name = var.name
policy = var.policy
}

View file

@ -0,0 +1,48 @@
variable "project_id" {}
variable "name" {}
variable "disk_format" {}
variable "local_file_path" {}
variable "min_disk_size" {}
variable "min_ram" {}
variable "label" {}
variable "boot_menu" {}
variable "cdrom_bus" {}
variable "disk_bus" {}
variable "nic_model" {}
variable "operating_system" {}
variable "operating_system_distro" {}
variable "operating_system_version" {}
variable "rescue_bus" {}
variable "rescue_device" {}
variable "secure_boot" {}
variable "uefi" {}
variable "video_model" {}
variable "virtio_scsi" {}
resource "stackit_image" "image" {
project_id = var.project_id
name = var.name
disk_format = var.disk_format
local_file_path = var.local_file_path
min_disk_size = var.min_disk_size
min_ram = var.min_ram
labels = {
"acc-test" : var.label
}
config = {
boot_menu = var.boot_menu
cdrom_bus = var.cdrom_bus
disk_bus = var.disk_bus
nic_model = var.nic_model
operating_system = var.operating_system
operating_system_distro = var.operating_system_distro
operating_system_version = var.operating_system_version
rescue_bus = var.rescue_bus
rescue_device = var.rescue_device
secure_boot = var.secure_boot
uefi = var.uefi
video_model = var.video_model
virtio_scsi = var.virtio_scsi
}
}

View file

@ -0,0 +1,11 @@
variable "project_id" {}
variable "name" {}
variable "disk_format" {}
variable "local_file_path" {}
resource "stackit_image" "image" {
project_id = var.project_id
name = var.name
disk_format = var.disk_format
local_file_path = var.local_file_path
}

View file

@ -0,0 +1,11 @@
variable "name" {}
variable "public_key" {}
variable "label" {}
resource "stackit_key_pair" "key_pair" {
name = var.name
public_key = var.public_key
labels = {
"acc-test" : var.label
}
}

View file

@ -0,0 +1,7 @@
variable "name" {}
variable "public_key" {}
resource "stackit_key_pair" "key_pair" {
name = var.name
public_key = var.public_key
}

View file

@ -0,0 +1,41 @@
variable "organization_id" {}
variable "name" {}
variable "transfer_network" {}
variable "network_ranges_prefix" {}
variable "default_nameservers" {}
variable "default_prefix_length" {}
variable "max_prefix_length" {}
variable "min_prefix_length" {}
variable "route_prefix" {}
variable "route_next_hop" {}
variable "label" {}
resource "stackit_network_area" "network_area" {
organization_id = var.organization_id
name = var.name
network_ranges = [
{
prefix = var.network_ranges_prefix
}
]
transfer_network = var.transfer_network
default_nameservers = [var.default_nameservers]
default_prefix_length = var.default_prefix_length
max_prefix_length = var.max_prefix_length
min_prefix_length = var.min_prefix_length
labels = {
"acc-test" : var.label
}
}
resource "stackit_network_area_route" "network_area_route" {
organization_id = stackit_network_area.network_area.organization_id
network_area_id = stackit_network_area.network_area.network_area_id
prefix = var.route_prefix
next_hop = var.route_next_hop
labels = {
"acc-test" : var.label
}
}

View file

@ -0,0 +1,26 @@
variable "organization_id" {}
variable "name" {}
variable "transfer_network" {}
variable "network_ranges_prefix" {}
variable "route_prefix" {}
variable "route_next_hop" {}
resource "stackit_network_area" "network_area" {
organization_id = var.organization_id
name = var.name
transfer_network = var.transfer_network
network_ranges = [
{
prefix = var.network_ranges_prefix
}
]
}
resource "stackit_network_area_route" "network_area_route" {
organization_id = stackit_network_area.network_area.organization_id
network_area_id = stackit_network_area.network_area.network_area_id
prefix = var.route_prefix
next_hop = var.route_next_hop
}

View file

@ -0,0 +1,54 @@
variable "project_id" {}
variable "name" {}
variable "allowed_address" {}
variable "ipv4" {}
variable "ipv4_prefix" {}
variable "security" {}
variable "label" {}
resource "stackit_network" "network" {
project_id = var.project_id
name = var.name
ipv4_prefix = var.ipv4_prefix
}
resource "stackit_network_interface" "network_interface" {
project_id = var.project_id
network_id = stackit_network.network.network_id
name = var.name
allowed_addresses = var.security ? [var.allowed_address] : null
ipv4 = var.ipv4
security = var.security
security_group_ids = var.security ? [stackit_security_group.security_group.security_group_id] : null
labels = {
"acc-test" : var.label
}
}
resource "stackit_public_ip" "public_ip" {
project_id = var.project_id
network_interface_id = stackit_network_interface.network_interface.network_interface_id
labels = {
"acc-test" : var.label
}
}
resource "stackit_network_interface" "network_interface_simple" {
project_id = var.project_id
network_id = stackit_network.network.network_id
}
resource "stackit_public_ip" "public_ip_simple" {
project_id = var.project_id
}
resource "stackit_public_ip_associate" "nic_public_ip_attach" {
project_id = var.project_id
network_interface_id = stackit_network_interface.network_interface_simple.network_interface_id
public_ip_id = stackit_public_ip.public_ip_simple.public_ip_id
}
resource "stackit_security_group" "security_group" {
project_id = var.project_id
name = var.name
}

View file

@ -0,0 +1,16 @@
variable "project_id" {}
variable "name" {}
resource "stackit_network" "network" {
project_id = var.project_id
name = var.name
}
resource "stackit_network_interface" "network_interface" {
project_id = var.project_id
network_id = stackit_network.network.network_id
}
resource "stackit_public_ip" "public_ip" {
project_id = var.project_id
}

View file

@ -0,0 +1,22 @@
variable "project_id" {}
variable "name" {}
variable "ipv4_gateway" {}
variable "ipv4_nameservers" {}
variable "ipv4_prefix" {}
variable "ipv4_prefix_length" {}
variable "routed" {}
variable "label" {}
resource "stackit_network" "network" {
project_id = var.project_id
name = var.name
ipv4_gateway = var.ipv4_gateway != "" ? var.ipv4_gateway : null
no_ipv4_gateway = var.ipv4_gateway != "" ? null : true
ipv4_nameservers = [var.ipv4_nameservers]
ipv4_prefix = var.ipv4_prefix
ipv4_prefix_length = var.ipv4_prefix_length
routed = var.routed
labels = {
"acc-test" : var.label
}
}

View file

@ -0,0 +1,7 @@
variable "project_id" {}
variable "name" {}
resource "stackit_network" "network" {
project_id = var.project_id
name = var.name
}

View file

@ -0,0 +1,72 @@
variable "project_id" {}
variable "name" {}
variable "description" {}
variable "description_rule" {}
variable "label" {}
variable "stateful" {}
variable "direction" {}
variable "ether_type" {}
variable "ip_range" {}
variable "port" {}
variable "protocol" {}
variable "icmp_code" {}
variable "icmp_type" {}
variable "name_remote" {}
resource "stackit_security_group" "security_group" {
project_id = var.project_id
name = var.name
description = var.description
labels = {
"acc-test" : var.label
}
stateful = var.stateful
}
resource "stackit_security_group_rule" "security_group_rule" {
project_id = var.project_id
security_group_id = stackit_security_group.security_group.security_group_id
direction = var.direction
description = var.description_rule
ether_type = var.ether_type
port_range = {
min = var.port
max = var.port
}
protocol = {
name = var.protocol
}
ip_range = var.ip_range
}
resource "stackit_security_group_rule" "security_group_rule_icmp" {
project_id = var.project_id
security_group_id = stackit_security_group.security_group.security_group_id
direction = var.direction
description = var.description_rule
ether_type = var.ether_type
icmp_parameters = {
code = var.icmp_code
type = var.icmp_type
}
protocol = {
name = "icmp"
}
ip_range = var.ip_range
}
resource "stackit_security_group" "security_group_remote" {
project_id = var.project_id
name = var.name_remote
}
resource "stackit_security_group_rule" "security_group_rule_remote_security_group" {
project_id = var.project_id
security_group_id = stackit_security_group.security_group.security_group_id
direction = var.direction
remote_security_group_id = stackit_security_group.security_group_remote.security_group_id
}

View file

@ -0,0 +1,15 @@
variable "project_id" {}
variable "name" {}
variable "direction" {}
resource "stackit_security_group" "security_group" {
project_id = var.project_id
name = var.name
}
resource "stackit_security_group_rule" "security_group_rule" {
project_id = var.project_id
security_group_id = stackit_security_group.security_group.security_group_id
direction = var.direction
}

View file

@ -0,0 +1,11 @@
resource "stackit_server_volume_attach" "data_volume_attachment" {
project_id = var.project_id
server_id = stackit_server.server.server_id
volume_id = stackit_volume.data_volume.volume_id
}
resource "stackit_server_network_interface_attach" "network_interface_second_attachment" {
project_id = var.project_id
network_interface_id = stackit_network_interface.network_interface_second.network_interface_id
server_id = stackit_server.server.server_id
}

View file

@ -0,0 +1,82 @@
variable "project_id" {}
variable "name" {}
variable "name_not_updated" {}
variable "machine_type" {}
variable "image_id" {}
variable "availability_zone" {}
variable "label" {}
variable "user_data" {}
variable "desired_status" {}
variable "policy" {}
variable "size" {}
variable "public_key" {}
variable "service_account_mail" {}
resource "stackit_affinity_group" "affinity_group" {
project_id = var.project_id
name = var.name_not_updated
policy = var.policy
}
resource "stackit_volume" "base_volume" {
project_id = var.project_id
availability_zone = var.availability_zone
size = var.size
source = {
id = var.image_id
type = "image"
}
}
resource "stackit_volume" "data_volume" {
project_id = var.project_id
availability_zone = var.availability_zone
size = var.size
}
resource "stackit_network" "network" {
project_id = var.project_id
name = var.name
}
resource "stackit_network_interface" "network_interface_init" {
project_id = var.project_id
network_id = stackit_network.network.network_id
}
resource "stackit_network_interface" "network_interface_second" {
project_id = var.project_id
network_id = stackit_network.network.network_id
}
resource "stackit_key_pair" "key_pair" {
name = var.name_not_updated
public_key = var.public_key
}
resource "stackit_server_service_account_attach" "attached_service_account" {
project_id = var.project_id
server_id = stackit_server.server.server_id
service_account_email = var.service_account_mail
}
resource "stackit_server" "server" {
project_id = var.project_id
name = var.name
machine_type = var.machine_type
affinity_group = stackit_affinity_group.affinity_group.affinity_group_id
availability_zone = var.availability_zone
keypair_name = stackit_key_pair.key_pair.name
desired_status = var.desired_status
network_interfaces = [stackit_network_interface.network_interface_init.network_interface_id]
user_data = var.user_data
boot_volume = {
source_type = "volume"
source_id = stackit_volume.base_volume.volume_id
}
labels = {
"acc-test" : var.label
}
}

View file

@ -0,0 +1,17 @@
variable "project_id" {}
variable "name" {}
variable "machine_type" {}
variable "image_id" {}
resource "stackit_server" "server" {
project_id = var.project_id
name = var.name
machine_type = var.machine_type
boot_volume = {
source_type = "image"
size = 16
source_id = var.image_id
delete_on_termination = true
}
}

View file

@ -0,0 +1,35 @@
variable "project_id" {}
variable "availability_zone" {}
variable "name" {}
variable "size" {}
variable "description" {}
variable "performance_class" {}
variable "label" {}
resource "stackit_volume" "volume_size" {
project_id = var.project_id
availability_zone = var.availability_zone
name = var.name
size = var.size
description = var.description
performance_class = var.performance_class
labels = {
"acc-test" : var.label
}
}
resource "stackit_volume" "volume_source" {
project_id = var.project_id
availability_zone = var.availability_zone
name = var.name
description = var.description
performance_class = var.performance_class
size = var.size
source = {
id = stackit_volume.volume_size.volume_id
type = "volume"
}
labels = {
"acc-test" : var.label
}
}

View file

@ -0,0 +1,18 @@
variable "project_id" {}
variable "availability_zone" {}
variable "size" {}
resource "stackit_volume" "volume_size" {
project_id = var.project_id
availability_zone = var.availability_zone
size = var.size
}
resource "stackit_volume" "volume_source" {
project_id = var.project_id
availability_zone = var.availability_zone
source = {
id = stackit_volume.volume_size.volume_id
type = "volume"
}
}

View file

@ -167,7 +167,6 @@ func (r *volumeResource) Schema(_ context.Context, _ resource.SchemaRequest, res
"server_id": schema.StringAttribute{
Description: "The server ID of the server to which the volume is attached to.",
Computed: true,
Optional: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
@ -234,6 +233,7 @@ func (r *volumeResource) Schema(_ context.Context, _ resource.SchemaRequest, res
"size": schema.Int64Attribute{
Description: "The size of the volume in GB. It can only be updated to a larger value than the current size. Either `size` or `source` must be provided",
Optional: true,
Computed: true,
PlanModifiers: []planmodifier.Int64{
volumeResizeModifier{},
},
@ -575,6 +575,10 @@ func mapFields(ctx context.Context, volumeResp *iaas.Volume, model *Model) error
model.AvailabilityZone = types.StringPointerValue(volumeResp.AvailabilityZone)
model.Description = types.StringPointerValue(volumeResp.Description)
model.Name = types.StringPointerValue(volumeResp.Name)
// Workaround for volumes with no names which return an empty string instead of nil
if name := volumeResp.Name; name != nil && *name == "" {
model.Name = types.StringNull()
}
model.Labels = labels
model.PerformanceClass = types.StringPointerValue(volumeResp.PerformanceClass)
model.ServerId = types.StringPointerValue(volumeResp.ServerId)