Skip to content

Commit ed23079

Browse files
committed
feat: allow human-readable IDs
This modifies our API contract to allow human-readable IDs, as opposed to UUIDs, in each user-specified ID field: - Cluster ID - Host ID - Database ID - Tenant ID Instance IDs are still deterministically generated from the host ID, database ID, and node name, but are designed to be more readable. For example, given these inputs: - Host ID: "us-east-1" - Database ID: "my-app" - Node name: "n1" We will generate the instance ID "my-app-n1-n5fe2mcy". We're using a short hash of the host ID rather than the host ID itself to enhance readability when the host IDs are long, such as those used by Cloud. For example, when both the host ID and database ID are UUIDs: - Host ID: "dbf5779c-492a-11f0-b11a-1b8cb15693a8" - Database ID: "ed2362ea-492a-11f0-956c-9f2171e8b9ab" - Node name: "n1" The instance ID is "ed2362ea-492a-11f0-956c-9f2171e8b9ab-n1-io5979nh" This commit contains breaking changes for the development environment, so it's recommended to do the following before updating to this commit: - Delete all databases via the API - Remove existing data directories: `rm -rf docker/control-plane-dev/data` This will reset your environment, so you'll need to reinitialize the cluster. PLAT-86
1 parent 4a8949d commit ed23079

File tree

113 files changed

+1683
-1618
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

113 files changed

+1683
-1618
lines changed

api/v1/design/api.go

