Skip to content

Commit 34388eb

Browse files
authored
Implement PostgreSQL Flex database resource and data source (#453)
* Implement db resource * Implement db data source * Extend acc test * Improve logs in other resources * Add examples * Generate docs * Fix linter
1 parent 3fb28d1 commit 34388eb

17 files changed

Lines changed: 923 additions & 29 deletions

File tree

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
---
2+
# generated by https://github.com/hashicorp/terraform-plugin-docs
3+
page_title: "stackit_postgresflex_database Data Source - stackit"
4+
subcategory: ""
5+
description: |-
6+
Postgres Flex database resource schema. Must have a region specified in the provider configuration.
7+
---
8+
9+
# stackit_postgresflex_database (Data Source)
10+
11+
Postgres Flex database resource schema. Must have a `region` specified in the provider configuration.
12+
13+
## Example Usage
14+
15+
```terraform
16+
data "stackit_postgresflex_database" "example" {
17+
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
18+
instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
19+
database_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
20+
}
21+
```
22+
23+
<!-- schema generated by tfplugindocs -->
24+
## Schema
25+
26+
### Required
27+
28+
- `database_id` (String) Database ID.
29+
- `instance_id` (String) ID of the Postgres Flex instance.
30+
- `project_id` (String) STACKIT project ID to which the instance is associated.
31+
32+
### Read-Only
33+
34+
- `id` (String) Terraform's internal resource ID. It is structured as "`project_id`,`instance_id`,`database_id`".
35+
- `name` (String) Database name.
36+
- `owner` (String) Username of the database owner.

docs/data-sources/postgresflex_instance.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
page_title: "stackit_postgresflex_instance Data Source - stackit"
44
subcategory: ""
55
description: |-
6-
PostgresFlex instance data source schema. Must have a region specified in the provider configuration.
6+
Postgres Flex instance data source schema. Must have a region specified in the provider configuration.
77
---
88

99
# stackit_postgresflex_instance (Data Source)
1010

11-
PostgresFlex instance data source schema. Must have a `region` specified in the provider configuration.
11+
Postgres Flex instance data source schema. Must have a `region` specified in the provider configuration.
1212

1313
## Example Usage
1414

docs/data-sources/postgresflex_user.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
page_title: "stackit_postgresflex_user Data Source - stackit"
44
subcategory: ""
55
description: |-
6-
PostgresFlex user data source schema. Must have a region specified in the provider configuration.
6+
Postgres Flex user data source schema. Must have a region specified in the provider configuration.
77
---
88

99
# stackit_postgresflex_user (Data Source)
1010

11-
PostgresFlex user data source schema. Must have a `region` specified in the provider configuration.
11+
Postgres Flex user data source schema. Must have a `region` specified in the provider configuration.
1212

1313
## Example Usage
1414

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
---
2+
# generated by https://github.com/hashicorp/terraform-plugin-docs
3+
page_title: "stackit_postgresflex_database Resource - stackit"
4+
subcategory: ""
5+
description: |-
6+
Postgres Flex database resource schema. Must have a region specified in the provider configuration.
7+
---
8+
9+
# stackit_postgresflex_database (Resource)
10+
11+
Postgres Flex database resource schema. Must have a `region` specified in the provider configuration.
12+
13+
## Example Usage
14+
15+
```terraform
16+
resource "stackit_postgresflex_database" "example" {
17+
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
18+
instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
19+
name = "mydb"
20+
owner = "myusername"
21+
}
22+
```
23+
24+
<!-- schema generated by tfplugindocs -->
25+
## Schema
26+
27+
### Required
28+
29+
- `instance_id` (String) ID of the Postgres Flex instance.
30+
- `name` (String) Database name.
31+
- `owner` (String) Username of the database owner.
32+
- `project_id` (String) STACKIT project ID to which the instance is associated.
33+
34+
### Read-Only
35+
36+
- `database_id` (String) Database ID.
37+
- `id` (String) Terraform's internal resource ID. It is structured as "`project_id`,`instance_id`,`database_id`".

docs/resources/postgresflex_instance.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
page_title: "stackit_postgresflex_instance Resource - stackit"
44
subcategory: ""
55
description: |-
6-
PostgresFlex instance resource schema. Must have a region specified in the provider configuration.
6+
Postgres Flex instance resource schema. Must have a region specified in the provider configuration.
77
---
88

99
# stackit_postgresflex_instance (Resource)
1010

11-
PostgresFlex instance resource schema. Must have a `region` specified in the provider configuration.
11+
Postgres Flex instance resource schema. Must have a `region` specified in the provider configuration.
1212

1313
## Example Usage
1414

docs/resources/postgresflex_user.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
page_title: "stackit_postgresflex_user Resource - stackit"
44
subcategory: ""
55
description: |-
6-
PostgresFlex user resource schema. Must have a region specified in the provider configuration.
6+
Postgres Flex user resource schema. Must have a region specified in the provider configuration.
77
---
88

99
# stackit_postgresflex_user (Resource)
1010

11-
PostgresFlex user resource schema. Must have a `region` specified in the provider configuration.
11+
Postgres Flex user resource schema. Must have a `region` specified in the provider configuration.
1212

1313
## Example Usage
1414

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
data "stackit_postgresflex_database" "example" {
2+
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
3+
instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
4+
database_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
5+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
resource "stackit_postgresflex_database" "example" {
2+
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
3+
instance_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
4+
name = "mydb"
5+
owner = "myusername"
6+
}
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
package postgresflex
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"net/http"
7+
8+
"github.com/hashicorp/terraform-plugin-framework/datasource"
9+
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
10+
"github.com/hashicorp/terraform-plugin-log/tflog"
11+
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
12+
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
13+
14+
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
15+
"github.com/stackitcloud/stackit-sdk-go/core/config"
16+
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
17+
"github.com/stackitcloud/stackit-sdk-go/services/postgresflex"
18+
)
19+
20+
// Ensure the implementation satisfies the expected interfaces.
21+
var (
22+
_ datasource.DataSource = &databaseDataSource{}
23+
)
24+
25+
// NewDatabaseDataSource is a helper function to simplify the provider implementation.
26+
func NewDatabaseDataSource() datasource.DataSource {
27+
return &databaseDataSource{}
28+
}
29+
30+
// databaseDataSource is the data source implementation.
31+
type databaseDataSource struct {
32+
client *postgresflex.APIClient
33+
}
34+
35+
// Metadata returns the data source type name.
36+
func (r *databaseDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
37+
resp.TypeName = req.ProviderTypeName + "_postgresflex_database"
38+
}
39+
40+
// Configure adds the provider configured client to the data source.
41+
func (r *databaseDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
42+
// Prevent panic if the provider has not been configured.
43+
if req.ProviderData == nil {
44+
return
45+
}
46+
47+
providerData, ok := req.ProviderData.(core.ProviderData)
48+
if !ok {
49+
core.LogAndAddError(ctx, &resp.Diagnostics, "Error configuring API client", fmt.Sprintf("Expected configure type stackit.ProviderData, got %T", req.ProviderData))
50+
return
51+
}
52+
53+
var apiClient *postgresflex.APIClient
54+
var err error
55+
if providerData.PostgresFlexCustomEndpoint != "" {
56+
apiClient, err = postgresflex.NewAPIClient(
57+
config.WithCustomAuth(providerData.RoundTripper),
58+
config.WithEndpoint(providerData.PostgresFlexCustomEndpoint),
59+
)
60+
} else {
61+
apiClient, err = postgresflex.NewAPIClient(
62+
config.WithCustomAuth(providerData.RoundTripper),
63+
config.WithRegion(providerData.Region),
64+
)
65+
}
66+
67+
if err != nil {
68+
core.LogAndAddError(ctx, &resp.Diagnostics, "Error configuring API client", fmt.Sprintf("Configuring client: %v. This is an error related to the provider configuration, not to the data source configuration", err))
69+
return
70+
}
71+
72+
r.client = apiClient
73+
tflog.Info(ctx, "Postgres Flex database client configured")
74+
}
75+
76+
// Schema defines the schema for the data source.
77+
func (r *databaseDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
78+
descriptions := map[string]string{
79+
"main": "Postgres Flex database resource schema. Must have a `region` specified in the provider configuration.",
80+
"id": "Terraform's internal resource ID. It is structured as \"`project_id`,`instance_id`,`database_id`\".",
81+
"database_id": "Database ID.",
82+
"instance_id": "ID of the Postgres Flex instance.",
83+
"project_id": "STACKIT project ID to which the instance is associated.",
84+
"name": "Database name.",
85+
"owner": "Username of the database owner.",
86+
}
87+
88+
resp.Schema = schema.Schema{
89+
Description: descriptions["main"],
90+
Attributes: map[string]schema.Attribute{
91+
"id": schema.StringAttribute{
92+
Description: descriptions["id"],
93+
Computed: true,
94+
},
95+
"database_id": schema.StringAttribute{
96+
Description: descriptions["database_id"],
97+
Required: true,
98+
Validators: []validator.String{
99+
validate.NoSeparator(),
100+
},
101+
},
102+
"instance_id": schema.StringAttribute{
103+
Description: descriptions["instance_id"],
104+
Required: true,
105+
Validators: []validator.String{
106+
validate.UUID(),
107+
validate.NoSeparator(),
108+
},
109+
},
110+
"project_id": schema.StringAttribute{
111+
Description: descriptions["project_id"],
112+
Required: true,
113+
Validators: []validator.String{
114+
validate.UUID(),
115+
validate.NoSeparator(),
116+
},
117+
},
118+
"name": schema.StringAttribute{
119+
Description: descriptions["name"],
120+
Computed: true,
121+
},
122+
"owner": schema.StringAttribute{
123+
Description: descriptions["owner"],
124+
Computed: true,
125+
},
126+
},
127+
}
128+
}
129+
130+
// Read refreshes the Terraform state with the latest data.
131+
func (r *databaseDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { // nolint:gocritic // function signature required by Terraform
132+
var model Model
133+
diags := req.Config.Get(ctx, &model)
134+
resp.Diagnostics.Append(diags...)
135+
if resp.Diagnostics.HasError() {
136+
return
137+
}
138+
projectId := model.ProjectId.ValueString()
139+
instanceId := model.InstanceId.ValueString()
140+
databaseId := model.DatabaseId.ValueString()
141+
ctx = tflog.SetField(ctx, "project_id", projectId)
142+
ctx = tflog.SetField(ctx, "instance_id", instanceId)
143+
ctx = tflog.SetField(ctx, "database_id", databaseId)
144+
145+
databaseResp, err := getDatabase(ctx, r.client, projectId, instanceId, databaseId)
146+
if err != nil {
147+
oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
148+
if ok && oapiErr.StatusCode == http.StatusNotFound {
149+
resp.State.RemoveResource(ctx)
150+
}
151+
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading database", fmt.Sprintf("Calling API: %v", err))
152+
return
153+
}
154+
155+
// Map response body to schema and populate Computed attribute values
156+
err = mapFields(databaseResp, &model)
157+
if err != nil {
158+
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading database", fmt.Sprintf("Processing API payload: %v", err))
159+
return
160+
}
161+
162+
// Set refreshed state
163+
diags = resp.State.Set(ctx, model)
164+
resp.Diagnostics.Append(diags...)
165+
if resp.Diagnostics.HasError() {
166+
return
167+
}
168+
tflog.Info(ctx, "Postgres Flex database read")
169+
}

0 commit comments

Comments
 (0)