Skip to content

Commit f46ab0c

Browse files
authored
Merge pull request #7 from OpenCHAMI/sqlite
feat: migrate storage backend to ent ORM with sqlite
2 parents accbdc2 + 2ef32b3 commit f46ab0c

60 files changed

Lines changed: 12732 additions & 750 deletions

Some content is hidden

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

README.md

Lines changed: 20 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,11 @@ Open a terminal and start the API server:
1919

2020
```bash
2121
go mod tidy
22-
go run ./cmd/server serve
22+
mkdir -p data
23+
go run ./cmd/server serve --database-url="file:./data/fru-tracker.db?cache=shared&_fk=1"
2324
```
2425

25-
*What is happening:* The server initializes the local file database, starts the internal event bus, and spins up the background reconciliation workers. It is now listening for requests on `http://localhost:8080`.
26+
*What is happening*: The server initializes the local SQLite database, starts the internal event bus, and spins up the background reconciliation workers. It is now listening for requests on http://localhost:8080.
2627

2728
### 2. Simulate a Hardware Discovery
2829
Open a **second** terminal. Create a file named `upload_request.json` containing a mock payload with a Node and a DIMM:
@@ -108,13 +109,12 @@ The current implementation has been validated with an end-to-end workflow using
108109
* **Two-Pass Reconciliation:**
109110
* **Pass 1 (Ingestion):** The reconciler parses the raw JSON payload and performs a get-or-create operation for each device, utilizing the `redfish_uri` from the properties map as a unique primary key.
110111
* **Pass 2 (Relationship Linking):** The reconciler evaluates the `parentSerialNumber` provided by the collector, identifies the corresponding parent device in the database, and updates the child device's `parentID` with the appropriate UUID.
111-
* **Storage Backend:** Validated using the local file storage backend for persisting resources.
112+
* **Storage Backend:** Validated using the Ent ORM backed by a local SQLite database for persisting resources.
112113

113114
### Future Work
114115

115116
While the core event-driven ingestion pipeline is functional, several enhancements are planned to make `fru-tracker` production-ready:
116117

