Skip to content

Commit

Permalink
Adding failover IP address data and resource blocks for lookup and at…
Browse files Browse the repository at this point in the history
…tachment to existing instances.

NB/ The work covers manipulating existing failover IP addresses only, it does not cover ordering new ones

Renaming to clarify intent of resource being able to move attachments between instances but not delete them

Adding documentation

fixing documentation
  • Loading branch information
steven authored and stevenleadbeater committed Mar 4, 2022
1 parent b0d920c commit cf80ced
Show file tree
Hide file tree
Showing 12 changed files with 528 additions and 1 deletion.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Terraform OVH Provider
- [![Gitter chat](https://badges.gitter.im/hashicorp-terraform/Lobby.png)](https://gitter.im/hashicorp-terraform/Lobby)
- Mailing list: [Google Groups](http://groups.google.com/group/terraform-tool)

<img src="https://cdn.rawgit.com/hashicorp/terraform-website/master/content/source/assets/images/logo-hashicorp.svg" width="600px">
<img alt="chat on gitter" src="https://cdn.rawgit.com/hashicorp/terraform-website/master/content/source/assets/images/logo-hashicorp.svg" width="600px">

Requirements
------------
Expand Down Expand Up @@ -90,6 +90,9 @@ export OVH_IP_BLOCK_TEST="..."
export OVH_IP_REVERSE_TEST="..."
export OVH_IPLB_SERVICE_TEST="..."
export OVH_CLOUD_PROJECT_SERVICE_TEST="..."
export OVH_CLOUD_PROJECT_FAILOVER_IP_TEST="..."
export OVH_CLOUD_PROJECT_FAILOVER_IP_ROUTED_TO_1_TEST="..."
export OVH_CLOUD_PROJECT_FAILOVER_IP_ROUTED_TO_2_TEST="..."
export OVH_VRACK_SERVICE_TEST="..."
export OVH_ZONE_TEST="..."

Expand Down
14 changes: 14 additions & 0 deletions ovh/data_cloud_project_failover_ip_attach.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package ovh

import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

func dataSourceCloudProjectFailoverIpAttach() *schema.Resource {
return &schema.Resource{
Read: func(d *schema.ResourceData, meta interface{}) error {
return resourceCloudProjectFailoverIpAttachRead(d, meta)
},
Schema: resourceCloudProjectFailoverIpAttachSchema(),
}
}
57 changes: 57 additions & 0 deletions ovh/data_cloud_project_failover_ip_attach_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package ovh

import (
"fmt"
"os"
"testing"

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

const testAccDataSourceCloudProjectFailoverIpAttach = `
data "ovh_cloud_project_failover_ip_attach" "myfailoverip" {
service_name = "%s"
ip = "%s"
}
`

func TestAccDataSourceCloudProjectFailoverIpAttach(t *testing.T) {
serviceName := os.Getenv("OVH_CLOUD_PROJECT_SERVICE_TEST")
ipAddress := os.Getenv("OVH_CLOUD_PROJECT_FAILOVER_IP_TEST")
config := fmt.Sprintf(
testAccDataSourceCloudProjectFailoverIpAttach,
serviceName,
ipAddress,
)

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheckFailoverIpAttach(t) },

Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: config,
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(
"data.ovh_cloud_project_failover_ip_attach.myfailoverip",
"service_name",
serviceName,
),
resource.TestCheckResourceAttr(
"data.ovh_cloud_project_failover_ip_attach.myfailoverip",
"ip",
ipAddress,
),
resource.TestCheckResourceAttrSet(
"data.ovh_cloud_project_failover_ip_attach.myfailoverip",
"id",
),
resource.TestCheckResourceAttrSet(
"data.ovh_cloud_project_failover_ip_attach.myfailoverip",
"routed_to",
),
),
},
},
})
}
2 changes: 2 additions & 0 deletions ovh/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ func Provider() *schema.Provider {
"ovh_cloud_project_containerregistries": dataSourceCloudProjectContainerRegistries(),
"ovh_cloud_project_containerregistry": dataSourceCloudProjectContainerRegistry(),
"ovh_cloud_project_containerregistry_users": dataSourceCloudProjectContainerRegistryUsers(),
"ovh_cloud_project_failover_ip_attach": dataSourceCloudProjectFailoverIpAttach(),
"ovh_cloud_project_kube": dataSourceCloudProjectKube(),
"ovh_cloud_project_region": dataSourceCloudProjectRegion(),
"ovh_cloud_project_regions": dataSourceCloudProjectRegions(),
Expand Down Expand Up @@ -84,6 +85,7 @@ func Provider() *schema.Provider {
"ovh_cloud_project": resourceCloudProject(),
"ovh_cloud_project_containerregistry": resourceCloudProjectContainerRegistry(),
"ovh_cloud_project_containerregistry_user": resourceCloudProjectContainerRegistryUser(),
"ovh_cloud_project_failover_ip_attach": resourceCloudProjectFailoverIpAttach(),
"ovh_cloud_project_kube": resourceCloudProjectKube(),
"ovh_cloud_project_kube_nodepool": resourceCloudProjectKubeNodePool(),
"ovh_cloud_project_network_private": resourceCloudProjectNetworkPrivate(),
Expand Down
10 changes: 10 additions & 0 deletions ovh/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,16 @@ func testAccPreCheckCloud(t *testing.T) {
checkEnvOrSkip(t, "OVH_CLOUD_PROJECT_SERVICE_TEST")
}

// Checks that the environment variables needed for the /cloud/project/{projectId}/ip/failover acceptance tests
// are set.
func testAccPreCheckFailoverIpAttach(t *testing.T) {
testAccPreCheckCredentials(t)
testAccPreCheckCloud(t)
checkEnvOrSkip(t, "OVH_CLOUD_PROJECT_FAILOVER_IP_TEST")
checkEnvOrSkip(t, "OVH_CLOUD_PROJECT_FAILOVER_IP_ROUTED_TO_1_TEST")
checkEnvOrSkip(t, "OVH_CLOUD_PROJECT_FAILOVER_IP_ROUTED_TO_2_TEST")
}

// Checks that the environment variables needed for the /cloud/{cloudId}/kube acceptance tests
// are set.
func testAccPreCheckKubernetes(t *testing.T) {
Expand Down
216 changes: 216 additions & 0 deletions ovh/resource_cloud_project_failover_ip_attach.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
package ovh

import (
"fmt"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/ovh/terraform-provider-ovh/ovh/helpers"
"log"
"net/url"
)

func resourceCloudProjectFailoverIpAttach() *schema.Resource {
return &schema.Resource{
Create: resourceCloudProjectFailoverIpAttachCreate,
Read: resourceCloudProjectFailoverIpAttachRead,
Delete: resourceCloudProjectFailoverIpAttachDelete,

Importer: &schema.ResourceImporter{
State: func(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
return []*schema.ResourceData{d}, nil
},
},

Schema: resourceCloudProjectFailoverIpAttachSchema(),
}
}

func resourceCloudProjectFailoverIpAttachSchema() map[string]*schema.Schema {
return map[string]*schema.Schema{
"service_name": {
Type: schema.TypeString,
Description: "The service name",
ForceNew: true,
Required: true,
},

"block": {
Type: schema.TypeString,
Description: "IP block",
Optional: true,
Computed: true,
ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
err := helpers.ValidateIp(v.(string))
if err != nil {
errors = append(errors, err)
}
return
},
},
"continent_code": {
Type: schema.TypeString,
Description: "Ip continent",
Optional: true,
Computed: true,
},
"geo_loc": {
Type: schema.TypeString,
Description: "Ip location",
Optional: true,
Computed: true,
},
"id": {
Type: schema.TypeString,
Description: "Ip id",
Computed: true,
},
"ip": {
Type: schema.TypeString,
Description: "Ip",
Optional: true,
Computed: true,
ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
err := helpers.ValidateIp(v.(string))
if err != nil {
errors = append(errors, err)
}
return
},
},
"progress": {
Type: schema.TypeInt,
Description: "Current operation progress in percent",
Computed: true,
},
"routed_to": {
Type: schema.TypeString,
Description: "Instance where ip is routed to",
Computed: true,
Optional: true,
},
"status": {
Type: schema.TypeString,
Description: "Ip status",
Computed: true,
},
"sub_type": {
Type: schema.TypeString,
Description: "IP sub type",
Computed: true,
},
}
}

func resourceCloudProjectFailoverIpAttachRead(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)

serviceName := d.Get("service_name").(string)

log.Printf("[DEBUG] Will read cloud project ip addresses %s", serviceName)
endpoint := fmt.Sprintf("/cloud/project/%s/ip/failover",
url.PathEscape(serviceName),
)

ips := []FailoverIp{}
if err := config.OVHClient.Get(endpoint, &ips); err != nil {
return fmt.Errorf("Error calling GET %s:\n\t %q", endpoint, err)
}

match := false
for _, ip := range ips {
if ip.Ip == d.Get("ip").(string) {
for k, v := range ip.ToMap() {
match = true
if k == "id" {
d.SetId(v.(string))
} else {
err := d.Set(k, v)
if err != nil {
return err
}
}
}
}
}

if !match {
return fmt.Errorf("your query returned no results, " +
"please change your search criteria and try again")
}

return nil
}

