From 9b54c11c58897a374ebc73be98e967b40197ddde Mon Sep 17 00:00:00 2001 From: Romain Dupont Date: Thu, 25 Apr 2024 09:18:08 +0000 Subject: [PATCH] feat(provider): add loadbalancer log subscription --- ...ct_region_loadbalancer_log_subscription.go | 114 +++++++++++ ...gion_loadbalancer_log_subscription_test.go | 1 + ...t_region_loadbalancer_log_subscriptions.go | 77 ++++++++ ...ion_loadbalancer_log_subscriptions_test.go | 1 + ovh/provider.go | 3 + ...ct_region_loadbalancer_log_subscription.go | 187 ++++++++++++++++++ ...types_cloud_project_region_loadbalancer.go | 54 +++++ 7 files changed, 437 insertions(+) create mode 100644 ovh/data_cloud_project_region_loadbalancer_log_subscription.go create mode 100644 ovh/data_cloud_project_region_loadbalancer_log_subscription_test.go create mode 100644 ovh/data_cloud_project_region_loadbalancer_log_subscriptions.go create mode 100644 ovh/data_cloud_project_region_loadbalancer_log_subscriptions_test.go create mode 100644 ovh/resource_cloud_project_region_loadbalancer_log_subscription.go create mode 100644 ovh/types_cloud_project_region_loadbalancer.go diff --git a/ovh/data_cloud_project_region_loadbalancer_log_subscription.go b/ovh/data_cloud_project_region_loadbalancer_log_subscription.go new file mode 100644 index 00000000..7bd78f19 --- /dev/null +++ b/ovh/data_cloud_project_region_loadbalancer_log_subscription.go @@ -0,0 +1,114 @@ +package ovh + +import ( + "context" + "fmt" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "log" + "net/url" +) + +func dataSourceCloudProjectRegionLoadbalancerLogSubscription() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceCloudProjectRegionLoadbalancerSubscriptionRead, + Schema: map[string]*schema.Schema{ + "service_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + DefaultFunc: schema.EnvDefaultFunc("OVH_CLOUD_PROJECT_SERVICE", nil), + Description: "Service name of the resource representing the id of the cloud project.", + }, + "region_name": { + Type: schema.TypeString, + Description: "Region name of the resource representing the name of the region.", + Required: true, + ForceNew: true, + }, + "loadbalancer_id": { + Type: schema.TypeString, + Description: "ID representing the loadbalancer of the resource", + Required: true, + ForceNew: true, + }, + "subscription_id": { + Type: schema.TypeString, + Description: "ID representing the subscription", + Required: true, + ForceNew: true, + }, + + //computed + "created_at": { + Type: schema.TypeString, + Description: "Creation date of the subscription", + Computed: true, + }, + "kind": { + Type: schema.TypeString, + Description: "Log kind name of this subscription", + Computed: true, + }, + "ldp_service_name": { + Type: schema.TypeString, + Description: "Name of the destination log service", + //Sensitive: true, + Computed: true, + }, + "resource_name": { + Type: schema.TypeString, + Description: "Name of subscribed resource, where the logs come from", + Computed: true, + }, + "resource_type": { + Type: schema.TypeString, + Description: "Type of subscribed resource, where the logs come from", + Computed: true, + }, + "stream_id": { + Type: schema.TypeString, + Description: "Id of the target Log data platform stream", + Computed: true, + }, + "updated_at": { + Type: schema.TypeString, + Description: "Last update date of the subscription", + Computed: true, + }, + }, + } +} + +func dataSourceCloudProjectRegionLoadbalancerSubscriptionRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + config := meta.(*Config) + serviceName := d.Get("service_name").(string) + regionName := d.Get("region_name").(string) + loadbalancerID := d.Get("loadbalancer_id").(string) + subscriptionID := d.Get("subscription_id").(string) + + log.Printf("[DEBUG] Will read public cloud loadbalancer %s log subscription %s for region %s for project: %s", loadbalancerID, subscriptionID, regionName, serviceName) + + response := &CloudProjectRegionLoadbalancerLogSubscriptionResponse{} + endpoint := fmt.Sprintf( + "/cloud/project/%s/region/%s/loadbalancing/loadbalancer/%s/log/subscription/%s", + url.PathEscape(serviceName), + url.PathEscape(regionName), + url.PathEscape(loadbalancerID), + url.PathEscape(subscriptionID), + ) + + if err := config.OVHClient.Get(endpoint, &response); err != nil { + return diag.Errorf("Error calling GET %s:\n\t %q", endpoint, err) + } + + d.SetId(response.StreamID) + d.Set("created_at", response.CreatedAt) + d.Set("kind", response.Kind) + d.Set("resource_name", response.Resource.Name) + d.Set("resource_type", response.Resource.Type) + d.Set("ldp_service_name", response.LDPServiceName) + d.Set("stream_id", response.StreamID) + d.Set("updated_at", response.UpdatedAt) + return nil +} diff --git a/ovh/data_cloud_project_region_loadbalancer_log_subscription_test.go b/ovh/data_cloud_project_region_loadbalancer_log_subscription_test.go new file mode 100644 index 00000000..094fa99a --- /dev/null +++ b/ovh/data_cloud_project_region_loadbalancer_log_subscription_test.go @@ -0,0 +1 @@ +package ovh diff --git a/ovh/data_cloud_project_region_loadbalancer_log_subscriptions.go b/ovh/data_cloud_project_region_loadbalancer_log_subscriptions.go new file mode 100644 index 00000000..9fa6a7be --- /dev/null +++ b/ovh/data_cloud_project_region_loadbalancer_log_subscriptions.go @@ -0,0 +1,77 @@ +package ovh + +import ( + "context" + "fmt" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/ovh/terraform-provider-ovh/ovh/helpers/hashcode" + "log" + "net/url" + "sort" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func dataSourceCloudProjectRegionLoadbalancerLogSubscriptions() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceCloudProjectRegionLoadbalancerSubscriptionsRead, + Schema: map[string]*schema.Schema{ + "service_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + DefaultFunc: schema.EnvDefaultFunc("OVH_CLOUD_PROJECT_SERVICE", nil), + Description: "Service name of the resource representing the id of the cloud project.", + }, + "region_name": { + Type: schema.TypeString, + Description: "Region name of the resource representing the name of the region.", + Required: true, + ForceNew: true, + }, + "loadbalancer_id": { + Type: schema.TypeString, + Description: "ID representing the loadbalancer of the resource", + Required: true, + ForceNew: true, + }, + "kind": { + Type: schema.TypeString, + Optional: true, + Description: "Kind representing the loadbalancer.", + }, + //computed + "subscription_ids": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + } +} + +func dataSourceCloudProjectRegionLoadbalancerSubscriptionsRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + config := meta.(*Config) + serviceName := d.Get("service_name").(string) + regionName := d.Get("region_name").(string) + loadbalancerID := d.Get("loadbalancer_id").(string) + + log.Printf("[DEBUG] Will read public cloud loadbalancer %s log subscriptions for region %s for project: %s", loadbalancerID, regionName, serviceName) + + response := make([]string, 0) + endpoint := fmt.Sprintf( + "/cloud/project/%s/region/%s/loadbalancing/loadbalancer/%s/log/subscription", + url.PathEscape(serviceName), + url.PathEscape(regionName), + url.PathEscape(loadbalancerID), + ) + + if err := config.OVHClient.Get(endpoint, &response); err != nil { + return diag.Errorf("Error calling GET %s:\n\t %q", endpoint, err) + } + sort.Strings(response) + + d.SetId(hashcode.Strings(response)) + d.Set("subscription_ids", response) + return nil +} diff --git a/ovh/data_cloud_project_region_loadbalancer_log_subscriptions_test.go b/ovh/data_cloud_project_region_loadbalancer_log_subscriptions_test.go new file mode 100644 index 00000000..094fa99a --- /dev/null +++ b/ovh/data_cloud_project_region_loadbalancer_log_subscriptions_test.go @@ -0,0 +1 @@ +package ovh diff --git a/ovh/provider.go b/ovh/provider.go index b5f90824..5926185c 100644 --- a/ovh/provider.go +++ b/ovh/provider.go @@ -84,6 +84,8 @@ func Provider() *schema.Provider { "ovh_cloud_project_kube_oidc": dataSourceCloudProjectKubeOIDC(), "ovh_cloud_project_kube_nodepool": dataSourceCloudProjectKubeNodepool(), "ovh_cloud_project_kube_nodes": dataSourceCloudProjectKubeNodes(), + "ovh_cloud_project_region_loadbalancer_log_subscriptions": dataSourceCloudProjectRegionLoadbalancerLogSubscriptions(), + "ovh_cloud_project_region_loadbalancer_log_subscription": dataSourceCloudProjectRegionLoadbalancerLogSubscription(), "ovh_cloud_project_region": dataSourceCloudProjectRegion(), "ovh_cloud_project_regions": dataSourceCloudProjectRegions(), "ovh_cloud_project_user": datasourceCloudProjectUser(), @@ -176,6 +178,7 @@ func Provider() *schema.Provider { "ovh_cloud_project_network_private": resourceCloudProjectNetworkPrivate(), "ovh_cloud_project_network_private_subnet": resourceCloudProjectNetworkPrivateSubnet(), "ovh_cloud_project_region_storage_presign": resourceCloudProjectRegionStoragePresign(), + "ovh_cloud_project_region_loadbalancer_log_subscription": resourceCloudProjectRegionLoadbalancerLogSubscription(), "ovh_cloud_project_user": resourceCloudProjectUser(), "ovh_cloud_project_user_s3_credential": resourceCloudProjectUserS3Credential(), "ovh_cloud_project_user_s3_policy": resourceCloudProjectUserS3Policy(), diff --git a/ovh/resource_cloud_project_region_loadbalancer_log_subscription.go b/ovh/resource_cloud_project_region_loadbalancer_log_subscription.go new file mode 100644 index 00000000..7b7c2aa5 --- /dev/null +++ b/ovh/resource_cloud_project_region_loadbalancer_log_subscription.go @@ -0,0 +1,187 @@ +package ovh + +import ( + "context" + "fmt" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/ovh/terraform-provider-ovh/ovh/helpers" + "log" + "net/url" +) + +func resourceCloudProjectRegionLoadbalancerLogSubscription() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceCloudProjectRegionLoadbalancerSubscriptionsCreate, + ReadContext: resourceCloudProjectRegionLoadbalancerSubscriptionsRead, + DeleteContext: resourceCloudProjectRegionLoadbalancerSubscriptionsDelete, + + Schema: map[string]*schema.Schema{ + "service_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + DefaultFunc: schema.EnvDefaultFunc("OVH_CLOUD_PROJECT_SERVICE", nil), + Description: "Service name of the resource representing the id of the cloud project.", + }, + "region_name": { + Type: schema.TypeString, + Description: "Region name of the resource representing the name of the region.", + Required: true, + ForceNew: true, + }, + "loadbalancer_id": { + Type: schema.TypeString, + Description: "ID representing the loadbalancer of the resource", + Required: true, + ForceNew: true, + }, + "stream_id": { + Type: schema.TypeString, + Description: "ID representing the stream of the resource", + Required: true, + ForceNew: true, + }, + "kind": { + Type: schema.TypeString, + Description: "Log kind name of this subscription", + Required: true, + ForceNew: true, + }, + + //computed + "created_at": { + Type: schema.TypeString, + Description: "Creation date of the subscription", + Computed: true, + }, + "ldp_service_name": { + Type: schema.TypeString, + Description: "Name of the destination log service", + Sensitive: true, + Computed: true, + }, + "operation_id": { + Type: schema.TypeString, + Description: "Identifier of the operation", + Computed: true, + }, + "resource_name": { + Type: schema.TypeString, + Description: "Name of subscribed resource, where the logs come from", + Computed: true, + }, + "resource_type": { + Type: schema.TypeString, + Description: "Type of subscribed resource, where the logs come from", + Computed: true, + }, + "updated_at": { + Type: schema.TypeString, + Description: "Last update date of the subscription", + Computed: true, + }, + }, + } +} + +func resourceCloudProjectRegionLoadbalancerSubscriptionsCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + config := meta.(*Config) + serviceName := d.Get("service_name").(string) + regionName := d.Get("region_name").(string) + loadbalancerID := d.Get("loadbalancer_id").(string) + + endpoint := fmt.Sprintf("/cloud/project/%s/region/%s/loadbalancing/loadbalancer/%s/log/subscription", + url.PathEscape(serviceName), + url.PathEscape(regionName), + url.PathEscape(loadbalancerID), + ) + params := (&CloudProjectRegionLoadbalancerLogSubscriptionResourceCreateOpts{}).fromResource(d) + res := &CloudProjectRegionLoadbalancerLogSubscriptionResponse{} + + log.Printf("[DEBUG] Will create Log subscrition : %+v for loadbalancer %s on region %s from project %s", params, loadbalancerID, regionName, serviceName) + err := config.OVHClient.Post(endpoint, params, res) + if err != nil { + diag.Errorf("calling Post %s with params %+v:\n\t %q", endpoint, params, err) + } + + log.Printf("[DEBUG] Waiting for Log subscription operation %s to be READY", res.OperationID) + op, err := waitForDbaasLogsOperation(ctx, config.OVHClient, res.LDPServiceName, res.OperationID) + if err != nil { + return diag.Errorf("timeout while waiting log subscrition operation %s to be READY: %q", res.OperationID, err) + } + log.Printf("[DEBUG] Log subscrition operation %s is READY", res.OperationID) + + d.SetId(*op.SubscriptionID) + d.Set("operation_id", res.OperationID) + + return resourceCloudProjectRegionLoadbalancerSubscriptionsRead(ctx, d, meta) +} + +func resourceCloudProjectRegionLoadbalancerSubscriptionsRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + config := meta.(*Config) + serviceName := d.Get("service_name").(string) + regionName := d.Get("region_name").(string) + loadbalancerID := d.Get("loadbalancer_id").(string) + id := d.Id() + + endpoint := fmt.Sprintf("/cloud/project/%s/region/%s/loadbalancing/loadbalancer/%s/log/subscription/%s", + url.PathEscape(serviceName), + url.PathEscape(regionName), + url.PathEscape(loadbalancerID), + url.PathEscape(id), + ) + res := &CloudProjectRegionLoadbalancerLogSubscriptionResponse{} + + log.Printf("[DEBUG] Will read log subscrition %s from loadbalancer %s on region %s from project %s", id, loadbalancerID, regionName, serviceName) + if err := config.OVHClient.GetWithContext(ctx, endpoint, res); err != nil { + return diag.FromErr(helpers.CheckDeleted(d, err, endpoint)) + } + + for k, v := range res.ToMap() { + if k == "operation_id" { + continue + } else if k != "id" { + d.Set(k, v) + } else { + d.SetId(fmt.Sprint(v)) + } + } + + log.Printf("[DEBUG] Read log subscrition %+v", res) + return nil +} + +func resourceCloudProjectRegionLoadbalancerSubscriptionsDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + config := meta.(*Config) + serviceName := d.Get("service_name").(string) + regionName := d.Get("region_name").(string) + loadbalancerID := d.Get("loadbalancer_id").(string) + id := d.Id() + + endpoint := fmt.Sprintf("/cloud/project/%s/region/%s/loadbalancing/loadbalancer/%s/log/subscription/%s", + url.PathEscape(serviceName), + url.PathEscape(regionName), + url.PathEscape(loadbalancerID), + url.PathEscape(id), + ) + + res := &CloudProjectRegionLoadbalancerLogSubscriptionResponse{} + + log.Printf("[DEBUG] Will delete Log subscrition for loadbalancer %s on region %s from project %s", loadbalancerID, regionName, serviceName) + err := config.OVHClient.DeleteWithContext(ctx, endpoint, res) + if err != nil { + diag.Errorf("calling DELETE %s:\n\t %q", endpoint, err) + } + + log.Printf("[DEBUG] Waiting for user %s to be DELETED", id) + _, err = waitForDbaasLogsOperation(ctx, config.OVHClient, res.LDPServiceName, res.OperationID) + if err != nil { + return diag.Errorf("timeout while waiting log subscription %s to be DELETED: %q", id, err) + } + log.Printf("[DEBUG] Log subsription %s is DELETED", id) + + d.SetId("") + + return nil +} diff --git a/ovh/types_cloud_project_region_loadbalancer.go b/ovh/types_cloud_project_region_loadbalancer.go new file mode 100644 index 00000000..092df8cc --- /dev/null +++ b/ovh/types_cloud_project_region_loadbalancer.go @@ -0,0 +1,54 @@ +package ovh + +import "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + +type CloudProjectRegionLoadbalancerLogSubscriptionResponse struct { + CreatedAt string `json:"createdAt"` + Kind string `json:"kind"` + OperationID string `json:"operationId"` + Resource CloudProjectRegionLoadbalancerLogSubscriptionResourceResponse `json:"resource"` + LDPServiceName string `json:"serviceName"` + StreamID string `json:"streamId"` + SubscriptionID string `json:"subscriptionId"` + UpdatedAt string `json:"updatedAt"` +} + +func (v CloudProjectRegionLoadbalancerLogSubscriptionResponse) ToMap() map[string]interface{} { + obj := make(map[string]interface{}) + + obj["createdAt"] = v.CreatedAt + obj["kind"] = v.Kind + obj["resource"] = v.Resource + obj["serviceName"] = v.LDPServiceName + obj["streamId"] = v.StreamID + obj["subscriptionId"] = v.SubscriptionID + obj["updatedAt"] = v.UpdatedAt + obj["operation_id"] = v.OperationID + + return obj +} + +type CloudProjectRegionLoadbalancerLogSubscriptionResourceResponse struct { + Name string `json:"name"` + Type string `json:"type"` +} + +func (v CloudProjectRegionLoadbalancerLogSubscriptionResourceResponse) ToMap() map[string]interface{} { + obj := make(map[string]interface{}) + + obj["name"] = v.Name + obj["type"] = v.Type + + return obj +} + +type CloudProjectRegionLoadbalancerLogSubscriptionResourceCreateOpts struct { + Kind string `json:"kind"` + StreamId string `json:"streamId"` +} + +func (opts *CloudProjectRegionLoadbalancerLogSubscriptionResourceCreateOpts) fromResource(d *schema.ResourceData) *CloudProjectRegionLoadbalancerLogSubscriptionResourceCreateOpts { + opts.StreamId = d.Get("stream_id").(string) + opts.Kind = d.Get("kind").(string) + return opts +}