diff --git a/docs/data-sources/loadbalancer.md b/docs/data-sources/loadbalancer.md new file mode 100644 index 00000000..6a66d85a --- /dev/null +++ b/docs/data-sources/loadbalancer.md @@ -0,0 +1,97 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "stackit_loadbalancer Data Source - stackit" +subcategory: "" +description: |- + Load Balancer resource schema. +--- + +# stackit_loadbalancer (Data Source) + +Load Balancer resource schema. + +## Example Usage + +```terraform +data "stackit_loadbalancer" "example" { + project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + name = "example-load-balancer" +} +``` + + +## Schema + +### Required + +- `name` (String) Load balancer name. +- `project_id` (String) STACKIT project ID to which the Load Balancer is associated. + +### Read-Only + +- `external_address` (String) External Load Balancer IP address where this Load Balancer is exposed. +- `id` (String) Terraform's internal resource ID. It is structured as "`project_id`","`name`". +- `listeners` (Attributes List) List of all listeners which will accept traffic. Limited to 20. (see [below for nested schema](#nestedatt--listeners)) +- `networks` (Attributes List) List of networks that listeners and targets reside in. (see [below for nested schema](#nestedatt--networks)) +- `options` (Attributes) Defines any optional functionality you want to have enabled on your load balancer. (see [below for nested schema](#nestedatt--options)) +- `private_address` (String) Transient private Load Balancer IP address. It can change any time. +- `target_pools` (Attributes List) List of all target pools which will be used in the Load Balancer. Limited to 20. (see [below for nested schema](#nestedatt--target_pools)) + + +### Nested Schema for `listeners` + +Read-Only: + +- `display_name` (String) +- `port` (Number) Port number where we listen for traffic. +- `protocol` (String) Protocol is the highest network protocol we understand to load balance. +- `target_pool` (String) Reference target pool by target pool name. + + + +### Nested Schema for `networks` + +Read-Only: + +- `network_id` (String) Openstack network ID. +- `role` (String) The role defines how the load balancer is using the network. + + + +### Nested Schema for `options` + +Read-Only: + +- `acl` (Set of String) Load Balancer is accessible only from an IP address in this range. +- `private_network_only` (Boolean) If true, Load Balancer is accessible only via a private network IP address. + + + +### Nested Schema for `target_pools` + +Read-Only: + +- `active_health_check` (Attributes) (see [below for nested schema](#nestedatt--target_pools--active_health_check)) +- `name` (String) Target pool name. +- `target_port` (Number) Identical port number where each target listens for traffic. +- `targets` (Attributes List) List of all targets which will be used in the pool. Limited to 250. (see [below for nested schema](#nestedatt--target_pools--targets)) + + +### Nested Schema for `target_pools.active_health_check` + +Read-Only: + +- `healthy_threshold` (Number) Healthy threshold of the health checking. +- `interval` (String) Interval duration of health checking in seconds. +- `interval_jitter` (String) Interval duration threshold of the health checking in seconds. +- `timeout` (String) Active health checking timeout duration in seconds. +- `unhealthy_threshold` (Number) Unhealthy threshold of the health checking. + + + +### Nested Schema for `target_pools.targets` + +Read-Only: + +- `display_name` (String) Target display name +- `ip` (String) Target IP diff --git a/docs/index.md b/docs/index.md index 9a0bf3cc..07ea1b18 100644 --- a/docs/index.md +++ b/docs/index.md @@ -116,6 +116,7 @@ Using this flow is less secure since the token is long-lived. You can provide th - `credentials_path` (String) Path of JSON from where the credentials are read. Takes precedence over the env var `STACKIT_CREDENTIALS_PATH`. Default value is `~/.stackit/credentials.json`. - `dns_custom_endpoint` (String) Custom endpoint for the DNS service - `jwks_custom_endpoint` (String) Custom endpoint for the jwks API, which is used to get the json web key sets (jwks) to validate tokens when using the key flow +- `loadbalancer_custom_endpoint` (String) Custom endpoint for the Load Balancer service - `logme_custom_endpoint` (String) Custom endpoint for the LogMe service - `mariadb_custom_endpoint` (String) Custom endpoint for the MariaDB service - `mongodbflex_custom_endpoint` (String) Custom endpoint for the MongoDB Flex service diff --git a/docs/resources/loadbalancer.md b/docs/resources/loadbalancer.md new file mode 100644 index 00000000..1d36d8b0 --- /dev/null +++ b/docs/resources/loadbalancer.md @@ -0,0 +1,261 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "stackit_loadbalancer Resource - stackit" +subcategory: "" +description: |- + Setting up supporting infrastructure + Configuring an OpenStack provider + To automate the creation of load balancers, OpenStack can be used to setup the supporting infrastructure. + To set up the OpenStack provider, you can create a token through the STACKIT Portal, in your project's Infrastructure API page. + There, the OpenStack user domain name, username, and password are generated and can be obtained. The provider can then be configured as follows: + ```terraform + terraform { + required_providers { + (...) + openstack = { + source = "terraform-provider-openstack/openstack" + } + } + } + provider "openstack" { + userdomainname = "{OpenStack user domain name}" + username = "{OpenStack username}" + password = "{OpenStack password}" + region = "RegionOne" + authurl = "https://keystone.api.iaas.eu01.stackit.cloud/v3" + } + ``` + Configuring the supporting infrastructure + The example below uses OpenStack to create the network, router, a public IP address and a compute instance. +--- + +# stackit_loadbalancer (Resource) + +## Setting up supporting infrastructure + + +### Configuring an OpenStack provider + + +To automate the creation of load balancers, OpenStack can be used to setup the supporting infrastructure. +To set up the OpenStack provider, you can create a token through the STACKIT Portal, in your project's Infrastructure API page. +There, the OpenStack user domain name, username, and password are generated and can be obtained. The provider can then be configured as follows: +```terraform +terraform { + required_providers { + (...) + openstack = { + source = "terraform-provider-openstack/openstack" + } + } +} + +provider "openstack" { + user_domain_name = "{OpenStack user domain name}" + user_name = "{OpenStack username}" + password = "{OpenStack password}" + region = "RegionOne" + auth_url = "https://keystone.api.iaas.eu01.stackit.cloud/v3" +} + +``` + +### Configuring the supporting infrastructure + +The example below uses OpenStack to create the network, router, a public IP address and a compute instance. + +## Example Usage + +```terraform +# Create a network +resource "openstack_networking_network_v2" "example" { + name = "example-network" +} + +# Create a subnet +resource "openstack_networking_subnet_v2" "example" { + name = "example-subnet" + cidr = "192.168.0.0/25" + dns_nameservers = ["8.8.8.8"] + network_id = openstack_networking_network_v2.example.id +} + +# Get public network +data "openstack_networking_network_v2" "public" { + name = "floating-net" +} + +# Create a floating IP +resource "openstack_networking_floatingip_v2" "example" { + pool = data.openstack_networking_network_v2.public.name +} + +# Get flavor for instance +data "openstack_compute_flavor_v2" "example" { + name = "g1.1" +} + +# Create an instance +resource "openstack_compute_instance_v2" "example" { + depends_on = [openstack_networking_subnet_v2.example] + name = "example-instance" + flavor_id = data.openstack_compute_flavor_v2.example.id + admin_pass = "example" + security_groups = ["default"] + + block_device { + uuid = "4364cdb2-dacd-429b-803e-f0f7cfde1c24" // Ubuntu 22.04 + volume_size = 32 + source_type = "image" + destination_type = "volume" + delete_on_termination = true + } + + network { + name = openstack_networking_network_v2.example.name + } +} + +# Create a router and attach it to the public network +resource "openstack_networking_router_v2" "example" { + name = "example-router" + admin_state_up = "true" + external_network_id = data.openstack_networking_network_v2.public.id +} + +# Attach the subnet to the router +resource "openstack_networking_router_interface_v2" "example_interface" { + router_id = openstack_networking_router_v2.example.id + subnet_id = openstack_networking_subnet_v2.example.id +} + +# Create a load balancer +resource "stackit_loadbalancer" "example" { + project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + name = "example-load-balancer" + target_pools = [ + { + name = "example-target-pool" + target_port = 80 + targets = [ + { + display_name = "example-target" + ip = openstack_compute_instance_v2.example.network.0.fixed_ip_v4 + } + ] + active_health_check = { + healthy_threshold = 10 + interval = "3s" + interval_jitter = "3s" + timeout = "3s" + unhealthy_threshold = 10 + } + } + ] + listeners = [ + { + display_name = "example-listener" + port = 80 + protocol = "PROTOCOL_TCP" + target_pool = "example-target-pool" + } + ] + networks = [ + { + network_id = openstack_networking_network_v2.example.id + role = "ROLE_LISTENERS_AND_TARGETS" + } + ] + external_address = openstack_networking_floatingip_v2.example.address + options = { + private_network_only = false + } +} +``` + + +## Schema + +### Required + +- `listeners` (Attributes List) List of all listeners which will accept traffic. Limited to 20. (see [below for nested schema](#nestedatt--listeners)) +- `name` (String) Load balancer name. +- `networks` (Attributes List) List of networks that listeners and targets reside in. (see [below for nested schema](#nestedatt--networks)) +- `project_id` (String) STACKIT project ID to which the Load Balancer is associated. +- `target_pools` (Attributes List) List of all target pools which will be used in the Load Balancer. Limited to 20. (see [below for nested schema](#nestedatt--target_pools)) + +### Optional + +- `external_address` (String) External Load Balancer IP address where this Load Balancer is exposed. +- `options` (Attributes) Defines any optional functionality you want to have enabled on your load balancer. (see [below for nested schema](#nestedatt--options)) + +### Read-Only + +- `id` (String) Terraform's internal resource ID. It is structured as "`project_id`","`name`". +- `private_address` (String) Transient private Load Balancer IP address. It can change any time. + + +### Nested Schema for `listeners` + +Optional: + +- `display_name` (String) +- `port` (Number) Port number where we listen for traffic. +- `protocol` (String) Protocol is the highest network protocol we understand to load balance. +- `target_pool` (String) Reference target pool by target pool name. + + + +### Nested Schema for `networks` + +Required: + +- `network_id` (String) Openstack network ID. + +Optional: + +- `role` (String) The role defines how the load balancer is using the network. + + + +### Nested Schema for `target_pools` + +Required: + +- `name` (String) Target pool name. +- `target_port` (Number) Identical port number where each target listens for traffic. +- `targets` (Attributes List) List of all targets which will be used in the pool. Limited to 250. (see [below for nested schema](#nestedatt--target_pools--targets)) + +Optional: + +- `active_health_check` (Attributes) (see [below for nested schema](#nestedatt--target_pools--active_health_check)) + + +### Nested Schema for `target_pools.targets` + +Required: + +- `display_name` (String) Target display name +- `ip` (String) Target IP + + + +### Nested Schema for `target_pools.active_health_check` + +Optional: + +- `healthy_threshold` (Number) Healthy threshold of the health checking. +- `interval` (String) Interval duration of health checking in seconds. +- `interval_jitter` (String) Interval duration threshold of the health checking in seconds. +- `timeout` (String) Active health checking timeout duration in seconds. +- `unhealthy_threshold` (Number) Unhealthy threshold of the health checking. + + + + +### Nested Schema for `options` + +Optional: + +- `acl` (Set of String) Load Balancer is accessible only from an IP address in this range. +- `private_network_only` (Boolean) If true, Load Balancer is accessible only via a private network IP address. diff --git a/examples/data-sources/stackit_loadbalancer/data-source.tf b/examples/data-sources/stackit_loadbalancer/data-source.tf new file mode 100644 index 00000000..52a04e6e --- /dev/null +++ b/examples/data-sources/stackit_loadbalancer/data-source.tf @@ -0,0 +1,4 @@ +data "stackit_loadbalancer" "example" { + project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + name = "example-load-balancer" +} \ No newline at end of file diff --git a/examples/resources/stackit_loadbalancer/resource.tf b/examples/resources/stackit_loadbalancer/resource.tf new file mode 100644 index 00000000..a1167264 --- /dev/null +++ b/examples/resources/stackit_loadbalancer/resource.tf @@ -0,0 +1,104 @@ +# Create a network +resource "openstack_networking_network_v2" "example" { + name = "example-network" +} + +# Create a subnet +resource "openstack_networking_subnet_v2" "example" { + name = "example-subnet" + cidr = "192.168.0.0/25" + dns_nameservers = ["8.8.8.8"] + network_id = openstack_networking_network_v2.example.id +} + +# Get public network +data "openstack_networking_network_v2" "public" { + name = "floating-net" +} + +# Create a floating IP +resource "openstack_networking_floatingip_v2" "example" { + pool = data.openstack_networking_network_v2.public.name +} + +# Get flavor for instance +data "openstack_compute_flavor_v2" "example" { + name = "g1.1" +} + +# Create an instance +resource "openstack_compute_instance_v2" "example" { + depends_on = [openstack_networking_subnet_v2.example] + name = "example-instance" + flavor_id = data.openstack_compute_flavor_v2.example.id + admin_pass = "example" + security_groups = ["default"] + + block_device { + uuid = "4364cdb2-dacd-429b-803e-f0f7cfde1c24" // Ubuntu 22.04 + volume_size = 32 + source_type = "image" + destination_type = "volume" + delete_on_termination = true + } + + network { + name = openstack_networking_network_v2.example.name + } +} + +# Create a router and attach it to the public network +resource "openstack_networking_router_v2" "example" { + name = "example-router" + admin_state_up = "true" + external_network_id = data.openstack_networking_network_v2.public.id +} + +# Attach the subnet to the router +resource "openstack_networking_router_interface_v2" "example_interface" { + router_id = openstack_networking_router_v2.example.id + subnet_id = openstack_networking_subnet_v2.example.id +} + +# Create a load balancer +resource "stackit_loadbalancer" "example" { + project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + name = "example-load-balancer" + target_pools = [ + { + name = "example-target-pool" + target_port = 80 + targets = [ + { + display_name = "example-target" + ip = openstack_compute_instance_v2.example.network.0.fixed_ip_v4 + } + ] + active_health_check = { + healthy_threshold = 10 + interval = "3s" + interval_jitter = "3s" + timeout = "3s" + unhealthy_threshold = 10 + } + } + ] + listeners = [ + { + display_name = "example-listener" + port = 80 + protocol = "PROTOCOL_TCP" + target_pool = "example-target-pool" + } + ] + networks = [ + { + network_id = openstack_networking_network_v2.example.id + role = "ROLE_LISTENERS_AND_TARGETS" + } + ] + external_address = openstack_networking_floatingip_v2.example.address + options = { + private_network_only = false + } +} \ No newline at end of file diff --git a/stackit/internal/services/loadbalancer/loadbalancer/resource.go b/stackit/internal/services/loadbalancer/loadbalancer/resource.go index 659e0efe..16bcf799 100644 --- a/stackit/internal/services/loadbalancer/loadbalancer/resource.go +++ b/stackit/internal/services/loadbalancer/loadbalancer/resource.go @@ -195,6 +195,38 @@ func (r *loadBalancerResource) Schema(_ context.Context, _ resource.SchemaReques resp.Schema = schema.Schema{ Description: descriptions["main"], + MarkdownDescription: ` +## Setting up supporting infrastructure` + "\n" + ` + +### Configuring an OpenStack provider` + "\n" + ` + +To automate the creation of load balancers, OpenStack can be used to setup the supporting infrastructure. +To set up the OpenStack provider, you can create a token through the STACKIT Portal, in your project's Infrastructure API page. +There, the OpenStack user domain name, username, and password are generated and can be obtained. The provider can then be configured as follows:` + "\n" + + + "```terraform" + ` +terraform { + required_providers { + (...) + openstack = { + source = "terraform-provider-openstack/openstack" + } + } +} + +provider "openstack" { + user_domain_name = "{OpenStack user domain name}" + user_name = "{OpenStack username}" + password = "{OpenStack password}" + region = "RegionOne" + auth_url = "https://keystone.api.iaas.eu01.stackit.cloud/v3" +} +` + "\n```" + ` + +### Configuring the supporting infrastructure + +The example below uses OpenStack to create the network, router, a public IP address and a compute instance. +`, Attributes: map[string]schema.Attribute{ "id": schema.StringAttribute{ Description: descriptions["id"], diff --git a/stackit/internal/services/loadbalancer/loadbalancer_acc_test.go b/stackit/internal/services/loadbalancer/loadbalancer_acc_test.go new file mode 100644 index 00000000..c626fd9d --- /dev/null +++ b/stackit/internal/services/loadbalancer/loadbalancer_acc_test.go @@ -0,0 +1,322 @@ +package loadbalancer_test + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" + + "github.com/stackitcloud/stackit-sdk-go/core/config" + "github.com/stackitcloud/stackit-sdk-go/services/loadbalancer" + "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/testutil" +) + +// Instance resource data +var loadBalancerResource = map[string]string{ + "project_id": testutil.ProjectId, + "name": fmt.Sprintf("tf-acc-%s", acctest.RandStringFromCharSet(7, acctest.CharSetAlphaNum)), + "target_pool_name": "example-target-pool", + "target_port": "5432", + "target_port_updated": "5431", + "target_display_name": "example-target", + "healthy_threshold": "3", + "interval": "10s", + "interval_jitter": "5s", + "timeout": "10s", + "unhealthy_threshold": "3", + "listener_display_name": "example-listener", + "listener_port": "5432", + "listener_protocol": "PROTOCOL_TCP", + "network_role": "ROLE_LISTENERS_AND_TARGETS", + "private_network_only": "true", +} + +func configResources(targetPort string) string { + return fmt.Sprintf(` + %s + + %s + + resource "stackit_loadbalancer" "loadbalancer" { + project_id = "%s" + name = "%s" + target_pools = [ + { + name = "%s" + target_port = %s + targets = [ + { + display_name = "%s" + ip = openstack_compute_instance_v2.example.network.0.fixed_ip_v4 + } + ] + active_health_check = { + healthy_threshold = %s + interval = "%s" + interval_jitter = "%s" + timeout = "%s" + unhealthy_threshold = %s + } + } + ] + listeners = [ + { + display_name = "%s" + port = %s + protocol = "%s" + target_pool = "%s" + } + ] + networks = [ + { + network_id = openstack_networking_network_v2.example.id + role = "%s" + } + ] + options = { + private_network_only = %s + } + } + `, + supportingInfraResources(loadBalancerResource["name"], OpenStack{ + userDomainName: testutil.OSUserDomainName, + userName: testutil.OSUserName, + password: testutil.OSPassword, + }), + testutil.LoadBalancerProviderConfig(), + loadBalancerResource["project_id"], + loadBalancerResource["name"], + loadBalancerResource["target_pool_name"], + targetPort, + loadBalancerResource["target_display_name"], + loadBalancerResource["healthy_threshold"], + loadBalancerResource["interval"], + loadBalancerResource["interval_jitter"], + loadBalancerResource["timeout"], + loadBalancerResource["unhealthy_threshold"], + loadBalancerResource["listener_display_name"], + loadBalancerResource["listener_port"], + loadBalancerResource["listener_protocol"], + loadBalancerResource["target_pool_name"], + loadBalancerResource["network_role"], + loadBalancerResource["private_network_only"], + ) +} + +func supportingInfraResources(name string, os OpenStack) string { + return fmt.Sprintf(` + provider "openstack" { + user_domain_name = "%s" + user_name = "%s" + password = "%s" + region = "RegionOne" + auth_url = "https://keystone.api.iaas.eu01.stackit.cloud/v3" + } + + # Create a network + resource "openstack_networking_network_v2" "example" { + name = "%s_network" + } + + resource "openstack_networking_subnet_v2" "example" { + name = "%s_subnet" + cidr = "192.168.0.0/25" + dns_nameservers = ["8.8.8.8"] + network_id = openstack_networking_network_v2.example.id + } + + data "openstack_networking_network_v2" "public" { + name = "floating-net" + } + + resource "openstack_networking_floatingip_v2" "example_ip" { + pool = data.openstack_networking_network_v2.public.name + } + + # Create an instance + data "openstack_compute_flavor_v2" "example" { + name = "g1.1" + } + + resource "openstack_compute_instance_v2" "example" { + depends_on = [openstack_networking_subnet_v2.example] + name = "%s_instance" + flavor_id = data.openstack_compute_flavor_v2.example.id + admin_pass = "example" + security_groups = ["default"] + + block_device { + uuid = "4364cdb2-dacd-429b-803e-f0f7cfde1c24" // Ubuntu 22.04 + volume_size = 32 + source_type = "image" + destination_type = "volume" + delete_on_termination = true + } + + network { + name = openstack_networking_network_v2.example.name + } + + lifecycle { + ignore_changes = [security_groups] + } + } + + resource "openstack_networking_router_v2" "example_router" { + name = "%s_router" + admin_state_up = "true" + external_network_id = data.openstack_networking_network_v2.public.id + } + + resource "openstack_networking_router_interface_v2" "example_interface" { + router_id = openstack_networking_router_v2.example_router.id + subnet_id = openstack_networking_subnet_v2.example.id + } + + `, + os.userDomainName, os.userName, os.password, name, name, name, name) +} + +func TestAccLoadBalancerResource(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories, + ExternalProviders: map[string]resource.ExternalProvider{ + "openstack": { + VersionConstraint: "= 1.52.1", + Source: "terraform-provider-openstack/openstack", + }, + }, + CheckDestroy: testAccCheckLoadBalancerDestroy, + Steps: []resource.TestStep{ + // Creation + { + Config: configResources(loadBalancerResource["target_port"]), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("stackit_loadbalancer.loadbalancer", "project_id", loadBalancerResource["project_id"]), + resource.TestCheckResourceAttr("stackit_loadbalancer.loadbalancer", "name", loadBalancerResource["name"]), + resource.TestCheckResourceAttr("stackit_loadbalancer.loadbalancer", "target_pools.0.name", loadBalancerResource["target_pool_name"]), + resource.TestCheckResourceAttr("stackit_loadbalancer.loadbalancer", "target_pools.0.target_port", loadBalancerResource["target_port"]), + resource.TestCheckResourceAttr("stackit_loadbalancer.loadbalancer", "target_pools.0.targets.0.display_name", loadBalancerResource["target_display_name"]), + resource.TestCheckResourceAttrSet("stackit_loadbalancer.loadbalancer", "target_pools.0.targets.0.ip"), + resource.TestCheckResourceAttr("stackit_loadbalancer.loadbalancer", "target_pools.0.active_health_check.healthy_threshold", loadBalancerResource["healthy_threshold"]), + resource.TestCheckResourceAttr("stackit_loadbalancer.loadbalancer", "target_pools.0.active_health_check.interval", loadBalancerResource["interval"]), + resource.TestCheckResourceAttr("stackit_loadbalancer.loadbalancer", "target_pools.0.active_health_check.interval_jitter", loadBalancerResource["interval_jitter"]), + resource.TestCheckResourceAttr("stackit_loadbalancer.loadbalancer", "target_pools.0.active_health_check.timeout", loadBalancerResource["timeout"]), + resource.TestCheckResourceAttr("stackit_loadbalancer.loadbalancer", "target_pools.0.active_health_check.unhealthy_threshold", loadBalancerResource["unhealthy_threshold"]), + resource.TestCheckResourceAttr("stackit_loadbalancer.loadbalancer", "listeners.0.display_name", loadBalancerResource["listener_display_name"]), + resource.TestCheckResourceAttr("stackit_loadbalancer.loadbalancer", "listeners.0.port", loadBalancerResource["listener_port"]), + resource.TestCheckResourceAttr("stackit_loadbalancer.loadbalancer", "listeners.0.protocol", loadBalancerResource["listener_protocol"]), + resource.TestCheckResourceAttr("stackit_loadbalancer.loadbalancer", "listeners.0.target_pool", loadBalancerResource["target_pool_name"]), + resource.TestCheckResourceAttrSet("stackit_loadbalancer.loadbalancer", "networks.0.network_id"), + resource.TestCheckResourceAttr("stackit_loadbalancer.loadbalancer", "networks.0.role", loadBalancerResource["network_role"]), + resource.TestCheckResourceAttr("stackit_loadbalancer.loadbalancer", "options.private_network_only", loadBalancerResource["private_network_only"]), + ), + }, + // Data source + { + Config: fmt.Sprintf(` + %s + + data "stackit_loadbalancer" "loadbalancer" { + project_id = stackit_loadbalancer.loadbalancer.project_id + name = stackit_loadbalancer.loadbalancer.name + } + `, + configResources(loadBalancerResource["target_port"]), + ), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("data.stackit_loadbalancer.loadbalancer", "project_id", loadBalancerResource["project_id"]), + resource.TestCheckResourceAttr("data.stackit_loadbalancer.loadbalancer", "name", loadBalancerResource["name"]), + resource.TestCheckResourceAttrPair( + "data.stackit_loadbalancer.loadbalancer", "project_id", + "stackit_loadbalancer.loadbalancer", "project_id", + ), + resource.TestCheckResourceAttrPair( + "data.stackit_loadbalancer.loadbalancer", "name", + "stackit_loadbalancer.loadbalancer", "name", + ), + + resource.TestCheckResourceAttr("data.stackit_loadbalancer.loadbalancer", "target_pools.0.name", loadBalancerResource["target_pool_name"]), + resource.TestCheckResourceAttr("data.stackit_loadbalancer.loadbalancer", "target_pools.0.target_port", loadBalancerResource["target_port"]), + resource.TestCheckResourceAttr("data.stackit_loadbalancer.loadbalancer", "target_pools.0.targets.0.display_name", loadBalancerResource["target_display_name"]), + resource.TestCheckResourceAttrSet("data.stackit_loadbalancer.loadbalancer", "target_pools.0.targets.0.ip"), + resource.TestCheckResourceAttr("data.stackit_loadbalancer.loadbalancer", "target_pools.0.active_health_check.healthy_threshold", loadBalancerResource["healthy_threshold"]), + resource.TestCheckResourceAttr("data.stackit_loadbalancer.loadbalancer", "target_pools.0.active_health_check.interval", loadBalancerResource["interval"]), + resource.TestCheckResourceAttr("data.stackit_loadbalancer.loadbalancer", "target_pools.0.active_health_check.interval_jitter", loadBalancerResource["interval_jitter"]), + resource.TestCheckResourceAttr("data.stackit_loadbalancer.loadbalancer", "target_pools.0.active_health_check.timeout", loadBalancerResource["timeout"]), + resource.TestCheckResourceAttr("data.stackit_loadbalancer.loadbalancer", "target_pools.0.active_health_check.unhealthy_threshold", loadBalancerResource["unhealthy_threshold"]), + resource.TestCheckResourceAttr("data.stackit_loadbalancer.loadbalancer", "listeners.0.display_name", loadBalancerResource["listener_display_name"]), + resource.TestCheckResourceAttr("data.stackit_loadbalancer.loadbalancer", "listeners.0.port", loadBalancerResource["listener_port"]), + resource.TestCheckResourceAttr("data.stackit_loadbalancer.loadbalancer", "listeners.0.protocol", loadBalancerResource["listener_protocol"]), + resource.TestCheckResourceAttr("data.stackit_loadbalancer.loadbalancer", "listeners.0.target_pool", loadBalancerResource["target_pool_name"]), + resource.TestCheckResourceAttrSet("data.stackit_loadbalancer.loadbalancer", "networks.0.network_id"), + resource.TestCheckResourceAttr("data.stackit_loadbalancer.loadbalancer", "networks.0.role", loadBalancerResource["network_role"]), + resource.TestCheckResourceAttr("data.stackit_loadbalancer.loadbalancer", "options.private_network_only", loadBalancerResource["private_network_only"]), + ), + }, + // Import + { + ResourceName: "stackit_loadbalancer.loadbalancer", + ImportStateIdFunc: func(s *terraform.State) (string, error) { + r, ok := s.RootModule().Resources["stackit_loadbalancer.loadbalancer"] + if !ok { + return "", fmt.Errorf("couldn't find resource stackit_loadbalancer.loadbalancer") + } + name, ok := r.Primary.Attributes["name"] + if !ok { + return "", fmt.Errorf("couldn't find attribute name") + } + + return fmt.Sprintf("%s,%s", testutil.ProjectId, name), nil + }, + ImportState: true, + ImportStateVerify: true, + }, + // Update + { + Config: configResources(loadBalancerResource["target_port_updated"]), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("stackit_loadbalancer.loadbalancer", "project_id", loadBalancerResource["project_id"]), + resource.TestCheckResourceAttr("stackit_loadbalancer.loadbalancer", "name", loadBalancerResource["name"]), + resource.TestCheckResourceAttr("stackit_loadbalancer.loadbalancer", "target_pools.0.target_port", loadBalancerResource["target_port_updated"]), + ), + }, + // Deletion is done by the framework implicitly + }, + }) +} + +type OpenStack struct { + userDomainName string + userName string + password string +} + +func testAccCheckLoadBalancerDestroy(_ *terraform.State) error { + ctx := context.Background() + var client *loadbalancer.APIClient + var err error + if testutil.LoadBalancerCustomEndpoint == "" { + client, err = loadbalancer.NewAPIClient( + config.WithRegion("eu01"), + ) + } else { + client, err = loadbalancer.NewAPIClient( + config.WithEndpoint(testutil.LoadBalancerCustomEndpoint), + ) + } + if err != nil { + return fmt.Errorf("creating client: %w", err) + } + + // Disabling loadbalancer functionality will delete all load balancers + _, err = client.DisableLoadBalancingExecute(ctx, testutil.ProjectId) + if err != nil { + return fmt.Errorf("disabling loadbalancer functionality: %w", err) + } + + return nil +} diff --git a/stackit/internal/testutil/testutil.go b/stackit/internal/testutil/testutil.go index a2659931..2c9d4792 100644 --- a/stackit/internal/testutil/testutil.go +++ b/stackit/internal/testutil/testutil.go @@ -50,6 +50,13 @@ var ( ResourceManagerCustomEndpoint = os.Getenv("TF_ACC_RESOURCEMANAGER_CUSTOM_ENDPOINT") SecretsManagerCustomEndpoint = os.Getenv("TF_ACC_SECRETSMANAGER_CUSTOM_ENDPOINT") SKECustomEndpoint = os.Getenv("TF_ACC_SKE_CUSTOM_ENDPOINT") + + // OpenStack user domain name + OSUserDomainName = os.Getenv("TF_ACC_OS_USER_DOMAIN_NAME") + // OpenStack user name + OSUserName = os.Getenv("TF_ACC_OS_USER_NAME") + // OpenStack password + OSPassword = os.Getenv("TF_ACC_OS_PASSWORD") ) // Provider config helper functions diff --git a/stackit/provider.go b/stackit/provider.go index 621daea1..de1530d5 100644 --- a/stackit/provider.go +++ b/stackit/provider.go @@ -14,6 +14,7 @@ import ( argusScrapeConfig "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/argus/scrapeconfig" dnsRecordSet "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/dns/recordset" dnsZone "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/dns/zone" + loadBalancer "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/loadbalancer/loadbalancer" logMeCredential "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/logme/credential" logMeInstance "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/logme/instance" mariaDBCredential "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/mariadb/credential" @@ -341,6 +342,7 @@ func (p *Provider) DataSources(_ context.Context) []func() datasource.DataSource argusScrapeConfig.NewScrapeConfigDataSource, dnsZone.NewZoneDataSource, dnsRecordSet.NewRecordSetDataSource, + loadBalancer.NewLoadBalancerDataSource, logMeInstance.NewInstanceDataSource, logMeCredential.NewCredentialDataSource, mariaDBInstance.NewInstanceDataSource, @@ -376,6 +378,7 @@ func (p *Provider) Resources(_ context.Context) []func() resource.Resource { argusScrapeConfig.NewScrapeConfigResource, dnsZone.NewZoneResource, dnsRecordSet.NewRecordSetResource, + loadBalancer.NewLoadBalancerResource, logMeInstance.NewInstanceResource, logMeCredential.NewCredentialResource, mariaDBInstance.NewInstanceResource,