Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
# Add Namespace-scoped ResourceBinding and ResourceSnapshot API Types

## Requirements

Add namespace-scoped ResourceBinding and ResourceSnapshot API types to the v1beta1 placement API package to complement the existing cluster-scoped types (ClusterResourceBinding and ClusterResourceSnapshot) and match the pattern established with ResourcePlacement.

### Current State Analysis
- ✅ ClusterResourceBinding exists in `apis/placement/v1beta1/binding_types.go`
- ✅ ClusterResourceSnapshot exists in `apis/placement/v1beta1/resourcesnapshot_types.go`
- ✅ ResourcePlacement exists in `apis/placement/v1beta1/clusterresourceplacement_types.go`
- ❌ Namespace-scoped ResourceBinding is missing
- ❌ Namespace-scoped ResourceSnapshot is missing

### Required Implementation
1. Add namespace-scoped `ResourceBinding` type following the same pattern as ClusterResourceBinding
2. Add namespace-scoped `ResourceSnapshot` type following the same pattern as ClusterResourceSnapshot
3. Ensure proper kubebuilder annotations for CRD generation
4. Follow existing v1beta1 API patterns and conventions

## Additional comments from user

User requested to continue the implementation based on the existing analysis.

## Plan

### Phase 1: Add namespace-scoped ResourceBinding type
- **Task 1.1**: Add ResourceBinding type definition to `binding_types.go` - ✅ COMPLETED
- Use the same spec and status structs as ClusterResourceBinding (ResourceBindingSpec, ResourceBindingStatus) - ✅ DONE
- Add appropriate kubebuilder annotations for namespace-scoped resource - ✅ DONE
- Include proper print columns and categories - ✅ DONE
- Add ResourceBindingList type - ✅ DONE
- Success criteria: ResourceBinding type properly defined with correct annotations - ✅ ACHIEVED

- **Task 1.2**: Add ResourceBinding methods and registration - ✅ COMPLETED
- Add SetConditions, RemoveCondition, GetCondition methods - ✅ DONE
- Register ResourceBinding and ResourceBindingList in init() function - ✅ DONE
- Success criteria: Methods implemented and types registered - ✅ ACHIEVED

### Phase 2: Add namespace-scoped ResourceSnapshot type
- **Task 2.1**: Add ResourceSnapshot type definition to `resourcesnapshot_types.go` - ✅ COMPLETED
- Use the same spec and status structs as ClusterResourceSnapshot (ResourceSnapshotSpec, ResourceSnapshotStatus) - ✅ DONE
- Add appropriate kubebuilder annotations for namespace-scoped resource - ✅ DONE
- Include proper print columns and categories - ✅ DONE
- Add ResourceSnapshotList type - ✅ DONE
- Success criteria: ResourceSnapshot type properly defined with correct annotations - ✅ ACHIEVED

- **Task 2.2**: Add ResourceSnapshot methods and registration - ✅ COMPLETED
- Add SetConditions, RemoveCondition, GetCondition methods - ✅ DONE
- Register ResourceSnapshot and ResourceSnapshotList in init() function - ✅ DONE
- Success criteria: Methods implemented and types registered - ✅ ACHIEVED

### Phase 3: Validate and test
- **Task 3.1**: Check for compilation errors
- Run `go build` to ensure no syntax errors
- Success criteria: Code compiles without errors

- **Task 3.2**: Verify CRD generation (if possible)
- Check if CRDs can be generated properly
- Success criteria: No CRD generation errors

## Decisions

1. **Reuse existing spec/status types**: Following the established pattern where cluster-scoped and namespace-scoped resources share the same spec and status definitions (like ClusterResourcePlacement and ResourcePlacement)

2. **Maintain consistent naming**: Using ResourceBinding and ResourceSnapshot (without "Cluster" prefix) for namespace-scoped variants, following the ResourcePlacement pattern

3. **Keep same file organization**: Adding namespace-scoped types to the same files as their cluster-scoped counterparts, following the existing pattern in clusterresourceplacement_types.go

## Implementation Details

### ResourceBinding Implementation in `binding_types.go`

Added namespace-scoped ResourceBinding type following the same pattern as ClusterResourceBinding:

```go
// +kubebuilder:object:root=true
// +kubebuilder:resource:scope=Namespaced,categories={fleet,fleet-placement},shortName=rb
// +kubebuilder:subresource:status
// +kubebuilder:storageversion
// +kubebuilder:printcolumn:JSONPath=`.status.conditions[?(@.type=="WorkSynchronized")].status`,name="WorkSynchronized",type=string
// +kubebuilder:printcolumn:JSONPath=`.status.conditions[?(@.type=="Applied")].status`,name="ResourcesApplied",type=string
// +kubebuilder:printcolumn:JSONPath=`.status.conditions[?(@.type=="Available")].status`,name="ResourceAvailable",priority=1,type=string
// +kubebuilder:printcolumn:JSONPath=`.metadata.creationTimestamp`,name="Age",type=date

// ResourceBinding represents a scheduling decision that binds a group of resources to a cluster.
// It MUST have a label named `CRPTrackingLabel` that points to the resource policy that creates it.
type ResourceBinding struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

// The desired state of ResourceBinding.
// +required
Spec ResourceBindingSpec `json:"spec"`

// The observed status of ResourceBinding.
// +optional
Status ResourceBindingStatus `json:"status,omitempty"`
}
```