func resourceCloudProjectFailoverIpAttachCreate(d *schema.ResourceData, meta interface{}) error {

serviceName := d.Get("service_name").(string)

config := meta.(*Config)

//Fetch Failover IP address to populate ID field
log.Printf("[DEBUG] Will read cloud project ip addresses %s", serviceName)
endpoint := fmt.Sprintf("/cloud/project/%s/ip/failover",
url.PathEscape(serviceName),
)

ips := []FailoverIp{}
if err := config.OVHClient.Get(endpoint, &ips); err != nil {
return fmt.Errorf("Error calling GET %s:\n\t %q", endpoint, err)
}

match := false
for _, ip := range ips {
if ip.Ip == d.Get("ip").(string) {
for k, v := range ip.ToMap() {
match = true
if k == "id" {
d.SetId(v.(string))
}
}
}
}

if !match {
return fmt.Errorf("your query returned no results, " +
"please change your search criteria and try again")
}

id := d.Get("id").(string)

log.Printf("[DEBUG] Will attach failover ip to an instance: %s", serviceName)
opts := (&ProjectIpFailoverAttachCreation{}).FromResource(d)
endpoint = fmt.Sprintf("/cloud/project/%s/ip/failover/%s/attach",
url.PathEscape(serviceName),
url.PathEscape(id),
)

ip := &FailoverIp{}
if err := config.OVHClient.Post(endpoint, opts, ip); err != nil {
return fmt.Errorf("calling Put %s: %q", endpoint, err)
}

for k, v := range ip.ToMap() {
match = true
if k != "id" {
err := d.Set(k, v)
if err != nil {
return err
}
}
}

for d.Get("status").(string) == "operationPending" {
if err := resourceCloudProjectFailoverIpAttachRead(d, meta); err != nil {
return err
}
}

return nil
}

func resourceCloudProjectFailoverIpAttachDelete(d *schema.ResourceData, meta interface{}) error {
// Failover IPs cannot be deleted, the best that can be done in this instance is to check it exists
if err := resourceCloudProjectFailoverIpAttachRead(d, meta); err != nil {
return err
}
return nil
}
Loading

0 comments on commit cf80ced

Please sign in to comment.