diff --git a/scaleway/provider.go b/scaleway/provider.go index 42880d6de..a8c9ac2a4 100644 --- a/scaleway/provider.go +++ b/scaleway/provider.go @@ -200,6 +200,7 @@ func Provider() terraform.ResourceProvider { "scaleway_k8s_cluster_beta": resourceScalewayK8SClusterBeta(), "scaleway_k8s_pool_beta": resourceScalewayK8SPoolBeta(), "scaleway_lb_beta": resourceScalewayLbBeta(), + "scaleway_lb_ip_beta": resourceScalewayLbIPBeta(), "scaleway_lb_backend_beta": resourceScalewayLbBackendBeta(), "scaleway_lb_certificate_beta": resourceScalewayLbCertificateBeta(), "scaleway_lb_frontend_beta": resourceScalewayLbFrontendBeta(), diff --git a/scaleway/resource_lb_beta.go b/scaleway/resource_lb_beta.go index 9cfac4f88..e2dba508a 100644 --- a/scaleway/resource_lb_beta.go +++ b/scaleway/resource_lb_beta.go @@ -3,6 +3,7 @@ package scaleway import ( "github.com/hashicorp/terraform-plugin-sdk/helper/schema" lb "github.com/scaleway/scaleway-sdk-go/api/lb/v1" + "github.com/scaleway/scaleway-sdk-go/scw" ) func resourceScalewayLbBeta() *schema.Resource { @@ -37,9 +38,11 @@ func resourceScalewayLbBeta() *schema.Resource { Description: "Array of tags to associate with the load-balancer", }, "ip_id": { - Type: schema.TypeString, - Computed: true, - Description: "The load-balance public IP ID", + Type: schema.TypeString, + Required: true, + Description: "The load-balance public IP ID", + ForceNew: true, + DiffSuppressFunc: diffSuppressFuncLocality, }, "ip_address": { Type: schema.TypeString, @@ -60,10 +63,12 @@ func resourceScalewayLbBetaCreate(d *schema.ResourceData, m interface{}) error { createReq := &lb.CreateLbRequest{ Region: region, + IPID: scw.StringPtr(expandID(d.Get("ip_id").(string))), OrganizationID: d.Get("organization_id").(string), Name: expandOrGenerateString(d.Get("name"), "lb"), Type: d.Get("type").(string), } + if raw, ok := d.GetOk("tags"); ok { for _, tag := range raw.([]interface{}) { createReq.Tags = append(createReq.Tags, tag.(string)) @@ -112,7 +117,7 @@ func resourceScalewayLbBetaRead(d *schema.ResourceData, m interface{}) error { _ = d.Set("organization_id", res.OrganizationID) _ = d.Set("tags", res.Tags) _ = d.Set("type", res.Type) - _ = d.Set("ip_id", res.IP[0].ID) + _ = d.Set("ip_id", newRegionalId(region, res.IP[0].ID)) _ = d.Set("ip_address", res.IP[0].IPAddress) return nil @@ -149,10 +154,9 @@ func resourceScalewayLbBetaDelete(d *schema.ResourceData, m interface{}) error { } err = lbAPI.DeleteLb(&lb.DeleteLbRequest{ - Region: region, - LbID: ID, - // This parameter will probably be breaking change when ip pre reservation will exist. - ReleaseIP: true, + Region: region, + LbID: ID, + ReleaseIP: false, }) if err != nil && !is404Error(err) { diff --git a/scaleway/resource_lb_beta_test.go b/scaleway/resource_lb_beta_test.go index 71e716a90..63e5d3332 100644 --- a/scaleway/resource_lb_beta_test.go +++ b/scaleway/resource_lb_beta_test.go @@ -43,7 +43,7 @@ func testSweepLB(region string) error { return nil } -func TestAccScalewayLbBeta(t *testing.T) { +func TestAccScalewayLbAndIPBeta(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, @@ -51,33 +51,41 @@ func TestAccScalewayLbBeta(t *testing.T) { Steps: []resource.TestStep{ { Config: ` + resource scaleway_lb_ip_beta ip01 { + } + resource scaleway_lb_beta lb01 { + ip_id = scaleway_lb_ip_beta.ip01.id name = "test-lb" type = "lb-s" } `, Check: resource.ComposeTestCheckFunc( testAccCheckScalewayLbBetaExists("scaleway_lb_beta.lb01"), + testAccCheckScalewayLbIPBetaExists("scaleway_lb_ip_beta.ip01"), resource.TestCheckResourceAttr("scaleway_lb_beta.lb01", "name", "test-lb"), testCheckResourceAttrUUID("scaleway_lb_beta.lb01", "ip_id"), testCheckResourceAttrIPv4("scaleway_lb_beta.lb01", "ip_address"), + resource.TestCheckResourceAttrPair("scaleway_lb_beta.lb01", "ip_id", "scaleway_lb_ip_beta.ip01", "id"), ), }, { Config: ` - resource scaleway_lb_beta lb01 { - name = "test-lb" - type = "lb-s" - tags = ["tag1", "tag2"] + resource scaleway_lb_ip_beta ip01 { } `, Check: resource.ComposeTestCheckFunc( - testAccCheckScalewayLbBetaExists("scaleway_lb_beta.lb01"), - resource.TestCheckResourceAttr("scaleway_lb_beta.lb01", "name", "test-lb"), - resource.TestCheckResourceAttr("scaleway_lb_beta.lb01", "tags.0", "tag1"), - resource.TestCheckResourceAttr("scaleway_lb_beta.lb01", "tags.1", "tag2"), - testCheckResourceAttrUUID("scaleway_lb_beta.lb01", "ip_id"), - testCheckResourceAttrIPv4("scaleway_lb_beta.lb01", "ip_address"), + testAccCheckScalewayLbIPBetaExists("scaleway_lb_ip_beta.ip01"), + ), + }, + { + Config: ` + resource scaleway_lb_ip_beta ip01 { + } + `, + Check: resource.ComposeTestCheckFunc( + testAccCheckScalewayLbIPBetaExists("scaleway_lb_ip_beta.ip01"), + resource.TestCheckResourceAttr("scaleway_lb_ip_beta.ip01", "lb_id", ""), ), }, }, diff --git a/scaleway/resource_lb_ip_beta.go b/scaleway/resource_lb_ip_beta.go new file mode 100644 index 000000000..10671bb25 --- /dev/null +++ b/scaleway/resource_lb_ip_beta.go @@ -0,0 +1,131 @@ +package scaleway + +import ( + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + lb "github.com/scaleway/scaleway-sdk-go/api/lb/v1" +) + +func resourceScalewayLbIPBeta() *schema.Resource { + return &schema.Resource{ + Create: resourceScalewayLbIPBetaCreate, + Read: resourceScalewayLbIPBetaRead, + Update: resourceScalewayLbIPBetaUpdate, + Delete: resourceScalewayLbIPBetaDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + SchemaVersion: 0, + Schema: map[string]*schema.Schema{ + "reverse": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "The reverse domain name for this IP", + }, + "region": regionSchema(), + "organization_id": organizationIDSchema(), + // Computed + "ip_address": { + Type: schema.TypeString, + Computed: true, + Description: "The load-balance public IP address", + }, + "lb_id": { + Type: schema.TypeString, + Computed: true, + Description: "The ID of the loadbalancer attached to this IP, if any", + }, + }, + } +} + +func resourceScalewayLbIPBetaCreate(d *schema.ResourceData, m interface{}) error { + lbAPI, region, err := lbAPIWithRegion(d, m) + if err != nil { + return err + } + + createReq := &lb.CreateIPRequest{ + Region: region, + OrganizationID: d.Get("organization_id").(string), + Reverse: expandStringPtr(d.Get("reverse")), + } + + res, err := lbAPI.CreateIP(createReq) + if err != nil { + return err + } + + d.SetId(newRegionalId(region, res.ID)) + + return resourceScalewayLbIPBetaRead(d, m) +} + +func resourceScalewayLbIPBetaRead(d *schema.ResourceData, m interface{}) error { + lbAPI, region, ID, err := lbAPIWithRegionAndID(m, d.Id()) + if err != nil { + return err + } + + res, err := lbAPI.GetIP(&lb.GetIPRequest{ + Region: region, + IPID: ID, + }) + + if err != nil { + if is404Error(err) { + d.SetId("") + return nil + } + return err + } + + _ = d.Set("region", string(region)) + _ = d.Set("organization_id", res.OrganizationID) + _ = d.Set("ip_id", res.ID) + _ = d.Set("ip_address", res.IPAddress) + _ = d.Set("reverse", res.Reverse) + _ = d.Set("lb_ip", flattenStringPtr(res.LbID)) + + return nil +} + +func resourceScalewayLbIPBetaUpdate(d *schema.ResourceData, m interface{}) error { + lbAPI, region, ID, err := lbAPIWithRegionAndID(m, d.Id()) + if err != nil { + return err + } + + if d.HasChange("reverse") { + req := &lb.UpdateIPRequest{ + Region: region, + IPID: ID, + Reverse: expandStringPtr(d.Get("reverse")), + } + + _, err = lbAPI.UpdateIP(req) + if err != nil { + return err + } + } + + return resourceScalewayLbIPBetaRead(d, m) +} + +func resourceScalewayLbIPBetaDelete(d *schema.ResourceData, m interface{}) error { + lbAPI, region, ID, err := lbAPIWithRegionAndID(m, d.Id()) + if err != nil { + return err + } + + err = lbAPI.ReleaseIP(&lb.ReleaseIPRequest{ + Region: region, + IPID: ID, + }) + + if err != nil && !is404Error(err) { + return err + } + + return nil +} diff --git a/scaleway/resource_lb_ip_beta_test.go b/scaleway/resource_lb_ip_beta_test.go new file mode 100644 index 000000000..55c4a98a2 --- /dev/null +++ b/scaleway/resource_lb_ip_beta_test.go @@ -0,0 +1,133 @@ +package scaleway + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/terraform" + "github.com/scaleway/scaleway-sdk-go/api/lb/v1" + "github.com/scaleway/scaleway-sdk-go/scw" +) + +func init() { + resource.AddTestSweepers("scaleway_lb_ip_beta", &resource.Sweeper{ + Name: "scaleway_lb_ip_beta", + F: testSweepLBIP, + }) +} + +func testSweepLBIP(region string) error { + scwClient, err := sharedClientForRegion(region) + if err != nil { + return fmt.Errorf("error getting client in sweeper: %s", err) + } + lbAPI := lb.NewAPI(scwClient) + + l.Debugf("sweeper: destroying the lb ips in (%s)", region) + listIPs, err := lbAPI.ListIPs(&lb.ListIPsRequest{}, scw.WithAllPages()) + if err != nil { + return fmt.Errorf("error listing lb ips in (%s) in sweeper: %s", region, err) + } + + for _, ip := range listIPs.IPs { + if ip.LbID == nil { + err := lbAPI.ReleaseIP(&lb.ReleaseIPRequest{ + IPID: ip.ID, + }) + if err != nil { + return fmt.Errorf("error deleting lb ip in sweeper: %s", err) + } + } + } + + return nil +} + +func TestAccScalewayLbIPBeta(t *testing.T) { + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckScalewayLbIPBetaDestroy, + Steps: []resource.TestStep{ + { + Config: ` + resource scaleway_lb_ip_beta ip01 { + } + `, + Check: resource.ComposeTestCheckFunc( + testAccCheckScalewayLbIPBetaExists("scaleway_lb_ip_beta.ip01"), + testCheckResourceAttrIPv4("scaleway_lb_ip_beta.ip01", "ip_address"), + resource.TestCheckResourceAttrSet("scaleway_lb_ip_beta.ip01", "reverse"), + ), + }, + { + Config: ` + resource scaleway_lb_ip_beta ip01 { + reverse = "myreverse.com" + } + `, + Check: resource.ComposeTestCheckFunc( + testAccCheckScalewayLbIPBetaExists("scaleway_lb_ip_beta.ip01"), + testCheckResourceAttrIPv4("scaleway_lb_ip_beta.ip01", "ip_address"), + resource.TestCheckResourceAttr("scaleway_lb_ip_beta.ip01", "reverse", "myreverse.com"), + ), + }, + }, + }) +} + +func testAccCheckScalewayLbIPBetaExists(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("resource not found: %s", n) + } + + lbAPI, region, ID, err := lbAPIWithRegionAndID(testAccProvider.Meta(), rs.Primary.ID) + if err != nil { + return err + } + + _, err = lbAPI.GetIP(&lb.GetIPRequest{ + IPID: ID, + Region: region, + }) + + if err != nil { + return err + } + + return nil + } +} + +func testAccCheckScalewayLbIPBetaDestroy(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "scaleway_lb_ip_beta" { + continue + } + + lbAPI, region, ID, err := lbAPIWithRegionAndID(testAccProvider.Meta(), rs.Primary.ID) + if err != nil { + return err + } + + _, err = lbAPI.GetIP(&lb.GetIPRequest{ + Region: region, + IPID: ID, + }) + + // If no error resource still exist + if err == nil { + return fmt.Errorf("IP (%s) still exists", rs.Primary.ID) + } + + // Unexpected api error we return it + if !is404Error(err) { + return err + } + } + + return nil +} diff --git a/website/docs/r/lb_beta.html.markdown b/website/docs/r/lb_beta.html.markdown index d68f02299..091c3b0ca 100644 --- a/website/docs/r/lb_beta.html.markdown +++ b/website/docs/r/lb_beta.html.markdown @@ -16,7 +16,11 @@ Creates and manages Scaleway Load-Balancers. For more information, see [the docu ### Basic ```hcl +resource "scaleway_lb_ip_beta" "ip" { +} + resource "scaleway_lb_beta" "base" { + ip_id = scaleway_lb_ip_beta.ip.id region = "fr-par" type = "LB-S" } @@ -26,6 +30,10 @@ resource "scaleway_lb_beta" "base" { The following arguments are supported: +- `ip_id` - (Required) The ID of the associated IP. See below. + +~> **Important:** Updates to `ip_id` will recreate the load-balancer. + - `type` - (Required) The type of the load-balancer. For now only `LB-S` is available ~> **Important:** Updates to `type` will recreate the load-balancer. @@ -38,15 +46,49 @@ The following arguments are supported: - `organization_id` - (Defaults to [provider](../index.html#organization_id) `organization_id`) The ID of the organization the load-balancer is associated with. - ## Attributes Reference In addition to all arguments above, the following attributes are exported: - `id` - The ID of the load-balancer. -- `ip_id` - The load-balance public IP ID - `ip_address` - The load-balance public IP Address +## IP ID + +Since v1.15.0, `ip_id` is a required field. This means that now a separate `scaleway_lb_ip_beta` is required. +When importing, the IP needs to be imported as well as the LB. +When upgrading to v1.15.0, you will need to create a new `scaleway_lb_ip_beta` resource and import it. + +For instance, if you had the following: +```hcl +resource "scaleway_lb_beta" "base" { + region = "fr-par" + type = "LB-S" +} +``` + +You will need to update it to: +```hcl +resource "scaleway_lb_ip_beta" "ip" { +} + +resource "scaleway_lb_beta" "base" { + ip_id = scaleway_lb_ip_beta.ip.id + region = "fr-par" + type = "LB-S" +} +``` + +And before running `terraform apply` you will need to import the IP with: +```bash +$ terraform import scaleway_lb_ip_beta.ip fr-par/11111111-1111-1111-1111-111111111111 +``` + +The IP ID can either be found in the console, or you can run: +```bash +$ terraform state show scaleway_lb_beta.base +``` +and look for `ip_id`. ## Import @@ -55,3 +97,5 @@ Load-Balancer can be imported using the `{region}/{id}`, e.g. ```bash $ terraform import scaleway_lb_beta.lb01 fr-par/11111111-1111-1111-1111-111111111111 ``` + +Be aware that you will also need to import the `scaleway_lb_ip_beta` resource. diff --git a/website/docs/r/lb_ip_beta.html.markdown b/website/docs/r/lb_ip_beta.html.markdown new file mode 100644 index 000000000..ab4710b24 --- /dev/null +++ b/website/docs/r/lb_ip_beta.html.markdown @@ -0,0 +1,45 @@ +--- +layout: "scaleway" +page_title: "Scaleway: scaleway_lb_ip_beta" +description: |- + Manages Scaleway Load-Balancers IPs. +--- + +# scaleway_lb_ip_beta + +-> **Note:** This terraform resource is flagged beta and might include breaking change in future releases. + +Creates and manages Scaleway Load-Balancers IPs. For more information, see [the documentation](https://developers.scaleway.com/en/products/lb/api). + +## Examples + +### Basic + +```hcl +resource "scaleway_lb_ip_beta" "ip" { + reverse = "my-reverse.com" +} +``` + +## Arguments Reference + +The following arguments are supported: + +- `reverse` - (Optional) The reverse domain associated with this IP. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +- `id` - The ID of the IP +- `lb_id` - The associated load-balance ID if any +- `ip_address` - The IP Address + + +## Import + +IPs can be imported using the `{region}/{id}`, e.g. + +```bash +$ terraform import scaleway_lb_ip_beta.ip01 fr-par/11111111-1111-1111-1111-111111111111 +```