Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
30526f4
feat(): add hub-and-spoke topology fields to SliceConfig CRD
Shreesha001 Jul 5, 2026
4962128
address review: single-hub schema cap, doc/comment fixes, regen CRD
Shreesha001 Jul 12, 2026
eea8f1f
address review: use field.Duplicate value form, drop duplicate topolo…
Shreesha001 Jul 13, 2026
0311682
feat: add TopologyResolver for hub-and-spoke edge computation
Shreesha001 Jul 12, 2026
7e7b16f
feat: gate WorkerSliceGateway creation on resolved topology edges
Shreesha001 Jul 12, 2026
084da7a
test: hub-and-spoke create skips spoke-to-spoke edges
Shreesha001 Jul 12, 2026
6879152
feat: remove no-longer-desired edges on topology change (cleanup gating)
Shreesha001 Jul 13, 2026
91bf1c7
feat: add gateway connectivity and slice topology status fields
Shreesha001 Jul 15, 2026
3c64e17
feat: aggregate gateway connectivity into TopologyConverged slice con…
Shreesha001 Jul 15, 2026
d9a589c
feat: mark spoke gateways to route entire slice subnet via hub
Shreesha001 Jul 16, 2026
007b2c5
test: end-to-end hub-and-spoke topology builds partial mesh (#304)
Shreesha001 Aug 3, 2026
c7b736b
fix: reconcile RouteEntireSliceSubnet on surviving gateways when topo…
Shreesha001 Aug 8, 2026
ed4d7dd
feat: add gateway connection status fields to WorkerSliceGateway CRD
Shreesha001 Aug 8, 2026
502904c
fix: clear RouteEntireSliceSubnet on server gateway after hub change
Shreesha001 Aug 24, 2026
9b45b12
test: add hub-and-spoke control-plane e2e suite and testing docs
Shreesha001 Aug 26, 2026
e2cf2f2
Merge branch 'feature/303-status-aggregation' into feature/hub-and-sp…
Shreesha001 Aug 27, 2026
3065d34
address review: correct hub-and-spoke sample note and enqueue SliceCo…
Shreesha001 Aug 28, 2026
b2a5d15
address review: reconcile TopologyConverged for no-network slices and…
Shreesha001 Aug 28, 2026
fa3ea3c
address review: reject HubAndSpoke on no-network slices, add hub-chan…
Shreesha001 Aug 28, 2026
e45597c
docs: update hub-and-spoke test counts for new webhook, derive, route…
Shreesha001 Aug 28, 2026
05a89e0
self-heal partial gateway pair on AlreadyExists; keep NONET test mock…
Shreesha001 Aug 28, 2026
3a54ff1
requeue instead of silently skipping gateway creation when a member c…
Shreesha001 Aug 28, 2026
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
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ test: manifests generate fmt vet envtest ## Run tests.
test-local: envtest ## Run tests.
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" go test ./controllers/controller/... -coverprofile cover.out

.PHONY: test-e2e-hns
test-e2e-hns: ## Run the Hub-and-Spoke (partial mesh) control-plane e2e suite against a real, disposable Kind cluster.
go test -tags e2e -v -timeout 20m ./test/e2e/...

.PHONY: int-test
int-test: envtest
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" go test ./controllers/controller/... -coverprofile cover.out
Expand Down
45 changes: 45 additions & 0 deletions apis/controller/v1alpha1/sliceconfig_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,31 @@ type SliceConfigSpec struct {
// RenewBefore is used for renew now!
RenewBefore *metav1.Time `json:"renewBefore,omitempty"`
VPNConfig *VPNConfiguration `json:"vpnConfig,omitempty"`
// Topology configures the inter-cluster connection topology for the slice.
// When absent, the slice uses full-mesh connectivity (existing behavior).
//+optional
Topology *TopologySpec `json:"topology,omitempty"`
}

// +kubebuilder:validation:Enum=FullMesh;HubAndSpoke
type TopologyMode string

const (
TopologyModeFullMesh TopologyMode = "FullMesh"
TopologyModeHubAndSpoke TopologyMode = "HubAndSpoke"
)

// TopologySpec defines the inter-cluster connection topology of the slice
type TopologySpec struct {
// Mode selects the connection topology. Absent defaults to FullMesh.
//+optional
Mode TopologyMode `json:"mode,omitempty"`
// Hubs lists the clusters acting as hubs when Mode is HubAndSpoke.
// Each entry must be a member of spec.clusters. All non-hub members
// become spokes. Exactly one hub is supported in this release.
//+optional
//+kubebuilder:validation:MaxItems=1
Hubs []string `json:"hubs,omitempty"`
Comment thread
Shreesha001 marked this conversation as resolved.
}

// ExternalGatewayConfig is the configuration for external gateways like 'istio', etc/
Expand Down Expand Up @@ -195,8 +220,28 @@ type KubesliceEvent struct {
}

// SliceConfigStatus defines the observed state of SliceConfig
// Slice status condition types and reasons for topology convergence.
const (
// SliceConditionTypeTopologyConverged reports whether every desired gateway
// link of the slice is Connected.
SliceConditionTypeTopologyConverged = "TopologyConverged"
// SliceReasonAllEdgesReady is set when all gateway links are Connected.
SliceReasonAllEdgesReady = "AllEdgesReady"
// SliceReasonEdgesNotReady is set when one or more gateway links are not Connected.
SliceReasonEdgesNotReady = "EdgesNotReady"
// SliceReasonNoGatewaysRequired is set when the slice needs no gateway links
// (single cluster or no-network mode) and is therefore trivially converged.
SliceReasonNoGatewaysRequired = "NoGatewaysRequired"
)

type SliceConfigStatus struct {
KubesliceEvents []KubesliceEvent `json:"kubesliceEvents,omitempty"`
// Conditions represent the latest available observations of the slice's
// topology state (e.g. TopologyConverged).
//+optional
//+listType=map
//+listMapKey=type
Conditions []metav1.Condition `json:"conditions,omitempty"`
}

//+kubebuilder:object:root=true
Expand Down
33 changes: 33 additions & 0 deletions apis/controller/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 29 additions & 0 deletions apis/worker/v1alpha1/workerslicegateway_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ type WorkerSliceGatewaySpec struct {
LocalGatewayConfig SliceGatewayConfig `json:"localGatewayConfig,omitempty"`
RemoteGatewayConfig SliceGatewayConfig `json:"remoteGatewayConfig,omitempty"`
GatewayNumber int `json:"gatewayNumber,omitempty"`
// RouteEntireSliceSubnet, when true, tells the worker to route the whole
// slice subnet (not just the peer gateway's subnet) via this gateway. The
// controller sets it on a spoke's gateway to the hub in HubAndSpoke topology,
// so a spoke forwards all slice-internal traffic (including traffic destined
// for other spokes) to the hub, which relays it.
RouteEntireSliceSubnet bool `json:"routeEntireSliceSubnet,omitempty"`
}

type SliceGatewayConfig struct {
Expand All @@ -61,9 +67,32 @@ type GatewayCredentials struct {
}

// WorkerSliceGatewayStatus defines the observed state of WorkerSliceGateway
// Gateway connection states reported by the worker on WorkerSliceGatewayStatus.
const (
// GatewayConnectionStateConnected means the gateway tunnel is up (at least
// one HA gateway pod reports its tunnel established).
GatewayConnectionStateConnected = "Connected"
// GatewayConnectionStateNotConnected means the tunnel is down (all gateway
// pods report their tunnel not established).
GatewayConnectionStateNotConnected = "NotConnected"
// GatewayConnectionStatePending means no connectivity has been reported yet
// (e.g. the gateway was just created). An empty ConnectionState is treated
// as Pending by the controller-side aggregation.
GatewayConnectionStatePending = "Pending"
)

type WorkerSliceGatewayStatus struct {
GatewayNumber int `json:"gatewayNumber,omitempty"`
ClusterInsertionIndex int `json:"clusterInsertionIndex,omitempty"`
// ConnectionState is the connectivity state of this gateway link as reported
// by the worker: Connected, NotConnected or Pending. Empty means Pending.
ConnectionState string `json:"connectionState,omitempty"`
// LastTransitionTime is the time ConnectionState last changed.
LastTransitionTime *metav1.Time `json:"lastTransitionTime,omitempty"`
// Reason is a short, machine-readable reason for the current ConnectionState.
Reason string `json:"reason,omitempty"`
// Message is a human-readable description of the current ConnectionState.
Message string `json:"message,omitempty"`
}

//+kubebuilder:object:root=true
Expand Down
6 changes: 5 additions & 1 deletion apis/worker/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

85 changes: 84 additions & 1 deletion config/crd/bases/controller.kubeslice.io_sliceconfigs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,28 @@ spec:
type: string
standardQosProfileName:
type: string
topology:
description: |-
Topology configures the inter-cluster connection topology for the slice.
When absent, the slice uses full-mesh connectivity (existing behavior).
properties:
hubs:
description: |-
Hubs lists the clusters acting as hubs when Mode is HubAndSpoke.
Each entry must be a member of spec.clusters. All non-hub members
become spokes. Exactly one hub is supported in this release.
items:
type: string
maxItems: 1
type: array
mode:
description: Mode selects the connection topology. Absent defaults
to FullMesh.
enum:
- FullMesh
- HubAndSpoke
type: string
type: object
vpnConfig:
description: VPNConfiguration defines the additional (optional) VPN
Configuration to customise
Expand All @@ -244,8 +266,69 @@ spec:
- maxClusters
type: object
status:
description: SliceConfigStatus defines the observed state of SliceConfig
properties:
conditions:
description: |-
Conditions represent the latest available observations of the slice's
topology state (e.g. TopologyConverged).
items:
description: Condition contains details for one aspect of the current
state of this API Resource.
properties:
lastTransitionTime:
description: |-
lastTransitionTime is the last time the condition transitioned from one status to another.
This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.
format: date-time
type: string
message:
description: |-
message is a human readable message indicating details about the transition.
This may be an empty string.
maxLength: 32768
type: string
observedGeneration:
description: |-
observedGeneration represents the .metadata.generation that the condition was set based upon.
For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
with respect to the current state of the instance.
format: int64
minimum: 0
type: integer
reason:
description: |-
reason contains a programmatic identifier indicating the reason for the condition's last transition.
Producers of specific condition types may define expected values and meanings for this field,
and whether the values are considered a guaranteed API.
The value should be a CamelCase string.
This field may not be empty.
maxLength: 1024
minLength: 1
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
type: string
status:
description: status of the condition, one of True, False, Unknown.
enum:
- "True"
- "False"
- Unknown
type: string
type:
description: type of condition in CamelCase or in foo.example.com/CamelCase.
maxLength: 316
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
type: string
required:
- lastTransitionTime
- message
- reason
- status
- type
type: object
type: array
x-kubernetes-list-map-keys:
- type
x-kubernetes-list-type: map
kubesliceEvents:
items:
properties:
Expand Down
26 changes: 25 additions & 1 deletion config/crd/bases/worker.kubeslice.io_workerslicegateways.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -123,16 +123,40 @@ spec:
vpnIp:
type: string
type: object
routeEntireSliceSubnet:
description: |-
RouteEntireSliceSubnet, when true, tells the worker to route the whole
slice subnet (not just the peer gateway's subnet) via this gateway. The
controller sets it on a spoke's gateway to the hub in HubAndSpoke topology,
so a spoke forwards all slice-internal traffic (including traffic destined
for other spokes) to the hub, which relays it.
type: boolean
sliceName:
type: string
type: object
status:
description: WorkerSliceGatewayStatus defines the observed state of WorkerSliceGateway
properties:
clusterInsertionIndex:
type: integer
connectionState:
description: 'ConnectionState is the connectivity state of this gateway
link as reported by the worker: Connected, NotConnected or Pending.
Empty means Pending.'
type: string
gatewayNumber:
type: integer
lastTransitionTime:
description: LastTransitionTime is the time ConnectionState last changed.
format: date-time
type: string
message:
description: Message is a human-readable description of the current
ConnectionState.
type: string
reason:
description: Reason is a short, machine-readable reason for the current
ConnectionState.
type: string
type: object
type: object
served: true
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Sample SliceConfig using the HubAndSpoke topology.
#
# worker-1 acts as the hub; worker-2 and worker-3 become spokes (all
# non-hub members are spokes).
#
# The controller consumes spec.topology for this sample and creates
# hub<->spoke links only: worker-1<->worker-2 and worker-1<->worker-3.
# No spoke<->spoke link is created.
#
# Omitting spec.topology entirely (or setting mode: FullMesh with no
# hubs) keeps the existing full-mesh behavior.
apiVersion: controller.kubeslice.io/v1alpha1
kind: SliceConfig
metadata:
name: hub-and-spoke-slice
spec:
sliceType: Application
sliceSubnet: 10.1.0.0/16
sliceGatewayProvider:
sliceGatewayType: OpenVPN
sliceCaType: Local
sliceIpamType: Local
clusters:
- worker-1
- worker-2
- worker-3
topology:
mode: HubAndSpoke
hubs:
- worker-1
qosProfileDetails:
queueType: HTB
priority: 1
tcType: BANDWIDTH_CONTROL
bandwidthCeilingKbps: 5120
bandwidthGuaranteedKbps: 2560
dscpClass: AF11
Loading