Key differences from ClusterResourceBinding:
- Scope changed from `Cluster` to `Namespaced`
- Short name changed from `crb` to `rb`
- Comment updated to refer to "resource policy" instead of "cluster resource policy"

### ResourceSnapshot Implementation in `resourcesnapshot_types.go`

Added namespace-scoped ResourceSnapshot type following the same pattern as ClusterResourceSnapshot:

```go
// +genclient
// +kubebuilder:object:root=true
// +kubebuilder:resource:scope="Namespaced",shortName=rs,categories={fleet,fleet-placement}
// +kubebuilder:subresource:status
// +kubebuilder:storageversion
// +kubebuilder:printcolumn:JSONPath=`.metadata.generation`,name="Gen",type=string
// +kubebuilder:printcolumn:JSONPath=`.metadata.creationTimestamp`,name="Age",type=date
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

// ResourceSnapshot is used to store a snapshot of selected resources by a resource placement policy.
type ResourceSnapshot struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

// The desired state of ResourceSnapshot.
// +required
Spec ResourceSnapshotSpec `json:"spec"`

// The observed status of ResourceSnapshot.
// +optional
Status ResourceSnapshotStatus `json:"status,omitempty"`
}
```

Key differences from ClusterResourceSnapshot:
- Scope changed from `Cluster` to `Namespaced`
- Short name changed from `crs` to `rs`
- Comment updated to refer to "resource placement policy" instead of "ResourcePlacement"
- Removed `+genclient:nonNamespaced` annotation for namespace-scoped resource

### Common Patterns

Both implementations follow the established patterns:
1. **Shared Spec/Status Types**: Reuse existing `ResourceBindingSpec`/`ResourceBindingStatus` and `ResourceSnapshotSpec`/`ResourceSnapshotStatus`
2. **Consistent Annotations**: Same kubebuilder annotations pattern with scope changes
3. **Helper Methods**: Same SetConditions, GetCondition methods
4. **Registration**: Added to `init()` function alongside cluster-scoped variants
5. **Generated Code**: DeepCopy methods generated automatically by `make generate`

## Changes Made

### Files Modified

1. **`/home/zhangryan/github/kubefleet/kubefleet/apis/placement/v1beta1/binding_types.go`**
- Added namespace-scoped `ResourceBinding` type
- Added `ResourceBindingList` type
- Added `SetConditions`, `RemoveCondition`, `GetCondition` methods for ResourceBinding
- Updated `init()` function to register ResourceBinding and ResourceBindingList types

2. **`/home/zhangryan/github/kubefleet/kubefleet/apis/placement/v1beta1/resourcesnapshot_types.go`**
- Added namespace-scoped `ResourceSnapshot` type
- Added `ResourceSnapshotList` type
- Added `SetConditions`, `GetCondition` methods for ResourceSnapshot
- Updated `init()` function to register ResourceSnapshot and ResourceSnapshotList types

### Generated Files Updated
- DeepCopy methods automatically generated for new types via `make generate`
- New types now implement the required `runtime.Object` interface

### Compilation Validation
- All files compile successfully with `go build ./apis/placement/v1beta1`
- No syntax or import errors detected

## Before/After Comparison

### Before Implementation

The v1beta1 placement API package was missing namespace-scoped variants of ResourceBinding and ResourceSnapshot:

**Missing Types:**
- ❌ `ResourceBinding` (namespace-scoped)
- ❌ `ResourceBindingList` (namespace-scoped)
- ❌ `ResourceSnapshot` (namespace-scoped)
- ❌ `ResourceSnapshotList` (namespace-scoped)

**Existing Types:**
- ✅ `ClusterResourceBinding` (cluster-scoped)
- ✅ `ClusterResourceBindingList` (cluster-scoped)
- ✅ `ClusterResourceSnapshot` (cluster-scoped)
- ✅ `ClusterResourceSnapshotList` (cluster-scoped)
- ✅ `ResourcePlacement` (namespace-scoped)
- ✅ `ClusterResourcePlacement` (cluster-scoped)

This created an inconsistency where ResourcePlacement had both cluster and namespace-scoped variants, but the associated ResourceBinding and ResourceSnapshot types only had cluster-scoped variants.

### After Implementation

Now the v1beta1 placement API package has complete symmetry between cluster-scoped and namespace-scoped resources:

**Cluster-Scoped Resources:**
- ✅ `ClusterResourcePlacement`
- ✅ `ClusterResourceBinding`
- ✅ `ClusterResourceSnapshot`

**Namespace-Scoped Resources:**
- ✅ `ResourcePlacement`
- ✅ `ResourceBinding` ← **NEW**
- ✅ `ResourceSnapshot` ← **NEW**

**Benefits:**
1. **API Consistency**: Complete symmetry between cluster and namespace-scoped placement resources
2. **Pattern Adherence**: Follows established kubebuilder annotation patterns
3. **Code Reuse**: Leverages existing spec/status type definitions
4. **Future Ready**: Enables namespace-scoped resource management workflows

