From ada25ad0a83ecdb1826634d64775e8ec9346d8a3 Mon Sep 17 00:00:00 2001 From: Thibaut Di Prima Date: Mon, 10 Jun 2024 09:55:26 +0000 Subject: [PATCH 1/3] create instance --- ovh/data_cloud_project_instance.go | 135 ++++++ ovh/data_cloud_project_instance_test.go | 46 +++ ovh/data_cloud_project_instances.go | 139 +++++++ ovh/data_cloud_project_instances_test.go | 47 +++ ovh/provider.go | 3 + ovh/resource_cloud_project_instance.go | 387 ++++++++++++++++++ ovh/resource_cloud_project_instance_test.go | 53 +++ ovh/types_cloud_project_instance.go | 240 +++++++++++ .../docs/d/cloud_project_instance.markdown | 44 ++ .../docs/d/cloud_project_instances.markdown | 42 ++ website/docs/index.html.markdown | 10 + .../docs/r/cloud_project_instance.markdown | 75 ++++ 12 files changed, 1221 insertions(+) create mode 100644 ovh/data_cloud_project_instance.go create mode 100644 ovh/data_cloud_project_instance_test.go create mode 100644 ovh/data_cloud_project_instances.go create mode 100644 ovh/data_cloud_project_instances_test.go create mode 100644 ovh/resource_cloud_project_instance.go create mode 100644 ovh/resource_cloud_project_instance_test.go create mode 100644 ovh/types_cloud_project_instance.go create mode 100644 website/docs/d/cloud_project_instance.markdown create mode 100644 website/docs/d/cloud_project_instances.markdown create mode 100644 website/docs/r/cloud_project_instance.markdown diff --git a/ovh/data_cloud_project_instance.go b/ovh/data_cloud_project_instance.go new file mode 100644 index 000000000..9f736271a --- /dev/null +++ b/ovh/data_cloud_project_instance.go @@ -0,0 +1,135 @@ +package ovh + +import ( + "fmt" + "log" + "net/url" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/ovh/terraform-provider-ovh/ovh/helpers" +) + +func dataSourceCloudProjectInstance() *schema.Resource { + return &schema.Resource{ + Read: dataSourceCloudProjectInstanceRead, + Schema: map[string]*schema.Schema{ + "service_name": { + Type: schema.TypeString, + Required: true, + DefaultFunc: schema.EnvDefaultFunc("OVH_CLOUD_PROJECT_SERVICE", nil), + Description: "Service name of the resource representing the id of the cloud project.", + }, + "region": { + Type: schema.TypeString, + Description: "Instance region", + Required: true, + ForceNew: true, + }, + "instance_id": { + Type: schema.TypeString, + Description: "Instance id", + Required: true, + ForceNew: true, + }, + // computed + "addresses": { + Type: schema.TypeList, + Computed: true, + Description: "Instance IP addresses", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "ip": { + Type: schema.TypeString, + Description: "IP address", + Computed: true, + }, + "version": { + Type: schema.TypeInt, + Description: "IP version", + Computed: true, + }, + }, + }, + }, + "attached_volumes": { + Type: schema.TypeList, + Computed: true, + Description: " Volumes attached to the instance", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "ip": { + Type: schema.TypeString, + Description: "Volume Id", + Computed: true, + }, + }, + }, + }, + "flavor_id": { + Type: schema.TypeString, + Description: "Flavor id", + Computed: true, + }, + "flavor_name": { + Type: schema.TypeString, + Description: "Flavor name", + Computed: true, + }, + "name": { + Type: schema.TypeString, + Description: "Flavor name", + Computed: true, + }, + "id": { + Type: schema.TypeString, + Description: "Instance id", + Computed: true, + }, + "image_id": { + Type: schema.TypeString, + Description: "Image id", + Computed: true, + }, + "ssh_key": { + Type: schema.TypeString, + Description: "Instance task state", + Computed: true, + }, + "task_state": { + Type: schema.TypeString, + Description: "Instance task state", + Computed: true, + }, + }, + } +} + +func dataSourceCloudProjectInstanceRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + serviceName := d.Get("service_name").(string) + region := d.Get("region").(string) + instanceId := d.Get("instance_id").(string) + log.Printf("[DEBUG] SCROUTCH") + endpoint := fmt.Sprintf("/cloud/project/%s/region/%s/instance/%s", + url.PathEscape(serviceName), + url.PathEscape(region), + url.PathEscape(instanceId), + ) + var res CloudProjectInstanceResponse + + log.Printf("[DEBUG] Will read instance %s from project %s in region %s", instanceId, serviceName, region) + if err := config.OVHClient.Get(endpoint, &res); err != nil { + return helpers.CheckDeleted(d, err, endpoint) + } + + for k, v := range res.ToMap() { + if k != "id" { + d.Set(k, v) + } else { + d.SetId(fmt.Sprint(v)) + } + } + + log.Printf("[DEBUG] Read instance: %+v", res) + return nil +} diff --git a/ovh/data_cloud_project_instance_test.go b/ovh/data_cloud_project_instance_test.go new file mode 100644 index 000000000..73a01a837 --- /dev/null +++ b/ovh/data_cloud_project_instance_test.go @@ -0,0 +1,46 @@ +package ovh + +import ( + "fmt" + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func TestAccDataSourceCloudProjecInstance_basic(t *testing.T) { + + config := fmt.Sprintf( + testAccDataSourceCloudProjectInstance, + os.Getenv("OVH_CLOUD_PROJECT_SERVICE_TEST"), + os.Getenv("OVH_CLOUD_PROJECT_REGION_TEST"), + os.Getenv("OVH_CLOUD_PROJECT_INSTANCE_TEST"), + ) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheckCloud(t) + testAccCheckCloudProjectExists(t) + }, + + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("data.ovh_cloud_project_instance.test", "flavor_name"), + resource.TestCheckResourceAttrSet("data.ovh_cloud_project_instance.test", "id"), + resource.TestCheckResourceAttrSet("data.ovh_cloud_project_instance.test", "image_id"), + ), + }, + }, + }) +} + +var testAccDataSourceCloudProjectInstance = ` +data "ovh_cloud_project_instance" "test" { + service_name = "%s" + region = "%s" + instance_id = "%s" +} +` diff --git a/ovh/data_cloud_project_instances.go b/ovh/data_cloud_project_instances.go new file mode 100644 index 000000000..194dbebc9 --- /dev/null +++ b/ovh/data_cloud_project_instances.go @@ -0,0 +1,139 @@ +package ovh + +import ( + "fmt" + "log" + "net/url" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/ovh/terraform-provider-ovh/ovh/helpers" + "github.com/ovh/terraform-provider-ovh/ovh/helpers/hashcode" +) + +func dataSourceCloudProjectInstances() *schema.Resource { + return &schema.Resource{ + Read: dataSourceCloudProjectInstancesRead, + Schema: map[string]*schema.Schema{ + "service_name": { + Type: schema.TypeString, + Required: true, + DefaultFunc: schema.EnvDefaultFunc("OVH_CLOUD_PROJECT_SERVICE", nil), + Description: "Service name of the resource representing the id of the cloud project.", + }, + "region": { + Type: schema.TypeString, + Description: "Instance region", + Required: true, + ForceNew: true, + }, + // computed + "instances": { + Type: schema.TypeList, + Description: "List of instances", + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "addresses": { + Type: schema.TypeList, + Computed: true, + Description: "Instance IP addresses", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "ip": { + Type: schema.TypeString, + Description: "IP address", + Computed: true, + }, + "version": { + Type: schema.TypeInt, + Description: "IP version", + Computed: true, + }, + }, + }, + }, + "attached_volumes": { + Type: schema.TypeList, + Computed: true, + Description: " Volumes attached to the instance", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "ip": { + Type: schema.TypeString, + Description: "Volume Id", + Computed: true, + }, + }, + }, + }, + "flavor_id": { + Type: schema.TypeString, + Description: "Flavor id", + Computed: true, + }, + "flavor_name": { + Type: schema.TypeString, + Description: "Flavor name", + Computed: true, + }, + "name": { + Type: schema.TypeString, + Description: "Flavor name", + Computed: true, + }, + "id": { + Type: schema.TypeString, + Description: "Instance id", + Computed: true, + }, + "image_id": { + Type: schema.TypeString, + Description: "Image id", + Computed: true, + }, + "ssh_key": { + Type: schema.TypeString, + Description: "Instance task state", + Computed: true, + }, + "task_state": { + Type: schema.TypeString, + Description: "Instance task state", + Computed: true, + }, + }, + }, + }, + }, + } +} + +func dataSourceCloudProjectInstancesRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + serviceName := d.Get("service_name").(string) + region := d.Get("region").(string) + + endpoint := fmt.Sprintf("/cloud/project/%s/region/%s/instance", + url.PathEscape(serviceName), + url.PathEscape(region), + ) + var res []CloudProjectInstanceResponse + + log.Printf("[DEBUG] Will read instances from project %s in region %s", serviceName, region) + if err := config.OVHClient.Get(endpoint, &res); err != nil { + return helpers.CheckDeleted(d, err, endpoint) + } + instances := make([]map[string]interface{}, len(res)) + ids := make([]string, len(instances)) + + for i, instance := range res { + instances[i] = instance.ToMap() + ids = append(ids, instance.Id) + } + + d.SetId(hashcode.Strings(ids)) + d.Set("instances", instances) + + log.Printf("[DEBUG] Read instances: %+v", res) + return nil +} diff --git a/ovh/data_cloud_project_instances_test.go b/ovh/data_cloud_project_instances_test.go new file mode 100644 index 000000000..b0835076c --- /dev/null +++ b/ovh/data_cloud_project_instances_test.go @@ -0,0 +1,47 @@ +package ovh + +import ( + "fmt" + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +const testAccDataSourceCloudProjecInstancesConfig_basic = ` +data "ovh_cloud_project_instances" "instances" { + service_name = "%s" + region = "%s" +} +` + +func TestAccDataSourceCloudProjecInstances_basic(t *testing.T) { + serviceName := os.Getenv("OVH_CLOUD_PROJECT_SERVICE_TEST") + region := os.Getenv("OVH_CLOUD_PROJECT_REGION_TEST") + + config := fmt.Sprintf( + testAccDataSourceCloudProjecInstancesConfig_basic, + serviceName, + region, + ) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheckCloud(t) + testAccCheckCloudProjectExists(t) + }, + + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet( + "data.ovh_cloud_project_instances.instances", + "instances.#", + ), + ), + }, + }, + }) +} diff --git a/ovh/provider.go b/ovh/provider.go index 7c94b4eb0..868108807 100644 --- a/ovh/provider.go +++ b/ovh/provider.go @@ -106,6 +106,8 @@ func Provider() *schema.Provider { "ovh_cloud_project_database_user": dataSourceCloudProjectDatabaseUser(), "ovh_cloud_project_database_users": dataSourceCloudProjectDatabaseUsers(), "ovh_cloud_project_failover_ip_attach": dataSourceCloudProjectFailoverIpAttach(), + "ovh_cloud_project_instance": dataSourceCloudProjectInstance(), + "ovh_cloud_project_instances": dataSourceCloudProjectInstances(), "ovh_cloud_project_kube": dataSourceCloudProjectKube(), "ovh_cloud_project_kube_iprestrictions": dataSourceCloudProjectKubeIPRestrictions(), "ovh_cloud_project_kube_nodepool_nodes": dataSourceCloudProjectKubeNodepoolNodes(), @@ -196,6 +198,7 @@ func Provider() *schema.Provider { "ovh_cloud_project_database_user": resourceCloudProjectDatabaseUser(), "ovh_cloud_project_failover_ip_attach": resourceCloudProjectFailoverIpAttach(), "ovh_cloud_project_gateway": resourceCloudProjectGateway(), + "ovh_cloud_project_instance": resourceCloudProjectInstance(), "ovh_cloud_project_kube": resourceCloudProjectKube(), "ovh_cloud_project_kube_nodepool": resourceCloudProjectKubeNodePool(), "ovh_cloud_project_kube_oidc": resourceCloudProjectKubeOIDC(), diff --git a/ovh/resource_cloud_project_instance.go b/ovh/resource_cloud_project_instance.go new file mode 100644 index 000000000..3cc5a89b4 --- /dev/null +++ b/ovh/resource_cloud_project_instance.go @@ -0,0 +1,387 @@ +package ovh + +import ( + "context" + "fmt" + "log" + "net/url" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/ovh/go-ovh/ovh" +) + +func resourceCloudProjectInstance() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceCloudProjectInstanceCreate, + ReadContext: resourceCloudProjectInstanceRead, + DeleteContext: resourceCloudProjectInstanceDelete, + + Schema: map[string]*schema.Schema{ + "service_name": { + Type: schema.TypeString, + Required: true, + DefaultFunc: schema.EnvDefaultFunc("OVH_CLOUD_PROJECT_SERVICE", nil), + Description: "Service name of the resource representing the id of the cloud project.", + ForceNew: true, + }, + "region": { + Type: schema.TypeString, + Description: "Instance region", + Required: true, + ForceNew: true, + }, + "auto_backup": { + Type: schema.TypeList, + Optional: true, + Description: "Create an autobackup workflow after instance start up", + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "cron": { + Type: schema.TypeString, + Description: "Unix cron pattern", + Optional: true, + ForceNew: true, + }, + "rotation": { + Type: schema.TypeInt, + Description: "Number of backup to keep", + Optional: true, + ForceNew: true, + }, + }, + }, + }, + "billing_period": { + Type: schema.TypeString, + Description: "Number of backup to keep", + Required: true, + ForceNew: true, + }, + "boot_from": { + Type: schema.TypeList, + Required: true, + Description: "Boot the instance from an image or a volume", + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "image_id": { + Type: schema.TypeString, + Description: "Instance image id", + Optional: true, + }, + "volume_id": { + Type: schema.TypeString, + Description: "Instance volume id", + Optional: true, + }, + }, + }, + }, + "bulk": { + Type: schema.TypeInt, + Description: "Create multiple instances", + Optional: true, + ForceNew: true, + }, + "flavor": { + Type: schema.TypeList, + Required: true, + Description: "Flavor information", + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "flavor_id": { + Type: schema.TypeString, + Description: "Flavor id", + Required: true, + }, + }, + }, + }, + "group": { + Type: schema.TypeList, + Optional: true, + Description: "Start instance in group", + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "group_id": { + Type: schema.TypeString, + Description: "Group id", + Optional: true, + }, + }, + }, + }, + "name": { + Type: schema.TypeString, + Description: "Instance name", + Required: true, + ForceNew: true, + }, + "ssh_key": { + Type: schema.TypeList, + Optional: true, + Description: "Existing SSH Keypair", + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Description: "SSH Keypair name", + Optional: true, + }, + }, + }, + }, + "ssh_key_create": { + Type: schema.TypeList, + Optional: true, + Description: "Start instance in group", + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Description: "SSH Keypair name", + Optional: true, + }, + "public_key": { + Type: schema.TypeString, + Description: "SSH Public key", + Optional: true, + }, + }, + }, + }, + "user_data": { + Type: schema.TypeString, + Description: "Configuration information or scripts to use upon launch", + Optional: true, + ForceNew: true, + }, + "network": { + Type: schema.TypeList, + Required: true, + ForceNew: true, + Description: "Create network interfaces", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "private": { + Type: schema.TypeString, + Description: "Private network information", + Optional: true, + }, + "public": { + Type: schema.TypeBool, + Description: "Set the new instance as public", + Optional: true, + }, + }, + }, + }, + // computed + "addresses": { + Type: schema.TypeList, + Computed: true, + Description: "Instance IP addresses", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "ip": { + Type: schema.TypeString, + Description: "IP address", + Computed: true, + }, + "version": { + Type: schema.TypeInt, + Description: "IP version", + Computed: true, + }, + }, + }, + }, + "attached_volumes": { + Type: schema.TypeList, + Computed: true, + Description: " Volumes attached to the instance", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Description: "Volume Id", + Computed: true, + }, + }, + }, + }, + "flavor_id": { + Type: schema.TypeString, + Description: "Flavor id", + Computed: true, + }, + "flavor_name": { + Type: schema.TypeString, + Description: "Flavor name", + Computed: true, + }, + "id": { + Type: schema.TypeString, + Description: "Instance id", + Computed: true, + }, + "image_id": { + Type: schema.TypeString, + Description: "Image id", + Computed: true, + }, + "task_state": { + Type: schema.TypeString, + Description: "Instance task state", + Computed: true, + }, + }, + } +} + +func resourceCloudProjectInstanceCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + config := meta.(*Config) + serviceName := d.Get("service_name").(string) + region := d.Get("region").(string) + params := (&CloudProjectInstanceCreateOpts{}).FromResource(d) + + r := &CloudProjectOperation{} + + endpoint := fmt.Sprintf("/cloud/project/%s/region/%s/instance", + url.PathEscape(serviceName), + url.PathEscape(region), + ) + + if err := config.OVHClient.Post(endpoint, params, r); err != nil { + return diag.Errorf("calling %s with params %v:\n\t %q", endpoint, params, err) + } + + err := waitForInstanceCreation(ctx, config.OVHClient, serviceName, r.Id) + if err != nil { + return diag.Errorf("timeout instance creation: %s", err) + } + + endpointInstance := fmt.Sprintf("/cloud/project/%s/operation/%s", + url.PathEscape(serviceName), + url.PathEscape(r.Id), + ) + + err = config.OVHClient.GetWithContext(ctx, endpointInstance, r) + if err != nil { + return diag.Errorf("failed to get instance id: %s", err) + } + + d.SetId(r.SubOperations[0].ResourceId) + d.Set("region", region) + + return resourceCloudProjectInstanceRead(ctx, d, meta) +} + +func waitForInstanceCreation(ctx context.Context, client *ovh.Client, serviceName, operationId string) error { + stateConf := &retry.StateChangeConf{ + Pending: []string{"null", "in-progress", "created", ""}, + Target: []string{"completed"}, + Refresh: func() (interface{}, string, error) { + res := &CloudProjectOperation{} + endpoint := fmt.Sprintf("/cloud/project/%s/operation/%s", + url.PathEscape(serviceName), + url.PathEscape(operationId), + ) + err := client.GetWithContext(ctx, endpoint, res) + if err != nil { + return res, "", err + } + return res, res.Status, nil + }, + Timeout: 360 * time.Second, + Delay: 10 * time.Second, + MinTimeout: 3 * time.Second, + } + + _, err := stateConf.WaitForStateContext(ctx) + return err +} + +func resourceCloudProjectInstanceRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + config := meta.(*Config) + id := d.Id() + region := d.Get("region").(string) + serviceName := d.Get("service_name").(string) + + r := &CloudProjectInstanceResponse{} + + endpoint := fmt.Sprintf("/cloud/project/%s/region/%s/instance/%s", + url.PathEscape(serviceName), + url.PathEscape(region), + url.PathEscape(id), + ) + + if err := config.OVHClient.Get(endpoint, r); err != nil { + return diag.Errorf("Error calling post %s:\n\t %q", endpoint, err) + } + + d.Set("flavor_id", r.FlavorId) + d.Set("flavor_name", r.FlavorName) + d.Set("image_id", r.ImageId) + d.Set("region", r.Region) + d.Set("task_state", r.TaskState) + d.Set("name", d.Get("name").(string)) + // d.Set("name", r.Name) + d.Set("id", r.Id) + + addresses := make([]map[string]interface{}, 0) + if r.Addresses != nil { + for _, add := range r.Addresses { + address := make(map[string]interface{}) + address["ip"] = add.Ip + address["version"] = add.Version + addresses = append(addresses, address) + } + } + d.Set("addresses", addresses) + + attachedVolumes := make([]map[string]interface{}, 0) + if r.AttachedVolumes != nil { + for _, att := range r.AttachedVolumes { + attachedVolume := make(map[string]interface{}) + attachedVolume["id"] = att.Id + attachedVolumes = append(attachedVolumes, attachedVolume) + } + } + d.Set("attached_volumes", attachedVolumes) + + return nil +} + +func resourceCloudProjectInstanceDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + config := meta.(*Config) + serviceName := d.Get("service_name").(string) + region := d.Get("region").(string) + + id := d.Id() + + log.Printf("[DEBUG] Will delete public cloud instance for project: %s, region: %s, id: %s", serviceName, region, id) + + endpoint := fmt.Sprintf("/cloud/project/%s/instance/%s", + url.PathEscape(serviceName), + url.PathEscape(id), + ) + + r := &CloudProjectInstanceResponse{} + if err := config.OVHClient.Delete(endpoint, r); err != nil { + return diag.Errorf("Error calling post %s:\n\t %q", endpoint, err) + } + + d.SetId("") + + log.Printf("[DEBUG] Deleted Public Cloud %s Gateway %s", serviceName, id) + return nil +} diff --git a/ovh/resource_cloud_project_instance_test.go b/ovh/resource_cloud_project_instance_test.go new file mode 100644 index 000000000..2676e5be2 --- /dev/null +++ b/ovh/resource_cloud_project_instance_test.go @@ -0,0 +1,53 @@ +package ovh + +import ( + "fmt" + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func TestAccCloudProjectInstance_basic(t *testing.T) { + var testCreateLoadBalancerLogSubscription = fmt.Sprintf(` + resource "ovh_cloud_project_instance" "instance" { + service_name = "%s" + region = "%s" + billing_period = "hourly" + boot_from { + image_id = "%s" + } + flavor { + flavor_id = "%s" + } + name = "haproxy" + ssh_key { + name = "%s" + } + network { + public = true + } + } +`, + os.Getenv("OVH_CLOUD_PROJECT_SERVICE_TEST"), + os.Getenv("OVH_CLOUD_PROJECT_REGION_TEST"), + os.Getenv("OVH_CLOUD_PROJECT_IMAGE_ID_TEST"), + os.Getenv("OVH_CLOUD_PROJECT_FLAVOR_ID_TEST"), + os.Getenv("OVH_CLOUD_PROJECT_SSH_NAME_TEST")) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheckCloud(t) + testAccCheckCloudProjectExists(t) + }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testCreateLoadBalancerLogSubscription, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("ovh_cloud_project_instance.instance", "id"), + ), + }, + }, + }) +} diff --git a/ovh/types_cloud_project_instance.go b/ovh/types_cloud_project_instance.go new file mode 100644 index 000000000..c915687a7 --- /dev/null +++ b/ovh/types_cloud_project_instance.go @@ -0,0 +1,240 @@ +package ovh + +import ( + "fmt" + "log" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/ovh/terraform-provider-ovh/ovh/helpers" +) + +type AutoBackup struct { + Cron *string `json:"cron",omitempty` + Rotation *int `json:"rotation",omitempty` +} + +func (ab *AutoBackup) FromResource(d *schema.ResourceData, parent string) *AutoBackup { + ab.Cron = helpers.GetNilStringPointerFromData(d, fmt.Sprintf("%s.cron", parent)) + ab.Rotation = helpers.GetNilIntPointerFromData(d, fmt.Sprintf("%s.rotation", parent)) + return ab +} + +type Flavor struct { + FlavorId *string `json:"id",omitempty` +} + +func (fl *Flavor) FromResource(d *schema.ResourceData, parent string) *Flavor { + fl.FlavorId = helpers.GetNilStringPointerFromData(d, fmt.Sprintf("%s.flavor_id", parent)) + return fl +} + +type BootFrom struct { + ImageId *string `json:"imageId",omitempty` + VolumeId *string `json:"volumeId",omitempty` +} + +func (bf *BootFrom) FromResource(d *schema.ResourceData, parent string) *BootFrom { + bf.ImageId = helpers.GetNilStringPointerFromData(d, fmt.Sprintf("%s.image_id", parent)) + bf.VolumeId = helpers.GetNilStringPointerFromData(d, fmt.Sprintf("%s.volume_id", parent)) + return bf +} + +type Group struct { + GroupId *string `json:"id";omitempty` +} + +func (g *Group) FromResource(d *schema.ResourceData, parent string) *Group { + g.GroupId = helpers.GetNilStringPointerFromData(d, fmt.Sprintf("%s.id", parent)) + return g +} + +type SshKey struct { + Name *string `json:"name",omitempty` +} + +func (sk *SshKey) FromResource(d *schema.ResourceData, parent string) *SshKey { + sk.Name = helpers.GetNilStringPointerFromData(d, fmt.Sprintf("%s.name", parent)) + return sk +} + +type SshKeyCreate struct { + Name *string `json:"name",omitempty` + PublicKey *string `json:"publicKey",omitempty` +} + +func (skc *SshKeyCreate) FromResource(d *schema.ResourceData, parent string) *SshKeyCreate { + skc.Name = helpers.GetNilStringPointerFromData(d, fmt.Sprintf("%s.name", parent)) + skc.PublicKey = helpers.GetNilStringPointerFromData(d, fmt.Sprintf("%s.public_key", parent)) + return skc +} + +type Network struct { + Public *bool `json:"public",omitempty` +} + +func (n *Network) FromResource(d *schema.ResourceData, parent string) *Network { + n.Public = helpers.GetNilBoolPointerFromData(d, fmt.Sprintf("%s.public", parent)) + return n +} + +type CloudProjectInstanceCreateOpts struct { + AutoBackup *AutoBackup `json:"autobackup"` + BillingPeriod string `json:"billingPeriod"` + BootFrom *BootFrom `json:"bootFrom"` + Bulk int `json:"bulk"` + Flavor *Flavor `json:"flavor"` + Group *Group `json:"group"` + Name string `json:"name"` + SshKey *SshKey `json:"sshKey"` + SshKeyCreate *SshKeyCreate `json:"sshKeyCreate"` + UserData string `json:"userData"` + Network *Network `json:"network"` +} + +type Address struct { + Ip *string `json:"ip",omitempty` + Version *int `json:"version",omitempty` +} + +type AttachedVolume struct { + Id string `json:"id",omitempty` +} + +type CloudProjectInstanceResponse struct { + Addresses []Address `json:"addresses"` + AttachedVolumes []AttachedVolume `json:"attachedVolumes"` + FlavorId string `json:"flavorId"` + FlavorName string `json:"flavorName"` + Id string `json:"id"` + ImageId string `json:"imageId"` + Name string `json:"name"` + Region string `json:"region"` + SshKey string `json:"sshKey"` + TaskState string `json:"taskState"` +} + +type CloudProjectOperation struct { + Id string `json:"id"` + Status string `json:"status"` + SubOperations []SubOperation `json:"subOperations"` +} + +type SubOperation struct { + Id string `json:"id"` + ResourceId string `json:"resourceId"` + Status string `json:"status"` +} + +func (sb SubOperation) ToMap() map[string]interface{} { + obj := make(map[string]interface{}) + obj["id"] = sb.Id + obj["resourceId"] = sb.ResourceId + obj["status"] = sb.Status + + log.Printf("[DEBUG] tata suboperation %+v:", obj) + return obj +} + +func (o CloudProjectOperation) ToMap() map[string]interface{} { + obj := make(map[string]interface{}) + obj["id"] = o.Id + obj["status"] = o.Status + subOperations := make([]map[string]interface{}, len(o.SubOperations)) + log.Printf("[DEBUG] titi operation %+v:", o) + log.Printf("[DEBUG] titi operation %+v:", o.SubOperations) + for _, subOperation := range o.SubOperations { + log.Printf("[DEBUG] tutu operation %+v:", subOperation) + subOperations = append(subOperations, subOperation.ToMap()) + } + obj["subOperations"] = subOperations + log.Printf("[DEBUG] titi operation %+v:", obj) + return obj +} + +func (a Address) ToMap() map[string]interface{} { + obj := make(map[string]interface{}) + obj["ip"] = a.Ip + obj["version"] = a.Version + return obj +} + +func (a AttachedVolume) ToMap() map[string]interface{} { + obj := make(map[string]interface{}) + obj["attached_volumes"] = a.Id + return obj +} + +func (cpir CloudProjectInstanceResponse) ToMap() map[string]interface{} { + obj := make(map[string]interface{}) + obj["flavor_id"] = cpir.FlavorId + obj["flavor_name"] = cpir.FlavorName + obj["id"] = cpir.Id + obj["image_id"] = cpir.ImageId + obj["name"] = cpir.Name + obj["ssh_key"] = cpir.SshKey + obj["task_state"] = cpir.TaskState + + addresses := make([]map[string]interface{}, len(cpir.Addresses)) + for _, address := range cpir.Addresses { + addresses = append(addresses, address.ToMap()) + } + obj["addresses"] = addresses + + attachedVolumes := make([]map[string]interface{}, len(cpir.AttachedVolumes)) + for _, attachedVolume := range cpir.AttachedVolumes { + attachedVolumes = append(addresses, attachedVolume.ToMap()) + } + + obj["attached_volumes"] = attachedVolumes + return obj +} + +type CloudProjectInstanceResponseList struct { + Id string `json:"id"` + Name string `json:"name"` +} + +func (cpir *CloudProjectInstanceCreateOpts) FromResource(d *schema.ResourceData) *CloudProjectInstanceCreateOpts { + + bootFrom := d.Get("boot_from").([]interface{}) + flavor := d.Get("flavor").([]interface{}) + autoBackup := d.Get("auto_backup").([]interface{}) + group := d.Get("group").([]interface{}) + sshKey := d.Get("ssh_key").([]interface{}) + sshKeyCreate := d.Get("ssh_key_create").([]interface{}) + network := d.Get("network").([]interface{}) + + cpir.BillingPeriod = d.Get("billing_period").(string) + cpir.Name = d.Get("name").(string) + cpir.Bulk = 1 + + if len(bootFrom) == 1 { + cpir.BootFrom = (&BootFrom{}).FromResource(d, "boot_from.0") + } + + if len(flavor) == 1 { + cpir.Flavor = (&Flavor{}).FromResource(d, "flavor.0") + } + + if len(autoBackup) == 1 { + cpir.AutoBackup = (&AutoBackup{}).FromResource(d, "auto_backup.0") + } + + if len(group) == 1 { + cpir.Group = (&Group{}).FromResource(d, "group.0") + } + + if len(sshKey) == 1 { + cpir.SshKey = (&SshKey{}).FromResource(d, "ssh_key.0") + } + + if len(sshKeyCreate) == 1 { + cpir.SshKeyCreate = (&SshKeyCreate{}).FromResource(d, "ssh_key_create.0") + } + + if len(network) == 1 { + cpir.Network = (&Network{}).FromResource(d, "network.0") + } + + return cpir +} diff --git a/website/docs/d/cloud_project_instance.markdown b/website/docs/d/cloud_project_instance.markdown new file mode 100644 index 000000000..afac1b5d5 --- /dev/null +++ b/website/docs/d/cloud_project_instance.markdown @@ -0,0 +1,44 @@ +--- +subcategory : "Cloud Project" +--- + +# ovh_cloud_project_instance +**This datasource uses a Beta API** +Use this data source to get the instance of a public cloud project. + +## Example Usage + +To get information of an instance: + +```hcl +data "ovh_cloud_project_instance" "instance" { + service_name = "YYYY" + region = "XXXX" + instance_id = "ZZZZZ" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `service_name` - (Required, Forces new resource) The id of the public cloud project. If omitted, + the `OVH_CLOUD_PROJECT_SERVICE` environment variable is used. +* `region` - (Required, Forces new resource) Instance region. +* `instance_id` - (Required, Forces new resource) Instance id + +## Attributes Reference + +The following attributes are exported: + +* `addresses` - Instance IP addresses + * `ip` - IP address + * `version` - IP version +* `attached_volumes` - Volumes attached to the instance + * `id` - Volume Id +* `flavor_id` - Flavor id +* `flavor_name` - Flavor name +* `id` - Instance id +* `image_id` - Image id +* `task_state` - Instance task state +* `ssh_key` - SSH Keypair diff --git a/website/docs/d/cloud_project_instances.markdown b/website/docs/d/cloud_project_instances.markdown new file mode 100644 index 000000000..65ca70773 --- /dev/null +++ b/website/docs/d/cloud_project_instances.markdown @@ -0,0 +1,42 @@ +--- +subcategory : "Cloud Project" +--- + +# ovh_cloud_project_instance +**This datasource uses a Beta API** +Use this data source to get the list instance in a region of a public cloud project. + +## Example Usage + +To get information of an instance: + +```hcl +data "ovh_cloud_project_instances" "instance" { + service_name = "YYYY" + region = "XXXX" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `service_name` - (Required, Forces new resource) The id of the public cloud project. If omitted, + the `OVH_CLOUD_PROJECT_SERVICE` environment variable is used. +* `region` - (Required, Forces new resource) Instance region. + +## Attributes Reference + +The following attributes are exported: +* `instances` - Instance + * `addresses` - Instance IP addresses + * `ip` - IP address + * `version` - IP version + * `attached_volumes` - Volumes attached to the instance + * `id` - Volume Id + * `flavor_id` - Flavor id + * `flavor_name` - Flavor name + * `id` - Instance id + * `image_id` - Image id + * `task_state` - Instance task state + * `ssh_key` - SSH Keypair diff --git a/website/docs/index.html.markdown b/website/docs/index.html.markdown index c736fbae5..63215925a 100644 --- a/website/docs/index.html.markdown +++ b/website/docs/index.html.markdown @@ -246,11 +246,21 @@ variables must also be set: * `OVH_CLOUD_PROJECT_FAILOVER_IP_ROUTED_TO_2_TEST` - The GUID of a secondary instance to which failover IP addresses can be attached. There must be 2 as associations can only be updated not removed. To test effectively, the failover ip address must be moved between instances +* `OVH_CLOUD_PROJECT_FLAVOR_ID_TEST` - The ID of the Flavor to test + +* `OVH_CLOUD_PROJECT_IMAGE_ID_TEST` - The ID of the image to test + +* `OVH_CLOUD_PROJECT_INSTANCE_TEST` - The ID of the instance to test + * `OVH_CLOUD_PROJECT_KUBE_REGION_TEST` - The region of your public cloud kubernetes project. * `OVH_CLOUD_PROJECT_KUBE_VERSION_TEST` - The version of your public cloud kubernetes project. * `OVH_CLOUD_PROJECT_KUBE_PREV_VERSION_TEST` - The previous version of your public cloud kubernetes project. This is used to test upgrade. +* `OVH_CLOUD_PROJECT_REGION_TEST` - The Name of the region to test + +* `OVH_CLOUD_PROJECT_SSH_NAME_TEST` - The Name of the SSH Key to test + * `OVH_DEDICATED_SERVER` - The name of the dedicated server to test dedicated_server_networking resource. * `OVH_NASHA_SERVICE_TEST` - The name of your HA-NAS service. diff --git a/website/docs/r/cloud_project_instance.markdown b/website/docs/r/cloud_project_instance.markdown new file mode 100644 index 000000000..567079df6 --- /dev/null +++ b/website/docs/r/cloud_project_instance.markdown @@ -0,0 +1,75 @@ +--- +subcategory : "Cloud Project" +--- + +# ovh_cloud_project_instance +**This resource uses a Beta API** +Creates an instance associated with a public cloud project. + +## Example Usage + +Create a instance. + +```hcl +resource "ovh_cloud_project_instance" "instance" { + service_name = "XXX" + region = "RRRR" + billing_period = "hourly" + boot_from { + image_id = "UUID" + } + flavor { + flavor_id = "UUID" + } + name = "sshkeyname" + ssh_key { + name = "sshname" + } + network { + public = true + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `service_name` - (Required, Forces new resource) The id of the public cloud project. If omitted, + the `OVH_CLOUD_PROJECT_SERVICE` environment variable is used. +* `region` - (Required, Forces new resource) Instance region. +* `billing_period` - (Required, Forces new resource) Billing period - hourly or monthly +* `network` - (Required, Forces new resource) Create network interfaces + * `public` - (Optional, Forces new resource) Set the new instance as public boolean +* `flavor` - (Required, Forces new resource) Flavor information + * `flavor_id` - (Required, Forces new resource) Flavor ID +* `boot_from` - (Required, Forces new resource) Boot the instance from an image or a volume + * `image_id` - (Mandatory only if volume_id not used, Forces new resource) Instance image id + * `volume_id` - (Mandatory only if image_id not used, Forces new resource) Instance volume id +* `group`- (Optional, Forces new resource) Start instance in group + * `group_id` - (Optional, Forces new resource) Group id +* `name` - (Required, Forces new resource) Instance name +* `ssh_key` - (Mandatory only if ssh_key_create not used, Forces new resource) Existing SSH Keypair + * `name` - (Optional, Forces new resource) SSH Keypair name +* `ssh_key_create` - (Mandatory only if ssh_key not used, Forces new resource) Unix cron pattern + * `name` - (Optional, Forces new resource) SSH Keypair name + * `public_key` - (Optional, Forces new resource) SSH Public key +* `user_data`- (Optional, Forces new resource) Configuration information or scripts to use upon launch +* `auto_backup` - (Optional, Forces new resource) Create an autobackup workflow after instance start up. + * `cron` - (Optional, Forces new resource) Unix cron pattern + * `rotation` - (Optional, Forces new resource) Number of backup to keep + +## Attributes Reference + +The following attributes are exported: + +* `addresses` - Instance IP addresses + * `ip` - IP address + * `version` - IP version +* `attached_volumes` - Volumes attached to the instance + * `id` - Volume Id +* `flavor_id` - Flavor id +* `flavor_name` - Flavor name +* `id` - Instance id +* `image_id` - Image id +* `task_state` - Instance task state From 1af54b1c7b8d4722c26e28222e19b245b841e9be Mon Sep 17 00:00:00 2001 From: Thibaut Di Prima Date: Mon, 30 Sep 2024 12:54:14 +0000 Subject: [PATCH 2/3] datasource & resource instance --- ovh/data_cloud_project_instance.go | 42 ++- ovh/data_cloud_project_instance_test.go | 2 +- ovh/data_cloud_project_instances.go | 11 +- ovh/resource_cloud_project_instance.go | 54 ++-- ovh/resource_cloud_project_instance_test.go | 12 +- ovh/types_cloud_project_instance.go | 281 +++++++++++------- .../docs/d/cloud_project_instances.markdown | 7 +- website/docs/index.html.markdown | 4 +- 8 files changed, 245 insertions(+), 168 deletions(-) diff --git a/ovh/data_cloud_project_instance.go b/ovh/data_cloud_project_instance.go index 9f736271a..318ee89a5 100644 --- a/ovh/data_cloud_project_instance.go +++ b/ovh/data_cloud_project_instance.go @@ -23,17 +23,15 @@ func dataSourceCloudProjectInstance() *schema.Resource { Type: schema.TypeString, Description: "Instance region", Required: true, - ForceNew: true, }, "instance_id": { Type: schema.TypeString, Description: "Instance id", Required: true, - ForceNew: true, }, // computed "addresses": { - Type: schema.TypeList, + Type: schema.TypeSet, Computed: true, Description: "Instance IP addresses", Elem: &schema.Resource{ @@ -52,12 +50,12 @@ func dataSourceCloudProjectInstance() *schema.Resource { }, }, "attached_volumes": { - Type: schema.TypeList, + Type: schema.TypeSet, Computed: true, - Description: " Volumes attached to the instance", + Description: "Volumes attached to the instance", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "ip": { + "id": { Type: schema.TypeString, Description: "Volume Id", Computed: true, @@ -109,7 +107,7 @@ func dataSourceCloudProjectInstanceRead(d *schema.ResourceData, meta interface{} serviceName := d.Get("service_name").(string) region := d.Get("region").(string) instanceId := d.Get("instance_id").(string) - log.Printf("[DEBUG] SCROUTCH") + endpoint := fmt.Sprintf("/cloud/project/%s/region/%s/instance/%s", url.PathEscape(serviceName), url.PathEscape(region), @@ -121,15 +119,31 @@ func dataSourceCloudProjectInstanceRead(d *schema.ResourceData, meta interface{} if err := config.OVHClient.Get(endpoint, &res); err != nil { return helpers.CheckDeleted(d, err, endpoint) } + log.Printf("[DEBUG] Read instance: %+v", res) + + addresses := make([]map[string]interface{}, 0) + for i := range res.Addresses { + address := make(map[string]interface{}) + address["ip"] = res.Addresses[i].Ip + address["version"] = res.Addresses[i].Version + addresses = append(addresses, address) + } - for k, v := range res.ToMap() { - if k != "id" { - d.Set(k, v) - } else { - d.SetId(fmt.Sprint(v)) - } + attachedVolumes := make([]map[string]interface{}, 0) + for i := range res.AttachedVolumes { + attachedVolume := make(map[string]interface{}) + attachedVolume["id"] = res.AttachedVolumes[i].Id + attachedVolumes = append(attachedVolumes, attachedVolume) } + d.Set("addresses", addresses) + d.Set("flavor_id", res.FlavorId) + d.Set("flavor_name", res.FlavorName) + d.SetId(res.Id) + d.Set("image_id", res.ImageId) + d.Set("instance_id", res.Id) + d.Set("name", res.Name) + d.Set("ssh_key", res.SshKey) + d.Set("task_state", res.TaskState) - log.Printf("[DEBUG] Read instance: %+v", res) return nil } diff --git a/ovh/data_cloud_project_instance_test.go b/ovh/data_cloud_project_instance_test.go index 73a01a837..ac0f34ac7 100644 --- a/ovh/data_cloud_project_instance_test.go +++ b/ovh/data_cloud_project_instance_test.go @@ -14,7 +14,7 @@ func TestAccDataSourceCloudProjecInstance_basic(t *testing.T) { testAccDataSourceCloudProjectInstance, os.Getenv("OVH_CLOUD_PROJECT_SERVICE_TEST"), os.Getenv("OVH_CLOUD_PROJECT_REGION_TEST"), - os.Getenv("OVH_CLOUD_PROJECT_INSTANCE_TEST"), + os.Getenv("OVH_CLOUD_PROJECT_INSTANCE_ID_TEST"), ) resource.Test(t, resource.TestCase{ diff --git a/ovh/data_cloud_project_instances.go b/ovh/data_cloud_project_instances.go index 194dbebc9..af994338b 100644 --- a/ovh/data_cloud_project_instances.go +++ b/ovh/data_cloud_project_instances.go @@ -4,6 +4,7 @@ import ( "fmt" "log" "net/url" + "sort" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/ovh/terraform-provider-ovh/ovh/helpers" @@ -24,7 +25,6 @@ func dataSourceCloudProjectInstances() *schema.Resource { Type: schema.TypeString, Description: "Instance region", Required: true, - ForceNew: true, }, // computed "instances": { @@ -34,7 +34,7 @@ func dataSourceCloudProjectInstances() *schema.Resource { Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "addresses": { - Type: schema.TypeList, + Type: schema.TypeSet, Computed: true, Description: "Instance IP addresses", Elem: &schema.Resource{ @@ -58,7 +58,7 @@ func dataSourceCloudProjectInstances() *schema.Resource { Description: " Volumes attached to the instance", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "ip": { + "id": { Type: schema.TypeString, Description: "Volume Id", Computed: true, @@ -123,13 +123,14 @@ func dataSourceCloudProjectInstancesRead(d *schema.ResourceData, meta interface{ if err := config.OVHClient.Get(endpoint, &res); err != nil { return helpers.CheckDeleted(d, err, endpoint) } - instances := make([]map[string]interface{}, len(res)) - ids := make([]string, len(instances)) + instances := make([]map[string]interface{}, len(res)) + ids := make([]string, len(res)) for i, instance := range res { instances[i] = instance.ToMap() ids = append(ids, instance.Id) } + sort.Strings(ids) d.SetId(hashcode.Strings(ids)) d.Set("instances", instances) diff --git a/ovh/resource_cloud_project_instance.go b/ovh/resource_cloud_project_instance.go index 3cc5a89b4..7be1d1183 100644 --- a/ovh/resource_cloud_project_instance.go +++ b/ovh/resource_cloud_project_instance.go @@ -11,6 +11,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/ovh/go-ovh/ovh" + "github.com/ovh/terraform-provider-ovh/ovh/helpers" ) func resourceCloudProjectInstance() *schema.Resource { @@ -34,7 +35,7 @@ func resourceCloudProjectInstance() *schema.Resource { ForceNew: true, }, "auto_backup": { - Type: schema.TypeList, + Type: schema.TypeSet, Optional: true, Description: "Create an autobackup workflow after instance start up", ForceNew: true, @@ -43,26 +44,27 @@ func resourceCloudProjectInstance() *schema.Resource { "cron": { Type: schema.TypeString, Description: "Unix cron pattern", - Optional: true, ForceNew: true, + Required: true, }, "rotation": { Type: schema.TypeInt, Description: "Number of backup to keep", - Optional: true, ForceNew: true, + Required: true, }, }, }, }, "billing_period": { - Type: schema.TypeString, - Description: "Number of backup to keep", - Required: true, - ForceNew: true, + Type: schema.TypeString, + Description: "Billing period - hourly | monthly ", + Required: true, + ForceNew: true, + ValidateFunc: helpers.ValidateEnum([]string{"monthly", "hourly"}), }, "boot_from": { - Type: schema.TypeList, + Type: schema.TypeSet, Required: true, Description: "Boot the instance from an image or a volume", ForceNew: true, @@ -88,10 +90,9 @@ func resourceCloudProjectInstance() *schema.Resource { ForceNew: true, }, "flavor": { - Type: schema.TypeList, + Type: schema.TypeSet, Required: true, Description: "Flavor information", - ForceNew: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "flavor_id": { @@ -103,7 +104,7 @@ func resourceCloudProjectInstance() *schema.Resource { }, }, "group": { - Type: schema.TypeList, + Type: schema.TypeSet, Optional: true, Description: "Start instance in group", ForceNew: true, @@ -124,7 +125,7 @@ func resourceCloudProjectInstance() *schema.Resource { ForceNew: true, }, "ssh_key": { - Type: schema.TypeList, + Type: schema.TypeSet, Optional: true, Description: "Existing SSH Keypair", ForceNew: true, @@ -133,27 +134,27 @@ func resourceCloudProjectInstance() *schema.Resource { "name": { Type: schema.TypeString, Description: "SSH Keypair name", - Optional: true, + Required: true, }, }, }, }, "ssh_key_create": { - Type: schema.TypeList, + Type: schema.TypeSet, Optional: true, - Description: "Start instance in group", + Description: "Creatting SSH Keypair", ForceNew: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "name": { Type: schema.TypeString, Description: "SSH Keypair name", - Optional: true, + Required: true, }, "public_key": { Type: schema.TypeString, - Description: "SSH Public key", - Optional: true, + Description: "Group id", + Required: true, }, }, }, @@ -165,17 +166,12 @@ func resourceCloudProjectInstance() *schema.Resource { ForceNew: true, }, "network": { - Type: schema.TypeList, + Type: schema.TypeSet, Required: true, ForceNew: true, Description: "Create network interfaces", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "private": { - Type: schema.TypeString, - Description: "Private network information", - Optional: true, - }, "public": { Type: schema.TypeBool, Description: "Set the new instance as public", @@ -186,7 +182,7 @@ func resourceCloudProjectInstance() *schema.Resource { }, // computed "addresses": { - Type: schema.TypeList, + Type: schema.TypeSet, Computed: true, Description: "Instance IP addresses", Elem: &schema.Resource{ @@ -205,7 +201,7 @@ func resourceCloudProjectInstance() *schema.Resource { }, }, "attached_volumes": { - Type: schema.TypeList, + Type: schema.TypeSet, Computed: true, Description: " Volumes attached to the instance", Elem: &schema.Resource{ @@ -251,7 +247,8 @@ func resourceCloudProjectInstanceCreate(ctx context.Context, d *schema.ResourceD config := meta.(*Config) serviceName := d.Get("service_name").(string) region := d.Get("region").(string) - params := (&CloudProjectInstanceCreateOpts{}).FromResource(d) + params := new(CloudProjectInstanceCreateOpts) + params.FromResource(d) r := &CloudProjectOperation{} @@ -260,6 +257,8 @@ func resourceCloudProjectInstanceCreate(ctx context.Context, d *schema.ResourceD url.PathEscape(region), ) + log.Printf("[DEBUG] params ------- %+vv", params) + if err := config.OVHClient.Post(endpoint, params, r); err != nil { return diag.Errorf("calling %s with params %v:\n\t %q", endpoint, params, err) } @@ -334,7 +333,6 @@ func resourceCloudProjectInstanceRead(ctx context.Context, d *schema.ResourceDat d.Set("region", r.Region) d.Set("task_state", r.TaskState) d.Set("name", d.Get("name").(string)) - // d.Set("name", r.Name) d.Set("id", r.Id) addresses := make([]map[string]interface{}, 0) diff --git a/ovh/resource_cloud_project_instance_test.go b/ovh/resource_cloud_project_instance_test.go index 2676e5be2..aee850ea2 100644 --- a/ovh/resource_cloud_project_instance_test.go +++ b/ovh/resource_cloud_project_instance_test.go @@ -9,7 +9,7 @@ import ( ) func TestAccCloudProjectInstance_basic(t *testing.T) { - var testCreateLoadBalancerLogSubscription = fmt.Sprintf(` + var testCreateInstance = fmt.Sprintf(` resource "ovh_cloud_project_instance" "instance" { service_name = "%s" region = "%s" @@ -20,7 +20,7 @@ func TestAccCloudProjectInstance_basic(t *testing.T) { flavor { flavor_id = "%s" } - name = "haproxy" + name = "%s" ssh_key { name = "%s" } @@ -33,6 +33,7 @@ func TestAccCloudProjectInstance_basic(t *testing.T) { os.Getenv("OVH_CLOUD_PROJECT_REGION_TEST"), os.Getenv("OVH_CLOUD_PROJECT_IMAGE_ID_TEST"), os.Getenv("OVH_CLOUD_PROJECT_FLAVOR_ID_TEST"), + os.Getenv("OVH_CLOUD_PROJECT_INSTANCE_NAME_TEST"), os.Getenv("OVH_CLOUD_PROJECT_SSH_NAME_TEST")) resource.Test(t, resource.TestCase{ @@ -43,9 +44,14 @@ func TestAccCloudProjectInstance_basic(t *testing.T) { Providers: testAccProviders, Steps: []resource.TestStep{ { - Config: testCreateLoadBalancerLogSubscription, + Config: testCreateInstance, Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrSet("ovh_cloud_project_instance.instance", "id"), + resource.TestCheckResourceAttrSet("ovh_cloud_project_instance.instance", "flavor_name"), + resource.TestCheckResourceAttr("ovh_cloud_project_instance.instance", "flavor_id", os.Getenv("OVH_CLOUD_PROJECT_FLAVOR_ID_TEST")), + resource.TestCheckResourceAttr("ovh_cloud_project_instance.instance", "image_id", os.Getenv("OVH_CLOUD_PROJECT_IMAGE_ID_TEST")), + resource.TestCheckResourceAttr("ovh_cloud_project_instance.instance", "region", os.Getenv("OVH_CLOUD_PROJECT_REGION_TEST")), + resource.TestCheckResourceAttr("ovh_cloud_project_instance.instance", "name", os.Getenv("OVH_CLOUD_PROJECT_INSTANCE_NAME_TEST")), ), }, }, diff --git a/ovh/types_cloud_project_instance.go b/ovh/types_cloud_project_instance.go index c915687a7..fb46c3d77 100644 --- a/ovh/types_cloud_project_instance.go +++ b/ovh/types_cloud_project_instance.go @@ -1,7 +1,6 @@ package ovh import ( - "fmt" "log" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -9,95 +8,57 @@ import ( ) type AutoBackup struct { - Cron *string `json:"cron",omitempty` - Rotation *int `json:"rotation",omitempty` -} - -func (ab *AutoBackup) FromResource(d *schema.ResourceData, parent string) *AutoBackup { - ab.Cron = helpers.GetNilStringPointerFromData(d, fmt.Sprintf("%s.cron", parent)) - ab.Rotation = helpers.GetNilIntPointerFromData(d, fmt.Sprintf("%s.rotation", parent)) - return ab + Cron string `json:"cron"` + Rotation int `json:"rotation"` } type Flavor struct { - FlavorId *string `json:"id",omitempty` -} - -func (fl *Flavor) FromResource(d *schema.ResourceData, parent string) *Flavor { - fl.FlavorId = helpers.GetNilStringPointerFromData(d, fmt.Sprintf("%s.flavor_id", parent)) - return fl + FlavorId string `json:"id"` } type BootFrom struct { - ImageId *string `json:"imageId",omitempty` - VolumeId *string `json:"volumeId",omitempty` -} - -func (bf *BootFrom) FromResource(d *schema.ResourceData, parent string) *BootFrom { - bf.ImageId = helpers.GetNilStringPointerFromData(d, fmt.Sprintf("%s.image_id", parent)) - bf.VolumeId = helpers.GetNilStringPointerFromData(d, fmt.Sprintf("%s.volume_id", parent)) - return bf + ImageId *string `json:"imageId,omitempty"` + VolumeId *string `json:"volumeId,omitempty"` } type Group struct { - GroupId *string `json:"id";omitempty` -} - -func (g *Group) FromResource(d *schema.ResourceData, parent string) *Group { - g.GroupId = helpers.GetNilStringPointerFromData(d, fmt.Sprintf("%s.id", parent)) - return g + GroupId string `json:"id"` } type SshKey struct { - Name *string `json:"name",omitempty` -} - -func (sk *SshKey) FromResource(d *schema.ResourceData, parent string) *SshKey { - sk.Name = helpers.GetNilStringPointerFromData(d, fmt.Sprintf("%s.name", parent)) - return sk + Name string `json:"name"` } type SshKeyCreate struct { - Name *string `json:"name",omitempty` - PublicKey *string `json:"publicKey",omitempty` -} - -func (skc *SshKeyCreate) FromResource(d *schema.ResourceData, parent string) *SshKeyCreate { - skc.Name = helpers.GetNilStringPointerFromData(d, fmt.Sprintf("%s.name", parent)) - skc.PublicKey = helpers.GetNilStringPointerFromData(d, fmt.Sprintf("%s.public_key", parent)) - return skc + Name string `json:"name"` + PublicKey string `json:"publicKey"` } type Network struct { - Public *bool `json:"public",omitempty` -} - -func (n *Network) FromResource(d *schema.ResourceData, parent string) *Network { - n.Public = helpers.GetNilBoolPointerFromData(d, fmt.Sprintf("%s.public", parent)) - return n + Public bool `json:"public"` } type CloudProjectInstanceCreateOpts struct { - AutoBackup *AutoBackup `json:"autobackup"` + AutoBackup *AutoBackup `json:"autobackup,omitempty"` BillingPeriod string `json:"billingPeriod"` - BootFrom *BootFrom `json:"bootFrom"` + BootFrom *BootFrom `json:"bootFrom,omitempty"` Bulk int `json:"bulk"` - Flavor *Flavor `json:"flavor"` - Group *Group `json:"group"` + Flavor *Flavor `json:"flavor,omitempty"` + Group *Group `json:"group,omitempty"` Name string `json:"name"` - SshKey *SshKey `json:"sshKey"` - SshKeyCreate *SshKeyCreate `json:"sshKeyCreate"` - UserData string `json:"userData"` - Network *Network `json:"network"` + SshKey *SshKey `json:"sshKey,omitempty"` + SshKeyCreate *SshKeyCreate `json:"sshKeyCreate,omitempty"` + UserData *string `json:"userData,omitempty"` + Network *Network `json:"network,omitempty"` } type Address struct { - Ip *string `json:"ip",omitempty` - Version *int `json:"version",omitempty` + Ip *string `json:"ip"` + Version *int `json:"version"` } type AttachedVolume struct { - Id string `json:"id",omitempty` + Id string `json:"id"` } type CloudProjectInstanceResponse struct { @@ -113,6 +74,37 @@ type CloudProjectInstanceResponse struct { TaskState string `json:"taskState"` } +func (v CloudProjectInstanceResponse) ToMap() map[string]interface{} { + obj := make(map[string]interface{}) + obj["flavor_id"] = v.FlavorId + obj["flavor_name"] = v.FlavorName + obj["image_id"] = v.ImageId + obj["id"] = v.Id + obj["name"] = v.Name + obj["ssh_key"] = v.SshKey + obj["task_state"] = v.TaskState + + addresses := make([]map[string]interface{}, 0) + for i := range v.Addresses { + address := make(map[string]interface{}) + address["ip"] = v.Addresses[i].Ip + address["version"] = v.Addresses[i].Version + addresses = append(addresses, address) + } + obj["addresses"] = addresses + + attachedVolumes := make([]map[string]interface{}, 0) + for i := range v.AttachedVolumes { + attachedVolume := make(map[string]interface{}) + attachedVolume["id"] = v.AttachedVolumes[i].Id + attachedVolumes = append(attachedVolumes, attachedVolume) + } + + obj["attached_volumes"] = attachedVolumes + + return obj +} + type CloudProjectOperation struct { Id string `json:"id"` Status string `json:"status"` @@ -140,14 +132,10 @@ func (o CloudProjectOperation) ToMap() map[string]interface{} { obj["id"] = o.Id obj["status"] = o.Status subOperations := make([]map[string]interface{}, len(o.SubOperations)) - log.Printf("[DEBUG] titi operation %+v:", o) - log.Printf("[DEBUG] titi operation %+v:", o.SubOperations) for _, subOperation := range o.SubOperations { - log.Printf("[DEBUG] tutu operation %+v:", subOperation) subOperations = append(subOperations, subOperation.ToMap()) } obj["subOperations"] = subOperations - log.Printf("[DEBUG] titi operation %+v:", obj) return obj } @@ -164,77 +152,144 @@ func (a AttachedVolume) ToMap() map[string]interface{} { return obj } -func (cpir CloudProjectInstanceResponse) ToMap() map[string]interface{} { - obj := make(map[string]interface{}) - obj["flavor_id"] = cpir.FlavorId - obj["flavor_name"] = cpir.FlavorName - obj["id"] = cpir.Id - obj["image_id"] = cpir.ImageId - obj["name"] = cpir.Name - obj["ssh_key"] = cpir.SshKey - obj["task_state"] = cpir.TaskState - - addresses := make([]map[string]interface{}, len(cpir.Addresses)) - for _, address := range cpir.Addresses { - addresses = append(addresses, address.ToMap()) +type CloudProjectInstanceResponseList struct { + Id string `json:"id"` + Name string `json:"name"` +} + +func GetFlaorId(i interface{}) *Flavor { + if i == nil { + return nil } - obj["addresses"] = addresses + flavorId := Flavor{} + flavorSet := i.(*schema.Set).List() + for _, flavor := range flavorSet { + mapping := flavor.(map[string]interface{}) + flavorId.FlavorId = mapping["flavor_id"].(string) + } + return &flavorId +} - attachedVolumes := make([]map[string]interface{}, len(cpir.AttachedVolumes)) - for _, attachedVolume := range cpir.AttachedVolumes { - attachedVolumes = append(addresses, attachedVolume.ToMap()) +func GetAutoBackup(i interface{}) *AutoBackup { + if i == nil { + return nil } + autoBackupOut := AutoBackup{} - obj["attached_volumes"] = attachedVolumes - return obj + autoBackupSet := i.(*schema.Set).List() + if len(autoBackupSet) == 0 { + return nil + } + for _, autoBackup := range autoBackupSet { + mapping := autoBackup.(map[string]interface{}) + autoBackupOut.Cron = mapping["cron"].(string) + autoBackupOut.Rotation = mapping["rotation"].(int) + } + return &autoBackupOut } -type CloudProjectInstanceResponseList struct { - Id string `json:"id"` - Name string `json:"name"` -} +func GetBootFrom(i interface{}) *BootFrom { + log.Printf("[DEBUG] BootFrom ------- %v", i) + if i == nil { + return nil + } + bootFromOutput := BootFrom{} -func (cpir *CloudProjectInstanceCreateOpts) FromResource(d *schema.ResourceData) *CloudProjectInstanceCreateOpts { + bootFromSet := i.(*schema.Set).List() + for _, bootFrom := range bootFromSet { + mapping := bootFrom.(map[string]interface{}) + bootFromOutput.ImageId = helpers.GetNilStringPointerFromData(mapping, "image_id") + bootFromOutput.VolumeId = helpers.GetNilStringPointerFromData(mapping, "volume_id") + } - bootFrom := d.Get("boot_from").([]interface{}) - flavor := d.Get("flavor").([]interface{}) - autoBackup := d.Get("auto_backup").([]interface{}) - group := d.Get("group").([]interface{}) - sshKey := d.Get("ssh_key").([]interface{}) - sshKeyCreate := d.Get("ssh_key_create").([]interface{}) - network := d.Get("network").([]interface{}) + return &bootFromOutput +} - cpir.BillingPeriod = d.Get("billing_period").(string) - cpir.Name = d.Get("name").(string) - cpir.Bulk = 1 +func GetGroup(i interface{}) *Group { + log.Printf("[DEBUG] Group ------- %v", i) + if i == nil { + return nil + } + groupOut := Group{} - if len(bootFrom) == 1 { - cpir.BootFrom = (&BootFrom{}).FromResource(d, "boot_from.0") + groupSet := i.(*schema.Set).List() + for _, group := range groupSet { + mapping := group.(map[string]interface{}) + groupOut.GroupId = mapping["id"].(string) } + return &groupOut +} - if len(flavor) == 1 { - cpir.Flavor = (&Flavor{}).FromResource(d, "flavor.0") +func GetSshKey(i interface{}) *SshKey { + log.Printf("[DEBUG] SshKey ------- %v", i) + if i == nil { + return nil } + sshOutput := SshKey{} - if len(autoBackup) == 1 { - cpir.AutoBackup = (&AutoBackup{}).FromResource(d, "auto_backup.0") + sshSet := i.(*schema.Set).List() + for _, ssh := range sshSet { + mapping := ssh.(map[string]interface{}) + sshOutput.Name = mapping["name"].(string) } - if len(group) == 1 { - cpir.Group = (&Group{}).FromResource(d, "group.0") + return &sshOutput +} + +func GetSshKeyCreate(i interface{}) *SshKeyCreate { + log.Printf("[DEBUG] SshKeyCreate ------- %v", i) + if i == nil { + return nil } + sshCreateOutput := SshKeyCreate{} - if len(sshKey) == 1 { - cpir.SshKey = (&SshKey{}).FromResource(d, "ssh_key.0") + sshCreateSet := i.(*schema.Set).List() + if len(sshCreateSet) == 0 { + return nil + } + for _, ssh := range sshCreateSet { + mapping := ssh.(map[string]interface{}) + sshCreateOutput.Name = mapping["name"].(string) + sshCreateOutput.Name = mapping["public_key"].(string) } - if len(sshKeyCreate) == 1 { - cpir.SshKeyCreate = (&SshKeyCreate{}).FromResource(d, "ssh_key_create.0") + return &sshCreateOutput +} + +func GetNetwork(i interface{}) *Network { + log.Printf("[DEBUG] Network ------- %v", i) + if i == nil { + return nil } + networkOutput := Network{} - if len(network) == 1 { - cpir.Network = (&Network{}).FromResource(d, "network.0") + networkSet := i.(*schema.Set).List() + for _, network := range networkSet { + mapping := network.(map[string]interface{}) + networkOutput.Public = mapping["public"].(bool) } + return &networkOutput +} - return cpir +func (cpir *CloudProjectInstanceCreateOpts) FromResource(d *schema.ResourceData) { + cpir.Flavor = GetFlaorId(d.Get("flavor")) + log.Printf("[DEBUG] flavor ------- %v", cpir.Flavor) + cpir.AutoBackup = GetAutoBackup(d.Get("auto_backup")) + log.Printf("[DEBUG] auto ------- %v", cpir.AutoBackup) + cpir.BootFrom = GetBootFrom(d.Get("boot_from")) + log.Printf("[DEBUG] boot_from ------- %v", cpir.BootFrom) + cpir.Group = GetGroup(d.Get("group")) + log.Printf("[DEBUG] group ------- %v", cpir.Group) + cpir.SshKey = GetSshKey(d.Get("ssh_key")) + log.Printf("[DEBUG] ssh_key ------- %v", cpir.SshKey) + cpir.SshKeyCreate = GetSshKeyCreate(d.Get("ssh_key_create")) + log.Printf("[DEBUG] ssh_key_create ------- %v", cpir.SshKeyCreate) + cpir.Network = GetNetwork(d.Get("network")) + log.Printf("[DEBUG] network ------- %v", cpir.Network) + cpir.BillingPeriod = d.Get("billing_period").(string) + log.Printf("[DEBUG] billing_period ------- %v", cpir.BillingPeriod) + cpir.Name = d.Get("name").(string) + log.Printf("[DEBUG] name ------- %v", cpir.Name) + cpir.UserData = helpers.GetNilStringPointerFromData(d, "user_data") + log.Printf("[DEBUG] user_data ------- %v", cpir.UserData) } diff --git a/website/docs/d/cloud_project_instances.markdown b/website/docs/d/cloud_project_instances.markdown index 65ca70773..41076c020 100644 --- a/website/docs/d/cloud_project_instances.markdown +++ b/website/docs/d/cloud_project_instances.markdown @@ -4,11 +4,12 @@ subcategory : "Cloud Project" # ovh_cloud_project_instance **This datasource uses a Beta API** -Use this data source to get the list instance in a region of a public cloud project. + +Use this data source to get the list of instance in a region of a public cloud project. ## Example Usage -To get information of an instance: +To list your instances: ```hcl data "ovh_cloud_project_instances" "instance" { @@ -21,7 +22,7 @@ data "ovh_cloud_project_instances" "instance" { The following arguments are supported: -* `service_name` - (Required, Forces new resource) The id of the public cloud project. If omitted, +* `service_name` - (Required) The id of the public cloud project. If omitted, the `OVH_CLOUD_PROJECT_SERVICE` environment variable is used. * `region` - (Required, Forces new resource) Instance region. diff --git a/website/docs/index.html.markdown b/website/docs/index.html.markdown index 63215925a..3c004701b 100644 --- a/website/docs/index.html.markdown +++ b/website/docs/index.html.markdown @@ -250,7 +250,9 @@ variables must also be set: * `OVH_CLOUD_PROJECT_IMAGE_ID_TEST` - The ID of the image to test -* `OVH_CLOUD_PROJECT_INSTANCE_TEST` - The ID of the instance to test +* `OVH_CLOUD_PROJECT_INSTANCE_ID_TEST` - The ID of the instance to test + +* `OVH_CLOUD_PROJECT_INSTANCE_NAME_TEST` - The ID of the instance to test * `OVH_CLOUD_PROJECT_KUBE_REGION_TEST` - The region of your public cloud kubernetes project. From 340c8b7b5e760adb910296bd787552a1b9b25d59 Mon Sep 17 00:00:00 2001 From: Thibaut Di Prima Date: Wed, 9 Oct 2024 14:59:48 +0000 Subject: [PATCH 3/3] datasource & resource instance --- ovh/data_cloud_project_instance.go | 1 + ovh/data_cloud_project_instance_test.go | 4 ++++ ovh/data_cloud_project_instances_test.go | 18 +++++++++++++++++- ovh/resource_cloud_project_instance.go | 1 + website/docs/index.html.markdown | 2 +- 5 files changed, 24 insertions(+), 2 deletions(-) diff --git a/ovh/data_cloud_project_instance.go b/ovh/data_cloud_project_instance.go index 318ee89a5..a1011e8ad 100644 --- a/ovh/data_cloud_project_instance.go +++ b/ovh/data_cloud_project_instance.go @@ -135,6 +135,7 @@ func dataSourceCloudProjectInstanceRead(d *schema.ResourceData, meta interface{} attachedVolume["id"] = res.AttachedVolumes[i].Id attachedVolumes = append(attachedVolumes, attachedVolume) } + d.Set("addresses", addresses) d.Set("flavor_id", res.FlavorId) d.Set("flavor_name", res.FlavorName) diff --git a/ovh/data_cloud_project_instance_test.go b/ovh/data_cloud_project_instance_test.go index ac0f34ac7..70bcaa47a 100644 --- a/ovh/data_cloud_project_instance_test.go +++ b/ovh/data_cloud_project_instance_test.go @@ -29,8 +29,12 @@ func TestAccDataSourceCloudProjecInstance_basic(t *testing.T) { Config: config, Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrSet("data.ovh_cloud_project_instance.test", "flavor_name"), + resource.TestCheckResourceAttrSet("data.ovh_cloud_project_instance.test", "flavor_id"), resource.TestCheckResourceAttrSet("data.ovh_cloud_project_instance.test", "id"), resource.TestCheckResourceAttrSet("data.ovh_cloud_project_instance.test", "image_id"), + resource.TestCheckResourceAttrSet("data.ovh_cloud_project_instance.test", "name"), + resource.TestCheckResourceAttrSet("data.ovh_cloud_project_instance.test", "ssh_key"), + resource.TestCheckResourceAttrSet("data.ovh_cloud_project_instance.test", "region"), ), }, }, diff --git a/ovh/data_cloud_project_instances_test.go b/ovh/data_cloud_project_instances_test.go index b0835076c..6990fae1a 100644 --- a/ovh/data_cloud_project_instances_test.go +++ b/ovh/data_cloud_project_instances_test.go @@ -38,7 +38,23 @@ func TestAccDataSourceCloudProjecInstances_basic(t *testing.T) { Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrSet( "data.ovh_cloud_project_instances.instances", - "instances.#", + "instances.0.flavor_id", + ), + resource.TestCheckResourceAttrSet( + "data.ovh_cloud_project_instances.instances", + "instances.0.flavor_name", + ), + resource.TestCheckResourceAttrSet( + "data.ovh_cloud_project_instances.instances", + "instances.0.id", + ), + resource.TestCheckResourceAttrSet( + "data.ovh_cloud_project_instances.instances", + "instances.0.image_id", + ), + resource.TestCheckResourceAttrSet( + "data.ovh_cloud_project_instances.instances", + "instances.0.ssh_key", ), ), }, diff --git a/ovh/resource_cloud_project_instance.go b/ovh/resource_cloud_project_instance.go index 7be1d1183..54b72b46d 100644 --- a/ovh/resource_cloud_project_instance.go +++ b/ovh/resource_cloud_project_instance.go @@ -93,6 +93,7 @@ func resourceCloudProjectInstance() *schema.Resource { Type: schema.TypeSet, Required: true, Description: "Flavor information", + ForceNew: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "flavor_id": { diff --git a/website/docs/index.html.markdown b/website/docs/index.html.markdown index 3c004701b..a92aea6db 100644 --- a/website/docs/index.html.markdown +++ b/website/docs/index.html.markdown @@ -252,7 +252,7 @@ variables must also be set: * `OVH_CLOUD_PROJECT_INSTANCE_ID_TEST` - The ID of the instance to test -* `OVH_CLOUD_PROJECT_INSTANCE_NAME_TEST` - The ID of the instance to test +* `OVH_CLOUD_PROJECT_INSTANCE_NAME_TEST` - The Name of the instance to test * `OVH_CLOUD_PROJECT_KUBE_REGION_TEST` - The region of your public cloud kubernetes project.