Skip to content
Open
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
2 changes: 2 additions & 0 deletions api/v1beta1/slice_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ type SliceConfig struct {
SliceGatewayProtocol string `json:"sliceGatewayProtocol,omitempty"`
// Slice overlay network deployment mode: single-network, multi-network or no-network
SliceOverlayNetworkDeploymentMode controllerv1alpha1.NetworkType `json:"sliceOverlayNetworkDeploymentMode,omitempty"`
// Topology configuration
TopologyConfig *controllerv1alpha1.TopologyConfig `json:"topologyConfig,omitempty"`
}

// NamespaceIsolationProfile defines the namespace isolation policy for the slice
Expand Down
10 changes: 10 additions & 0 deletions controllers/slicegateway/slicegateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -953,10 +953,20 @@ func (r *SliceGwReconciler) SendConnectionContextAndQosToGwPod(ctx context.Conte
log.Info("Gw podIPs not available yet, requeuing")
return ctrl.Result{RequeueAfter: 5 * time.Second}, nil, true
}

topologyType := "full-mesh"
if slice.Status.SliceConfig != nil && slice.Status.SliceConfig.TopologyConfig != nil {
topologyType = slice.Status.SliceConfig.TopologyConfig.TopologyType
}

connCtx := &gwsidecar.GwConnectionContext{
RemoteSliceGwVpnIP: slicegateway.Status.Config.SliceGatewayRemoteVpnIP,
RemoteSliceGwNsmSubnet: slicegateway.Status.Config.SliceGatewayRemoteSubnet,
TopologyType: topologyType,
}

log.Info("Sending connection context to gateway pod", "topology", topologyType, "remoteCluster", slicegateway.Status.Config.SliceGatewayRemoteClusterID)

for i := range gwPodsInfo {
sidecarGrpcAddress := gwPodsInfo[i].PodIP + ":5000"

Expand Down
2 changes: 2 additions & 0 deletions pkg/gwsidecar/gwsidecar.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ type GwStatus struct {
type GwConnectionContext struct {
RemoteSliceGwVpnIP string
RemoteSliceGwNsmSubnet string
TopologyType string
}

type gwSidecarClient struct {
Expand Down Expand Up @@ -149,6 +150,7 @@ func (worker gwSidecarClient) SendConnectionContext(ctx context.Context, serverA
msg := &sidecar.SliceGwConnectionContext{
RemoteSliceGwVpnIP: gwConnCtx.RemoteSliceGwVpnIP,
RemoteSliceGwNsmSubnet: gwConnCtx.RemoteSliceGwNsmSubnet,
TopologyType: gwConnCtx.TopologyType,
}

log.Info("SliceGwConnectionContext", "SliceGwConnectionContext", msg)
Expand Down
98 changes: 98 additions & 0 deletions pkg/hub/controllers/slice_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ package controllers
import (
"context"
"fmt"
"reflect"
"time"

"github.com/go-logr/logr"
Expand Down Expand Up @@ -328,6 +329,10 @@ func (r *SliceReconciler) updateSliceConfig(ctx context.Context, meshSlice *kube
VPCServiceAccess: extGwCfg.VPCServiceAccess,
}

if err := r.validateAndCopyTopology(ctx, meshSlice, spokeSlice); err != nil {
return err
}

return r.MeshClient.Status().Update(ctx, meshSlice)
}

Expand Down Expand Up @@ -661,3 +666,96 @@ func (r *SliceReconciler) UpdateSliceHealthMetrics(slice *spokev1alpha1.WorkerSl
}
}
}

func (r *SliceReconciler) validateAndCopyTopology(ctx context.Context, meshSlice *kubeslicev1beta1.Slice, spokeSlice *spokev1alpha1.WorkerSliceConfig) error {
log := logger.FromContext(ctx)
topologyValue := reflect.ValueOf(spokeSlice.Spec).FieldByName("TopologyConfig")
if !topologyValue.IsValid() || topologyValue.IsNil() {
log.V(1).Info("TopologyConfig field not present or nil, skipping topology copy")
return nil
}
topologyInterface := topologyValue.Interface()
if topologyInterface == nil {
return nil
}
topologyType := reflect.TypeOf(topologyInterface)
if topologyType.Kind() != reflect.Ptr {
return nil
}
topologyElem := topologyValue.Elem()
if !topologyElem.IsValid() {
return nil
}
topologyTypeField := topologyElem.FieldByName("TopologyType")
if !topologyTypeField.IsValid() {
return nil
}
connectivityMatrix := topologyElem.FieldByName("ConnectivityMatrix")
forbiddenEdges := topologyElem.FieldByName("ForbiddenEdges")
if !connectivityMatrix.IsValid() || !forbiddenEdges.IsValid() {
return nil
}
clustersValue := reflect.ValueOf(spokeSlice.Spec).FieldByName("Clusters")
if !clustersValue.IsValid() || clustersValue.Kind() != reflect.Slice {
return nil
}
clusterSet := make(map[string]struct{})
for i := 0; i < clustersValue.Len(); i++ {
cluster := clustersValue.Index(i).String()
clusterSet[cluster] = struct{}{}
}
if connectivityMatrix.Len() > 0 {
for i := 0; i < connectivityMatrix.Len(); i++ {
entry := connectivityMatrix.Index(i)
sourceCluster := entry.FieldByName("SourceCluster").String()
if _, ok := clusterSet[sourceCluster]; !ok {
return fmt.Errorf("connectivityMatrix[%d]: sourceCluster %q not in clusters", i, sourceCluster)
}
targetClusters := entry.FieldByName("TargetClusters")
if targetClusters.IsValid() && targetClusters.Kind() == reflect.Slice {
for j := 0; j < targetClusters.Len(); j++ {
target := targetClusters.Index(j).String()
if _, ok := clusterSet[target]; !ok {
return fmt.Errorf("connectivityMatrix[%d].targetClusters[%d]: %q not in clusters", i, j, target)
}
}
}
}
}
if forbiddenEdges.Len() > 0 {
for i := 0; i < forbiddenEdges.Len(); i++ {
edge := forbiddenEdges.Index(i)
sourceCluster := edge.FieldByName("SourceCluster").String()
if _, ok := clusterSet[sourceCluster]; !ok {
return fmt.Errorf("forbiddenEdges[%d]: sourceCluster %q not in clusters", i, sourceCluster)
}
targetClusters := edge.FieldByName("TargetClusters")
if targetClusters.IsValid() && targetClusters.Kind() == reflect.Slice {
for j := 0; j < targetClusters.Len(); j++ {
target := targetClusters.Index(j).String()
if _, ok := clusterSet[target]; !ok {
return fmt.Errorf("forbiddenEdges[%d].targetClusters[%d]: %q not in clusters", i, j, target)
}
}
}
}
}
statusConfigValue := reflect.ValueOf(meshSlice.Status.SliceConfig).Elem()
topologyConfigField := statusConfigValue.FieldByName("TopologyConfig")
if !topologyConfigField.IsValid() || !topologyConfigField.CanSet() {
log.V(1).Info("TopologyConfig field not available in worker SliceConfig, skipping copy")
return nil
}
newTopology := reflect.New(topologyConfigField.Type().Elem())
newTopologyElem := newTopology.Elem()
newTopologyElem.FieldByName("TopologyType").SetString(topologyTypeField.String())
if connectivityMatrix.IsValid() && connectivityMatrix.Len() > 0 {
newTopologyElem.FieldByName("ConnectivityMatrix").Set(connectivityMatrix)
}
if forbiddenEdges.IsValid() && forbiddenEdges.Len() > 0 {
newTopologyElem.FieldByName("ForbiddenEdges").Set(forbiddenEdges)
}
topologyConfigField.Set(newTopology)
log.Info("Topology configuration copied to worker slice", "topologyType", topologyTypeField.String())
return nil
}