## References

- `/home/zhangryan/github/kubefleet/kubefleet/apis/placement/v1beta1/binding_types.go` - Contains ClusterResourceBinding definition
- `/home/zhangryan/github/kubefleet/kubefleet/apis/placement/v1beta1/resourcesnapshot_types.go` - Contains ClusterResourceSnapshot definition
- `/home/zhangryan/github/kubefleet/kubefleet/apis/placement/v1beta1/clusterresourceplacement_types.go` - Contains both ClusterResourcePlacement and ResourcePlacement definitions (pattern to follow)
- `/home/zhangryan/github/kubefleet/kubefleet/apis/placement/v1/binding_types.go` - Reference implementation from v1 API
- `/home/zhangryan/github/kubefleet/kubefleet/apis/placement/v1/resourcesnapshot_types.go` - Reference implementation from v1 API

## Task Checklist

### Phase 1: Add namespace-scoped ResourceBinding type
- [x] Task 1.1: Add ResourceBinding type definition to `binding_types.go`
- [x] Task 1.2: Add ResourceBinding methods and registration

### Phase 2: Add namespace-scoped ResourceSnapshot type
- [x] Task 2.1: Add ResourceSnapshot type definition to `resourcesnapshot_types.go`
- [x] Task 2.2: Add ResourceSnapshot methods and registration

### Phase 3: Validate and test
- [x] Task 3.1: Check for compilation errors
- [x] Task 3.2: Verify CRD generation (if possible)

## Success Criteria

The implementation is complete when:
1. ✅ Namespace-scoped ResourceBinding type is properly defined with correct kubebuilder annotations
2. ✅ Namespace-scoped ResourceSnapshot type is properly defined with correct kubebuilder annotations
3. ✅ Both types follow the established v1beta1 API patterns
4. ✅ Code compiles without errors
5. ✅ Types are properly registered in the scheme

**🎉 ALL SUCCESS CRITERIA ACHIEVED - IMPLEMENTATION COMPLETE**
54 changes: 53 additions & 1 deletion apis/placement/v1beta1/binding_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,41 @@ type ClusterResourceBindingList struct {
Items []ClusterResourceBinding `json:"items"`
}

// +kubebuilder:object:root=true
// +kubebuilder:resource:scope=Namespaced,categories={fleet,fleet-placement},shortName=rb
// +kubebuilder:subresource:status
// +kubebuilder:storageversion
// +kubebuilder:printcolumn:JSONPath=`.status.conditions[?(@.type=="WorkSynchronized")].status`,name="WorkSynchronized",type=string
// +kubebuilder:printcolumn:JSONPath=`.status.conditions[?(@.type=="Applied")].status`,name="ResourcesApplied",type=string
// +kubebuilder:printcolumn:JSONPath=`.status.conditions[?(@.type=="Available")].status`,name="ResourceAvailable",priority=1,type=string
// +kubebuilder:printcolumn:JSONPath=`.metadata.creationTimestamp`,name="Age",type=date

// ResourceBinding represents a scheduling decision that binds a group of resources to a cluster.
// It MUST have a label named `CRPTrackingLabel` that points to the resource placement that creates it.
type ResourceBinding struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

// The desired state of ResourceBinding.
// +required
Spec ResourceBindingSpec `json:"spec"`

// The observed status of ResourceBinding.
// +optional
Status ResourceBindingStatus `json:"status,omitempty"`
}

// ResourceBindingList is a collection of ResourceBinding.
// +kubebuilder:resource:scope="Namespaced"
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type ResourceBindingList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`

// items is the list of ResourceBindings.
Items []ResourceBinding `json:"items"`
}

// SetConditions set the given conditions on the ClusterResourceBinding.
func (b *ClusterResourceBinding) SetConditions(conditions ...metav1.Condition) {
for _, c := range conditions {
Expand All @@ -228,6 +263,23 @@ func (b *ClusterResourceBinding) GetCondition(conditionType string) *metav1.Cond
return meta.FindStatusCondition(b.Status.Conditions, conditionType)
}

// SetConditions set the given conditions on the ResourceBinding.
func (b *ResourceBinding) SetConditions(conditions ...metav1.Condition) {
for _, c := range conditions {
meta.SetStatusCondition(&b.Status.Conditions, c)
}
}

// RemoveCondition removes the condition of the given ResourceBinding.
func (b *ResourceBinding) RemoveCondition(conditionType string) {
meta.RemoveStatusCondition(&b.Status.Conditions, conditionType)
}

// GetCondition returns the condition of the given ResourceBinding.
func (b *ResourceBinding) GetCondition(conditionType string) *metav1.Condition {
return meta.FindStatusCondition(b.Status.Conditions, conditionType)
}

func init() {
SchemeBuilder.Register(&ClusterResourceBinding{}, &ClusterResourceBindingList{})
SchemeBuilder.Register(&ClusterResourceBinding{}, &ClusterResourceBindingList{}, &ResourceBinding{}, &ResourceBindingList{})
}
Loading
Loading