Skip to content

Commit 2cfb5f0

Browse files
authored
feat: ydb_external_data_source resource and data source (#61)
1 parent c7afd17 commit 2cfb5f0

14 files changed

Lines changed: 1871 additions & 13 deletions

File tree

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
# ydb_external_data_source resource
2+
3+
`ydb_external_data_source` resource is used to manage YDB [external data source](https://ydb.tech/docs/ru/concepts/datamodel/external_data_source) entity.
4+
5+
All attributes are immutable. Any change triggers resource recreation (drop + create).
6+
7+
## Example
8+
9+
```tf
10+
resource "ydb_external_data_source" "object_storage" {
11+
path = "path/to/external_data_source"
12+
connection_string = "grpc://localhost:2136/?database=/local"
13+
14+
source_type = "ObjectStorage"
15+
location = "localhost:12345"
16+
auth_method = "NONE"
17+
}
18+
```
19+
20+
```tf
21+
resource "ydb_external_data_source" "clickhouse" {
22+
path = "path/to/clickhouse_source"
23+
connection_string = "grpc://localhost:2136/?database=/local"
24+
25+
source_type = "ClickHouse"
26+
location = "clickhouse-host:9000"
27+
auth_method = "BASIC"
28+
login = "user"
29+
password_secret_name = "my_password_secret"
30+
database_name = "default"
31+
protocol = "NATIVE"
32+
use_tls = true
33+
}
34+
```
35+
36+
```tf
37+
resource "ydb_external_data_source" "postgresql" {
38+
path = "path/to/pg_source"
39+
connection_string = "grpc://localhost:2136/?database=/local"
40+
41+
source_type = "PostgreSQL"
42+
location = "rc1a-xxx.mdb.yandexcloud.net:6432"
43+
auth_method = "MDB_BASIC"
44+
service_account_id = "sa-id-123"
45+
service_account_secret_name = "sa_secret"
46+
login = "pguser"
47+
password_secret_name = "pg_pass"
48+
database_name = "mydb"
49+
mdb_cluster_id = "c9q1234567890"
50+
use_tls = true
51+
}
52+
```
53+
54+
```tf
55+
resource "ydb_external_data_source" "s3_aws" {
56+
path = "path/to/s3_source"
57+
connection_string = "grpc://localhost:2136/?database=/local"
58+
59+
source_type = "ObjectStorage"
60+
location = "s3.us-east-1.amazonaws.com"
61+
auth_method = "AWS"
62+
aws_access_key_id_secret_name = "aws_key_id"
63+
aws_secret_access_key_secret_name = "aws_secret_key"
64+
aws_region = "us-east-1"
65+
}
66+
```
67+
68+
## Argument Reference
69+
70+
### Required
71+
72+
- `connection_string` (String) - Database connection string, e.g. `grpc://localhost:2136/?database=/local`.
73+
- `path` (String) - Path to the external data source within the database.
74+
- `source_type` (String) - Type of external data source: `ObjectStorage`, `ClickHouse`, `PostgreSQL`.
75+
- `location` (String) - Network address of the external data source (host:port or URL).
76+
77+
### Optional
78+
79+
- `auth_method` (String) - Authentication method. One of: `NONE`, `BASIC`, `MDB_BASIC`, `AWS`, `TOKEN`, `SERVICE_ACCOUNT`.
80+
- `database_name` (String) - Database name in the external source (for ClickHouse/PostgreSQL).
81+
- `protocol` (String) - Communication protocol: `NATIVE`, `HTTP`.
82+
- `use_tls` (Boolean) - Enable TLS for the external data source connection.
83+
- `mdb_cluster_id` (String) - Managed Database cluster ID.
84+
85+
#### BASIC / MDB_BASIC auth parameters
86+
87+
- `login` (String) - Username.
88+
- `password_secret_name` (String) - Secret name for the password.
89+
- `password_secret_path` (String) - Secret path for the password.
90+
91+
> Specify either `password_secret_name` or `password_secret_path`, not both.
92+
93+
#### MDB_BASIC additional parameters
94+
95+
- `service_account_id` (String) - Service account ID.
96+
- `service_account_secret_name` (String) - Secret name for the service account.
97+
- `service_account_secret_path` (String) - Secret path for the service account.
98+
99+
#### SERVICE_ACCOUNT auth parameters
100+
101+
- `service_account_id` (String) - Service account ID.
102+
- `service_account_secret_name` (String) - Secret name for the service account.
103+
- `service_account_secret_path` (String) - Secret path for the service account.
104+
105+
#### AWS auth parameters
106+
107+
- `aws_access_key_id_secret_name` (String) - Secret name for AWS access key ID.
108+
- `aws_access_key_id_secret_path` (String) - Secret path for AWS access key ID.
109+
- `aws_secret_access_key_secret_name` (String) - Secret name for AWS secret access key.
110+
- `aws_secret_access_key_secret_path` (String) - Secret path for AWS secret access key.
111+
- `aws_region` (String) - AWS region.
112+
113+
#### TOKEN auth parameters
114+
115+
- `token_secret_name` (String) - Secret name for the token.
116+
- `token_secret_path` (String) - Secret path for the token.
117+
118+
## Validation
119+
120+
The provider validates `auth_method` parameters before sending requests to YDB:
121+
122+
- **Unsupported parameters** - fields belonging to a different auth method are rejected (e.g. `aws_region` with `AUTH_METHOD = "BASIC"`).
123+
- **Required parameters** - mandatory fields for each auth method must be provided (e.g. `login` for `BASIC`).
124+
- **Secret name/path exclusivity** - for each secret, specify either `*_secret_name` or `*_secret_path`, not both.
125+
- **No mixed secret types** - within a single auth method, all secrets must use the same reference type (all `*_name` or all `*_path`).
126+
127+
## Data Source
128+
129+
`ydb_external_data_source` data source reads an existing external data source. Only `connection_string` and `path` are required; all other attributes are computed.
130+
131+
```tf
132+
data "ydb_external_data_source" "example" {
133+
path = "path/to/external_data_source"
134+
connection_string = "grpc://localhost:2136/?database=/local"
135+
}
136+
```
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package externaldatasource
2+
3+
import (
4+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
5+
)
6+
7+
// ValidateResourceDiffAuth validates auth fields from the planned diff (CustomizeDiff / plan).
8+
func ValidateResourceDiffAuth(d *schema.ResourceDiff) error {
9+
return validateResourceAuth(resourceFromDiff(d))
10+
}
11+
12+
// ValidateResourceDiffSourceType validates auth_method and properties against source_type.
13+
func ValidateResourceDiffSourceType(d *schema.ResourceDiff) error {
14+
return validateSourceType(resourceFromDiff(d))
15+
}
16+
17+
func resourceFromDiff(d *schema.ResourceDiff) *Resource {
18+
vals := make(map[string]string, len(allStringAttrKeys))
19+
for _, k := range allStringAttrKeys {
20+
vals[k] = diffString(d, k)
21+
}
22+
r := &Resource{Values: vals}
23+
if v, ok := d.GetOk("use_tls"); ok {
24+
if b, ok := v.(bool); ok {
25+
r.UseTLS = &b
26+
}
27+
}
28+
return r
29+
}
30+
31+
func diffString(d *schema.ResourceDiff, key string) string {
32+
v := d.Get(key)
33+
if v == nil {
34+
return ""
35+
}
36+
s, ok := v.(string)
37+
if !ok {
38+
return ""
39+
}
40+
return s
41+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package externaldatasource
2+
3+
import (
4+
"context"
5+
"net/url"
6+
7+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
8+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
9+
10+
tbl "github.com/ydb-platform/terraform-provider-ydb/internal/table"
11+
)
12+
13+
func (h *Handler) Create(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
14+
r, err := resourceSchemaToResource(d)
15+
if err != nil {
16+
return diag.FromErr(err)
17+
}
18+
19+
db, err := tbl.CreateDBConnection(ctx, tbl.ClientParams{
20+
DatabaseEndpoint: r.getConnectionString(),
21+
AuthCreds: h.authCreds,
22+
})
23+
if err != nil {
24+
return diag.Errorf("failed to initialize client: %s", err)
25+
}
26+
defer func() { _ = db.Close(ctx) }()
27+
28+
databaseEndpoint := d.Get("connection_string").(string)
29+
databaseURL, err := url.Parse(databaseEndpoint)
30+
if err != nil {
31+
return diag.Errorf("failed to parse database endpoint: %s", err)
32+
}
33+
fullPath := databaseURL.Query().Get("database") + "/" + r.Path
34+
35+
q := PrepareCreateQuery(fullPath, r)
36+
err = db.Query().Exec(ctx, q)
37+
if err != nil {
38+
return diag.Errorf("failed to create external data source: %s", err)
39+
}
40+
41+
d.SetId(databaseEndpoint + "?path=" + r.Path)
42+
43+
return h.Read(ctx, d, meta)
44+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package externaldatasource
2+
3+
import (
4+
"context"
5+
6+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
7+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
8+
9+
"github.com/ydb-platform/terraform-provider-ydb/internal/helpers"
10+
tbl "github.com/ydb-platform/terraform-provider-ydb/internal/table"
11+
)
12+
13+
func (h *Handler) Delete(ctx context.Context, d *schema.ResourceData, _ interface{}) diag.Diagnostics {
14+
entity, err := helpers.ParseYDBEntityID(d.Id())
15+
if err != nil {
16+
return diag.FromErr(err)
17+
}
18+
19+
db, err := tbl.CreateDBConnection(ctx, tbl.ClientParams{
20+
DatabaseEndpoint: entity.PrepareFullYDBEndpoint(),
21+
AuthCreds: h.authCreds,
22+
})
23+
if err != nil {
24+
return diag.Errorf("failed to initialize client: %s", err)
25+
}
26+
defer func() { _ = db.Close(ctx) }()
27+
28+
q := PrepareDropQuery(entity.GetFullEntityPath())
29+
err = db.Query().Exec(ctx, q)
30+
if err != nil {
31+
return diag.Errorf("failed to drop external data source %q: %s", entity.GetEntityPath(), err)
32+
}
33+
34+
return nil
35+
}

0 commit comments

Comments
 (0)