Lines changed: 34 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -146,9 +146,10 @@ var _ = g.Service("control-plane", func() {
146146
g.Description("Returns information about a particular host in the cluster.")
147147
g.Meta("openapi:summary", "Get host")
148148
g.Payload(func() {
149-
g.Attribute("host_id", g.String, func() {
149+
g.Attribute("host_id", Identifier, func() {
150150
g.Description("ID of the host to get.")
151-
g.Format(g.FormatUUID)
151+
g.Example("host-1")
152+
g.Example("us-east-1")
152153
g.Example("de3b1388-1f0c-42f1-a86c-59ab72f255ec")
153154
})
154155

@@ -170,9 +171,10 @@ var _ = g.Service("control-plane", func() {
170171
g.Description("Removes a host from the cluster.")
171172
g.Meta("openapi:summary", "Remove host")
172173
g.Payload(func() {
173-
g.Attribute("host_id", g.String, func() {
174+
g.Attribute("host_id", Identifier, func() {
174175
g.Description("ID of the host to remove.")
175-
g.Format(g.FormatUUID)
176+
g.Example("host-1")
177+
g.Example("us-east-1")
176178
g.Example("de3b1388-1f0c-42f1-a86c-59ab72f255ec")
177179
})
178180

@@ -226,9 +228,10 @@ var _ = g.Service("control-plane", func() {
226228
g.Description("Returns information about a particular database in the cluster.")
227229
g.Meta("openapi:summary", "Get database")
228230
g.Payload(func() {
229-
g.Attribute("database_id", g.String, func() {
231+
g.Attribute("database_id", Identifier, func() {
230232
g.Description("ID of the database to get.")
231-
g.Format(g.FormatUUID)
233+
g.Example("production")
234+
g.Example("my-app")
232235
g.Example("02f1a7db-fca8-4521-b57a-2a375c1ced51")
233236
})
234237

@@ -252,9 +255,10 @@ var _ = g.Service("control-plane", func() {
252255
g.Description("Updates a database with the given specification.")
253256
g.Meta("openapi:summary", "Update database")
254257
g.Payload(func() {
255-
g.Attribute("database_id", g.String, func() {
258+
g.Attribute("database_id", Identifier, func() {
256259
g.Description("ID of the database to update.")
257-
g.Format(g.FormatUUID)
260+
g.Example("production")
261+
g.Example("my-app")
258262
g.Example("02f1a7db-fca8-4521-b57a-2a375c1ced51")
259263
})
260264
g.Attribute("force_update", g.Boolean, func() {
@@ -286,9 +290,10 @@ var _ = g.Service("control-plane", func() {
286290
g.Description("Deletes a database from the cluster.")
287291
g.Meta("openapi:summary", "Delete database")
288292
g.Payload(func() {
289-
g.Attribute("database_id", g.String, func() {
290-
g.Format(g.FormatUUID)
293+
g.Attribute("database_id", Identifier, func() {
291294
g.Description("ID of the database to delete.")
295+
g.Example("production")
296+
g.Example("my-app")
292297
g.Example("02f1a7db-fca8-4521-b57a-2a375c1ced51")
293298
})
294299

@@ -312,9 +317,10 @@ var _ = g.Service("control-plane", func() {
312317
g.Description("Initiates a backup for a database node.")
313318
g.Meta("openapi:summary", "Backup database node")
314319
g.Payload(func() {
315-
g.Attribute("database_id", g.String, func() {
316-
g.Format(g.FormatUUID)
320+
g.Attribute("database_id", Identifier, func() {
317321
g.Description("ID of the database to back up.")
322+
g.Example("production")
323+
g.Example("my-app")
318324
g.Example("02f1a7db-fca8-4521-b57a-2a375c1ced51")
319325
})
320326
g.Attribute("node_name", g.String, func() {
@@ -345,14 +351,15 @@ var _ = g.Service("control-plane", func() {
345351
g.Description("Lists all tasks for a database.")
346352
g.Meta("openapi:summary", "List database tasks")
347353
g.Payload(func() {
348-
g.Attribute("database_id", g.String, func() {
349-
g.Format(g.FormatUUID)
354+
g.Attribute("database_id", Identifier, func() {
350355
g.Description("ID of the database to list tasks for.")
356+
g.Example("production")
357+
g.Example("my-app")
351358
g.Example("02f1a7db-fca8-4521-b57a-2a375c1ced51")
352359
})
353360
g.Attribute("after_task_id", g.String, func() {
354-
g.Format(g.FormatUUID)
355361
g.Description("ID of the task to start from.")
362+
g.Format(g.FormatUUID)
356363
g.Example("3c875a27-f6a6-4c1c-ba5f-6972fb1fc348")
357364
})
358365
g.Attribute("limit", g.Int, func() {
@@ -386,14 +393,15 @@ var _ = g.Service("control-plane", func() {
386393
g.Description("Returns information about a particular task.")
387394
g.Meta("openapi:summary", "Get database task")
388395
g.Payload(func() {
389-
g.Attribute("database_id", g.String, func() {
390-
g.Format(g.FormatUUID)
396+
g.Attribute("database_id", Identifier, func() {
391397
g.Description("ID of the database the task belongs to.")
398+
g.Example("production")
399+
g.Example("my-app")
392400
g.Example("02f1a7db-fca8-4521-b57a-2a375c1ced51")
393401
})
394402
g.Attribute("task_id", g.String, func() {
395-
g.Format(g.FormatUUID)
396403
g.Description("ID of the task to get.")
404+
g.Format(g.FormatUUID)
397405
g.Example("3c875a27-f6a6-4c1c-ba5f-6972fb1fc348")
398406
})
399407

@@ -415,19 +423,20 @@ var _ = g.Service("control-plane", func() {
415423
g.Description("Returns the log of a particular task for a database.")
416424
g.Meta("openapi:summary", "Get database task log")
417425
g.Payload(func() {
418-
g.Attribute("database_id", g.String, func() {
419-
g.Format(g.FormatUUID)
426+
g.Attribute("database_id", Identifier, func() {
420427
g.Description("ID of the database to get task log for.")
428+
g.Example("production")
429+
g.Example("my-app")
421430
g.Example("02f1a7db-fca8-4521-b57a-2a375c1ced51")
422431
})
423432
g.Attribute("task_id", g.String, func() {
424-
g.Format(g.FormatUUID)
425433
g.Description("ID of the task to get log for.")
434+
g.Format(g.FormatUUID)
426435
g.Example("3c875a27-f6a6-4c1c-ba5f-6972fb1fc348")
427436
})
428437
g.Attribute("after_entry_id", g.String, func() {
429-
g.Format(g.FormatUUID)
430438
g.Description("ID of the entry to start from.")
439+
g.Format(g.FormatUUID)
431440
g.Example("3c875a27-f6a6-4c1c-ba5f-6972fb1fc348")
432441
})
433442
g.Attribute("limit", g.Int, func() {
@@ -455,9 +464,10 @@ var _ = g.Service("control-plane", func() {
455464
g.Description("Perform an in-place restore one or more nodes using the given restore configuration.")
456465
g.Meta("openapi:summary", "Restore database")
457466
g.Payload(func() {
458-
g.Attribute("database_id", g.String, func() {
459-
g.Format(g.FormatUUID)
467+
g.Attribute("database_id", Identifier, func() {
460468
g.Description("ID of the database to restore.")
469+
g.Example("production")
470+
g.Example("my-app")
461471
g.Example("02f1a7db-fca8-4521-b57a-2a375c1ced51")
462472
})
463473
g.Attribute("request", RestoreDatabaseRequest)

api/v1/design/cluster.go

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@ var ClusterStatus = g.Type("ClusterStatus", func() {
1414
})
1515

1616
var Cluster = g.Type("Cluster", func() {
17-
g.Attribute("id", g.String, func() {
18-
g.Format(g.FormatUUID)
17+
g.Attribute("id", Identifier, func() {
1918
g.Description("Unique identifier for the cluster.")
19+
g.Example("production")
2020
g.Example("a67cbb36-c3c3-49c9-8aac-f4a0438a883d")
2121
})
22-
g.Attribute("tenant_id", g.String, func() {
23-
g.Format(g.FormatUUID)
22+
g.Attribute("tenant_id", Identifier, func() {
2423
g.Description("Unique identifier for the cluster's owner.")
24+
g.Example("engineering")
2525
g.Example("8210ec10-2dca-406c-ac4a-0661d2189954")
2626
})
2727
g.Attribute("status", ClusterStatus, func() {
@@ -54,9 +54,10 @@ var ClusterJoinRequest = g.Type("ClusterJoinRequest", func() {
5454
g.Pattern(`^PGEDGE-[\w]{64}-[\w]{32}$`)
5555
g.Example("PGEDGE-dd440afcf5de20ef8e8cf54f6cb9f125fd55f90e64faa94b906130b31235e730-41e975f41d7ea61058f2fe2572cb52dd")
5656
})
57-
g.Attribute("host_id", g.String, func() {
58-
g.Format(g.FormatUUID)
57+
g.Attribute("host_id", Identifier, func() {
5958
g.Description("The unique identifier for the host that's joining the cluster.")
59+
g.Example("host-1")
60+
g.Example("us-east-1")
6061
g.Example("de3b1388-1f0c-42f1-a86c-59ab72f255ec")
6162
})
6263
g.Attribute("hostname", g.String, func() {
@@ -76,8 +77,9 @@ var ClusterJoinRequest = g.Type("ClusterJoinRequest", func() {
7677

7778
var ClusterPeer = g.Type("ClusterPeer", func() {
7879
g.Attribute("name", g.String, func() {
79-
g.Format(g.FormatUUID)
80-
g.Description("The name of the cluster member.")
80+
g.Description("The name of the Etcd cluster member.")
81+
g.Example("host-1")
82+
g.Example("us-east-1")
8183
g.Example("de3b1388-1f0c-42f1-a86c-59ab72f255ec")
8284
})
8385
g.Attribute("peer_url", g.String, func() {

api/v1/design/common.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package design
2+
3+
import (
4+
g "goa.design/goa/v3/dsl"
5+
)
6+
7+
// Allows IDs that are:
8+
// - 1-63 characters
9+
// - Contain only lower-cased letters and hyphens
10+
// - Starts with a letter or number
11+
// - Ends with a letter or number
12+
// The handlers must also validate that there are no consecutive hyphens.
13+
const idPattern = `^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$`
14+
15+
var Identifier = g.Type("Identifier", g.String, func() {
16+
g.Description("A user-specified identifier. Must be 1-63 characters, contain only lower-cased letters and hyphens, start and end with a letter or number, and not contain consecutive hyphens.")
17+
g.Pattern(idPattern)
18+
g.Example("production")
19+
g.Example("my-app")
20+
g.Example("76f9b8c0-4958-11f0-a489-3bb29577c696")
21+
})

api/v1/design/database.go

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@ const (
1212
cpuPattern = `^[0-9]+(\.[0-9]{1,3}|m)?$`
1313
)
1414

15-
var HostIDs = g.ArrayOf(g.String, func() {
16-
g.Format(g.FormatUUID)
15+
var HostIDs = g.ArrayOf(Identifier, func() {
16+
g.Example("host-1")
17+
g.Example("us-east-1")
1718
g.Example("de3b1388-1f0c-42f1-a86c-59ab72f255ec")
1819
})
1920

@@ -104,10 +105,9 @@ var DatabaseUserSpec = g.Type("DatabaseUserSpec", func() {
104105
})
105106

106107
var BackupRepositorySpec = g.Type("BackupRepositorySpec", func() {
107-
g.Attribute("id", g.String, func() {
108+
g.Attribute("id", Identifier, func() {
108109
g.Description("The unique identifier of this repository.")
109-
g.MinLength(1)
110-
g.MaxLength(64)
110+
g.Example("my-app-1")
111111
g.Example("f6b84a99-5e91-4203-be1e-131fe82e5984")
112112
})
113113
g.Attribute("type", g.String, func() {
@@ -246,10 +246,9 @@ var BackupConfigSpec = g.Type("BackupConfigSpec", func() {
246246
})
247247

248248
var RestoreRepositorySpec = g.Type("RestoreRepositorySpec", func() {
249-
g.Attribute("id", g.String, func() {
249+
g.Attribute("id", Identifier, func() {
250250
g.Description("The unique identifier of this repository.")
251-
g.MinLength(1)
252-
g.MaxLength(64)
251+
g.Example("my-app-1")
253252
g.Example("f6b84a99-5e91-4203-be1e-131fe82e5984")
254253
})
255254
g.Attribute("type", g.String, func() {
@@ -342,10 +341,11 @@ var RestoreRepositorySpec = g.Type("RestoreRepositorySpec", func() {
342341
})
343342

344343
var RestoreConfigSpec = g.Type("RestoreConfigSpec", func() {
345-
g.Attribute("source_database_id", g.String, func() {
344+
g.Attribute("source_database_id", Identifier, func() {
346345
g.Description("The ID of the database to restore this database from.")
347-
g.Format(g.FormatUUID)
348-
g.Example("6c8e43ee-26ea-47b8-a8f8-89897e0137bd")
346+
g.Example("production")
347+
g.Example("my-app")
348+
g.Example("02f1a7db-fca8-4521-b57a-2a375c1ced51")
349349
})
350350
g.Attribute("source_node_name", g.String, func() {
351351
g.Description("The name of the node to restore this database from.")
@@ -450,14 +450,15 @@ var DatabaseSpec = g.Type("DatabaseSpec", func() {
450450

451451
var Database = g.ResultType("Database", func() {
452452
g.Attributes(func() {
453-
g.Attribute("id", g.String, func() {
454-
g.Format(g.FormatUUID)
453+
g.Attribute("id", Identifier, func() {
455454
g.Description("Unique identifier for the database.")
455+
g.Example("production")
456+
g.Example("my-app")
456457
g.Example("02f1a7db-fca8-4521-b57a-2a375c1ced51")
457458
})
458-
g.Attribute("tenant_id", g.String, func() {
459-
g.Format(g.FormatUUID)
459+
g.Attribute("tenant_id", Identifier, func() {
460460
g.Description("Unique identifier for the databases's owner.")
461+
g.Example("engineering")
461462
g.Example("8210ec10-2dca-406c-ac4a-0661d2189954")
462463
})
463464
g.Attribute("created_at", g.String, func() {
@@ -516,14 +517,15 @@ var Database = g.ResultType("Database", func() {
516517
})
517518

518519
var CreateDatabaseRequest = g.Type("CreateDatabaseRequest", func() {
519-
g.Attribute("id", g.String, func() {
520-
g.Format(g.FormatUUID)
520+
g.Attribute("id", Identifier, func() {
521521
g.Description("Unique identifier for the database.")
522+
g.Example("production")
523+
g.Example("my-app")
522524
g.Example("02f1a7db-fca8-4521-b57a-2a375c1ced51")
523525
})
524-
g.Attribute("tenant_id", g.String, func() {
525-
g.Format(g.FormatUUID)
526+
g.Attribute("tenant_id", Identifier, func() {
526527
g.Description("Unique identifier for the databases's owner.")
528+
g.Example("engineering")
527529
g.Example("8210ec10-2dca-406c-ac4a-0661d2189954")
528530
})
529531
g.Attribute("spec", DatabaseSpec, func() {
@@ -545,9 +547,9 @@ var CreateDatabaseResponse = g.Type("CreateDatabaseResponse", func() {
545547
})
546548

547549
var UpdateDatabaseRequest = g.Type("UpdateDatabaseRequest", func() {
548-
g.Attribute("tenant_id", g.String, func() {
549-
g.Format(g.FormatUUID)
550+
g.Attribute("tenant_id", Identifier, func() {
550551
g.Description("Unique identifier for the databases's owner.")
552+
g.Example("engineering")
551553
g.Example("8210ec10-2dca-406c-ac4a-0661d2189954")
552554
})
553555
g.Attribute("spec", DatabaseSpec, func() {

api/v1/design/host.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,10 @@ var HostCohort = g.Type("HostCohort", func() {
7575
})
7676

7777
var Host = g.Type("Host", func() {
78-
g.Attribute("id", g.String, func() {
79-
g.Format(g.FormatUUID)
78+
g.Attribute("id", Identifier, func() {
8079
g.Description("Unique identifier for the host.")
80+
g.Example("host-1")
81+
g.Example("us-east-1")
8182
g.Example("de3b1388-1f0c-42f1-a86c-59ab72f255ec")
8283
})
8384
g.Attribute("orchestrator", g.String, func() {

api/v1/design/instance.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,12 +96,10 @@ var Instance = g.ResultType("Instance", func() {
9696
g.Description("An instance of pgEdge Postgres running on a host.")
9797
g.Attributes(func() {
9898
g.Attribute("id", g.String, func() {
99-
g.Format(g.FormatUUID)
10099
g.Description("Unique identifier for the instance.")
101100
g.Example("a67cbb36-c3c3-49c9-8aac-f4a0438a883d")
102101
})
103102
g.Attribute("host_id", g.String, func() {
104-
g.Format(g.FormatUUID)
105103
g.Description("The ID of the host this instance is running on.")
106104
g.Example("de3b1388-1f0c-42f1-a86c-59ab72f255ec")
107105
})

api/v1/design/task.go

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ var Task = g.Type("Task", func() {
1111
g.Example("439eb515-e700-4740-b508-4a3f12ec4f83")
1212
})
1313
g.Attribute("database_id", g.String, func() {
14-
g.Format(g.FormatUUID)
1514
g.Description("The database ID of the task.")
1615
g.Example("02f1a7db-fca8-4521-b57a-2a375c1ced51")
1716
})
@@ -20,12 +19,10 @@ var Task = g.Type("Task", func() {
2019
g.Example("n1")
2120
})
2221
g.Attribute("instance_id", g.String, func() {
23-
g.Format(g.FormatUUID)
2422
g.Description("The ID of the instance that the task is operating on.")
2523
g.Example("3c875a27-f6a6-4c1c-ba5f-6972fb1fc348")
2624
})
2725
g.Attribute("host_id", g.String, func() {
28-
g.Format(g.FormatUUID)
2926
g.Description("The ID of the host that the task is running on.")
3027
g.Example("2e52dcde-86d8-4f71-b58e-8dc3a10c936a")
3128
})
@@ -83,12 +80,10 @@ var TaskLogEntry = g.Type("TaskLogEntry", func() {
8380

8481
var TaskLog = g.Type("TaskLog", func() {
8582
g.Attribute("database_id", g.String, func() {
86-
g.Format(g.FormatUUID)
8783
g.Description("The database ID of the task log.")
8884
g.Example("02f1a7db-fca8-4521-b57a-2a375c1ced51")
8985
})
9086
g.Attribute("task_id", g.String, func() {
91-
g.Format(g.FormatUUID)
9287
g.Description("The unique ID of the task log.")
9388
g.Example("3c875a27-f6a6-4c1c-ba5f-6972fb1fc348")
9489
})
@@ -98,7 +93,6 @@ var TaskLog = g.Type("TaskLog", func() {
9893
g.Example("pending")
9994
})
10095
g.Attribute("last_entry_id", g.String, func() {
101-
g.Format(g.FormatUUID)
10296
g.Description("The ID of the last entry in the task log.")
10397
g.Example("3c875a27-f6a6-4c1c-ba5f-6972fb1fc348")
10498
})

0 commit comments

Comments
 (0)