Skip to content

Commit 76bdaec

Browse files
authored
Merge pull request #859 from 131/role_n_permission
feat: (dbaas/logs) role & permission
2 parents 22d8bab + 18dce8a commit 76bdaec

7 files changed

+495
-0
lines changed

ovh/provider.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,8 @@ func Provider() *schema.Provider {
229229
"ovh_cloud_project_user_s3_policy": resourceCloudProjectUserS3Policy(),
230230
"ovh_cloud_project_workflow_backup": resourceCloudProjectWorkflowBackup(),
231231
"ovh_dbaas_logs_cluster": resourceDbaasLogsCluster(),
232+
"ovh_dbaas_logs_role": resourceDbaasLogsRole(),
233+
"ovh_dbaas_logs_role_permission_stream": resourceDbaasLogsRolePermissionStream(),
232234
"ovh_dbaas_logs_input": resourceDbaasLogsInput(),
233235
"ovh_dbaas_logs_output_graylog_stream": resourceDbaasLogsOutputGraylogStream(),
234236
"ovh_dbaas_logs_output_opensearch_alias": resourceDbaasLogsOutputOpensearchAlias(),

ovh/resource_dbaas_logs_role.go

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
package ovh
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"log"
7+
"net/url"
8+
"strings"
9+
10+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
11+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
12+
"github.com/ovh/terraform-provider-ovh/ovh/helpers"
13+
)
14+
15+
func resourceDbaasLogsRole() *schema.Resource {
16+
return &schema.Resource{
17+
CreateContext: resourceDbaasLogsRoleCreate,
18+
ReadContext: resourceDbaasLogsRoleRead,
19+
UpdateContext: resourceDbaasLogsRoleUpdate,
20+
DeleteContext: resourceDbaasLogsRoleDelete,
21+
Importer: &schema.ResourceImporter{
22+
State: resourceDbaasLogsRoleImportState,
23+
},
24+
Schema: map[string]*schema.Schema{
25+
"service_name": {
26+
Type: schema.TypeString,
27+
Description: "The service name",
28+
Required: true,
29+
},
30+
"name": {
31+
Type: schema.TypeString,
32+
Description: "The role name",
33+
Required: true,
34+
},
35+
"description": {
36+
Type: schema.TypeString,
37+
Description: "The role description",
38+
Required: true,
39+
},
40+
// Computed fields from the API response
41+
"created_at": {
42+
Type: schema.TypeString,
43+
Description: "Role creation date",
44+
Computed: true,
45+
},
46+
"nb_member": {
47+
Type: schema.TypeInt,
48+
Description: "Number of members in the role",
49+
Computed: true,
50+
},
51+
"nb_permission": {
52+
Type: schema.TypeInt,
53+
Description: "Number of permissions assigned to the role",
54+
Computed: true,
55+
},
56+
"role_id": {
57+
Type: schema.TypeString,
58+
Description: "Role identifier",
59+
Computed: true,
60+
},
61+
"updated_at": {
62+
Type: schema.TypeString,
63+
Description: "Role last update date",
64+
Computed: true,
65+
},
66+
},
67+
}
68+
}
69+
70+
// resourceDbaasLogsRoleCreate creates a new role using an asynchronous operation.
71+
func resourceDbaasLogsRoleCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
72+
config := meta.(*Config)
73+
serviceName := d.Get("service_name").(string)
74+
75+
log.Printf("[DEBUG] Creating dbaas logs role for service: %s", serviceName)
76+
77+
opts := (&DbaasLogsRoleCreateOpts{}).FromResource(d)
78+
opRes := &DbaasLogsOperation{}
79+
endpoint := fmt.Sprintf("/dbaas/logs/%s/role", url.PathEscape(serviceName))
80+
if err := config.OVHClient.Post(endpoint, opts, opRes); err != nil {
81+
return diag.Errorf("Error calling POST %s:\n\t%q", endpoint, err)
82+
}
83+
84+
// Wait for asynchronous operation to complete.
85+
op, err := waitForDbaasLogsOperation(ctx, config.OVHClient, serviceName, opRes.OperationId)
86+
if err != nil {
87+
return diag.FromErr(err)
88+
}
89+
90+
if op.RoleId == nil {
91+
return diag.Errorf("Role Id is nil. This should not happen: operation %s/%s", serviceName, opRes.OperationId)
92+
}
93+
d.SetId(*op.RoleId)
94+
95+
return resourceDbaasLogsRoleRead(ctx, d, meta)
96+
}
97+
98+
// resourceDbaasLogsRoleRead reads the role resource.
99+
func resourceDbaasLogsRoleRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
100+
config := meta.(*Config)
101+
serviceName := d.Get("service_name").(string)
102+
roleId := d.Id()
103+
104+
log.Printf("[DEBUG] Reading dbaas logs role for service: %s, role: %s", serviceName, roleId)
105+
106+
role := &DbaasLogsRole{}
107+
endpoint := fmt.Sprintf("/dbaas/logs/%s/role/%s", url.PathEscape(serviceName), url.PathEscape(roleId))
108+
if err := config.OVHClient.Get(endpoint, role); err != nil {
109+
return diag.FromErr(helpers.CheckDeleted(d, err, endpoint))
110+
}
111+
112+
for k, v := range role.ToMap() {
113+
if k != "role_id" {
114+
d.Set(k, v)
115+
} else {
116+
d.SetId(fmt.Sprint(v))
117+
}
118+
}
119+
120+
return nil
121+
}
122+
123+
// resourceDbaasLogsRoleUpdate updates the role using an asynchronous operation.
124+
func resourceDbaasLogsRoleUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
125+
config := meta.(*Config)
126+
serviceName := d.Get("service_name").(string)
127+
roleId := d.Id()
128+
129+
log.Printf("[DEBUG] Updating dbaas logs role for service: %s, role: %s", serviceName, roleId)
130+
131+
opts := (&DbaasLogsRoleCreateOpts{}).FromResource(d)
132+
opRes := &DbaasLogsOperation{}
133+
endpoint := fmt.Sprintf("/dbaas/logs/%s/role/%s", url.PathEscape(serviceName), url.PathEscape(roleId))
134+
if err := config.OVHClient.Put(endpoint, opts, opRes); err != nil {
135+
return diag.Errorf("Error calling PUT %s:\n\t%q", endpoint, err)
136+
}
137+
138+
// Wait for asynchronous operation to complete.
139+
if _, err := waitForDbaasLogsOperation(ctx, config.OVHClient, serviceName, opRes.OperationId); err != nil {
140+
return diag.FromErr(err)
141+
}
142+
143+
return resourceDbaasLogsRoleRead(ctx, d, meta)
144+
}
145+
146+
// resourceDbaasLogsRoleDelete deletes the role using an asynchronous operation.
147+
func resourceDbaasLogsRoleDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
148+
config := meta.(*Config)
149+
serviceName := d.Get("service_name").(string)
150+
roleId := d.Id()
151+
152+
log.Printf("[DEBUG] Deleting dbaas logs role for service: %s, role: %s", serviceName, roleId)
153+
opRes := &DbaasLogsOperation{}
154+
endpoint := fmt.Sprintf("/dbaas/logs/%s/role/%s", url.PathEscape(serviceName), url.PathEscape(roleId))
155+
if err := config.OVHClient.Delete(endpoint, opRes); err != nil {
156+
return diag.FromErr(helpers.CheckDeleted(d, err, endpoint))
157+
}
158+
159+
// Wait for asynchronous operation to complete.
160+
if _, err := waitForDbaasLogsOperation(ctx, config.OVHClient, serviceName, opRes.OperationId); err != nil {
161+
return diag.FromErr(err)
162+
}
163+
164+
d.SetId("")
165+
return nil
166+
}
167+
168+
// resourceDbaasLogsRoleImportState allows importing a role using service_name/roleId format.
169+
func resourceDbaasLogsRoleImportState(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
170+
givenID := d.Id()
171+
splitID := strings.SplitN(givenID, "/", 2)
172+
if len(splitID) != 2 {
173+
return nil, fmt.Errorf("Import ID must be in service_name/roleId format")
174+
}
175+
serviceName := splitID[0]
176+
roleId := splitID[1]
177+
d.SetId(roleId)
178+
d.Set("service_name", serviceName)
179+
180+
return []*schema.ResourceData{d}, nil
181+
}
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
package ovh
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"log"
7+
"net/url"
8+
"strings"
9+
10+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
11+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
12+
"github.com/ovh/terraform-provider-ovh/ovh/helpers"
13+
)
14+
15+
func resourceDbaasLogsRolePermissionStream() *schema.Resource {
16+
return &schema.Resource{
17+
CreateContext: resourceDbaasLogsRolePermissionStreamCreate,
18+
ReadContext: resourceDbaasLogsRolePermissionStreamRead,
19+
DeleteContext: resourceDbaasLogsRolePermissionStreamDelete,
20+
Importer: &schema.ResourceImporter{
21+
State: resourceDbaasLogsRolePermissionStreamImportState,
22+
},
23+
Schema: map[string]*schema.Schema{
24+
"service_name": {
25+
Type: schema.TypeString,
26+
Description: "Service name",
27+
Required: true,
28+
ForceNew: true,
29+
},
30+
"role_id": {
31+
Type: schema.TypeString,
32+
Description: "Role ID to which the permission will be appended",
33+
Required: true,
34+
ForceNew: true,
35+
},
36+
"stream_id": {
37+
Type: schema.TypeString,
38+
Description: "Graylog stream ID to be associated as a permission",
39+
Required: true,
40+
ForceNew: true,
41+
},
42+
// Computed fields from the API GET response.
43+
"permission_id": {
44+
Type: schema.TypeString,
45+
Description: "Permission ID",
46+
Computed: true,
47+
},
48+
"permission_type": {
49+
Type: schema.TypeString,
50+
Description: "Permission type (e.g., READ_ONLY)",
51+
Computed: true,
52+
},
53+
},
54+
}
55+
}
56+
57+
// resourceDbaasLogsRolePermissionStreamCreate adds a stream permission to a role.
58+
func resourceDbaasLogsRolePermissionStreamCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
59+
config := meta.(*Config)
60+
serviceName := d.Get("service_name").(string)
61+
roleId := d.Get("role_id").(string)
62+
streamId := d.Get("stream_id").(string)
63+
64+
log.Printf("[DEBUG] Adding Graylog stream permission: service=%s, role=%s", serviceName, roleId)
65+
66+
opts := (&DbaasLogsRolePermissionStreamOpts{}).FromResource(d)
67+
res := &DbaasLogsOperation{}
68+
endpoint := fmt.Sprintf("/dbaas/logs/%s/role/%s/permission/stream", url.PathEscape(serviceName), url.PathEscape(roleId))
69+
if err := config.OVHClient.Post(endpoint, opts, res); err != nil {
70+
return diag.Errorf("Error calling POST %s:\n\t%q", endpoint, err)
71+
}
72+
73+
// Wait for the asynchronous operation to complete.
74+
if _, err := waitForDbaasLogsOperation(ctx, config.OVHClient, serviceName, res.OperationId); err != nil {
75+
return diag.FromErr(err)
76+
}
77+
78+
// Retrieve the full list of permission IDs for the role.
79+
listEndpoint := fmt.Sprintf("/dbaas/logs/%s/role/%s/permission", url.PathEscape(serviceName), url.PathEscape(roleId))
80+
var permissionIDs []string
81+
if err := config.OVHClient.Get(listEndpoint, &permissionIDs); err != nil {
82+
return diag.Errorf("Error retrieving permissions list (%s):\n\t%q", listEndpoint, err)
83+
}
84+
85+
// Iterate over each permission ID to retrieve full details and filter by streamId.
86+
var foundPermission *DbaasLogsRolePermissionStream
87+
for _, pid := range permissionIDs {
88+
permEndpoint := fmt.Sprintf("/dbaas/logs/%s/role/%s/permission/%s", url.PathEscape(serviceName), url.PathEscape(roleId), url.PathEscape(pid))
89+
var permission DbaasLogsRolePermissionStream
90+
if err := config.OVHClient.Get(permEndpoint, &permission); err != nil {
91+
// Optionally, log error and continue with next permission.
92+
log.Printf("[WARN] Failed to get permission details for ID %s: %v", pid, err)
93+
continue
94+
}
95+
if permission.StreamId == streamId {
96+
foundPermission = &permission
97+
break
98+
}
99+
}
100+
101+
if foundPermission == nil {
102+
return diag.Errorf("No permission found for streamId %s", streamId)
103+
}
104+
105+
d.SetId(foundPermission.PermissionId)
106+
return resourceDbaasLogsRolePermissionStreamRead(ctx, d, meta)
107+
}
108+
109+
// resourceDbaasLogsRolePermissionStreamRead reads the details of a permission.
110+
func resourceDbaasLogsRolePermissionStreamRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
111+
config := meta.(*Config)
112+
serviceName := d.Get("service_name").(string)
113+
roleId := d.Get("role_id").(string)
114+
permissionId := d.Id()
115+
116+
log.Printf("[DEBUG] Reading permission: service=%s, role=%s, permission=%s", serviceName, roleId, permissionId)
117+
118+
permission := &DbaasLogsRolePermissionStream{}
119+
endpoint := fmt.Sprintf("/dbaas/logs/%s/role/%s/permission/%s", url.PathEscape(serviceName), url.PathEscape(roleId), url.PathEscape(permissionId))
120+
if err := config.OVHClient.Get(endpoint, permission); err != nil {
121+
return diag.FromErr(helpers.CheckDeleted(d, err, endpoint))
122+
}
123+
124+
d.Set("permission_id", permission.PermissionId)
125+
d.Set("permission_type", permission.PermissionType)
126+
d.Set("stream_id", permission.StreamId)
127+
128+
return nil
129+
}
130+
131+
// resourceDbaasLogsRolePermissionStreamDelete deletes the specified permission.
132+
func resourceDbaasLogsRolePermissionStreamDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
133+
config := meta.(*Config)
134+
serviceName := d.Get("service_name").(string)
135+
roleId := d.Get("role_id").(string)
136+
permissionId := d.Id()
137+
138+
log.Printf("[DEBUG] Deleting permission: service=%s, role=%s, permission=%s", serviceName, roleId, permissionId)
139+
140+
res := &DbaasLogsOperation{}
141+
endpoint := fmt.Sprintf("/dbaas/logs/%s/role/%s/permission/%s", url.PathEscape(serviceName), url.PathEscape(roleId), url.PathEscape(permissionId))
142+
if err := config.OVHClient.Delete(endpoint, res); err != nil {
143+
return diag.FromErr(helpers.CheckDeleted(d, err, endpoint))
144+
}
145+
146+
// Wait for the asynchronous deletion operation to complete.
147+
if _, err := waitForDbaasLogsOperation(ctx, config.OVHClient, serviceName, res.OperationId); err != nil {
148+
return diag.FromErr(err)
149+
}
150+
151+
d.SetId("")
152+
return nil
153+
}
154+
155+
// resourceDbaasLogsRolePermissionStreamImportState imports a permission given an ID in the format "service_name/role_id/permission_id".
156+
func resourceDbaasLogsRolePermissionStreamImportState(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
157+
givenID := d.Id()
158+
splitID := strings.SplitN(givenID, "/", 3)
159+
if len(splitID) != 3 {
160+
return nil, fmt.Errorf("Import ID must be in service_name/role_id/permission_id format")
161+
}
162+
serviceName := splitID[0]
163+
roleId := splitID[1]
164+
permissionId := splitID[2]
165+
166+
d.Set("service_name", serviceName)
167+
d.Set("role_id", roleId)
168+
d.SetId(permissionId)
169+
170+
return []*schema.ResourceData{d}, nil
171+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package ovh
2+
3+
import (
4+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
5+
)
6+
7+
// DbaasLogsRolePermissionStreamOpts holds the payload for appending a stream permission.
8+
type DbaasLogsRolePermissionStreamOpts struct {
9+
StreamId string `json:"streamId"`
10+
}
11+
12+
func (opts *DbaasLogsRolePermissionStreamOpts) FromResource(d *schema.ResourceData) *DbaasLogsRolePermissionStreamOpts {
13+
opts.StreamId = d.Get("stream_id").(string)
14+
return opts
15+
}
16+
17+
// DbaasLogsRolePermission represents the permission as returned by the GET endpoint.
18+
type DbaasLogsRolePermissionStream struct {
19+
PermissionId string `json:"permissionId"`
20+
PermissionType string `json:"permissionType"`
21+
StreamId string `json:"streamId"`
22+
}

0 commit comments

Comments
 (0)