diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index fe0ad3d27..feef975ff 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -12,6 +12,7 @@ resources: - bases/controller.kubeslice.io_sliceqosconfigs.yaml - bases/worker.kubeslice.io_workerslicegwrecyclers.yaml - bases/controller.kubeslice.io_vpnkeyrotations.yaml + - bases/controller.kubeslice.io_sliceipams.yaml #+kubebuilder:scaffold:crdkustomizeresource patchesStrategicMerge: diff --git a/service/mocks/IWorkerSliceConfigService.go b/service/mocks/IWorkerSliceConfigService.go index f9308b3a8..ba25f56b2 100644 --- a/service/mocks/IWorkerSliceConfigService.go +++ b/service/mocks/IWorkerSliceConfigService.go @@ -39,7 +39,7 @@ func (_m *IWorkerSliceConfigService) ComputeClusterMap(clusterNames []string, wo } // CreateMinimalWorkerSliceConfig provides a mock function with given fields: ctx, clusters, namespace, label, name, sliceSubnet, clusterCidr, sliceGwSvcTypeMap, sliceConfig, sipam -func (_m *IWorkerSliceConfigService) CreateMinimalWorkerSliceConfig(ctx context.Context, clusters []string, namespace string, label map[string]string, name string, sliceSubnet string, clusterCidr string, sliceGwSvcTypeMap map[string]*controllerv1alpha1.SliceGatewayServiceType, sliceConfig *controllerv1alpha1.SliceConfig, sipam interface{}) (map[string]int, error) { +func (_m *IWorkerSliceConfigService) CreateMinimalWorkerSliceConfig(ctx context.Context, clusters []string, namespace string, label map[string]string, name string, sliceSubnet string, clusterCidr string, sliceGwSvcTypeMap map[string]*controllerv1alpha1.SliceGatewayServiceType, sliceConfig *controllerv1alpha1.SliceConfig, sipam interface{}) (map[string]int, map[string]string, error) { ret := _m.Called(ctx, clusters, namespace, label, name, sliceSubnet, clusterCidr, sliceGwSvcTypeMap, sliceConfig, sipam) if len(ret) == 0 { @@ -47,8 +47,9 @@ func (_m *IWorkerSliceConfigService) CreateMinimalWorkerSliceConfig(ctx context. } var r0 map[string]int - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, []string, string, map[string]string, string, string, string, map[string]*controllerv1alpha1.SliceGatewayServiceType, *controllerv1alpha1.SliceConfig, interface{}) (map[string]int, error)); ok { + var r1 map[string]string + var r2 error + if rf, ok := ret.Get(0).(func(context.Context, []string, string, map[string]string, string, string, string, map[string]*controllerv1alpha1.SliceGatewayServiceType, *controllerv1alpha1.SliceConfig, interface{}) (map[string]int, map[string]string, error)); ok { return rf(ctx, clusters, namespace, label, name, sliceSubnet, clusterCidr, sliceGwSvcTypeMap, sliceConfig, sipam) } if rf, ok := ret.Get(0).(func(context.Context, []string, string, map[string]string, string, string, string, map[string]*controllerv1alpha1.SliceGatewayServiceType, *controllerv1alpha1.SliceConfig, interface{}) map[string]int); ok { @@ -59,13 +60,21 @@ func (_m *IWorkerSliceConfigService) CreateMinimalWorkerSliceConfig(ctx context. } } - if rf, ok := ret.Get(1).(func(context.Context, []string, string, map[string]string, string, string, string, map[string]*controllerv1alpha1.SliceGatewayServiceType, *controllerv1alpha1.SliceConfig, interface{}) error); ok { + if rf, ok := ret.Get(1).(func(context.Context, []string, string, map[string]string, string, string, string, map[string]*controllerv1alpha1.SliceGatewayServiceType, *controllerv1alpha1.SliceConfig, interface{}) map[string]string); ok { r1 = rf(ctx, clusters, namespace, label, name, sliceSubnet, clusterCidr, sliceGwSvcTypeMap, sliceConfig, sipam) } else { - r1 = ret.Error(1) + if ret.Get(1) != nil { + r1 = ret.Get(1).(map[string]string) + } } - return r0, r1 + if rf, ok := ret.Get(2).(func(context.Context, []string, string, map[string]string, string, string, string, map[string]*controllerv1alpha1.SliceGatewayServiceType, *controllerv1alpha1.SliceConfig, interface{}) error); ok { + r2 = rf(ctx, clusters, namespace, label, name, sliceSubnet, clusterCidr, sliceGwSvcTypeMap, sliceConfig, sipam) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 } // CreateMinimalWorkerSliceConfigForNoNetworkSlice provides a mock function with given fields: ctx, clusters, namespace, label, name diff --git a/service/mocks/IWorkerSliceGatewayService.go b/service/mocks/IWorkerSliceGatewayService.go index c17b2e785..d195c2759 100644 --- a/service/mocks/IWorkerSliceGatewayService.go +++ b/service/mocks/IWorkerSliceGatewayService.go @@ -20,17 +20,17 @@ type IWorkerSliceGatewayService struct { mock.Mock } -// BuildNetworkAddresses provides a mock function with given fields: sliceSubnet, sourceClusterName, destinationClusterName, clusterMap, clusterCidr -func (_m *IWorkerSliceGatewayService) BuildNetworkAddresses(sliceSubnet string, sourceClusterName string, destinationClusterName string, clusterMap map[string]int, clusterCidr string) util.WorkerSliceGatewayNetworkAddresses { - ret := _m.Called(sliceSubnet, sourceClusterName, destinationClusterName, clusterMap, clusterCidr) +// BuildNetworkAddresses provides a mock function with given fields: sliceSubnet, sourceClusterName, destinationClusterName, clusterMap, subnetMap, clusterCidr +func (_m *IWorkerSliceGatewayService) BuildNetworkAddresses(sliceSubnet string, sourceClusterName string, destinationClusterName string, clusterMap map[string]int, subnetMap map[string]string, clusterCidr string) util.WorkerSliceGatewayNetworkAddresses { + ret := _m.Called(sliceSubnet, sourceClusterName, destinationClusterName, clusterMap, subnetMap, clusterCidr) if len(ret) == 0 { panic("no return value specified for BuildNetworkAddresses") } var r0 util.WorkerSliceGatewayNetworkAddresses - if rf, ok := ret.Get(0).(func(string, string, string, map[string]int, string) util.WorkerSliceGatewayNetworkAddresses); ok { - r0 = rf(sliceSubnet, sourceClusterName, destinationClusterName, clusterMap, clusterCidr) + if rf, ok := ret.Get(0).(func(string, string, string, map[string]int, map[string]string, string) util.WorkerSliceGatewayNetworkAddresses); ok { + r0 = rf(sliceSubnet, sourceClusterName, destinationClusterName, clusterMap, subnetMap, clusterCidr) } else { r0 = ret.Get(0).(util.WorkerSliceGatewayNetworkAddresses) } @@ -38,9 +38,9 @@ func (_m *IWorkerSliceGatewayService) BuildNetworkAddresses(sliceSubnet string, return r0 } -// CreateMinimumWorkerSliceGateways provides a mock function with given fields: ctx, sliceName, clusterNames, namespace, label, clusterMap, sliceSubnet, clusterCidr, sliceGwSvcTypeMap -func (_m *IWorkerSliceGatewayService) CreateMinimumWorkerSliceGateways(ctx context.Context, sliceName string, clusterNames []string, namespace string, label map[string]string, clusterMap map[string]int, sliceSubnet string, clusterCidr string, sliceGwSvcTypeMap map[string]*v1alpha1.SliceGatewayServiceType) (reconcile.Result, error) { - ret := _m.Called(ctx, sliceName, clusterNames, namespace, label, clusterMap, sliceSubnet, clusterCidr, sliceGwSvcTypeMap) +// CreateMinimumWorkerSliceGateways provides a mock function with given fields: ctx, sliceName, clusterNames, namespace, label, clusterMap, subnetMap, sliceSubnet, clusterCidr, sliceGwSvcTypeMap +func (_m *IWorkerSliceGatewayService) CreateMinimumWorkerSliceGateways(ctx context.Context, sliceName string, clusterNames []string, namespace string, label map[string]string, clusterMap map[string]int, subnetMap map[string]string, sliceSubnet string, clusterCidr string, sliceGwSvcTypeMap map[string]*v1alpha1.SliceGatewayServiceType) (reconcile.Result, error) { + ret := _m.Called(ctx, sliceName, clusterNames, namespace, label, clusterMap, subnetMap, sliceSubnet, clusterCidr, sliceGwSvcTypeMap) if len(ret) == 0 { panic("no return value specified for CreateMinimumWorkerSliceGateways") @@ -48,17 +48,17 @@ func (_m *IWorkerSliceGatewayService) CreateMinimumWorkerSliceGateways(ctx conte var r0 reconcile.Result var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, []string, string, map[string]string, map[string]int, string, string, map[string]*v1alpha1.SliceGatewayServiceType) (reconcile.Result, error)); ok { - return rf(ctx, sliceName, clusterNames, namespace, label, clusterMap, sliceSubnet, clusterCidr, sliceGwSvcTypeMap) + if rf, ok := ret.Get(0).(func(context.Context, string, []string, string, map[string]string, map[string]int, map[string]string, string, string, map[string]*v1alpha1.SliceGatewayServiceType) (reconcile.Result, error)); ok { + return rf(ctx, sliceName, clusterNames, namespace, label, clusterMap, subnetMap, sliceSubnet, clusterCidr, sliceGwSvcTypeMap) } - if rf, ok := ret.Get(0).(func(context.Context, string, []string, string, map[string]string, map[string]int, string, string, map[string]*v1alpha1.SliceGatewayServiceType) reconcile.Result); ok { - r0 = rf(ctx, sliceName, clusterNames, namespace, label, clusterMap, sliceSubnet, clusterCidr, sliceGwSvcTypeMap) + if rf, ok := ret.Get(0).(func(context.Context, string, []string, string, map[string]string, map[string]int, map[string]string, string, string, map[string]*v1alpha1.SliceGatewayServiceType) reconcile.Result); ok { + r0 = rf(ctx, sliceName, clusterNames, namespace, label, clusterMap, subnetMap, sliceSubnet, clusterCidr, sliceGwSvcTypeMap) } else { r0 = ret.Get(0).(reconcile.Result) } - if rf, ok := ret.Get(1).(func(context.Context, string, []string, string, map[string]string, map[string]int, string, string, map[string]*v1alpha1.SliceGatewayServiceType) error); ok { - r1 = rf(ctx, sliceName, clusterNames, namespace, label, clusterMap, sliceSubnet, clusterCidr, sliceGwSvcTypeMap) + if rf, ok := ret.Get(1).(func(context.Context, string, []string, string, map[string]string, map[string]int, map[string]string, string, string, map[string]*v1alpha1.SliceGatewayServiceType) error); ok { + r1 = rf(ctx, sliceName, clusterNames, namespace, label, clusterMap, subnetMap, sliceSubnet, clusterCidr, sliceGwSvcTypeMap) } else { r1 = ret.Error(1) } diff --git a/service/slice_config_service.go b/service/slice_config_service.go index 7e0c035aa..064ef1335 100644 --- a/service/slice_config_service.go +++ b/service/slice_config_service.go @@ -240,17 +240,32 @@ func (s *SliceConfigService) ReconcileSliceConfig(ctx context.Context, req ctrl. logger.Infof("Using Static IPAM for slice %s with clusterCidr %s", sliceConfig.Name, clusterCidr) } + // For Dynamic IPAM, we need the SliceSubnet from the SliceIpam resource to generate VPN addresses + // sliceConfig.Spec.SliceSubnet is empty for Dynamic IPAM + effectiveSliceSubnet := sliceConfig.Spec.SliceSubnet + if sliceConfig.Spec.SliceIpamType == "Dynamic" && s.sipam != nil { + sliceIpam := &v1alpha1.SliceIpam{} + ipamKey := types.NamespacedName{Name: sliceConfig.Name, Namespace: sliceConfig.Namespace} + foundIpam, ipamErr := util.GetResourceIfExist(ctx, ipamKey, sliceIpam) + if ipamErr == nil && foundIpam { + effectiveSliceSubnet = sliceIpam.Spec.SliceSubnet + logger.Infof("Using SliceSubnet %s from SliceIpam for VPN address generation", effectiveSliceSubnet) + } else { + logger.Warnf("Could not get SliceSubnet from SliceIpam for VPN address generation") + } + } + // collect slice gw svc info for given clusters sliceGwSvcTypeMap := getSliceGwSvcTypes(sliceConfig) // Pass sliceConfig and sipam service to enable Dynamic IPAM in worker slice config creation - clusterMap, err := s.ms.CreateMinimalWorkerSliceConfig(ctx, sliceConfig.Spec.Clusters, req.Namespace, ownershipLabel, sliceConfig.Name, sliceConfig.Spec.SliceSubnet, clusterCidr, sliceGwSvcTypeMap, sliceConfig, s.sipam) + clusterMap, subnetMap, err := s.ms.CreateMinimalWorkerSliceConfig(ctx, sliceConfig.Spec.Clusters, req.Namespace, ownershipLabel, sliceConfig.Name, effectiveSliceSubnet, clusterCidr, sliceGwSvcTypeMap, sliceConfig, s.sipam) if err != nil { return ctrl.Result{}, err } // Create gateways with minimum specification - _, err = s.sgs.CreateMinimumWorkerSliceGateways(ctx, sliceConfig.Name, sliceConfig.Spec.Clusters, req.Namespace, ownershipLabel, clusterMap, sliceConfig.Spec.SliceSubnet, clusterCidr, sliceGwSvcTypeMap) + _, err = s.sgs.CreateMinimumWorkerSliceGateways(ctx, sliceConfig.Name, sliceConfig.Spec.Clusters, req.Namespace, ownershipLabel, clusterMap, subnetMap, effectiveSliceSubnet, clusterCidr, sliceGwSvcTypeMap) if err != nil { return ctrl.Result{}, err } diff --git a/service/vpn_key_rotation_service.go b/service/vpn_key_rotation_service.go index 7da8437ca..385a12cb5 100644 --- a/service/vpn_key_rotation_service.go +++ b/service/vpn_key_rotation_service.go @@ -45,6 +45,7 @@ type IVpnKeyRotationService interface { type VpnKeyRotationService struct { wsgs IWorkerSliceGatewayService wscs IWorkerSliceConfigService + sipam ISliceIpamService jobCreationInProgress atomic.Bool } @@ -353,8 +354,28 @@ func (v *VpnKeyRotationService) triggerJobsForCertCreation(ctx context.Context, return err } clusterMap := v.wscs.ComputeClusterMap(s.Spec.Clusters, workerSliceConfigs) + + // Construct subnetMap from WorkerSliceConfigs + subnetMap := make(map[string]string) + for _, wsc := range workerSliceConfigs { + if wsc.Spec.ClusterSubnetCIDR != "" { + subnetMap[wsc.Labels["worker-cluster"]] = wsc.Spec.ClusterSubnetCIDR + } + } + + // For Dynamic IPAM, we need the SliceSubnet from the SliceIpam resource + effectiveSliceSubnet := s.Spec.SliceSubnet + if s.Spec.SliceIpamType == "Dynamic" && v.sipam != nil { + sliceIpam := &controllerv1alpha1.SliceIpam{} + ipamKey := types.NamespacedName{Name: s.Name, Namespace: s.Namespace} + foundIpam, ipamErr := util.GetResourceIfExist(ctx, ipamKey, sliceIpam) + if ipamErr == nil && foundIpam { + effectiveSliceSubnet = sliceIpam.Spec.SliceSubnet + } + } + // contruct gw address - gatewayAddresses := v.wsgs.BuildNetworkAddresses(s.Spec.SliceSubnet, gateway.Spec.LocalGatewayConfig.ClusterName, gateway.Spec.RemoteGatewayConfig.ClusterName, clusterMap, clusterCidr) + gatewayAddresses := v.wsgs.BuildNetworkAddresses(effectiveSliceSubnet, gateway.Spec.LocalGatewayConfig.ClusterName, gateway.Spec.RemoteGatewayConfig.ClusterName, clusterMap, subnetMap, clusterCidr) // call GenerateCerts() if err := v.wsgs.GenerateCerts(ctx, s.Name, s.Namespace, gateway.Spec.GatewayProtocol, &gateway, cl, gatewayAddresses); err != nil { return err diff --git a/service/worker_slice_config_service.go b/service/worker_slice_config_service.go index 82c922873..57401ff69 100644 --- a/service/worker_slice_config_service.go +++ b/service/worker_slice_config_service.go @@ -42,7 +42,7 @@ type IWorkerSliceConfigService interface { DeleteWorkerSliceConfigByLabel(ctx context.Context, label map[string]string, namespace string) error ListWorkerSliceConfigs(ctx context.Context, ownerLabel map[string]string, namespace string) ([]workerv1alpha1.WorkerSliceConfig, error) ComputeClusterMap(clusterNames []string, workerSliceConfigs []workerv1alpha1.WorkerSliceConfig) map[string]int - CreateMinimalWorkerSliceConfig(ctx context.Context, clusters []string, namespace string, label map[string]string, name, sliceSubnet string, clusterCidr string, sliceGwSvcTypeMap map[string]*controllerv1alpha1.SliceGatewayServiceType, sliceConfig *controllerv1alpha1.SliceConfig, sipam interface{}) (map[string]int, error) + CreateMinimalWorkerSliceConfig(ctx context.Context, clusters []string, namespace string, label map[string]string, name, sliceSubnet string, clusterCidr string, sliceGwSvcTypeMap map[string]*controllerv1alpha1.SliceGatewayServiceType, sliceConfig *controllerv1alpha1.SliceConfig, sipam interface{}) (map[string]int, map[string]string, error) CreateMinimalWorkerSliceConfigForNoNetworkSlice(ctx context.Context, clusters []string, namespace string, label map[string]string, name string) error } @@ -288,7 +288,7 @@ outer: // CreateMinimalWorkerSliceConfig CreateWorkerSliceConfig is a function to create the worker slice configs with minimum number of fields. // More fields are added in reconciliation loop. -func (s *WorkerSliceConfigService) CreateMinimalWorkerSliceConfig(ctx context.Context, clusters []string, namespace string, label map[string]string, name, sliceSubnet string, clusterCidr string, sliceGwSvcTypeMap map[string]*controllerv1alpha1.SliceGatewayServiceType, sliceConfig *controllerv1alpha1.SliceConfig, sipam interface{}) (map[string]int, error) { +func (s *WorkerSliceConfigService) CreateMinimalWorkerSliceConfig(ctx context.Context, clusters []string, namespace string, label map[string]string, name, sliceSubnet string, clusterCidr string, sliceGwSvcTypeMap map[string]*controllerv1alpha1.SliceGatewayServiceType, sliceConfig *controllerv1alpha1.SliceConfig, sipam interface{}) (map[string]int, map[string]string, error) { logger := util.CtxLogger(ctx) //Load Event Recorder with project name, slice name and namespace @@ -304,13 +304,14 @@ func (s *WorkerSliceConfigService) CreateMinimalWorkerSliceConfig(ctx context.Co err := s.cleanUpSlices(ctx, label, namespace, clusters) if err != nil { - return nil, err + return nil, nil, err } workerSliceConfigs, err := s.ListWorkerSliceConfigs(ctx, label, namespace) if err != nil { - return nil, err + return nil, nil, err } clusterMap := s.ComputeClusterMap(clusters, workerSliceConfigs) + subnetMap := make(map[string]string) // Check if we're using Dynamic IPAM and convert sipam to proper type var sliceIpamService ISliceIpamService @@ -332,7 +333,7 @@ func (s *WorkerSliceConfigService) CreateMinimalWorkerSliceConfig(ctx context.Co }, existingSlice) if err != nil { - return clusterMap, err + return clusterMap, nil, err } // Determine cluster subnet based on IPAM type @@ -345,7 +346,7 @@ func (s *WorkerSliceConfigService) CreateMinimalWorkerSliceConfig(ctx context.Co subnet, allocErr := sliceIpamService.AllocateSubnetForCluster(ctx, name, cluster, namespace) if allocErr != nil { logger.Errorf("Failed to allocate subnet for cluster %s: %v", cluster, allocErr) - return clusterMap, fmt.Errorf("dynamic IPAM allocation failed for cluster %s: %v", cluster, allocErr) + return clusterMap, nil, fmt.Errorf("dynamic IPAM allocation failed for cluster %s: %v", cluster, allocErr) } clusterSubnetCIDR = subnet logger.Infof("Allocated subnet %s to cluster %s via Dynamic IPAM", subnet, cluster) @@ -354,6 +355,7 @@ func (s *WorkerSliceConfigService) CreateMinimalWorkerSliceConfig(ctx context.Co clusterSubnetCIDR = util.GetClusterPrefixPool(sliceSubnet, ipamOctet, clusterCidr) logger.Debugf("Using Static IPAM: subnet %s for cluster %s", clusterSubnetCIDR, cluster) } + subnetMap[cluster] = clusterSubnetCIDR // determine gw svc type sliceGwSvcType := defaultSliceGatewayServiceType @@ -400,7 +402,7 @@ func (s *WorkerSliceConfigService) CreateMinimalWorkerSliceConfig(ctx context.Co if !k8sErrors.IsAlreadyExists(err) { // ignores resource already exists error(for handling parallel calls to create same resource) logger.Debug("failed to create worker slice %s since it already exists, namespace - %s ", expectedSlice.Name, namespace) - return clusterMap, err + return clusterMap, nil, err } } //Register an event for worker slice config creation success @@ -439,7 +441,7 @@ func (s *WorkerSliceConfigService) CreateMinimalWorkerSliceConfig(ctx context.Co if !k8sErrors.IsAlreadyExists(err) { // ignores resource already exists error(for handling parallel calls to create same resource) logger.Debug("failed to create worker slice %s since it already exists, namespace - %s ", workerSliceConfigName, namespace) - return clusterMap, err + return clusterMap, nil, err } } //Register an event for worker slice config update success @@ -454,7 +456,7 @@ func (s *WorkerSliceConfigService) CreateMinimalWorkerSliceConfig(ctx context.Co ) } } - return clusterMap, nil + return clusterMap, subnetMap, nil } func (s *WorkerSliceConfigService) CreateMinimalWorkerSliceConfigForNoNetworkSlice(ctx context.Context, clusters []string, namespace string, label map[string]string, name string) error { diff --git a/service/worker_slice_config_service_test.go b/service/worker_slice_config_service_test.go index 26d2a4317..9bc74b0ff 100644 --- a/service/worker_slice_config_service_test.go +++ b/service/worker_slice_config_service_test.go @@ -339,7 +339,7 @@ func testCreateWorkerSliceConfigNewClusterSuccess(t *testing.T) { clientMock.On("Create", ctx, mock.Anything).Return(nil).Once() clientMock.On("Create", ctx, mock.AnythingOfType("*v1.Event")).Return(nil).Once() mMock.On("RecordCounterMetric", mock.Anything, mock.Anything).Return().Once() - result, err := WorkerSliceService.CreateMinimalWorkerSliceConfig(ctx, []string{"cluster-1", "cluster-2"}, requestObj.Namespace, label, "red", "198.23.54.47/16", "/20", nil, nil, nil) + result, _, err := WorkerSliceService.CreateMinimalWorkerSliceConfig(ctx, []string{"cluster-1", "cluster-2"}, requestObj.Namespace, label, "red", "198.23.54.47/16", "/20", nil, nil, nil) require.Equal(t, len(result), 2) require.NoError(t, nil) require.Nil(t, err) @@ -406,7 +406,7 @@ func testCreateWorkerSliceConfigNewClusterFails(t *testing.T) { clientMock.On("Create", ctx, mock.Anything).Return(err1).Once() clientMock.On("Create", ctx, mock.AnythingOfType("*v1.Event")).Return(nil).Once() mMock.On("RecordCounterMetric", mock.Anything, mock.Anything).Return().Once() - result, err := WorkerSliceService.CreateMinimalWorkerSliceConfig(ctx, []string{"cluster-1", "cluster-2"}, requestObj.Namespace, label, "red", "198.23.54.47/16", "/20", nil, nil, nil) + result, _, err := WorkerSliceService.CreateMinimalWorkerSliceConfig(ctx, []string{"cluster-1", "cluster-2"}, requestObj.Namespace, label, "red", "198.23.54.47/16", "/20", nil, nil, nil) require.Error(t, err) require.Equal(t, len(result), 2) require.Equal(t, err, err1) @@ -473,7 +473,7 @@ func testCreateWorkerSliceConfigUpdateClusterSuccess(t *testing.T) { mMock.On("RecordCounterMetric", mock.Anything, mock.Anything).Return().Once() clientMock.On("Update", ctx, mock.AnythingOfType("*v1.Event")).Return(nil).Once() mMock.On("RecordCounterMetric", mock.Anything, mock.Anything).Return().Once() - result, err := WorkerSliceService.CreateMinimalWorkerSliceConfig(ctx, []string{"cluster-1", "cluster-2"}, requestObj.Namespace, label, "red", "198.23.54.47/16", "/20", nil, nil, nil) + result, _, err := WorkerSliceService.CreateMinimalWorkerSliceConfig(ctx, []string{"cluster-1", "cluster-2"}, requestObj.Namespace, label, "red", "198.23.54.47/16", "/20", nil, nil, nil) require.Equal(t, len(result), 2) require.NoError(t, nil) require.Nil(t, err) @@ -539,7 +539,7 @@ func testCreateWorkerSliceConfigUpdateClusterFails(t *testing.T) { clientMock.On("Update", ctx, mock.Anything).Return(err1).Once() clientMock.On("Create", ctx, mock.AnythingOfType("*v1.Event")).Return(nil).Once() mMock.On("RecordCounterMetric", mock.Anything, mock.Anything).Return().Once() - result, err := WorkerSliceService.CreateMinimalWorkerSliceConfig(ctx, []string{"cluster-1", "cluster-2"}, requestObj.Namespace, label, "red", "198.23.54.47/16", "/20", nil, nil, nil) + result, _, err := WorkerSliceService.CreateMinimalWorkerSliceConfig(ctx, []string{"cluster-1", "cluster-2"}, requestObj.Namespace, label, "red", "198.23.54.47/16", "/20", nil, nil, nil) require.Error(t, err) require.Equal(t, len(result), 2) require.Equal(t, err, err1) diff --git a/service/worker_slice_gateway_service.go b/service/worker_slice_gateway_service.go index 1da5079f1..5f826d2b0 100644 --- a/service/worker_slice_gateway_service.go +++ b/service/worker_slice_gateway_service.go @@ -46,7 +46,7 @@ const gatewayName = "%s-%s-%s" type IWorkerSliceGatewayService interface { ReconcileWorkerSliceGateways(ctx context.Context, req ctrl.Request) (ctrl.Result, error) CreateMinimumWorkerSliceGateways(ctx context.Context, sliceName string, clusterNames []string, namespace string, - label map[string]string, clusterMap map[string]int, sliceSubnet string, clusterCidr string, sliceGwSvcTypeMap map[string]*controllerv1alpha1.SliceGatewayServiceType) (ctrl.Result, error) + label map[string]string, clusterMap map[string]int, subnetMap map[string]string, sliceSubnet string, clusterCidr string, sliceGwSvcTypeMap map[string]*controllerv1alpha1.SliceGatewayServiceType) (ctrl.Result, error) ListWorkerSliceGateways(ctx context.Context, ownerLabel map[string]string, namespace string) ([]v1alpha1.WorkerSliceGateway, error) DeleteWorkerSliceGatewaysByLabel(ctx context.Context, label map[string]string, namespace string) error NodeIpReconciliationOfWorkerSliceGateways(ctx context.Context, cluster *controllerv1alpha1.Cluster, namespace string) error @@ -54,7 +54,7 @@ type IWorkerSliceGatewayService interface { serverGateway *v1alpha1.WorkerSliceGateway, clientGateway *v1alpha1.WorkerSliceGateway, gatewayAddresses util.WorkerSliceGatewayNetworkAddresses) error BuildNetworkAddresses(sliceSubnet, sourceClusterName, destinationClusterName string, - clusterMap map[string]int, clusterCidr string) util.WorkerSliceGatewayNetworkAddresses + clusterMap map[string]int, subnetMap map[string]string, clusterCidr string) util.WorkerSliceGatewayNetworkAddresses } // WorkerSliceGatewayService is a schema for interfaces JobService, WorkerSliceConfigService, SecretService @@ -348,7 +348,7 @@ type IndividualCertPairRequest struct { // CreateMinimumWorkerSliceGateways is a function to create gateways with minimum specification func (s *WorkerSliceGatewayService) CreateMinimumWorkerSliceGateways(ctx context.Context, sliceName string, clusterNames []string, namespace string, label map[string]string, clusterMap map[string]int, - sliceSubnet string, clusterCidr string, sliceGwSvcTypeMap map[string]*controllerv1alpha1.SliceGatewayServiceType) (ctrl.Result, error) { + subnetMap map[string]string, sliceSubnet string, clusterCidr string, sliceGwSvcTypeMap map[string]*controllerv1alpha1.SliceGatewayServiceType) (ctrl.Result, error) { err := s.cleanupObsoleteGateways(ctx, namespace, label, clusterNames, clusterMap) if err != nil { @@ -358,7 +358,7 @@ func (s *WorkerSliceGatewayService) CreateMinimumWorkerSliceGateways(ctx context return ctrl.Result{}, nil } - _, err = s.createMinimumGatewaysIfNotExists(ctx, sliceName, clusterNames, namespace, label, clusterMap, sliceSubnet, clusterCidr, sliceGwSvcTypeMap) + _, err = s.createMinimumGatewaysIfNotExists(ctx, sliceName, clusterNames, namespace, label, clusterMap, subnetMap, sliceSubnet, clusterCidr, sliceGwSvcTypeMap) if err != nil { return ctrl.Result{}, err } @@ -438,7 +438,7 @@ func (s *WorkerSliceGatewayService) cleanupObsoleteGateways(ctx context.Context, // createMinimumGatewaysIfNotExists is a helper function to create the gateways between worker clusters if not exists func (s *WorkerSliceGatewayService) createMinimumGatewaysIfNotExists(ctx context.Context, sliceName string, clusterNames []string, namespace string, ownerLabel map[string]string, clusterMap map[string]int, - sliceSubnet string, clusterCidr string, sliceGwSvcTypeMap map[string]*controllerv1alpha1.SliceGatewayServiceType) (ctrl.Result, error) { + subnetMap map[string]string, sliceSubnet string, clusterCidr string, sliceGwSvcTypeMap map[string]*controllerv1alpha1.SliceGatewayServiceType) (ctrl.Result, error) { noClusters := len(clusterNames) logger := util.CtxLogger(ctx) clusterMapping := map[string]*controllerv1alpha1.Cluster{} @@ -454,7 +454,7 @@ func (s *WorkerSliceGatewayService) createMinimumGatewaysIfNotExists(ctx context for j := i + 1; j < noClusters; j++ { sourceCluster, destinationCluster := clusterMapping[clusterNames[i]], clusterMapping[clusterNames[j]] gatewayNumber := s.calculateGatewayNumber(clusterMap[sourceCluster.Name], clusterMap[destinationCluster.Name]) - gatewayAddresses := s.BuildNetworkAddresses(sliceSubnet, sourceCluster.Name, destinationCluster.Name, clusterMap, clusterCidr) + gatewayAddresses := s.BuildNetworkAddresses(sliceSubnet, sourceCluster.Name, destinationCluster.Name, clusterMap, subnetMap, clusterCidr) // determine the gateway svc parameters sliceGwSvcType := defaultSliceGatewayServiceType gwSvcProtocol := defaultSliceGatewayServiceProtocol @@ -576,11 +576,21 @@ func (s *WorkerSliceGatewayService) createMinimumGateWayPairIfNotExists(ctx cont // buildNetworkAddresses - function generates the object of WorkerSliceGatewayNetworkAddresses func (s *WorkerSliceGatewayService) BuildNetworkAddresses(sliceSubnet, sourceClusterName, destinationClusterName string, - clusterMap map[string]int, clusterCidr string) util.WorkerSliceGatewayNetworkAddresses { + clusterMap map[string]int, subnetMap map[string]string, clusterCidr string) util.WorkerSliceGatewayNetworkAddresses { gatewayAddresses := util.WorkerSliceGatewayNetworkAddresses{} ipr := strings.Split(sliceSubnet, ".") - serverSubnet := util.GetClusterPrefixPool(sliceSubnet, clusterMap[sourceClusterName], clusterCidr) - clientSubnet := util.GetClusterPrefixPool(sliceSubnet, clusterMap[destinationClusterName], clusterCidr) + var serverSubnet, clientSubnet string + + // Use subnetMap if available (preferred for Dynamic IPAM and consistent Static IPAM) + if subnetMap != nil { + serverSubnet = subnetMap[sourceClusterName] + clientSubnet = subnetMap[destinationClusterName] + } else { + // Fallback to calculation (Legacy Static IPAM) + serverSubnet = util.GetClusterPrefixPool(sliceSubnet, clusterMap[sourceClusterName], clusterCidr) + clientSubnet = util.GetClusterPrefixPool(sliceSubnet, clusterMap[destinationClusterName], clusterCidr) + } + gatewayAddresses.ServerNetwork = strings.SplitN(serverSubnet, "/", -1)[0] gatewayAddresses.ClientNetwork = strings.SplitN(clientSubnet, "/", -1)[0] gatewayAddresses.ServerSubnet = serverSubnet diff --git a/service/worker_slice_gateway_service_test.go b/service/worker_slice_gateway_service_test.go index 9b27dd97d..e1a7d1662 100644 --- a/service/worker_slice_gateway_service_test.go +++ b/service/worker_slice_gateway_service_test.go @@ -320,7 +320,7 @@ func testCreateMinimumWorkerSliceGatewaysAlreadyExists(t *testing.T) { //environment := make(map[string]string, 5) //jobMock.On("CreateJob", ctx, requestObj.Namespace, "image", environment).Return(ctrl.Result{}, nil).Once() - result, err := workerSliceGatewayService.CreateMinimumWorkerSliceGateways(ctx, "red", clusterNames, requestObj.Namespace, label, clusterMap, "10.10.10.10/16", "/16", nil) + result, err := workerSliceGatewayService.CreateMinimumWorkerSliceGateways(ctx, "red", clusterNames, requestObj.Namespace, label, clusterMap, nil, "10.10.10.10/16", "/16", nil) expectedResult := ctrl.Result{} require.NoError(t, nil) require.Equal(t, result, expectedResult) @@ -407,7 +407,8 @@ func testCreateMinimumWorkerSliceGatewaysNotExists(t *testing.T) { clientMock.On("Update", ctx, mock.AnythingOfType("*v1.Event")).Return(nil).Once() clientMock.On("Get", ctx, mock.Anything, mock.Anything).Return(nil).Once() mMock.On("RecordCounterMetric", mock.Anything, mock.Anything).Return().Once() - result, err := workerSliceGatewayService.CreateMinimumWorkerSliceGateways(ctx, "red", clusterNames, requestObj.Namespace, label, clusterMap, "10.10.10.10/16", "/16", nil) + subnetMap := make(map[string]string) + result, err := workerSliceGatewayService.CreateMinimumWorkerSliceGateways(ctx, "red", clusterNames, requestObj.Namespace, label, clusterMap, subnetMap, "10.10.10.10/16", "/16", nil) expectedResult := ctrl.Result{} require.NoError(t, nil) require.Equal(t, result, expectedResult)