Skip to content

Commit 1ef1f46

Browse files
committed
feat(datacenter): implement CRUD operations for datacenters in memory, mongo, postgres, and sqlite stores
- Added InsertDatacenter, GetDatacenterByID, GetDatacenterBySlug, ListDatacenters, UpdateDatacenter, DeleteDatacenter, CountDatacentersByTenant, and CountInstancesByDatacenter methods to handle datacenter management. - Created datacenter models for mongo, postgres, and sqlite with appropriate serialization and deserialization logic. - Enhanced instance listing to filter by datacenter. - Updated Store interface to include datacenter.Store for better abstraction.
1 parent 271244d commit 1ef1f46

43 files changed

Lines changed: 5926 additions & 49 deletions

Some content is hidden

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

app/controlplane.go

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
ctrlplane "github.com/xraph/ctrlplane"
1010
"github.com/xraph/ctrlplane/admin"
1111
"github.com/xraph/ctrlplane/auth"
12+
"github.com/xraph/ctrlplane/datacenter"
1213
"github.com/xraph/ctrlplane/deploy"
1314
"github.com/xraph/ctrlplane/deploy/strategies"
1415
"github.com/xraph/ctrlplane/event"
@@ -37,13 +38,14 @@ type CtrlPlane struct {
3738
pendingExts []plugin.Extension
3839

3940
// Services are the public subsystem interfaces.
40-
Instances instance.Service
41-
Deploys deploy.Service
42-
Health health.Service
43-
Telemetry telemetry.Service
44-
Network network.Service
45-
Secrets secrets.Service
46-
Admin admin.Service
41+
Datacenters datacenter.Service
42+
Instances instance.Service
43+
Deploys deploy.Service
44+
Health health.Service
45+
Telemetry telemetry.Service
46+
Network network.Service
47+
Secrets secrets.Service
48+
Admin admin.Service
4749
}
4850

4951
// New creates a CtrlPlane with the given options.
@@ -98,6 +100,24 @@ func (cp *CtrlPlane) Vault() secrets.Vault {
98100
return cp.vault
99101
}
100102

103+
// vaultSetter is satisfied by services that support late-binding a vault.
104+
type vaultSetter interface {
105+
SetVault(v secrets.Vault)
106+
}
107+
108+
// SetVault replaces the vault and propagates it to dependent services.
109+
func (cp *CtrlPlane) SetVault(v secrets.Vault) {
110+
cp.vault = v
111+
112+
if vs, ok := cp.Deploys.(vaultSetter); ok {
113+
vs.SetVault(v)
114+
}
115+
116+
if vs, ok := cp.Secrets.(vaultSetter); ok {
117+
vs.SetVault(v)
118+
}
119+
}
120+
101121
// Scheduler returns the background worker scheduler.
102122
func (cp *CtrlPlane) Scheduler() *worker.Scheduler {
103123
return cp.scheduler
@@ -145,8 +165,11 @@ func (cp *CtrlPlane) wireServices() {
145165
cp.vault = memoryvault.New()
146166
}
147167

168+
// Datacenter service (wired before instances so it can serve as resolver).
169+
cp.Datacenters = datacenter.NewService(cp.store, cp.providers, cp.events, cp.auth)
170+
148171
// Instance service.
149-
cp.Instances = instance.NewService(cp.store, cp.providers, cp.events, cp.auth)
172+
cp.Instances = instance.NewService(cp.store, cp.providers, cp.events, cp.auth, cp.Datacenters)
150173

151174
// Deploy service with strategies.
152175
deploySvc := deploy.NewService(cp.store, cp.store, cp.providers, cp.events, cp.auth, cp.vault)
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package components
2+
3+
import (
4+
"github.com/xraph/forgeui/components/badge"
5+
6+
"github.com/xraph/ctrlplane/datacenter"
7+
)
8+
9+
// DatacenterStatusBadge renders a badge for a datacenter status.
10+
templ DatacenterStatusBadge(status datacenter.Status) {
11+
switch status {
12+
case datacenter.StatusActive:
13+
<span class="inline-flex items-center rounded-sm px-2 py-0.5 text-xs font-medium bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400">
14+
Active
15+
</span>
16+
case datacenter.StatusMaintenance:
17+
<span class="inline-flex items-center rounded-sm px-2 py-0.5 text-xs font-medium bg-yellow-100 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-400">
18+
Maintenance
19+
</span>
20+
case datacenter.StatusDraining:
21+
<span class="inline-flex items-center rounded-sm px-2 py-0.5 text-xs font-medium bg-orange-100 text-orange-800 dark:bg-orange-900/30 dark:text-orange-400">
22+
Draining
23+
</span>
24+
case datacenter.StatusOffline:
25+
@badge.Badge(badge.Props{Variant: badge.VariantDestructive}) {
26+
Offline
27+
}
28+
default:
29+
@badge.Badge(badge.Props{Variant: badge.VariantOutline}) {
30+
{ string(status) }
31+
}
32+
}
33+
}

dashboard/components/datacenter_status_badge_templ.go

Lines changed: 110 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package components
2+
3+
import (
4+
"strconv"
5+
6+
"github.com/xraph/forgeui/components/table"
7+
8+
"github.com/xraph/ctrlplane/datacenter"
9+
)
10+
11+
// DatacenterTable renders a table of datacenters.
12+
templ DatacenterTable(datacenters []*datacenter.Datacenter, instanceCounts map[string]int) {
13+
if len(datacenters) == 0 {
14+
@EmptyState("map-pin", "No datacenters", "Create a datacenter to define a deployment location.")
15+
} else {
16+
@table.Table() {
17+
@table.Header() {
18+
@table.Row() {
19+
@table.Head() { Name }
20+
@table.Head() { Provider }
21+
@table.Head() { Region }
22+
@table.Head() { Zone }
23+
@table.Head() { Status }
24+
@table.Head() { Instances }
25+
}
26+
}
27+
@table.Body() {
28+
for _, dc := range datacenters {
29+
@table.Row(table.RowProps{
30+
Class: "cursor-pointer",
31+
Attributes: templ.Attributes{
32+
"hx-get": "./datacenters/detail?datacenter_id=" + dc.ID.String(),
33+
"hx-target": "#content",
34+
"hx-push-url": "true",
35+
"hx-swap": "innerHTML",
36+
},
37+
}) {
38+
@table.Cell() {
39+
<div>
40+
<span class="font-medium">{ dc.Name }</span>
41+
<span class="ml-2 text-xs text-muted-foreground font-mono">{ dc.Slug }</span>
42+
</div>
43+
}
44+
@table.Cell() {
45+
<span class="text-muted-foreground">{ dc.ProviderName }</span>
46+
}
47+
@table.Cell() {
48+
<span class="text-muted-foreground">{ dc.Region }</span>
49+
}
50+
@table.Cell() {
51+
if dc.Zone != "" {
52+
<span class="text-muted-foreground">{ dc.Zone }</span>
53+
} else {
54+
<span class="text-muted-foreground">-</span>
55+
}
56+
}
57+
@table.Cell() {
58+
@DatacenterStatusBadge(dc.Status)
59+
}
60+
@table.Cell() {
61+
<span class="text-muted-foreground">{ strconv.Itoa(instanceCounts[dc.ID.String()]) }</span>
62+
}
63+
}
64+
}
65+
}
66+
}
67+
}
68+
}

0 commit comments

Comments
 (0)