117-
* **Production Storage Backend:** Migrate testing and deployment documentation from the local `file` storage backend to a robust relational database (e.g., SMD using Fabrica's `ent` backend option).
118118
* **Hardware Removal Handling:** Enhance the `DiscoverySnapshotReconciler` to detect missing components. If a previously tracked child device is absent from a new snapshot, the reconciler should update the existing `Device` record to mark it as removed, offline, or inactive.
119119
* **Event Delta Consumer:** Build a reference implementation of an event subscriber. This service will listen to the message bus for `fru-tracker.resource.device.updated` and `deleted` events to generate human-readable changelogs and trigger alerts.
120120
* **Collector Enhancements:** * Expand the reference Redfish collector to support additional component types (e.g., Drives, PowerSupplies, NetworkAdapters).
@@ -171,9 +171,10 @@ The server runs the API endpoints and the background reconciliation controller.
171171
``` bash
172172
# Install dependencies
173173
go mod tidy
174+
mkdir -p data
174175

175176
# Run the server (using the 'serve' command for cobra)
176-
go run ./cmd/server serve
177+
go run ./cmd/server serve --database-url="file:./data/fru-tracker.db?cache=shared&_fk=1"
177178
```
178179

179180
The server will start on `http://localhost:8080`.
@@ -214,31 +215,23 @@ Inventory collection and posting completed successfully.
214215
The server logs show the generated handler receiving the post, the event bus dispatching the event, and the `DiscoverySnapshotReconciler` executing the two-pass logic.
215216

216217
``` bash
217-
$ go run ./cmd/server serve
218+
$ go run ./cmd/server serve --database-url="file:./data/fru-tracker.db?cache=shared&_fk=1"
218219
...
219-
[INFO] Reconciliation controller started with 5 workers
220+
2026/02/26 11:18:05 Database schema migrated successfully
221+
2026/02/26 11:18:05 Ent storage initialized with sqlite3 database
222+
...
223+
[INFO] Starting reconciliation controller with 5 workers
220224
[INFO] Server starting on 0.0.0.0:8080
221225
...
222-
[DEBUG] Processing reconciliation for DiscoverySnapshot/discoverysnapshot-639ab206 (reason: Event: fru-tracker.resource.discoverysnapshot.created)
223-
[DEBUG] Reconciling DiscoverySnapshot DiscoverySnapshot/discoverysnapshot-639ab206
224-
[INFO] Reconciling snapshot-172.24.0.2-1770836443: Starting reconciliation
225-
[INFO] Reconciling snapshot-172.24.0.2-1770836443: Loaded 2 devices by URI and 2 by Serial
226-
[INFO] Reconciling snapshot-172.24.0.2-1770836443 (Pass 1): Creating new device: /Systems/QSBP82909274
227-
[INFO] Reconciling snapshot-172.24.0.2-1770836443 (Pass 1): Creating new device: /Systems/QSBP82909274/Processors/CPU1
228-
[INFO] Reconciling snapshot-172.24.0.2-1770836443 (Pass 1): Creating new device: /Systems/QSBP82909274/Processors/CPU2
229-
[INFO] Reconciling snapshot-172.24.0.2-1770836443 (Pass 1): Creating new device: /Systems/QSBP82909274/Memory/Memory1
230-
[INFO] Reconciling snapshot-172.24.0.2-1770836443 (Pass 1): Creating new device: /Systems/QSBP82909274/Memory/Memory2
231-
[INFO] Reconciling snapshot-172.24.0.2-1770836443 (Pass 1): Creating new device: /Systems/QSBP82909274/Memory/Memory3
232-
[INFO] Reconciling snapshot-172.24.0.2-1770836443 (Pass 1): Creating new device: /Systems/QSBP82909274/Memory/Memory4
233-
[INFO] Reconciling snapshot-172.24.0.2-1770836443 (Pass 2): Linking parent relationships...
234-
[INFO] Reconciling snapshot-172.24.0.2-1770836443 (Pass 2): Linking /Systems/QSBP82909274/Processors/CPU1 (UID: device-244b078d) to parent /Systems/QSBP82909274 (UID: device-6dad4952)
235-
[INFO] Reconciling snapshot-172.24.0.2-1770836443 (Pass 2): Linking /Systems/QSBP82909274/Processors/CPU2 (UID: device-e4973199) to parent /Systems/QSBP82909274 (UID: device-6dad4952)
236-
[INFO] Reconciling snapshot-172.24.0.2-1770836443 (Pass 2): Linking /Systems/QSBP82909274/Memory/Memory1 (UID: device-27b7425d) to parent /Systems/QSBP82909274 (UID: device-6dad4952)
237-
[INFO] Reconciling snapshot-172.24.0.2-1770836443 (Pass 2): Linking /Systems/QSBP82909274/Memory/Memory2 (UID: device-506327c2) to parent /Systems/QSBP82909274 (UID: device-6dad4952)
238-
[INFO] Reconciling snapshot-172.24.0.2-1770836443 (Pass 2): Linking /Systems/QSBP82909274/Memory/Memory3 (UID: device-693d311f) to parent /Systems/QSBP82909274 (UID: device-6dad4952)
239-
[INFO] Reconciling snapshot-172.24.0.2-1770836443 (Pass 2): Linking /Systems/QSBP82909274/Memory/Memory4 (UID: device-3f4acf01) to parent /Systems/QSBP82909274 (UID: device-6dad4952)
240-
[INFO] Reconciling snapshot-172.24.0.2-1770836443: Successfully reconciled
241-
[DEBUG] Reconciliation successful for DiscoverySnapshot/discoverysnapshot-639ab206
226+
[DEBUG] Processing reconciliation for DiscoverySnapshot/discoverysnapshot-ff300d52 (reason: Event: fru-tracker.resource.discoverysnapshot.created)
227+
[DEBUG] Reconciling DiscoverySnapshot DiscoverySnapshot/discoverysnapshot-ff300d52
228+
[INFO] Reconciling test-01: Starting reconciliation
229+
[INFO] Reconciling test-01: Loaded 0 devices by URI and 0 by Serial
230+
[INFO] Reconciling test-01 (Pass 1): Creating new device: /Systems/NODE1
231+
[INFO] Reconciling test-01 (Pass 1): Creating new device: /Systems/NODE1/Memory/1
232+
[INFO] Reconciling test-01 (Pass 2): Linking parent relationships...
233+
[INFO] Reconciling test-01 (Pass 2): Linking /Systems/NODE1/Memory/1 (UID: device-3e8d0706) to parent /Systems/NODE1 (UID: device-262b2465)
234+
[INFO] Reconciling test-01: Successfully reconciled
242235
```
243236

244237
### Step 3: Final Data in API (Result)

apis/example.fabrica.dev/v1/device_types.go

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,17 @@ package v1
66

77
import (
88
"context"
9-
"encoding/json"
109
"github.com/openchami/fabrica/pkg/fabrica"
10+
"encoding/json"
1111
)
1212

13-
// Device represents a Device resource
13+
// Device represents a device resource
1414
type Device struct {
1515
APIVersion string `json:"apiVersion"`
1616
Kind string `json:"kind"`
1717
Metadata fabrica.Metadata `json:"metadata"`
18-
Spec DeviceSpec `json:"spec" validate:"required"`
19-
Status DeviceStatus `json:"status,omitempty"`
18+
Spec DeviceSpec `json:"spec" validate:"required"`
19+
Status DeviceStatus `json:"status,omitempty"`
2020
}
2121

2222
// DeviceSpec defines the desired state of Device
@@ -40,19 +40,24 @@ type DeviceSpec struct {
4040

4141
// DeviceStatus defines the observed state of Device
4242
type DeviceStatus struct {
43-
Phase string `json:"phase,omitempty"`
44-
Message string `json:"message,omitempty"`
45-
Ready bool `json:"ready"`
46-
47-
// ChildrenDeviceIds is a read-only list of devices contained within this one.
43+
Phase string `json:"phase,omitempty"`
44+
Message string `json:"message,omitempty"`
45+
Ready bool `json:"ready"`
46+
47+
// ChildrenDeviceIds is a read-only list of devices contained within this one.
4848
ChildrenDeviceIds []string `json:"childrenDeviceIds,omitempty"`
4949
}
5050

5151
// Validate implements custom validation logic for Device
5252
func (r *Device) Validate(ctx context.Context) error {
53+
// Add custom validation logic here
54+
// Example:
55+
// if r.Spec.Description == "forbidden" {
56+
// return errors.New("description 'forbidden' is not allowed")
57+
// }
58+
5359
return nil
5460
}
55-
5661
// GetKind returns the kind of the resource
5762
func (r *Device) GetKind() string {
5863
return "Device"
@@ -67,3 +72,6 @@ func (r *Device) GetName() string {
6772
func (r *Device) GetUID() string {
6873
return r.Metadata.UID
6974
}
75+
76+
// IsHub marks this as the hub/storage version
77+
func (r *Device) IsHub() {}

apis/example.fabrica.dev/v1/discoverysnapshot_types.go

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@ package v1
66

77
import (
88
"context"
9-
"encoding/json"
109
"github.com/openchami/fabrica/pkg/fabrica"
10+
"encoding/json"
1111
)
1212

13-
// DiscoverySnapshot represents a DiscoverySnapshot resource
13+
// DiscoverySnapshot represents a discoverysnapshot resource
1414
type DiscoverySnapshot struct {
15-
APIVersion string `json:"apiVersion"`
16-
Kind string `json:"kind"`
17-
Metadata fabrica.Metadata `json:"metadata"`
15+
APIVersion string `json:"apiVersion"`
16+
Kind string `json:"kind"`
17+
Metadata fabrica.Metadata `json:"metadata"`
1818
Spec DiscoverySnapshotSpec `json:"spec" validate:"required"`
1919
Status DiscoverySnapshotStatus `json:"status,omitempty"`
2020
}
@@ -35,9 +35,14 @@ type DiscoverySnapshotStatus struct {
3535

3636
// Validate implements custom validation logic for DiscoverySnapshot
3737
func (r *DiscoverySnapshot) Validate(ctx context.Context) error {
38+
// Add custom validation logic here
39+
// Example:
40+
// if r.Spec.Description == "forbidden" {
41+
// return errors.New("description 'forbidden' is not allowed")
42+
// }
43+
3844
return nil
3945
}
40-
4146
// GetKind returns the kind of the resource
4247
func (r *DiscoverySnapshot) GetKind() string {
4348
return "DiscoverySnapshot"
@@ -52,3 +57,6 @@ func (r *DiscoverySnapshot) GetName() string {
5257
func (r *DiscoverySnapshot) GetUID() string {
5358
return r.Metadata.UID
5459
}
60+
61+
// IsHub marks this as the hub/storage version
62+
func (r *DiscoverySnapshot) IsHub() {}

cmd/client/main.go

Lines changed: 24 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
File renamed without changes.

cmd/server/device_handlers_generated.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cmd/server/discoverysnapshot_handlers_generated.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)