-
Notifications
You must be signed in to change notification settings - Fork 1
Feature/kubebuilder style versioning #27
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Add core infrastructure for hub/spoke API versioning: - pkg/apiversion: Registry for API groups and versions, Hub/Convertible interfaces - pkg/imports/catalog: Type metadata resolution for external imports These packages provide the runtime infrastructure needed for version negotiation and automatic conversion between hub (storage) and spoke (external) API versions. Part of: Hub/Spoke API Versioning feature Signed-off-by: Alex Lovell-Troy <alex@lovelltroy.org>
Add code generation templates for hub/spoke versioning: - Hub (storage) version types with flattened envelopes - Spoke (external) version types with conversion functions - Version registry initialization Integrate into generator: - Load apiversion templates in LoadTemplates() - Implement GenerateAPIVersions() method with placeholder - Wire into GenerateAll() flow after models generation Templates generate explicit APIVersion, Kind, Metadata, Spec, Status fields instead of embedding resource.Resource, improving Go autodoc and navigation. Part of: Hub/Spoke API Versioning feature Signed-off-by: Alex Lovell-Troy <alex@lovelltroy.org>
Add configuration support for hub/spoke API versioning: - APIsConfig struct with APIGroup, APIResource definitions - Field mapping structs for version-specific transformations - Import support for external type references - LoadAPIsConfig() function to read apis.yaml - ValidateAPIsConfig() with comprehensive validation rules Validation ensures: - At least one API group defined - Group name and storageVersion required - storageVersion must be in versions list - Versions list not empty This enables projects to declare multiple API versions via apis.yaml configuration file, supporting hub/spoke versioning pattern. Part of: Hub/Spoke API Versioning feature Signed-off-by: Alex Lovell-Troy <alex@lovelltroy.org>
Add comprehensive documentation for hub/spoke API versioning: Documentation: - docs/versioning.md: Complete guide (400+ lines) covering model, usage, migration path, and best practices - README.md: Updated with versioning feature in Key Features - CHANGELOG.md: Documented breaking change (flattened envelope) - examples/README.md: Added "What's New in v0.4" section - examples/01-basic-crud/README.md: Added versioning migration note New Example: - examples/08-api-versioning: Complete walkthrough demonstrating hub/spoke versioning with v1alpha1, v1beta1, v1 versions - apis.yaml.example: Configuration template for multi-version APIs Integration Tests: - test/integration/versioning_test.go: 5 comprehensive test cases - Flattened envelope structure validation - apis.yaml placeholder functionality - Backward compatibility verification - Config validation - JSON format compatibility BREAKING CHANGE: Generated resource types now use flattened envelope structure with explicit APIVersion, Kind, Metadata, Spec, Status fields instead of embedding resource.Resource. JSON wire format unchanged. Migration guide: docs/versioning.md#migration-from-pre-flattening Part of: Hub/Spoke API Versioning feature Signed-off-by: Alex Lovell-Troy <alex@lovelltroy.org>
…adata - Eliminate pkg/resources/ and apis/ redundancy in versioned mode - Merge apis.yaml into .fabrica.yaml (single config) - Add fabrica.Metadata type alias for cleaner imports - New commands: `fabrica add version`, versioned `fabrica init` - Generator discovers resources from apis/<group>/<storage-version>/ - Complete example 8 rewrite with working project structure - Flattened envelope: explicit APIVersion, Kind, Metadata fields Breaking: Versioned projects now define types directly, no generation Signed-off-by: Alex Lovell-Troy <alex@lovelltroy.org>
Signed-off-by: Alex Lovell-Troy <alex@lovelltroy.org>
Signed-off-by: Alex Lovell-Troy <alex@lovelltroy.org>
- Deleted go.sum.license and storage.go files as they are no longer needed. - Enhanced code generation to support versioned projects by introducing a new `IsVersioned` flag. - Updated handler templates to differentiate between versioned and legacy modes for resource creation, updates, and deletions. - Added resource prefix registration to ensure proper UID generation for versioned resources. - Improved event publishing logic to accommodate versioned resources. Signed-off-by: Alex Lovell-Troy <alovelltroy@lanl.gov>
…structure - Updated package names to use versioning (v1). - Added APIVersion and Kind fields to resource structs. - Enhanced metadata handling by incorporating fabrica.Metadata. - Improved JSON struct tags for better validation and clarity. - Added IsHub method to mark resources as hub/storage versions. - Reformatted comments for consistency and readability across all resource types. Signed-off-by: Alex Lovell-Troy <alovelltroy@lanl.gov>
Signed-off-by: Alex Lovell-Troy <alovelltroy@lanl.gov>
…ocumentation Signed-off-by: Alex Lovell-Troy <alovelltroy@lanl.gov>
…flow Signed-off-by: Alex Lovell-Troy <alovelltroy@lanl.gov>
Signed-off-by: Alex Lovell-Troy <alovelltroy@lanl.gov>
- Updated FabricaConfig structure to include detailed comments on configuration options. - Deprecated versioning configuration in FabricaConfig; moved to apis.yaml. - Added readAPIsConfig function to load apis.yaml for versioning. - Modified generate command to support resource discovery from apis.yaml. - Improved init command to create apis.yaml alongside .fabrica.yaml during project initialization. - Added comprehensive documentation for apis.yaml structure and usage. - Updated versioning command to work with the new apis.yaml format. - Adjusted generator defaults to disable versioning by default. Signed-off-by: Alex Lovell-Troy <alovelltroy@lanl.gov>
…nversion and discovery Signed-off-by: Alex Lovell-Troy <alovelltroy@lanl.gov>
Signed-off-by: Alex Lovell-Troy <alovelltroy@lanl.gov>
…validation Signed-off-by: Alex Lovell-Troy <alovelltroy@lanl.gov>
…initialization Signed-off-by: Alex Lovell-Troy <alovelltroy@lanl.gov>
|
It looks like some of the main README documentation needs to be updated like the quickstart before merging. |
…rce management - Updated README.md to include a Quickstart guide and improved navigation links. - Refined usage instructions in USAGE.md for initializing projects with custom API groups. - Improved error messages in add.go for better clarity on versioning and resource addition. - Removed unnecessary versioning configuration from config.go. - Enhanced generate.go to check for existing generated handler files before performing version checks. - Updated init.go to provide clearer next steps for users after project initialization. - Expanded quickstart.md with optional API versioning customization and detailed steps for resource addition. - Adjusted storage-ent.md to clarify migration steps for existing projects. - Revised codegen.md to reflect changes in resource discovery and registration. - Updated versioning.md with detailed steps for adding new API versions and managing resource evolution. - Modified examples to reflect new directory structure and resource definition locations. - Improved main.go.tmpl to clarify reconciliation controller initialization and registration of reconcilers. Signed-off-by: Alex Lovell-Troy <alovelltroy@lanl.gov>
Signed-off-by: Alex Lovell-Troy <alovelltroy@lanl.gov>
* Add import Signed-off-by: Ben McDonald <ben.mcdonald@hpe.com> * Add helpers Signed-off-by: Ben McDonald <ben.mcdonald@hpe.com> * Add example 1 test Signed-off-by: Ben McDonald <ben.mcdonald@hpe.com> * Remove outdated step Signed-off-by: Ben McDonald <ben.mcdonald@hpe.com>
…adability Signed-off-by: Alex Lovell-Troy <alovelltroy@lanl.gov>
…ioned projects Signed-off-by: Alex Lovell-Troy <alovelltroy@lanl.gov>
Signed-off-by: Alex Lovell-Troy <alovelltroy@lanl.gov>
examples/01-basic-crud/README.md
Outdated
|
|
||
| ### Step 3: Customize Your Resource | ||
|
|
||
| Edit `pkg/resources/device/device.go` to add domain-specific fields. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be apis/example.fabrica.dev/v1/device_types.go instead following the updated guide. I believe the example.fabrica.dev and the v1 portions are generated based on the contents of the apis.yaml file.
|
A couple of notes testing this PR at this commit: I went through the basic CRUD example and the basics seemed to work like expected and I'm able to add multiple versions of the API with Here's the ./client device create --spec '{
"description": "Core network switch",
"ipAddress": "192.168.1.10",
"location": "DataCenter A",
"rack": "R42"
}'
{
"apiVersion": "v1",
"kind": "Device",
"metadata": {
"name": "",
"uid": "device-f260b82a",
"createdAt": "2026-01-06T14:03:30.424022558-07:00",
"updatedAt": "2026-01-06T14:03:30.424022558-07:00"
},
"spec": {
"description": "Core network switch",
"ipAddress": "192.168.1.10",
"location": "DataCenter A",
"rack": "R42"
},
"status": {
"ready": false
}
}And the corresponding type DeviceSpec struct {
Description string `json:"description,omitempty" validate:"max=200"`
IPAddress string `json:"ipAddress,omitempty" validate:"omitempty,ip"`
Location string `json:"location,omitempty"`
Rack string `json:"rack,omitempty"`
}Here's the command again, but specifying the ./client device create --spec '{
"description": "Core network switch",
"ipAddress": "192.168.1.10",
"location": "DataCenter A",
"rack": "R42"
}' --version v2
{
"apiVersion": "v1",
"kind": "Device",
"metadata": {
"name": "",
"uid": "device-1ab74b62",
"createdAt": "2026-01-06T14:03:44.894932171-07:00",
"updatedAt": "2026-01-06T14:03:44.894932171-07:00"
},
"spec": {
"description": "Core network switch",
"ipAddress": "192.168.1.10",
"location": "DataCenter A",
"rack": "R42"
},
"status": {
"ready": false
}
}Here's the type DeviceSpec struct {
Description string `json:"description,omitempty" validate:"max=200"`
}I made sure to run I'm guessing that some of this field validation should probably be handled in I built the binary using I also had to add Edit 1: I noticed running fabrica add resource Device --version v1
Error: adding resource to non-alpha version v1 requires --force flag
Stable versions should not have new resources added after release.
Use --force if you understand the implications, or consider adding to an alpha version first.
Usage:
fabrica add resource [name] [flags]
Flags:
--force Force adding to non-alpha version
-h, --help help for resource
--package string Package name (defaults to lowercase resource name)
--version string API version (required for versioned projects, e.g., v1alpha1)
--with-status Include Status struct (default true)
--with-validation Include validation tags (default true)
--with-versioning Enable per-resource spec versioning (snapshots). Status is never versioned.
adding resource to non-alpha version v1 requires --force flag
Stable versions should not have new resources added after release.
Use --force if you understand the implications, or consider adding to an alpha version first.The cat apis.yaml
groups:
- name: example.fabrica.dev
storageVersion: v1
versions:
- v1Removing the |
| Edit the generated resource file `apis/example.fabrica.dev/v1/fru_types.go` with this structure: | ||
|
|
||
| Or create your own `pkg/resources/fru/fru.go` with this structure: | ||
| (Or reference the example in `examples/03-fru-service/pkg/resources/fru/fru.go` for guidance) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These files look slightly different. Would it be better to only reference the generated file at apis/example.fabrica.dev/v1/fru_types.go?
Diff output of files
diff apis/example.fabrica.dev/v1/fru_types.go ../pkg/resources/fru/fru.go
0a1,4
> //go:build ignore
>
> // The line above is necessary to prevent this file from being included in the main build of fabrica. You may need to remove it to succeed with the example.
> //
5c9,15
< package v1
---
> // Package fru provides resource definitions for Field Replaceable Units (FRUs).
> //
> // This package defines the FRU resource type for managing hardware components
> // that can be replaced in a system, such as CPUs, memory modules, storage devices,
> // power supplies, and network cards. FRUs track hardware inventory, location,
> // status, and lifecycle information for data center equipment management.
> package fru
8,9c18
< "context"
< "github.com/openchami/fabrica/pkg/fabrica"
---
> "github.com/openchami/fabrica/pkg/resource"
12,18c21,25
< // FRU represents a fru resource
< type FRU struct {
< APIVersion string `json:"apiVersion"`
< Kind string `json:"kind"`
< Metadata fabrica.Metadata `json:"metadata"`
< Spec FRUSpec `json:"spec" validate:"required"`
< Status FRUStatus `json:"status,omitempty"`
---
> // FRU represents a Field Replaceable Unit
> type FRU struct { //nolint:revive
> resource.Resource `json:",inline"`
> Spec FRUSpec `json:"spec"`
> Status FRUStatus `json:"status"`
22c29
< type FRUSpec struct {
---
> type FRUSpec struct { //nolint:revive
24,35c31,45
< FRUType string `json:"fruType"` // e.g., "CPU", "Memory", "Storage"
< SerialNumber string `json:"serialNumber"`
< PartNumber string `json:"partNumber"`
< Manufacturer string `json:"manufacturer"`
< Model string `json:"model"`
<
< // Location information
< Location FRULocation `json:"location"`
<
< // Relationships
< ParentUID string `json:"parentUID,omitempty"`
< ChildrenUIDs []string `json:"childrenUIDs,omitempty"`
---
> FRUType string `json:"fruType"` // e.g., "CPU", "Memory", "Storage"
> SerialNumber string `json:"serialNumber"`
> PartNumber string `json:"partNumber"`
> Manufacturer string `json:"manufacturer"`
> Model string `json:"model"`
>
> // Location information
> Location FRULocation `json:"location"`
>
> // Relationships
> ParentUID string `json:"parentUID,omitempty"` // Parent FRU
> ChildrenUIDs []string `json:"childrenUIDs,omitempty"` // Child FRUs
>
> // Redfish path for management
> RedfishPath string `json:"redfishPath,omitempty"`
37,38c47,48
< // Redfish path for management
< RedfishPath string `json:"redfishPath,omitempty"`
---
> // Custom properties
> Properties map[string]string `json:"properties,omitempty"`
56,78c66,83
< // FRUStatus defines the observed state of FRU
< type FRUStatus struct {
< Health string `json:"health"` // "OK", "Warning", "Critical", "Unknown"
< State string `json:"state"` // "Present", "Absent", "Disabled", "Unknown"
< Functional string `json:"functional"` // "Enabled", "Disabled", "Unknown"
< LastSeen string `json:"lastSeen,omitempty"`
< LastScanned string `json:"lastScanned,omitempty"`
< Errors []string `json:"errors,omitempty"`
< Temperature float64 `json:"temperature,omitempty"`
< Power float64 `json:"power,omitempty"`
< Metrics map[string]float64 `json:"metrics,omitempty"`
< Conditions []resource.Condition `json:"conditions,omitempty"`
< }
<
< // Validate implements custom validation logic for FRU
< func (r *FRU) Validate(ctx context.Context) error {
< // Add custom validation logic here
< // Example:
< // if r.Spec.Description == "forbidden" {
< // return errors.New("description 'forbidden' is not allowed")
< // }
<
< return nil
---
> type FRUStatus struct { //nolint:revive
> // Health and operational status
> Health string `json:"health"` // "OK", "Warning", "Critical", "Unknown"
> State string `json:"state"` // "Present", "Absent", "Disabled", "Unknown"
> Functional string `json:"functional"` // "Enabled", "Disabled", "Unknown"
>
> // Timestamps
> LastSeen string `json:"lastSeen,omitempty"`
> LastScanned string `json:"lastScanned,omitempty"`
>
> // Error conditions
> Errors []string `json:"errors,omitempty"`
>
> // Additional status information
> Temperature float64 `json:"temperature,omitempty"`
> Power float64 `json:"power,omitempty"`
> Metrics map[string]float64 `json:"metrics,omitempty"`
> Conditions []resource.Condition `json:"conditions,omitempty"`
79a85
>
81c87
< func (r *FRU) GetKind() string {
---
> func (f *FRU) GetKind() string {
86,87c92,93
< func (r *FRU) GetName() string {
< return r.Metadata.Name
---
> func (f *FRU) GetName() string {
> return f.Metadata.Name
91,92c97,98
< func (r *FRU) GetUID() string {
< return r.Metadata.UID
---
> func (f *FRU) GetUID() string {
> return f.Metadata.UID
95,96c101,104
< // IsHub marks this as the hub/storage version
< func (r *FRU) IsHub() {}
---
> func init() {
> // Register resource type prefix for storage
> resource.RegisterResourcePrefix("FRU", "fru")
> }
| ```bash | ||
| cp -r ../../fabrica/examples/03-fru-service/pkg/resources/fru pkg/resources/ | ||
| ``` | ||
| Edit the generated resource file `apis/example.fabrica.dev/v1/fru_types.go` with this structure: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When I try to run fabrica generate after this step, I run into an error:
Error from undefined resource
fabrica generate
🔧 Generating code...
📦 Found 1 resource(s): FRU
📝 Registration file not found, creating it...
🔍 Discovering resources...
📦 Found 1 resource(s): FRU
✅ Generated pkg/resources/register_generated.go
Next steps:
fabrica generate # Generate handlers and storage
go mod tidy # Update dependencies
go run ./cmd/server/ # Start the server
# github.com/example/fru-service/apis/example.fabrica.dev/v1
apis/example.fabrica.dev/v1/fru_types.go:67:19: undefined: resource
Error: failed to generate server code: code generation failed: exit status 1
Usage:
fabrica generate [flags]
Flags:
--client Generate client code
--debug Enable debug output showing detailed generation steps
--force Force regeneration even with version warnings
--handlers Generate HTTP handlers
-h, --help help for generate
--openapi Generate OpenAPI spec
--storage Generate storage adapters
failed to generate server code: code generation failed: exit status 1
I noticed in both the example README and the generated code that there's a []resource.Condition field in FRUStatus but I could not find where this was defined. I would have expected it to possibly be in pkg/resources/register_generated.go or another file in that directory.
Signed-off-by: Alex Lovell-Troy <alovelltroy@lanl.gov>
examples/08-api-versioning/README.md
Outdated
| ```yaml | ||
| groups: | ||
| - name: infra.example.io | ||
| storageVfra.example.io |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this line is a typo and should be deleted.
examples/08-api-versioning/README.md
Outdated
|
|
||
| **First, manually add v2alpha1** to `.fabrica.yaml`: | ||
|
|
||
| ``Use the CLI to add the version**: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the first two back ticks are supposed to be asterisks here.
| - v1 | ||
| - v2alpha1 | ||
| - v2beta1 # Add this | ||
| ```bash |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there should be closing back ticks for the YAML file here.
…move deprecated device_types.go Signed-off-by: Alex Lovell-Troy <alovelltroy@lanl.gov>
|
From a usability standpoint, I find it a bit annoying and tedious having to list and then delete all of my devices individually with The current way looks something like this to delete multiple devices. ./client device list
# ...show list of devices...
./client device delete device-0fc0a34f
Device device-0fc0a34f deleted successfully
# ...scroll back up or run './client device list' again
./client device delete device-71ea3e92
Device device-71ea3e92 deleted successfully
# ...repeat...
./client device delete device-a305f539
Device device-a305f539 deleted successfully
# ...repeat...
./client device delete device-a9fc4c25
Device device-a9fc4c25 deleted successfully
# ...repeat...
./client device delete device-fa6b7ef7
Device device-fa6b7ef7 deleted successfully |
- Introduced quick-start and test scripts for the Ent Advanced example. - Implemented export and import commands in the server for resource management. - Added templates for generating query builders and transaction support. - Enhanced README documentation for examples and usage instructions. - Updated code generation to include new export/import commands and helpers. Signed-off-by: Alex Lovell-Troy <alovelltroy@lanl.gov>
This pull request introduces a comprehensive hub/spoke API versioning system, major changes to the resource code generation flow, and updates to project configuration and documentation. The main focus is on enabling Kubebuilder-style API versioning, supporting automatic conversion between versions, and enforcing a new, explicit resource envelope structure. It also deprecates legacy, non-versioned resource management and updates legal metadata files for SPDX compliance.
API Versioning and Resource Generation:
Implements hub/spoke (Kubebuilder-style) API versioning:
apis.yamlconfiguration for managing groups, versions, and imports.apiVersion,kind,metadata,spec, andstatusfields, replacing the previous embeddedresource.Resourceapproach. [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12]Updates the CLI:
Configuration and Validation:
.fabrica.yamlschema and validation:Documentation:
Legal and Metadata Updates:
.reuse/dep5with a new SPDX-compliantREUSE.tomlfile for better license tracking. [1] [2]Breaking Changes:
resource.Resource; any custom code referencing the embedded field must be updated to use explicit fields.References:
[1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] Fd5f02adL61R61, [14] [15] [16]
Checklist
make test(or equivalent) locally and all tests passgit commit -s) with my real name and emailDescription
Please include a summary of the change and which issue is fixed.
Also include relevant motivation and context.
Fixes #(issue)
Type of Change
For more info, see Contributing Guidelines.