diff --git a/ovh/provider.go b/ovh/provider.go index bdc52e358..868d2f50b 100644 --- a/ovh/provider.go +++ b/ovh/provider.go @@ -150,6 +150,7 @@ func Provider() *schema.Provider { "ovh_cloud_project_database_redis_user": resourceCloudProjectDatabaseRedisUser(), "ovh_cloud_project_database_user": resourceCloudProjectDatabaseUser(), "ovh_cloud_project_failover_ip_attach": resourceCloudProjectFailoverIpAttach(), + "ovh_cloud_project_instance_amb": resourceCloudProjectInstanceActiveMonthlyBilling(), "ovh_cloud_project_kube": resourceCloudProjectKube(), "ovh_cloud_project_kube_nodepool": resourceCloudProjectKubeNodePool(), "ovh_cloud_project_kube_oidc": resourceCloudProjectKubeOIDC(), diff --git a/ovh/provider_test.go b/ovh/provider_test.go index 8776233e5..948d5103b 100644 --- a/ovh/provider_test.go +++ b/ovh/provider_test.go @@ -351,3 +351,8 @@ func testAccPreCheckWorkflowBackup(t *testing.T) { checkEnvOrSkip(t, WORKFLOW_BACKUP_TEST_INSTANCE_ID_ENV_VAR) checkEnvOrSkip(t, WORKFLOW_BACKUP_TEST_REGION_ENV_VAR) } + +func testAccPreCheckActiveMonthlyBilling(t *testing.T) { + testAccPreCheckCloud(t) + checkEnvOrSkip(t, ACTIVE_MONTHLY_BILLING_INSTANCE_ID_TEST) +} diff --git a/ovh/resource_cloud_project_instance_amb.go b/ovh/resource_cloud_project_instance_amb.go new file mode 100644 index 000000000..25748317c --- /dev/null +++ b/ovh/resource_cloud_project_instance_amb.go @@ -0,0 +1,153 @@ +package ovh + +import ( + "fmt" + "log" + "net/url" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/ovh/go-ovh/ovh" + "github.com/ovh/terraform-provider-ovh/ovh/helpers" +) + +func resourceCloudProjectInstanceActiveMonthlyBilling() *schema.Resource { + return &schema.Resource{ + Create: resourceCloudProjectInstanceActiveMonthlyBillingCreate, + Update: resourceCloudProjectInstanceActiveMonthlyBillingUpdate, + Read: resourceCloudProjectInstanceActiveMonthlyBillingRead, + Delete: resourceCloudProjectInstanceActiveMonthlyBillingDelete, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(45 * time.Minute), + }, + + Schema: map[string]*schema.Schema{ + "service_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "The internal name of your dedicated server", + }, + "instance_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "Public Cloud instance ID", + }, + "wait_activation": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: "Wait for monthly billing activation", + }, + + // Computed + "monthly_billing_since": { + Type: schema.TypeString, + Computed: true, + Description: "Monthly billing activated since", + }, + + "monthly_billing_status": { + Type: schema.TypeString, + Computed: true, + Description: "Monthly billing status", + }, + }, + } +} + +func waitForCloudProjectInstanceActiveMonthlyBillingDone(client *ovh.Client, serviceName string, instanceId string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + r := &CloudProjectInstanceActiveMonthlyBillingResponse{} + endpoint := fmt.Sprintf("/cloud/project/%s/instance/%s", url.PathEscape(serviceName), url.PathEscape(instanceId)) + if err := client.Get(endpoint, r); err != nil { + return r, "", err + } + + log.Printf("[DEBUG] Pending active monthly billing: %s", r) + return r, r.MonthlyBilling.Status, nil + } +} + +func resourceCloudProjectInstanceActiveMonthlyBillingCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + serviceName := d.Get("service_name").(string) + instanceId := d.Get("instance_id").(string) + waitActivation := d.Get("wait_activation").(bool) + + endpoint := fmt.Sprintf( + "/cloud/project/%s/instance/%s/activeMonthlyBilling", url.PathEscape(serviceName), url.PathEscape(instanceId), + ) + + params := (&CloudProjectInstanceActiveMonthlyBillingCreateOpts{}).FromResource(d) + + r := &CloudProjectInstanceActiveMonthlyBillingResponse{} + + log.Printf("[DEBUG] Will install active monthly billing: %s", params) + + if err := config.OVHClient.Post(endpoint, params, r); err != nil { + return fmt.Errorf("Error calling POST %s:\n\t %q", endpoint, err) + } + + if waitActivation { + log.Printf("[DEBUG] Waiting for active monthly billing %s:", r) + + stateConf := &resource.StateChangeConf{ + Pending: []string{"activationPending"}, + Target: []string{"ok"}, + Refresh: waitForCloudProjectInstanceActiveMonthlyBillingDone(config.OVHClient, serviceName, instanceId), + Timeout: 45 * time.Minute, + Delay: 10 * time.Second, + MinTimeout: 3 * time.Second, + } + + if _, err := stateConf.WaitForState(); err != nil { + return fmt.Errorf("waiting for active monthly billing (%s): %s", params, err) + } + } + + log.Printf("[DEBUG] Created active monthly billing %s", r) + + return CloudProjectInstanceActiveMonthlyBillingRead(d, meta) +} + +func CloudProjectInstanceActiveMonthlyBillingRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + serviceName := d.Get("service_name").(string) + instanceId := d.Get("instance_id").(string) + + r := &CloudProjectInstanceActiveMonthlyBillingResponse{} + + log.Printf("[DEBUG] Will read active monthly billing: %s", serviceName) + + endpoint := fmt.Sprintf("/cloud/project/%s/instance/%s", url.PathEscape(serviceName), url.PathEscape(instanceId)) + + if err := config.OVHClient.Get(endpoint, r); err != nil { + return helpers.CheckDeleted(d, err, endpoint) + } + + if r.MonthlyBilling != nil { + d.Set("monthly_billing_since", r.MonthlyBilling.Since) + d.Set("monthly_billing_status", r.MonthlyBilling.Status) + } + + log.Printf("[DEBUG] Read active monthly billing %s", r) + return nil +} + +func resourceCloudProjectInstanceActiveMonthlyBillingUpdate(d *schema.ResourceData, meta interface{}) error { + // nothing to do on update + return resourceCloudProjectInstanceActiveMonthlyBillingRead(d, meta) +} + +func resourceCloudProjectInstanceActiveMonthlyBillingRead(d *schema.ResourceData, meta interface{}) error { + return CloudProjectInstanceActiveMonthlyBillingRead(d, meta) +} + +func resourceCloudProjectInstanceActiveMonthlyBillingDelete(d *schema.ResourceData, meta interface{}) error { + // Nothing to do on DELETE + return nil +} diff --git a/ovh/resource_cloud_project_instance_amb_test.go b/ovh/resource_cloud_project_instance_amb_test.go new file mode 100644 index 000000000..5ec26a243 --- /dev/null +++ b/ovh/resource_cloud_project_instance_amb_test.go @@ -0,0 +1,48 @@ +package ovh + +import ( + "fmt" + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +const ACTIVE_MONTHLY_BILLING_INSTANCE_ID_TEST = "OVH_CLOUD_PROJECT_ACTIVE_MONTHLY_BILLING_INSTANCE_ID_TEST" + +func TestAccCloudProjectInstanceActiveMonthlyBilling_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheckActiveMonthlyBilling(t) + }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCloudProjectInstanceActiveMonthlyBillingConfig(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "ovh_cloud_project_instance_amb.monthly", "monthly_billing_status", "ok"), + ), + }, + }, + }) +} + +func testAccCloudProjectInstanceActiveMonthlyBillingConfig() string { + serviceName := os.Getenv("OVH_CLOUD_PROJECT_SERVICE_TEST") + instanceId := os.Getenv(ACTIVE_MONTHLY_BILLING_INSTANCE_ID_TEST) + + return fmt.Sprintf( + testAccCloudProjectInstanceActiveMonthlyBillingConfig_Basic, + serviceName, + instanceId, + ) +} + +const testAccCloudProjectInstanceActiveMonthlyBillingConfig_Basic = ` +resource "ovh_cloud_project_instance_amb" "monthly" { + service_name = "%s" + instance_id = "%s" + wait_activation = true +} +` diff --git a/ovh/types_cloud_project_instance_amb.go b/ovh/types_cloud_project_instance_amb.go new file mode 100644 index 000000000..7777cae0a --- /dev/null +++ b/ovh/types_cloud_project_instance_amb.go @@ -0,0 +1,36 @@ +package ovh + +import ( + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +type CloudProjectInstanceActiveMonthlyBillingCreateOpts struct { +} + +func (p *CloudProjectInstanceActiveMonthlyBillingCreateOpts) String() string { + return fmt.Sprintf("") +} + +func (p *CloudProjectInstanceActiveMonthlyBillingCreateOpts) FromResource(d *schema.ResourceData) *CloudProjectInstanceActiveMonthlyBillingCreateOpts { + params := &CloudProjectInstanceActiveMonthlyBillingCreateOpts{} + return params +} + +type CloudProjectInstanceActiveMonthlyBillingResponseMonthlyBilling struct { + Since string `json:"since"` + Status string `json:"status"` +} + +func (p *CloudProjectInstanceActiveMonthlyBillingResponseMonthlyBilling) String() string { + return fmt.Sprintf("since: %s, status: %s", p.Since, p.Status) +} + +type CloudProjectInstanceActiveMonthlyBillingResponse struct { + MonthlyBilling *CloudProjectInstanceActiveMonthlyBillingResponseMonthlyBilling `json:"monthlyBilling"` +} + +func (p *CloudProjectInstanceActiveMonthlyBillingResponse) String() string { + return fmt.Sprintf("monthlyBilling: %s", p.MonthlyBilling) +} diff --git a/website/docs/index.html.markdown b/website/docs/index.html.markdown index 688a67cd8..4f1d7643a 100644 --- a/website/docs/index.html.markdown +++ b/website/docs/index.html.markdown @@ -191,6 +191,7 @@ variables must also be set: * `OVH_CLOUD_PROJECT_WORKFLOW_BACKUP_REGION_TEST` - The openstack region in which the workflow will be defined * `OVH_CLOUD_PROJECT_WORKFLOW_BACKUP_INSTANCE_ID_TEST` - The openstack id of the instance to backup +* `OVH_CLOUD_PROJECT_ACTIVE_MONTHLY_BILLING_INSTANCE_ID_TEST` - The openstack id of the instance to activate monthly billing ### Used by OVHcloud internal account only: