Skip to content

Commit

Permalink
feat(flexibleip): add support for flexible ip (#1022)
Browse files Browse the repository at this point in the history
Co-authored-by: Yacine FODIL <yfodil@scaleway.com>
Co-authored-by: Jules Castéran <jcasteran@scaleway.com>
  • Loading branch information
3 people committed Jul 12, 2022
1 parent 05b7d09 commit f9f55ab
Show file tree
Hide file tree
Showing 16 changed files with 47,363 additions and 2 deletions.
40 changes: 40 additions & 0 deletions docs/data-sources/flexible_ip.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
page_title: "Scaleway: scaleway_flexible_ip"
description: |-
Gets information about a Flexible IP.
---

# scaleway_flexible_ip

Gets information about a Flexible IP.

## Example Usage

```hcl
# Get info by IP address
data "scaleway_flexible_ip" "my_ip" {
ip_address = "1.2.3.4"
}
# Get info by IP ID
data "scaleway_flexible_ip" "my_ip" {
ip_id = "11111111-1111-1111-1111-111111111111"
}
```

## Argument Reference

- `ip_address` - (Optional) The IP address.
Only one of `ip_address` and `ip_id` should be specified.

- `ip_id` - (Optional) The IP ID.
Only one of `ip_address` and `ip_id` should be specified.

## Attributes Reference

In addition to all above arguments, the following attributes are exported:

- `reverse` - The reverse domain associated with this IP.
- `server_id` - The associated server ID if any
- `organization_id` - (Defaults to [provider](../index.md#organization_id) `organization_id`) The ID of the organization the IP is in.
- `project_id` - (Defaults to [provider](../index.md#project_id) `project_id`) The ID of the project the IP is in.
87 changes: 87 additions & 0 deletions docs/resources/flexible_ip.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
---
page_title: "Scaleway: scaleway_flexible_ip"
description: |-
Manages Scaleway Flexible IPs.
---

# scaleway_flexible_ip

Creates and manages Scaleway flexible IPs.
For more information, see [the documentation](https://developers.scaleway.com/en/products/flexible-ip/api).

## Examples

### Basic

```hcl
resource "scaleway_flexible_ip" "main" {
reverse = "my-reverse.com"
}
```

### With zone

```hcl
resource "scaleway_flexible_ip" "main" {
zone = "fr-par-2"
}
```

### With baremetal server

```hcl
resource "scaleway_account_ssh_key" "main" {
name = "main"
public_key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILHy/M5FVm5ydLGcal3e5LNcfTalbeN7QL/ZGCvDEdqJ foobar@example.com"
}
data "scaleway_baremetal_os" "by_id" {
zone = "fr-par-2"
name = "Ubuntu"
version = "20.04 LTS (Focal Fossa)"
}
data "scaleway_baremetal_offer" "my_offer" {
zone = "fr-par-2"
name = "EM-A210R-HDD"
}
resource "scaleway_baremetal_server" "base" {
zone = "fr-par-2"
offer = data.scaleway_baremetal_offer.my_offer.offer_id
os = data.scaleway_baremetal_os.by_id.os_id
ssh_key_ids = scaleway_account_ssh_key.main.id
}
resource "scaleway_flexible_ip" "main" {
server_id = scaleway_baremetal_server.base.id
zone = "fr-par-2"
}
```

## Arguments Reference

The following arguments are supported:

- `description`: (Optional) A description of the flexible IP.
- `tags`: (Optional) A list of tags to apply to the flexible IP.
- `reverse` - (Optional) The reverse domain associated with this flexible IP.

## Attributes Reference

In addition to all arguments above, the following attributes are exported:

- `id` - The ID of the Flexible IP
- `ip_address` - The IPv4 address of the Flexible IP
- `zone` - The zone of the Flexible IP
- `organization_id` - The organization of the Flexible IP
- `project_id` - The project of the Flexible IP
- `server_id` - The ID of the associated server

## Import

Flexible IPs can be imported using the `{zone}/{id}`, e.g.

```bash
$ terraform import scaleway_flexible_ip.main fr-par-1/11111111-1111-1111-1111-111111111111
```
9 changes: 8 additions & 1 deletion scaleway/data_source_baremetal_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,5 +62,12 @@ func dataSourceScalewayBaremetalServerRead(ctx context.Context, d *schema.Resour
if err != nil {
return diag.FromErr(err)
}
return resourceScalewayBaremetalServerRead(ctx, d, meta)
diags := resourceScalewayBaremetalServerRead(ctx, d, meta)
if diags != nil {
return diags
}
if d.Id() == "" {
return diag.Errorf("baremetal server (%s) not found", zoneID)
}
return nil
}
91 changes: 91 additions & 0 deletions scaleway/data_source_flexible_ip.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package scaleway

import (
"context"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
flexibleip "github.com/scaleway/scaleway-sdk-go/api/flexibleip/v1alpha1"
"github.com/scaleway/scaleway-sdk-go/scw"
)

func dataSourceScalewayFlexibleIP() *schema.Resource {
// Generate datasource schema from resource
dsSchema := datasourceSchemaFromResourceSchema(resourceScalewayFlexibleIP().Schema)

dsSchema["ip_address"] = &schema.Schema{
Type: schema.TypeString,
Optional: true,
Description: "The IPv4 address",
ConflictsWith: []string{"flexible_ip_id"},
}
dsSchema["flexible_ip_id"] = &schema.Schema{
Type: schema.TypeString,
Optional: true,
Description: "The ID of the IPv4 address",
ConflictsWith: []string{"ip_address"},
ValidateFunc: validationUUIDorUUIDWithLocality(),
}
dsSchema["project_id"] = &schema.Schema{
Type: schema.TypeString,
Description: "The project_id you want to attach the resource to",
Optional: true,
ForceNew: true,
Computed: true,
ValidateFunc: validationUUID(),
}

return &schema.Resource{
ReadContext: dataSourceScalewayFlexibleIPRead,
Schema: dsSchema,
}
}

func dataSourceScalewayFlexibleIPRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
fipAPI, zone, err := fipAPIWithZone(d, meta)
if err != nil {
return diag.FromErr(err)
}

ipID, ipIDExists := d.GetOk("flexible_ip_id")

if !ipIDExists {
res, err := fipAPI.ListFlexibleIPs(&flexibleip.ListFlexibleIPsRequest{
Zone: zone,
ProjectID: expandStringPtr(d.Get("project_id")),
}, scw.WithContext(ctx))
if err != nil {
return diag.FromErr(err)
}

for _, ip := range res.FlexibleIPs {
if ip.IPAddress.String() == d.Get("ip_address").(string) {
if ipID != "" {
return diag.Errorf("more than 1 flexible ip found with the same IPv4 address %s", d.Get("ip_address"))
}
ipID = ip.ID
}
}
if ipID == "" {
return diag.Errorf("no flexible ip found with the same IPv4 address %s", d.Get("ip_address"))
}
}

zoneID := datasourceNewZonedID(ipID, zone)
d.SetId(zoneID)
err = d.Set("flexible_ip_id", zoneID)
if err != nil {
return diag.FromErr(err)
}

diags := resourceScalewayFlexibleIPRead(ctx, d, meta)
if diags != nil {
return append(diags, diag.Errorf("failed to read flexible ip state")...)
}

if d.Id() == "" {
return diag.Errorf("flexible ip (%s) not found", ipID)
}

return nil
}
102 changes: 102 additions & 0 deletions scaleway/data_source_flexible_ip_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package scaleway

import (
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

func TestAccScalewayDataSourceFlexibleIP_Basic(t *testing.T) {
tt := NewTestTools(t)
defer tt.Cleanup()
resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProviderFactories: tt.ProviderFactories,
CheckDestroy: testAccCheckScalewayFlexibleIPDestroy(tt),
Steps: []resource.TestStep{
{
Config: `
resource "scaleway_flexible_ip" "main" {
}
data "scaleway_flexible_ip" "by_address" {
ip_address = "${scaleway_flexible_ip.main.ip_address}"
}
data "scaleway_flexible_ip" "by_id" {
flexible_ip_id = "${scaleway_flexible_ip.main.id}"
}
`,
Check: resource.ComposeTestCheckFunc(
testAccCheckScalewayFlexibleIPExists(tt, "scaleway_flexible_ip.main"),

resource.TestCheckResourceAttrSet("scaleway_flexible_ip.main", "ip_address"),
resource.TestCheckResourceAttrSet("data.scaleway_flexible_ip.by_address", "ip_address"),
resource.TestCheckResourceAttrSet("data.scaleway_flexible_ip.by_id", "ip_address"),

resource.TestCheckResourceAttrSet("scaleway_flexible_ip.main", "id"),
resource.TestCheckResourceAttrSet("data.scaleway_flexible_ip.by_address", "id"),
resource.TestCheckResourceAttrSet("data.scaleway_flexible_ip.by_id", "flexible_ip_id"),
),
},
},
})
}

func TestAccScalewayDataSourceFlexibleIP_Multiple(t *testing.T) {
tt := NewTestTools(t)
defer tt.Cleanup()
resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProviderFactories: tt.ProviderFactories,
CheckDestroy: testAccCheckScalewayFlexibleIPDestroy(tt),
Steps: []resource.TestStep{
{
Config: `
resource "scaleway_flexible_ip" "first" {
}
data "scaleway_flexible_ip" "by_address_first" {
ip_address = "${scaleway_flexible_ip.first.ip_address}"
}
data "scaleway_flexible_ip" "by_id_first" {
flexible_ip_id = "${scaleway_flexible_ip.first.id}"
}
resource "scaleway_flexible_ip" "second" {
}
data "scaleway_flexible_ip" "by_address_second" {
ip_address = "${scaleway_flexible_ip.second.ip_address}"
}
data "scaleway_flexible_ip" "by_id_second" {
flexible_ip_id = "${scaleway_flexible_ip.second.id}"
}
`,
Check: resource.ComposeTestCheckFunc(
testAccCheckScalewayFlexibleIPExists(tt, "scaleway_flexible_ip.first"),

resource.TestCheckResourceAttrSet("scaleway_flexible_ip.first", "ip_address"),
resource.TestCheckResourceAttrSet("data.scaleway_flexible_ip.by_address_first", "ip_address"),
resource.TestCheckResourceAttrSet("data.scaleway_flexible_ip.by_id_first", "ip_address"),

resource.TestCheckResourceAttrSet("scaleway_flexible_ip.first", "id"),
resource.TestCheckResourceAttrSet("data.scaleway_flexible_ip.by_address_first", "id"),
resource.TestCheckResourceAttrSet("data.scaleway_flexible_ip.by_id_first", "flexible_ip_id"),

testAccCheckScalewayFlexibleIPExists(tt, "scaleway_flexible_ip.second"),

resource.TestCheckResourceAttrSet("scaleway_flexible_ip.second", "ip_address"),
resource.TestCheckResourceAttrSet("data.scaleway_flexible_ip.by_address_second", "ip_address"),
resource.TestCheckResourceAttrSet("data.scaleway_flexible_ip.by_id_second", "ip_address"),

resource.TestCheckResourceAttrSet("scaleway_flexible_ip.second", "id"),
resource.TestCheckResourceAttrSet("data.scaleway_flexible_ip.by_address_second", "id"),
resource.TestCheckResourceAttrSet("data.scaleway_flexible_ip.by_id_second", "flexible_ip_id"),
),
},
},
})
}
53 changes: 53 additions & 0 deletions scaleway/helpers_flexible_ip.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package scaleway

import (
"context"
"time"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
flexibleip "github.com/scaleway/scaleway-sdk-go/api/flexibleip/v1alpha1"
"github.com/scaleway/scaleway-sdk-go/scw"
)

const (
defaultFlexibleIPTimeout = 1 * time.Minute
retryFlexibleIPInterval = 5 * time.Second
)

// fipAPIWithZone returns an lb API WITH zone for a Create request
func fipAPIWithZone(d *schema.ResourceData, m interface{}) (*flexibleip.API, scw.Zone, error) {
meta := m.(*Meta)
flexibleipAPI := flexibleip.NewAPI(meta.scwClient)

zone, err := extractZone(d, meta)
if err != nil {
return nil, "", err
}
return flexibleipAPI, zone, nil
}

// fipAPIWithZoneAndID returns an flexibleip API with zone and ID extracted from the state
func fipAPIWithZoneAndID(m interface{}, id string) (*flexibleip.API, scw.Zone, string, error) {
meta := m.(*Meta)
fipAPI := flexibleip.NewAPI(meta.scwClient)

zone, ID, err := parseZonedID(id)
if err != nil {
return nil, "", "", err
}
return fipAPI, zone, ID, nil
}

func waitFlexibleIP(ctx context.Context, api *flexibleip.API, zone scw.Zone, id string, timeout time.Duration) (*flexibleip.FlexibleIP, error) {
retryInterval := retryFlexibleIPInterval
if DefaultWaitRetryInterval != nil {
retryInterval = *DefaultWaitRetryInterval
}

return api.WaitForFlexibleIP(&flexibleip.WaitForFlexibleIPRequest{
FipID: id,
Zone: zone,
Timeout: scw.TimeDurationPtr(timeout),
RetryInterval: &retryInterval,
}, scw.WithContext(ctx))
}

0 comments on commit f9f55ab

Please sign in to comment.