diff --git a/pkg/registry/core/service/storage/rest.go b/pkg/registry/core/service/storage/rest.go index 6e8ea1d3f33..4ccbb5415d3 100644 --- a/pkg/registry/core/service/storage/rest.go +++ b/pkg/registry/core/service/storage/rest.go @@ -721,29 +721,14 @@ func isMatchingPreferDualStackClusterIPFields(oldService, service *api.Service) return false } - // compare ClusterIPs lengths. - // due to validation. - if len(service.Spec.ClusterIPs) != len(oldService.Spec.ClusterIPs) { + if !sameClusterIPs(oldService, service) { return false } - for i, ip := range service.Spec.ClusterIPs { - if oldService.Spec.ClusterIPs[i] != ip { - return false - } - } - - // compare IPFamilies - if len(service.Spec.IPFamilies) != len(oldService.Spec.IPFamilies) { + if !sameIPFamilies(oldService, service) { return false } - for i, family := range service.Spec.IPFamilies { - if oldService.Spec.IPFamilies[i] != family { - return false - } - } - // they match on // Policy: preferDualStack // ClusterIPs @@ -865,6 +850,7 @@ func (al *RESTAllocStuff) initIPFamilyFields(oldService, service *api.Service) e // Update: As long as ClusterIPs and IPFamilies have not changed, // setting policy to single-stack is clear intent. if *(service.Spec.IPFamilyPolicy) == api.IPFamilyPolicySingleStack { + // ClusterIPs[0] is immutable, so it is safe to keep. if sameClusterIPs(oldService, service) && len(service.Spec.ClusterIPs) > 1 { service.Spec.ClusterIPs = service.Spec.ClusterIPs[0:1] } diff --git a/pkg/registry/core/service/storage/rest_test.go b/pkg/registry/core/service/storage/rest_test.go index c79d7532aad..f42093322ef 100644 --- a/pkg/registry/core/service/storage/rest_test.go +++ b/pkg/registry/core/service/storage/rest_test.go @@ -26,7 +26,6 @@ import ( "sort" "testing" - "k8s.io/apimachinery/pkg/api/errors" metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -40,11 +39,8 @@ import ( etcd3testing "k8s.io/apiserver/pkg/storage/etcd3/testing" "k8s.io/apiserver/pkg/storage/storagebackend" "k8s.io/apiserver/pkg/util/dryrun" - utilfeature "k8s.io/apiserver/pkg/util/feature" - featuregatetesting "k8s.io/component-base/featuregate/testing" svctest "k8s.io/kubernetes/pkg/api/service/testing" api "k8s.io/kubernetes/pkg/apis/core" - "k8s.io/kubernetes/pkg/features" endpointstore "k8s.io/kubernetes/pkg/registry/core/endpoint/storage" podstore "k8s.io/kubernetes/pkg/registry/core/pod/storage" "k8s.io/kubernetes/pkg/registry/core/service/ipallocator" @@ -676,56 +672,6 @@ func TestServiceRegistryList(t *testing.T) { } } -func TestServiceRegistryIPUpdate(t *testing.T) { - storage, server := NewTestREST(t, []api.IPFamily{api.IPv4Protocol}) - defer server.Terminate(t) - - svc := svctest.MakeService("foo") - ctx := genericapirequest.NewDefaultContext() - createdSvc, err := storage.Create(ctx, svc, rest.ValidateAllObjectFunc, &metav1.CreateOptions{}) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - createdService := createdSvc.(*api.Service) - if createdService.Spec.Ports[0].Port != svc.Spec.Ports[0].Port { - t.Errorf("Expected port %d, but got %v", svc.Spec.Ports[0].Port, createdService.Spec.Ports[0].Port) - } - if !makeIPNet(t).Contains(netutils.ParseIPSloppy(createdService.Spec.ClusterIPs[0])) { - t.Errorf("Unexpected ClusterIP: %s", createdService.Spec.ClusterIPs[0]) - } - - update := createdService.DeepCopy() - update.Spec.Ports[0].Port = 6503 - - updatedSvc, _, errUpdate := storage.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(update), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc, false, &metav1.UpdateOptions{}) - if errUpdate != nil { - t.Fatalf("unexpected error during update %v", errUpdate) - } - updatedService := updatedSvc.(*api.Service) - if updatedService.Spec.Ports[0].Port != 6503 { - t.Errorf("Expected port 6503, but got %v", updatedService.Spec.Ports[0].Port) - } - - testIPs := []string{"1.2.3.93", "1.2.3.94", "1.2.3.95", "1.2.3.96"} - testIP := "" - for _, ip := range testIPs { - if !ipIsAllocated(t, storage.alloc.serviceIPAllocatorsByFamily[storage.alloc.defaultServiceIPFamily].(*ipallocator.Range), ip) { - testIP = ip - break - } - } - - update = updatedService.DeepCopy() - update.Spec.Ports[0].Port = 6503 - update.Spec.ClusterIP = testIP - update.Spec.ClusterIPs[0] = testIP - - _, _, err = storage.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(update), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc, false, &metav1.UpdateOptions{}) - if err == nil || !errors.IsInvalid(err) { - t.Errorf("Unexpected error type: %v", err) - } -} - // Validate the internalTrafficPolicy field when set to "Cluster" then updated to "Local" func TestServiceRegistryInternalTrafficPolicyClusterThenLocal(t *testing.T) { ctx := genericapirequest.NewDefaultContext() @@ -789,422 +735,3 @@ func TestServiceRegistryInternalTrafficPolicyLocalThenCluster(t *testing.T) { t.Errorf("Expected internalTrafficPolicy to be Cluster, got: %s", *updatedService.Spec.InternalTrafficPolicy) } } - -func TestServiceUpgrade(t *testing.T) { - requireDualStack := api.IPFamilyPolicyRequireDualStack - - ctx := genericapirequest.NewDefaultContext() - testCases := []struct { - name string - updateFunc func(svc *api.Service) - enableDualStackAllocator bool - enableDualStackGate bool - allocateIPsBeforeUpdate map[api.IPFamily]string - expectUpgradeError bool - svc *api.Service - }{{ - name: "normal, no upgrade needed", - enableDualStackAllocator: false, - enableDualStackGate: true, - allocateIPsBeforeUpdate: nil, - expectUpgradeError: false, - - updateFunc: func(s *api.Service) { - s.Spec.Selector = map[string]string{"bar": "baz2"} - }, - - svc: svctest.MakeService("foo"), - }, { - name: "error, no upgrade (has single allocator)", - enableDualStackAllocator: false, - enableDualStackGate: true, - allocateIPsBeforeUpdate: nil, - expectUpgradeError: true, - - updateFunc: func(s *api.Service) { - s.Spec.IPFamilyPolicy = &requireDualStack - s.Spec.IPFamilies = []api.IPFamily{api.IPv4Protocol, api.IPv6Protocol} - }, - - svc: svctest.MakeService("foo", func(s *api.Service) { - s.Spec.IPFamilies = []api.IPFamily{api.IPv4Protocol} - }), - }, { - name: "upgrade to v4,6", - enableDualStackAllocator: true, - enableDualStackGate: true, - allocateIPsBeforeUpdate: nil, - expectUpgradeError: false, - - updateFunc: func(s *api.Service) { - s.Spec.IPFamilyPolicy = &requireDualStack - s.Spec.IPFamilies = []api.IPFamily{api.IPv4Protocol, api.IPv6Protocol} - }, - - svc: svctest.MakeService("foo", func(s *api.Service) { - s.Spec.IPFamilies = []api.IPFamily{api.IPv4Protocol} - }), - }, { - name: "upgrade to v4,6 (specific ip)", - enableDualStackAllocator: true, - enableDualStackGate: true, - allocateIPsBeforeUpdate: nil, - expectUpgradeError: false, - - updateFunc: func(s *api.Service) { - s.Spec.IPFamilyPolicy = &requireDualStack - s.Spec.ClusterIPs = append(s.Spec.ClusterIPs, "2000:0:0:0:0:0:0:1") - s.Spec.IPFamilies = []api.IPFamily{api.IPv4Protocol, api.IPv6Protocol} - }, - - svc: svctest.MakeService("foo", func(s *api.Service) { - s.Spec.IPFamilies = []api.IPFamily{api.IPv4Protocol} - }), - }, { - name: "upgrade to v4,6 (specific ip) - fail, ip is not available", - enableDualStackAllocator: true, - enableDualStackGate: true, - allocateIPsBeforeUpdate: map[api.IPFamily]string{api.IPv6Protocol: "2000:0:0:0:0:0:0:1"}, - expectUpgradeError: true, - - updateFunc: func(s *api.Service) { - s.Spec.IPFamilyPolicy = &requireDualStack - s.Spec.ClusterIPs = append(s.Spec.ClusterIPs, "2000:0:0:0:0:0:0:1") - s.Spec.IPFamilies = []api.IPFamily{api.IPv4Protocol, api.IPv6Protocol} - }, - - svc: svctest.MakeService("foo", func(s *api.Service) { - s.Spec.IPFamilies = []api.IPFamily{api.IPv4Protocol} - }), - }, { - name: "upgrade to v6,4", - enableDualStackAllocator: true, - enableDualStackGate: true, - allocateIPsBeforeUpdate: nil, - expectUpgradeError: false, - - updateFunc: func(s *api.Service) { - s.Spec.IPFamilyPolicy = &requireDualStack - s.Spec.IPFamilies = []api.IPFamily{api.IPv6Protocol, api.IPv4Protocol} - }, - - svc: svctest.MakeService("foo", func(s *api.Service) { - s.Spec.IPFamilies = []api.IPFamily{api.IPv6Protocol} - }), - }, { - name: "upgrade to v6,4 (specific ip)", - enableDualStackAllocator: true, - enableDualStackGate: true, - allocateIPsBeforeUpdate: nil, - expectUpgradeError: false, - - updateFunc: func(s *api.Service) { - s.Spec.IPFamilyPolicy = &requireDualStack - s.Spec.ClusterIPs = append(s.Spec.ClusterIPs, "1.2.3.4") - s.Spec.IPFamilies = []api.IPFamily{api.IPv6Protocol, api.IPv4Protocol} - }, - - svc: svctest.MakeService("foo", func(s *api.Service) { - s.Spec.IPFamilies = []api.IPFamily{api.IPv6Protocol} - }), - }, { - name: "upgrade to v6,4 (specific ip) - fail ip is already allocated", - enableDualStackAllocator: true, - enableDualStackGate: true, - allocateIPsBeforeUpdate: map[api.IPFamily]string{api.IPv4Protocol: "1.2.3.4"}, - expectUpgradeError: true, - - updateFunc: func(s *api.Service) { - s.Spec.IPFamilyPolicy = &requireDualStack - s.Spec.ClusterIPs = append(s.Spec.ClusterIPs, "1.2.3.4") - s.Spec.IPFamilies = []api.IPFamily{api.IPv6Protocol, api.IPv4Protocol} - }, - - svc: svctest.MakeService("foo", func(s *api.Service) { - s.Spec.IPFamilies = []api.IPFamily{api.IPv6Protocol} - }), - }} - - for _, testCase := range testCases { - t.Run(testCase.name, func(t *testing.T) { - families := []api.IPFamily{api.IPv4Protocol} - if testCase.enableDualStackAllocator { - families = append(families, api.IPv6Protocol) - } - storage, server := NewTestREST(t, families) - defer server.Terminate(t) - defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.IPv6DualStack, testCase.enableDualStackGate)() - - obj, err := storage.Create(ctx, testCase.svc, rest.ValidateAllObjectFunc, &metav1.CreateOptions{}) - if err != nil { - t.Fatalf("error is unexpected: %v", err) - } - - createdSvc := obj.(*api.Service) - // allocated IP - for family, ip := range testCase.allocateIPsBeforeUpdate { - alloc := storage.alloc.serviceIPAllocatorsByFamily[family] - if err := alloc.Allocate(netutils.ParseIPSloppy(ip)); err != nil { - t.Fatalf("test is incorrect, unable to preallocate ip:%v", ip) - } - } - // run the modifier - testCase.updateFunc(createdSvc) - - // run the update - updated, _, err := storage.Update(ctx, - createdSvc.Name, - rest.DefaultUpdatedObjectInfo(createdSvc), - rest.ValidateAllObjectFunc, - rest.ValidateAllObjectUpdateFunc, - false, - &metav1.UpdateOptions{}) - - if err != nil && !testCase.expectUpgradeError { - t.Fatalf("an error was not expected during upgrade %v", err) - } - - if err == nil && testCase.expectUpgradeError { - t.Fatalf("error was expected during upgrade") - } - - if err != nil { - return - } - - updatedSvc := updated.(*api.Service) - isValidClusterIPFields(t, storage, updatedSvc, updatedSvc) - - shouldUpgrade := len(createdSvc.Spec.IPFamilies) == 2 && *(createdSvc.Spec.IPFamilyPolicy) != api.IPFamilyPolicySingleStack && len(storage.alloc.serviceIPAllocatorsByFamily) == 2 - if shouldUpgrade && len(updatedSvc.Spec.ClusterIPs) < 2 { - t.Fatalf("Service should have been upgraded %+v", createdSvc) - } - - if !shouldUpgrade && len(updatedSvc.Spec.ClusterIPs) > 1 { - t.Fatalf("Service should not have been upgraded %+v", createdSvc) - } - - // make sure that ips were allocated, correctly - for i, family := range updatedSvc.Spec.IPFamilies { - ip := updatedSvc.Spec.ClusterIPs[i] - allocator := storage.alloc.serviceIPAllocatorsByFamily[family] - if !ipIsAllocated(t, allocator, ip) { - t.Fatalf("expected ip:%v to be allocated by %v allocator. it was not", ip, family) - } - } - }) - } -} - -func TestServiceDowngrade(t *testing.T) { - requiredDualStack := api.IPFamilyPolicyRequireDualStack - singleStack := api.IPFamilyPolicySingleStack - ctx := genericapirequest.NewDefaultContext() - testCases := []struct { - name string - updateFunc func(svc *api.Service) - enableDualStackAllocator bool - enableDualStackGate bool - expectDowngradeError bool - svc *api.Service - }{{ - name: "normal, no downgrade needed. single stack => single stack", - enableDualStackAllocator: true, - enableDualStackGate: true, - expectDowngradeError: false, - - updateFunc: func(s *api.Service) { s.Spec.Selector = map[string]string{"bar": "baz2"} }, - - svc: svctest.MakeService("foo", func(s *api.Service) { - s.Spec.IPFamilyPolicy = &requiredDualStack - s.Spec.IPFamilies = []api.IPFamily{api.IPv4Protocol} - }), - }, { - name: "normal, no downgrade needed. dual stack => dual stack", - enableDualStackAllocator: true, - enableDualStackGate: true, - expectDowngradeError: false, - - updateFunc: func(s *api.Service) { s.Spec.Selector = map[string]string{"bar": "baz2"} }, - - svc: svctest.MakeService("foo", func(s *api.Service) { - s.Spec.IPFamilyPolicy = &requiredDualStack - s.Spec.IPFamilies = []api.IPFamily{api.IPv4Protocol, api.IPv6Protocol} - }), - }, { - name: "normal, downgrade v4,v6 => v4", - enableDualStackAllocator: true, - enableDualStackGate: true, - expectDowngradeError: false, - - updateFunc: func(s *api.Service) { - s.Spec.IPFamilyPolicy = &singleStack - s.Spec.ClusterIPs = s.Spec.ClusterIPs[0:1] - s.Spec.IPFamilies = s.Spec.IPFamilies[0:1] - }, - - svc: svctest.MakeService("foo", func(s *api.Service) { - s.Spec.IPFamilyPolicy = &requiredDualStack - s.Spec.IPFamilies = []api.IPFamily{api.IPv4Protocol, api.IPv6Protocol} - }), - }, { - name: "normal, downgrade v6,v4 => v6", - enableDualStackAllocator: true, - enableDualStackGate: true, - expectDowngradeError: false, - - updateFunc: func(s *api.Service) { - s.Spec.IPFamilyPolicy = &singleStack - s.Spec.ClusterIPs = s.Spec.ClusterIPs[0:1] - s.Spec.IPFamilies = s.Spec.IPFamilies[0:1] - }, - - svc: svctest.MakeService("foo", func(s *api.Service) { - s.Spec.IPFamilyPolicy = &requiredDualStack - s.Spec.IPFamilies = []api.IPFamily{api.IPv4Protocol, api.IPv6Protocol} - }), - }} - - for _, testCase := range testCases { - t.Run(testCase.name, func(t *testing.T) { - storage, server := NewTestREST(t, []api.IPFamily{api.IPv4Protocol, api.IPv6Protocol}) - defer server.Terminate(t) - defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.IPv6DualStack, testCase.enableDualStackGate)() - - obj, err := storage.Create(ctx, testCase.svc, rest.ValidateAllObjectFunc, &metav1.CreateOptions{}) - if err != nil { - t.Fatalf("error is unexpected: %v", err) - } - - createdSvc := obj.(*api.Service) - copySvc := createdSvc.DeepCopy() - - // run the modifier - testCase.updateFunc(createdSvc) - - // run the update - updated, _, err := storage.Update(ctx, - createdSvc.Name, - rest.DefaultUpdatedObjectInfo(createdSvc), - rest.ValidateAllObjectFunc, - rest.ValidateAllObjectUpdateFunc, - false, - &metav1.UpdateOptions{}) - - if err != nil && !testCase.expectDowngradeError { - t.Fatalf("an error was not expected during upgrade %v", err) - } - - if err == nil && testCase.expectDowngradeError { - t.Fatalf("error was expected during upgrade") - } - - if err != nil { - return - } - - updatedSvc := updated.(*api.Service) - isValidClusterIPFields(t, storage, createdSvc, updatedSvc) - - shouldDowngrade := len(copySvc.Spec.ClusterIPs) == 2 && *(createdSvc.Spec.IPFamilyPolicy) == api.IPFamilyPolicySingleStack - - if shouldDowngrade && len(updatedSvc.Spec.ClusterIPs) > 1 { - t.Fatalf("Service should have been downgraded %+v", createdSvc) - } - - if !shouldDowngrade && len(updatedSvc.Spec.ClusterIPs) < 2 { - t.Fatalf("Service should not have been downgraded %+v", createdSvc) - } - - if shouldDowngrade { - releasedIP := copySvc.Spec.ClusterIPs[1] - releasedIPFamily := copySvc.Spec.IPFamilies[1] - allocator := storage.alloc.serviceIPAllocatorsByFamily[releasedIPFamily] - - if ipIsAllocated(t, allocator, releasedIP) { - t.Fatalf("expected ip:%v to be released by %v allocator. it was not", releasedIP, releasedIPFamily) - } - } - }) - } -} - -// validates that the service created, updated by REST -// has correct ClusterIPs related fields -func isValidClusterIPFields(t *testing.T, storage *REST, pre *api.Service, post *api.Service) { - t.Helper() - - // valid for gate off/on scenarios - // ClusterIP - if len(post.Spec.ClusterIP) == 0 { - t.Fatalf("service must have clusterIP : %+v", post) - } - // cluster IPs - if len(post.Spec.ClusterIPs) == 0 { - t.Fatalf("new service must have at least one IP: %+v", post) - } - - if post.Spec.ClusterIP != post.Spec.ClusterIPs[0] { - t.Fatalf("clusterIP does not match ClusterIPs[0]: %+v", post) - } - - // if feature gate is not enabled then we need to ignore need fields - if !utilfeature.DefaultFeatureGate.Enabled(features.IPv6DualStack) { - if post.Spec.IPFamilyPolicy != nil { - t.Fatalf("service must be set to nil for IPFamilyPolicy: %+v", post) - } - - if len(post.Spec.IPFamilies) != 0 { - t.Fatalf("service must be set to nil for IPFamilies: %+v", post) - } - - return - } - - // for gate on scenarios - // prefer dual stack field - if post.Spec.IPFamilyPolicy == nil { - t.Fatalf("service must not have nil for IPFamilyPolicy: %+v", post) - } - - if pre.Spec.IPFamilyPolicy != nil && *(pre.Spec.IPFamilyPolicy) != *(post.Spec.IPFamilyPolicy) { - t.Fatalf("new service must not change PreferDualStack if it was set by user pre: %v post: %v", *(pre.Spec.IPFamilyPolicy), *(post.Spec.IPFamilyPolicy)) - } - - if pre.Spec.IPFamilyPolicy == nil && *(post.Spec.IPFamilyPolicy) != api.IPFamilyPolicySingleStack { - t.Fatalf("new services with prefer dual stack nil must be set to false (prefer dual stack) %+v", post) - } - - // external name or headless services offer no more ClusterIPs field validation - if post.Spec.ClusterIPs[0] == api.ClusterIPNone { - return - } - - // len of ClusteIPs can not be more than Families - // and for providedIPs it needs to match - - // if families are provided then it shouldn't be changed - // this applies on first entry on - if len(pre.Spec.IPFamilies) > 0 { - if len(post.Spec.IPFamilies) == 0 { - t.Fatalf("allocator shouldn't remove ipfamilies[0] pre:%+v, post:%+v", pre.Spec.IPFamilies, post.Spec.IPFamilies) - } - - if pre.Spec.IPFamilies[0] != post.Spec.IPFamilies[0] { - t.Fatalf("allocator shouldn't change post.Spec.IPFamilies[0] pre:%+v post:%+v", pre.Spec.IPFamilies, post.Spec.IPFamilies) - } - } - // if two families are assigned, then they must be dual stack - if len(post.Spec.IPFamilies) == 2 { - if post.Spec.IPFamilies[0] == post.Spec.IPFamilies[1] { - t.Fatalf("allocator assigned two of the same family %+v", post) - } - } - // ips must match families - for i, ip := range post.Spec.ClusterIPs { - isIPv6 := netutils.IsIPv6String(ip) - if isIPv6 && post.Spec.IPFamilies[i] != api.IPv6Protocol { - t.Fatalf("ips does not match assigned families %+v %+v", post.Spec.ClusterIPs, post.Spec.IPFamilies) - } - } -} diff --git a/pkg/registry/core/service/storage/storage_test.go b/pkg/registry/core/service/storage/storage_test.go index 16e9d6593ec..fd26a8408a3 100644 --- a/pkg/registry/core/service/storage/storage_test.go +++ b/pkg/registry/core/service/storage/storage_test.go @@ -5975,6 +5975,3145 @@ func TestUpdateDryRun(t *testing.T) { } } +// Proves that updates from single-stack to dual-stack work. +func TestUpdateSingleToDualStack(t *testing.T) { + prove := func(proofs ...svcTestProof) []svcTestProof { + return proofs + } + proveNumFamilies := func(n int) svcTestProof { + return func(t *testing.T, storage *GenericREST, before, after *api.Service) { + t.Helper() + if got := len(after.Spec.IPFamilies); got != n { + t.Errorf("wrong number of ipFamilies: expected %d, got %d", n, got) + } + } + } + + // Single-stack cases as control. + testCasesV4 := []cudTestCase{{ + name: "single-single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack)), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetSelector(map[string]string{"k2": "v2"})), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + }, { + name: "single-dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack)), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetIPFamilies(api.IPv4Protocol, api.IPv6Protocol)), + expectError: true, + }, + }, { + name: "single-dual_policy", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack)), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack)), + expectError: true, + }, + }, { + name: "single-dual_families", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack)), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilies(api.IPv4Protocol, api.IPv6Protocol)), + expectError: true, + }, + }} + + t.Run("singlestack:v4", func(t *testing.T) { + helpTestCreateUpdateDeleteWithFamilies(t, testCasesV4, []api.IPFamily{api.IPv4Protocol}) + }) + + // Dual-stack v4,v6 cases: Covers the full matrix of: + // policy={nil, single, prefer, require} + // families={nil, single, dual} + // ips={nil, single, dual} + testCasesV4V6 := []cudTestCase{{ + name: "policy:nil_families:nil_ips:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"})), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + }, { + name: "policy:nil_families:nil_ips:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + }, { + name: "policy:nil_families:nil_ips:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:nil_families:single_ips:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilies(api.IPv4Protocol)), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + }, { + name: "policy:nil_families:single_ips:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilies(api.IPv4Protocol), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + }, { + name: "policy:nil_families:single_ips:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilies(api.IPv4Protocol), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:nil_families:dual_ips:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilies(api.IPv4Protocol, api.IPv6Protocol)), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:nil_families:dual_ips:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilies(api.IPv4Protocol, api.IPv6Protocol), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:nil_families:dual_ips:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilies(api.IPv4Protocol, api.IPv6Protocol), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:single_families:nil_ips:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack)), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + }, { + name: "policy:single_families:nil_ips:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + }, { + name: "policy:single_families:nil_ips:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectError: true, + }, + }, { + name: "policy:single_families:single_ips:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetIPFamilies(api.IPv4Protocol)), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + }, { + name: "policy:single_families:single_ips:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetIPFamilies(api.IPv4Protocol), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + }, { + name: "policy:single_families:single_ips:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetIPFamilies(api.IPv4Protocol), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectError: true, + }, + }, { + name: "policy:single_families:dual_ips:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetIPFamilies(api.IPv4Protocol, api.IPv6Protocol)), + expectError: true, + }, + }, { + name: "policy:single_families:dual_ips:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetIPFamilies(api.IPv4Protocol, api.IPv6Protocol), + svctest.SetClusterIPs("10.0.0.1")), + expectError: true, + }, + }, { + name: "policy:single_families:dual_ips:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetIPFamilies(api.IPv4Protocol, api.IPv6Protocol), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectError: true, + }, + }, { + name: "policy:prefer_families:nil_ips:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyPreferDualStack)), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:prefer_families:nil_ips:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyPreferDualStack), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:prefer_families:nil_ips:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyPreferDualStack), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:prefer_families:single_ips:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyPreferDualStack), + svctest.SetIPFamilies(api.IPv4Protocol)), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:prefer_families:single_ips:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyPreferDualStack), + svctest.SetIPFamilies(api.IPv4Protocol), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:prefer_families:single_ips:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyPreferDualStack), + svctest.SetIPFamilies(api.IPv4Protocol), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:prefer_families:dual_ips:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyPreferDualStack), + svctest.SetIPFamilies(api.IPv4Protocol, api.IPv6Protocol)), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:prefer_families:dual_ips:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyPreferDualStack), + svctest.SetIPFamilies(api.IPv4Protocol, api.IPv6Protocol), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:prefer_families:dual_ips:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyPreferDualStack), + svctest.SetIPFamilies(api.IPv4Protocol, api.IPv6Protocol), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:require_families:nil_ips:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack)), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:require_families:nil_ips:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:require_families:nil_ips:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:require_families:single_ips:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetIPFamilies(api.IPv4Protocol)), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:require_families:single_ips:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetIPFamilies(api.IPv4Protocol), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:require_families:single_ips:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetIPFamilies(api.IPv4Protocol), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:require_families:dual_ips:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetIPFamilies(api.IPv4Protocol, api.IPv6Protocol)), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:require_families:dual_ips:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetIPFamilies(api.IPv4Protocol, api.IPv6Protocol), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:require_families:dual_ips:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetIPFamilies(api.IPv4Protocol, api.IPv6Protocol), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "single-dual_wrong_order_families", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetIPFamilies(api.IPv6Protocol, api.IPv4Protocol)), + expectError: true, + }, + }, { + name: "single-dual_wrong_order_ips", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectError: true, + }, + }, { + name: "single-dual_ip_in_use", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + beforeUpdate: func(t *testing.T, storage *GenericREST) { + alloc := storage.alloc.serviceIPAllocatorsByFamily[api.IPv6Protocol] + ip := "2000::1" + if err := alloc.Allocate(netutils.ParseIPSloppy(ip)); err != nil { + t.Fatalf("test is incorrect, unable to preallocate IP %q: %v", ip, err) + } + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetIPFamilies(api.IPv4Protocol, api.IPv6Protocol), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectError: true, + }, + }} + + t.Run("dualstack:v4v6", func(t *testing.T) { + helpTestCreateUpdateDeleteWithFamilies(t, testCasesV4V6, []api.IPFamily{api.IPv4Protocol, api.IPv6Protocol}) + }) + + // Dual-stack v6,v4 cases: Covers the full matrix of: + // policy={nil, single, prefer, require} + // families={nil, single, dual} + // ips={nil, single, dual} + testCasesV6V4 := []cudTestCase{{ + name: "policy:nil_families:nil_ips:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"})), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + }, { + name: "policy:nil_families:nil_ips:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + }, { + name: "policy:nil_families:nil_ips:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:nil_families:single_ips:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilies(api.IPv6Protocol)), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + }, { + name: "policy:nil_families:single_ips:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilies(api.IPv6Protocol), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + }, { + name: "policy:nil_families:single_ips:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilies(api.IPv6Protocol), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:nil_families:dual_ips:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilies(api.IPv6Protocol, api.IPv4Protocol)), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:nil_families:dual_ips:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilies(api.IPv6Protocol, api.IPv4Protocol), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:nil_families:dual_ips:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilies(api.IPv6Protocol, api.IPv4Protocol), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:single_families:nil_ips:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack)), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + }, { + name: "policy:single_families:nil_ips:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + }, { + name: "policy:single_families:nil_ips:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectError: true, + }, + }, { + name: "policy:single_families:single_ips:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetIPFamilies(api.IPv6Protocol)), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + }, { + name: "policy:single_families:single_ips:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetIPFamilies(api.IPv6Protocol), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + }, { + name: "policy:single_families:single_ips:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetIPFamilies(api.IPv6Protocol), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectError: true, + }, + }, { + name: "policy:single_families:dual_ips:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetIPFamilies(api.IPv6Protocol, api.IPv4Protocol)), + expectError: true, + }, + }, { + name: "policy:single_families:dual_ips:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetIPFamilies(api.IPv6Protocol, api.IPv4Protocol), + svctest.SetClusterIPs("2000::1")), + expectError: true, + }, + }, { + name: "policy:single_families:dual_ips:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetIPFamilies(api.IPv6Protocol, api.IPv4Protocol), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectError: true, + }, + }, { + name: "policy:prefer_families:nil_ips:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyPreferDualStack)), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:prefer_families:nil_ips:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyPreferDualStack), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:prefer_families:nil_ips:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyPreferDualStack), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:prefer_families:single_ips:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyPreferDualStack), + svctest.SetIPFamilies(api.IPv6Protocol)), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:prefer_families:single_ips:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyPreferDualStack), + svctest.SetIPFamilies(api.IPv6Protocol), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:prefer_families:single_ips:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyPreferDualStack), + svctest.SetIPFamilies(api.IPv6Protocol), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:prefer_families:dual_ips:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyPreferDualStack), + svctest.SetIPFamilies(api.IPv6Protocol, api.IPv4Protocol)), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:prefer_families:dual_ips:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyPreferDualStack), + svctest.SetIPFamilies(api.IPv6Protocol, api.IPv4Protocol), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:prefer_families:dual_ips:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyPreferDualStack), + svctest.SetIPFamilies(api.IPv6Protocol, api.IPv4Protocol), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:require_families:nil_ips:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack)), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:require_families:nil_ips:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:require_families:nil_ips:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:require_families:single_ips:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetIPFamilies(api.IPv6Protocol)), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:require_families:single_ips:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetIPFamilies(api.IPv6Protocol), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:require_families:single_ips:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetIPFamilies(api.IPv6Protocol), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:require_families:dual_ips:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetIPFamilies(api.IPv6Protocol, api.IPv4Protocol)), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:require_families:dual_ips:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetIPFamilies(api.IPv6Protocol, api.IPv4Protocol), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:require_families:dual_ips:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetIPFamilies(api.IPv6Protocol, api.IPv4Protocol), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "single-dual_wrong_order_families", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetIPFamilies(api.IPv4Protocol, api.IPv6Protocol)), + expectError: true, + }, + }, { + name: "single-dual_wrong_order_ips", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectError: true, + }, + }, { + name: "single-dual_ip_in_use", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + beforeUpdate: func(t *testing.T, storage *GenericREST) { + alloc := storage.alloc.serviceIPAllocatorsByFamily[api.IPv4Protocol] + ip := "10.0.0.1" + if err := alloc.Allocate(netutils.ParseIPSloppy(ip)); err != nil { + t.Fatalf("test is incorrect, unable to preallocate IP %q: %v", ip, err) + } + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetIPFamilies(api.IPv4Protocol, api.IPv6Protocol), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectError: true, + }, + }} + + t.Run("dualstack:v6v4", func(t *testing.T) { + helpTestCreateUpdateDeleteWithFamilies(t, testCasesV6V4, []api.IPFamily{api.IPv6Protocol, api.IPv4Protocol}) + }) + + // Headless cases: Covers the full matrix of: + // policy={nil, single, prefer, require} + // families={nil, single, dual} + testCasesHeadless := []cudTestCase{{ + name: "policy:nil_families:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs(api.ClusterIPNone)), + expectHeadless: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"})), + expectHeadless: true, + prove: prove(proveNumFamilies(1)), + }, + }, { + name: "policy:nil_families:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs(api.ClusterIPNone)), + expectHeadless: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilies("IPv4")), + expectHeadless: true, + prove: prove(proveNumFamilies(1)), + }, + }, { + name: "policy:nil_families:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs(api.ClusterIPNone)), + expectHeadless: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilies("IPv4", "IPv6")), + expectHeadless: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:single_families:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs(api.ClusterIPNone)), + expectHeadless: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack)), + expectHeadless: true, + prove: prove(proveNumFamilies(1)), + }, + }, { + name: "policy:single_families:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs(api.ClusterIPNone)), + expectHeadless: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetIPFamilies("IPv4")), + expectHeadless: true, + prove: prove(proveNumFamilies(1)), + }, + }, { + name: "policy:single_families:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs(api.ClusterIPNone)), + expectHeadless: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetIPFamilies("IPv4", "IPv6")), + expectError: true, + }, + }, { + name: "policy:prefer_families:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs(api.ClusterIPNone)), + expectHeadless: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyPreferDualStack)), + expectHeadless: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:prefer_families:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs(api.ClusterIPNone)), + expectHeadless: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyPreferDualStack), + svctest.SetIPFamilies("IPv4")), + expectHeadless: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:prefer_families:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs(api.ClusterIPNone)), + expectHeadless: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyPreferDualStack), + svctest.SetIPFamilies("IPv4", "IPv6")), + expectHeadless: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:require_families:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs(api.ClusterIPNone)), + expectHeadless: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack)), + expectHeadless: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:require_families:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs(api.ClusterIPNone)), + expectHeadless: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetIPFamilies("IPv4")), + expectHeadless: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:require_families:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs(api.ClusterIPNone)), + expectHeadless: true, + prove: prove(proveNumFamilies(1)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetIPFamilies("IPv4", "IPv6")), + expectHeadless: true, + prove: prove(proveNumFamilies(2)), + }, + }} + + t.Run("headless", func(t *testing.T) { + helpTestCreateUpdateDeleteWithFamilies(t, testCasesHeadless, []api.IPFamily{api.IPv4Protocol, api.IPv6Protocol}) + }) +} + +// Proves that updates from dual-stack to single-stack work. +func TestUpdateDualToSingleStack(t *testing.T) { + prove := func(proofs ...svcTestProof) []svcTestProof { + return proofs + } + proveNumFamilies := func(n int) svcTestProof { + return func(t *testing.T, storage *GenericREST, before, after *api.Service) { + t.Helper() + if got := len(after.Spec.IPFamilies); got != n { + t.Errorf("wrong number of ipFamilies: expected %d, got %d", n, got) + } + } + } + + // Dual-stack v4,v6 cases: Covers the full matrix of: + // policy={nil, single, prefer, require} + // families={nil, single, dual} + // ips={nil, single, dual} + testCasesV4V6 := []cudTestCase{{ + name: "policy:nil_families:nil_ips:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"})), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:nil_families:nil_ips:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetClusterIPs("10.0.0.1")), + expectError: true, + }, + }, { + name: "policy:nil_families:nil_ips:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:nil_families:single_ips:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilies(api.IPv4Protocol)), + expectError: true, + }, + }, { + name: "policy:nil_families:single_ips:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilies(api.IPv4Protocol), + svctest.SetClusterIPs("10.0.0.1")), + expectError: true, + }, + }, { + name: "policy:nil_families:single_ips:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilies(api.IPv4Protocol), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectError: true, + }, + }, { + name: "policy:nil_families:dual_ips:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilies(api.IPv4Protocol, api.IPv6Protocol)), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:nil_families:dual_ips:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilies(api.IPv4Protocol, api.IPv6Protocol), + svctest.SetClusterIPs("10.0.0.1")), + expectError: true, + }, + }, { + name: "policy:nil_families:dual_ips:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilies(api.IPv4Protocol, api.IPv6Protocol), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:single_families:nil_ips:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack)), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + }, { + name: "policy:single_families:nil_ips:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + }, { + name: "policy:single_families:nil_ips:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + expectStackDowngrade: true, + prove: prove(proveNumFamilies(1)), + }, + }, { + name: "policy:single_families:single_ips:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetIPFamilies(api.IPv4Protocol)), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + }, { + name: "policy:single_families:single_ips:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetIPFamilies(api.IPv4Protocol), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + }, { + name: "policy:single_families:single_ips:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetIPFamilies(api.IPv4Protocol), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + expectStackDowngrade: true, + prove: prove(proveNumFamilies(1)), + }, + }, { + name: "policy:single_families:dual_ips:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetIPFamilies(api.IPv4Protocol, api.IPv6Protocol)), + expectClusterIPs: true, + expectStackDowngrade: true, + prove: prove(proveNumFamilies(1)), + }, + }, { + name: "policy:single_families:dual_ips:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetIPFamilies(api.IPv4Protocol, api.IPv6Protocol), + svctest.SetClusterIPs("10.0.0.1")), + expectClusterIPs: true, + expectStackDowngrade: true, + prove: prove(proveNumFamilies(1)), + }, + }, { + name: "policy:single_families:dual_ips:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetIPFamilies(api.IPv4Protocol, api.IPv6Protocol), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + expectStackDowngrade: true, + prove: prove(proveNumFamilies(1)), + }, + }, { + name: "policy:prefer_families:nil_ips:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyPreferDualStack)), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:prefer_families:nil_ips:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyPreferDualStack), + svctest.SetClusterIPs("10.0.0.1")), + expectError: true, + }, + }, { + name: "policy:prefer_families:nil_ips:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyPreferDualStack), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:prefer_families:single_ips:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyPreferDualStack), + svctest.SetIPFamilies(api.IPv4Protocol)), + expectError: true, + }, + }, { + name: "policy:prefer_families:single_ips:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyPreferDualStack), + svctest.SetIPFamilies(api.IPv4Protocol), + svctest.SetClusterIPs("10.0.0.1")), + expectError: true, + }, + }, { + name: "policy:prefer_families:single_ips:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyPreferDualStack), + svctest.SetIPFamilies(api.IPv4Protocol), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectError: true, + }, + }, { + name: "policy:prefer_families:dual_ips:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyPreferDualStack), + svctest.SetIPFamilies(api.IPv4Protocol, api.IPv6Protocol)), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:prefer_families:dual_ips:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyPreferDualStack), + svctest.SetIPFamilies(api.IPv4Protocol, api.IPv6Protocol), + svctest.SetClusterIPs("10.0.0.1")), + expectError: true, + }, + }, { + name: "policy:prefer_families:dual_ips:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyPreferDualStack), + svctest.SetIPFamilies(api.IPv4Protocol, api.IPv6Protocol), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:require_families:nil_ips:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack)), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:require_families:nil_ips:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("10.0.0.1")), + expectError: true, + }, + }, { + name: "policy:require_families:nil_ips:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:require_families:single_ips:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetIPFamilies(api.IPv4Protocol)), + expectError: true, + }, + }, { + name: "policy:require_families:single_ips:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetIPFamilies(api.IPv4Protocol), + svctest.SetClusterIPs("10.0.0.1")), + expectError: true, + }, + }, { + name: "policy:require_families:single_ips:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetIPFamilies(api.IPv4Protocol), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectError: true, + }, + }, { + name: "policy:require_families:dual_ips:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetIPFamilies(api.IPv4Protocol, api.IPv6Protocol)), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:require_families:dual_ips:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetIPFamilies(api.IPv4Protocol, api.IPv6Protocol), + svctest.SetClusterIPs("10.0.0.1")), + expectError: true, + }, + }, { + name: "policy:require_families:dual_ips:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetIPFamilies(api.IPv4Protocol, api.IPv6Protocol), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "dual-single_wrong_order_families", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetIPFamilies(api.IPv6Protocol)), + expectError: true, + }, + }, { + name: "dual-single_wrong_order_ips", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("10.0.0.1", "2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("2000::1")), + expectError: true, + }, + }} + + t.Run("dualstack:v4v6", func(t *testing.T) { + helpTestCreateUpdateDeleteWithFamilies(t, testCasesV4V6, []api.IPFamily{api.IPv4Protocol, api.IPv6Protocol}) + }) + + // Dual-stack v6,v4 cases: Covers the full matrix of: + // policy={nil, single, prefer, require} + // families={nil, single, dual} + // ips={nil, single, dual} + testCasesV6V4 := []cudTestCase{{ + name: "policy:nil_families:nil_ips:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"})), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:nil_families:nil_ips:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetClusterIPs("2000::1")), + expectError: true, + }, + }, { + name: "policy:nil_families:nil_ips:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:nil_families:single_ips:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilies(api.IPv6Protocol)), + expectError: true, + }, + }, { + name: "policy:nil_families:single_ips:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilies(api.IPv6Protocol), + svctest.SetClusterIPs("2000::1")), + expectError: true, + }, + }, { + name: "policy:nil_families:single_ips:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilies(api.IPv6Protocol), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectError: true, + }, + }, { + name: "policy:nil_families:dual_ips:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilies(api.IPv6Protocol, api.IPv4Protocol)), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:nil_families:dual_ips:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilies(api.IPv6Protocol, api.IPv4Protocol), + svctest.SetClusterIPs("2000::1")), + expectError: true, + }, + }, { + name: "policy:nil_families:dual_ips:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilies(api.IPv6Protocol, api.IPv4Protocol), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:single_families:nil_ips:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack)), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + }, { + name: "policy:single_families:nil_ips:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + }, { + name: "policy:single_families:nil_ips:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + expectStackDowngrade: true, + prove: prove(proveNumFamilies(1)), + }, + }, { + name: "policy:single_families:single_ips:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetIPFamilies(api.IPv6Protocol)), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + }, { + name: "policy:single_families:single_ips:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetIPFamilies(api.IPv6Protocol), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(1)), + }, + }, { + name: "policy:single_families:single_ips:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetIPFamilies(api.IPv6Protocol), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + expectStackDowngrade: true, + prove: prove(proveNumFamilies(1)), + }, + }, { + name: "policy:single_families:dual_ips:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetIPFamilies(api.IPv6Protocol, api.IPv4Protocol)), + expectClusterIPs: true, + expectStackDowngrade: true, + prove: prove(proveNumFamilies(1)), + }, + }, { + name: "policy:single_families:dual_ips:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetIPFamilies(api.IPv6Protocol, api.IPv4Protocol), + svctest.SetClusterIPs("2000::1")), + expectClusterIPs: true, + expectStackDowngrade: true, + prove: prove(proveNumFamilies(1)), + }, + }, { + name: "policy:single_families:dual_ips:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetIPFamilies(api.IPv6Protocol, api.IPv4Protocol), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + expectStackDowngrade: true, + prove: prove(proveNumFamilies(1)), + }, + }, { + name: "policy:prefer_families:nil_ips:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyPreferDualStack)), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:prefer_families:nil_ips:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyPreferDualStack), + svctest.SetClusterIPs("2000::1")), + expectError: true, + }, + }, { + name: "policy:prefer_families:nil_ips:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyPreferDualStack), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:prefer_families:single_ips:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyPreferDualStack), + svctest.SetIPFamilies(api.IPv6Protocol)), + expectError: true, + }, + }, { + name: "policy:prefer_families:single_ips:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyPreferDualStack), + svctest.SetIPFamilies(api.IPv6Protocol), + svctest.SetClusterIPs("2000::1")), + expectError: true, + }, + }, { + name: "policy:prefer_families:single_ips:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyPreferDualStack), + svctest.SetIPFamilies(api.IPv6Protocol), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectError: true, + }, + }, { + name: "policy:prefer_families:dual_ips:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyPreferDualStack), + svctest.SetIPFamilies(api.IPv6Protocol, api.IPv4Protocol)), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:prefer_families:dual_ips:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyPreferDualStack), + svctest.SetIPFamilies(api.IPv6Protocol, api.IPv4Protocol), + svctest.SetClusterIPs("2000::1")), + expectError: true, + }, + }, { + name: "policy:prefer_families:dual_ips:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyPreferDualStack), + svctest.SetIPFamilies(api.IPv6Protocol, api.IPv4Protocol), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:require_families:nil_ips:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack)), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:require_families:nil_ips:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("2000::1")), + expectError: true, + }, + }, { + name: "policy:require_families:nil_ips:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:require_families:single_ips:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetIPFamilies(api.IPv6Protocol)), + expectError: true, + }, + }, { + name: "policy:require_families:single_ips:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetIPFamilies(api.IPv6Protocol), + svctest.SetClusterIPs("2000::1")), + expectError: true, + }, + }, { + name: "policy:require_families:single_ips:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetIPFamilies(api.IPv6Protocol), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectError: true, + }, + }, { + name: "policy:require_families:dual_ips:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetIPFamilies(api.IPv6Protocol, api.IPv4Protocol)), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:require_families:dual_ips:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetIPFamilies(api.IPv6Protocol, api.IPv4Protocol), + svctest.SetClusterIPs("2000::1")), + expectError: true, + }, + }, { + name: "policy:require_families:dual_ips:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetIPFamilies(api.IPv6Protocol, api.IPv4Protocol), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "dual-single_wrong_order_families", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetIPFamilies(api.IPv4Protocol)), + expectError: true, + }, + }, { + name: "dual-single_wrong_order_ips", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs("2000::1", "10.0.0.1")), + expectClusterIPs: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetClusterIPs("10.0.0.1")), + expectError: true, + }, + }} + + t.Run("dualstack:v6v4", func(t *testing.T) { + helpTestCreateUpdateDeleteWithFamilies(t, testCasesV6V4, []api.IPFamily{api.IPv6Protocol, api.IPv4Protocol}) + }) + + // Headless cases: Covers the full matrix of: + // policy={nil, single, prefer, require} + // families={nil, single, dual} + testCasesHeadless := []cudTestCase{{ + name: "policy:nil_families:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs(api.ClusterIPNone)), + expectHeadless: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"})), + expectHeadless: true, + prove: prove(proveNumFamilies(1)), + }, + }, { + name: "policy:nil_families:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs(api.ClusterIPNone)), + expectHeadless: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilies("IPv4")), + expectError: true, + }, + }, { + name: "policy:nil_families:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs(api.ClusterIPNone)), + expectHeadless: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilies("IPv4", "IPv6")), + expectHeadless: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:single_families:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs(api.ClusterIPNone)), + expectHeadless: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack)), + expectHeadless: true, + prove: prove(proveNumFamilies(1)), + }, + }, { + name: "policy:single_families:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs(api.ClusterIPNone)), + expectHeadless: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetIPFamilies("IPv4")), + expectHeadless: true, + prove: prove(proveNumFamilies(1)), + }, + }, { + name: "policy:single_families:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs(api.ClusterIPNone)), + expectHeadless: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicySingleStack), + svctest.SetIPFamilies("IPv4", "IPv6")), + expectHeadless: true, + expectStackDowngrade: true, + prove: prove(proveNumFamilies(1)), + }, + }, { + name: "policy:prefer_families:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs(api.ClusterIPNone)), + expectHeadless: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyPreferDualStack)), + expectHeadless: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:prefer_families:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs(api.ClusterIPNone)), + expectHeadless: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyPreferDualStack), + svctest.SetIPFamilies("IPv4")), + expectError: true, + }, + }, { + name: "policy:prefer_families:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs(api.ClusterIPNone)), + expectHeadless: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyPreferDualStack), + svctest.SetIPFamilies("IPv4", "IPv6")), + expectHeadless: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:require_families:nil", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs(api.ClusterIPNone)), + expectHeadless: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack)), + expectHeadless: true, + prove: prove(proveNumFamilies(2)), + }, + }, { + name: "policy:require_families:single", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs(api.ClusterIPNone)), + expectHeadless: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetIPFamilies("IPv4")), + expectError: true, + }, + }, { + name: "policy:require_families:dual", + create: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetClusterIPs(api.ClusterIPNone)), + expectHeadless: true, + prove: prove(proveNumFamilies(2)), + }, + update: svcTestCase{ + svc: svctest.MakeService("foo", svctest.SetTypeClusterIP, + svctest.SetSelector(map[string]string{"k2": "v2"}), + svctest.SetIPFamilyPolicy(api.IPFamilyPolicyRequireDualStack), + svctest.SetIPFamilies("IPv4", "IPv6")), + expectHeadless: true, + prove: prove(proveNumFamilies(2)), + }, + }} + + t.Run("headless", func(t *testing.T) { + helpTestCreateUpdateDeleteWithFamilies(t, testCasesHeadless, []api.IPFamily{api.IPv4Protocol, api.IPv6Protocol}) + }) +} + type svcTestCase struct { svc *api.Service expectError bool @@ -5983,6 +9122,7 @@ type svcTestCase struct { // vector for test bugs and more importantly it makes the test cases less // self-documenting. expectClusterIPs bool + expectStackDowngrade bool expectHeadless bool expectNodePorts bool expectHealthCheckNodePort bool @@ -6009,10 +9149,8 @@ func callName(before, after *api.Service) string { func proveClusterIPsAllocated(t *testing.T, storage *GenericREST, before, after *api.Service) { t.Helper() - if utilfeature.DefaultFeatureGate.Enabled(features.IPv6DualStack) { - if sing, plur := after.Spec.ClusterIP, after.Spec.ClusterIPs[0]; sing != plur { - t.Errorf("%s: expected clusterIP == clusterIPs[0]: %q != %q", callName(before, after), sing, plur) - } + if sing, plur := after.Spec.ClusterIP, after.Spec.ClusterIPs[0]; sing != plur { + t.Errorf("%s: expected clusterIP == clusterIPs[0]: %q != %q", callName(before, after), sing, plur) } clips := []string{} @@ -6039,20 +9177,46 @@ func proveClusterIPsAllocated(t *testing.T, storage *GenericREST, before, after } } + if utilfeature.DefaultFeatureGate.Enabled(features.IPv6DualStack) { + if after.Spec.IPFamilyPolicy == nil { + t.Errorf("%s: expected ipFamilyPolicy to be set", callName(before, after)) + } else { + pol := *after.Spec.IPFamilyPolicy + fams := len(after.Spec.IPFamilies) + clus := 1 + if storage.secondaryIPFamily != "" { + clus = 2 + } + if pol == api.IPFamilyPolicySingleStack && fams != 1 { + t.Errorf("%s: expected 1 ipFamily, got %d", callName(before, after), fams) + } else if pol == api.IPFamilyPolicyRequireDualStack && fams != 2 { + t.Errorf("%s: expected 2 ipFamilies, got %d", callName(before, after), fams) + } else if pol == api.IPFamilyPolicyPreferDualStack && fams != clus { + t.Errorf("%s: expected %d ipFamilies, got %d", callName(before, after), clus, fams) + } + } + } + if before != nil { if before.Spec.ClusterIP != "" { if want, got := before.Spec.ClusterIP, after.Spec.ClusterIP; want != got { t.Errorf("%s: wrong clusterIP: wanted %q, got %q", callName(before, after), want, got) } } - for i := range before.Spec.ClusterIPs { + min := func(x, y int) int { + if x < y { + return x + } + return y + } + for i := 0; i < min(len(before.Spec.ClusterIPs), len(after.Spec.ClusterIPs)); i++ { if want, got := before.Spec.ClusterIPs[i], after.Spec.ClusterIPs[i]; want != got { t.Errorf("%s: wrong clusterIPs[%d]: wanted %q, got %q", callName(before, after), i, want, got) } } - for i := range before.Spec.IPFamilies { + for i := 0; i < min(len(before.Spec.IPFamilies), len(after.Spec.IPFamilies)); i++ { if want, got := before.Spec.IPFamilies[i], after.Spec.IPFamilies[i]; want != got { - t.Errorf("%s: wrong IPFamilies[%d]: wanted %q, got %q", callName(before, after), i, want, got) + t.Errorf("%s: wrong ipFamilies[%d]: wanted %q, got %q", callName(before, after), i, want, got) } } } @@ -6266,17 +9430,22 @@ func verifyEquiv(t testingTInterface, call string, tc *svcTestCase, got *api.Ser want.Spec.ClusterIP = got.Spec.ClusterIP } if utilfeature.DefaultFeatureGate.Enabled(features.IPv6DualStack) { - if len(got.Spec.ClusterIPs) > len(want.Spec.ClusterIPs) { - want.Spec.ClusterIPs = append(want.Spec.ClusterIPs, got.Spec.ClusterIPs[len(want.Spec.ClusterIPs):]...) - } if want.Spec.IPFamilyPolicy == nil { want.Spec.IPFamilyPolicy = got.Spec.IPFamilyPolicy } - if len(got.Spec.IPFamilies) > len(want.Spec.IPFamilies) { + if tc.expectStackDowngrade && len(want.Spec.ClusterIPs) > len(got.Spec.ClusterIPs) { + want.Spec.ClusterIPs = want.Spec.ClusterIPs[0:1] + } else if len(got.Spec.ClusterIPs) > len(want.Spec.ClusterIPs) { + want.Spec.ClusterIPs = append(want.Spec.ClusterIPs, got.Spec.ClusterIPs[len(want.Spec.ClusterIPs):]...) + } + if tc.expectStackDowngrade && len(want.Spec.IPFamilies) > len(got.Spec.ClusterIPs) { + want.Spec.IPFamilies = want.Spec.IPFamilies[0:1] + } else if len(got.Spec.IPFamilies) > len(want.Spec.IPFamilies) { want.Spec.IPFamilies = append(want.Spec.IPFamilies, got.Spec.IPFamilies[len(want.Spec.IPFamilies):]...) } } } + if tc.expectNodePorts { for i := range want.Spec.Ports { p := &want.Spec.Ports[i] diff --git a/pkg/registry/core/service/strategy.go b/pkg/registry/core/service/strategy.go index 0a34b59876c..346cf2db6eb 100644 --- a/pkg/registry/core/service/strategy.go +++ b/pkg/registry/core/service/strategy.go @@ -124,7 +124,6 @@ func (strategy svcStrategy) PrepareForUpdate(ctx context.Context, obj, old runti NormalizeClusterIPs(oldService, newService) dropServiceDisabledFields(newService, oldService) dropTypeDependentFields(newService, oldService) - trimFieldsForDualStackDowngrade(newService, oldService) } // Validate validates a new service. @@ -314,7 +313,7 @@ func PatchAllocatedValues(newSvc, oldSvc *api.Service) { if newSvc.Spec.ClusterIP == "" { newSvc.Spec.ClusterIP = oldSvc.Spec.ClusterIP } - if len(newSvc.Spec.ClusterIPs) == 0 { + if len(newSvc.Spec.ClusterIPs) == 0 && len(oldSvc.Spec.ClusterIPs) > 0 { newSvc.Spec.ClusterIPs = oldSvc.Spec.ClusterIPs } } @@ -620,33 +619,3 @@ func needsExternalTrafficPolicy(svc *api.Service) bool { func sameExternalTrafficPolicy(oldSvc, newSvc *api.Service) bool { return oldSvc.Spec.ExternalTrafficPolicy == newSvc.Spec.ExternalTrafficPolicy } - -// this func allows user to downgrade a service by just changing -// IPFamilyPolicy to SingleStack -func trimFieldsForDualStackDowngrade(newService, oldService *api.Service) { - if !utilfeature.DefaultFeatureGate.Enabled(features.IPv6DualStack) { - return - } - - // not an update - if oldService == nil { - return - } - - oldIsDualStack := oldService.Spec.IPFamilyPolicy != nil && - (*oldService.Spec.IPFamilyPolicy == api.IPFamilyPolicyRequireDualStack || - *oldService.Spec.IPFamilyPolicy == api.IPFamilyPolicyPreferDualStack) - - newIsNotDualStack := newService.Spec.IPFamilyPolicy != nil && *newService.Spec.IPFamilyPolicy == api.IPFamilyPolicySingleStack - - // if user want to downgrade then we auto remove secondary ip and family - if oldIsDualStack && newIsNotDualStack { - if len(newService.Spec.ClusterIPs) > 1 { - newService.Spec.ClusterIPs = newService.Spec.ClusterIPs[0:1] - } - - if len(newService.Spec.IPFamilies) > 1 { - newService.Spec.IPFamilies = newService.Spec.IPFamilies[0:1] - } - } -} diff --git a/pkg/registry/core/service/strategy_test.go b/pkg/registry/core/service/strategy_test.go index 5603c90a766..0c9a0f31db2 100644 --- a/pkg/registry/core/service/strategy_test.go +++ b/pkg/registry/core/service/strategy_test.go @@ -930,123 +930,15 @@ func TestDropTypeDependentFields(t *testing.T) { } } -func TestTrimFieldsForDualStackDowngrade(t *testing.T) { - singleStack := api.IPFamilyPolicySingleStack - preferDualStack := api.IPFamilyPolicyPreferDualStack - requireDualStack := api.IPFamilyPolicyRequireDualStack - testCases := []struct { - name string - oldPolicy *api.IPFamilyPolicyType - oldClusterIPs []string - oldFamilies []api.IPFamily - - newPolicy *api.IPFamilyPolicyType - expectedClusterIPs []string - expectedIPFamilies []api.IPFamily - }{ - - { - name: "no change single to single", - oldPolicy: &singleStack, - oldClusterIPs: []string{"10.10.10.10"}, - oldFamilies: []api.IPFamily{api.IPv4Protocol}, - newPolicy: &singleStack, - expectedClusterIPs: []string{"10.10.10.10"}, - expectedIPFamilies: []api.IPFamily{api.IPv4Protocol}, - }, - - { - name: "dualstack to dualstack (preferred)", - oldPolicy: &preferDualStack, - oldClusterIPs: []string{"10.10.10.10", "2000::1"}, - oldFamilies: []api.IPFamily{api.IPv4Protocol, api.IPv6Protocol}, - newPolicy: &preferDualStack, - expectedClusterIPs: []string{"10.10.10.10", "2000::1"}, - expectedIPFamilies: []api.IPFamily{api.IPv4Protocol, api.IPv6Protocol}, - }, - - { - name: "dualstack to dualstack (required)", - oldPolicy: &requireDualStack, - oldClusterIPs: []string{"10.10.10.10", "2000::1"}, - oldFamilies: []api.IPFamily{api.IPv4Protocol, api.IPv6Protocol}, - newPolicy: &preferDualStack, - expectedClusterIPs: []string{"10.10.10.10", "2000::1"}, - expectedIPFamilies: []api.IPFamily{api.IPv4Protocol, api.IPv6Protocol}, - }, - - { - name: "dualstack (preferred) to single", - oldPolicy: &preferDualStack, - oldClusterIPs: []string{"10.10.10.10", "2000::1"}, - oldFamilies: []api.IPFamily{api.IPv4Protocol, api.IPv6Protocol}, - newPolicy: &singleStack, - expectedClusterIPs: []string{"10.10.10.10"}, - expectedIPFamilies: []api.IPFamily{api.IPv4Protocol}, - }, - - { - name: "dualstack (require) to single", - oldPolicy: &requireDualStack, - oldClusterIPs: []string{"2000::1", "10.10.10.10"}, - oldFamilies: []api.IPFamily{api.IPv6Protocol, api.IPv4Protocol}, - newPolicy: &singleStack, - expectedClusterIPs: []string{"2000::1"}, - expectedIPFamilies: []api.IPFamily{api.IPv6Protocol}, - }, - } - // only when gate is on - defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.IPv6DualStack, true)() - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - oldService := &api.Service{ - Spec: api.ServiceSpec{ - IPFamilyPolicy: tc.oldPolicy, - ClusterIPs: tc.oldClusterIPs, - IPFamilies: tc.oldFamilies, - }, - } - - newService := oldService.DeepCopy() - newService.Spec.IPFamilyPolicy = tc.newPolicy - - trimFieldsForDualStackDowngrade(newService, oldService) - - if len(newService.Spec.ClusterIPs) != len(tc.expectedClusterIPs) { - t.Fatalf("unexpected clusterIPs. expected %v and got %v", tc.expectedClusterIPs, newService.Spec.ClusterIPs) - } - - // compare clusterIPS - for i, expectedIP := range tc.expectedClusterIPs { - if expectedIP != newService.Spec.ClusterIPs[i] { - t.Fatalf("unexpected clusterIPs. expected %v and got %v", tc.expectedClusterIPs, newService.Spec.ClusterIPs) - } - } - - // families - if len(newService.Spec.IPFamilies) != len(tc.expectedIPFamilies) { - t.Fatalf("unexpected ipfamilies. expected %v and got %v", tc.expectedIPFamilies, newService.Spec.IPFamilies) - } - - // compare clusterIPS - for i, expectedIPFamily := range tc.expectedIPFamilies { - if expectedIPFamily != newService.Spec.IPFamilies[i] { - t.Fatalf("unexpected ipfamilies. expected %v and got %v", tc.expectedIPFamilies, newService.Spec.IPFamilies) - } - } - - }) - } -} - func TestPatchAllocatedValues(t *testing.T) { testCases := []struct { - name string - before *api.Service - update *api.Service - expectSameClusterIPs bool - expectSameNodePort bool - expectSameHCNP bool + name string + before *api.Service + update *api.Service + expectSameClusterIPs bool + expectReducedClusterIPs bool + expectSameNodePort bool + expectSameHCNP bool }{{ name: "all_patched", before: svctest.MakeService("foo", @@ -1120,16 +1012,28 @@ func TestPatchAllocatedValues(t *testing.T) { update := tc.update.DeepCopy() PatchAllocatedValues(update, tc.before) - if b, u := tc.before.Spec.ClusterIP, update.Spec.ClusterIP; tc.expectSameClusterIPs && b != u { - t.Errorf("expected clusterIP to be patched: %q != %q", b, u) - } else if !tc.expectSameClusterIPs && b == u { - t.Errorf("expected clusterIP to not be patched: %q == %q", b, u) + beforeIP := tc.before.Spec.ClusterIP + updateIP := update.Spec.ClusterIP + if tc.expectSameClusterIPs || tc.expectReducedClusterIPs { + if beforeIP != updateIP { + t.Errorf("expected clusterIP to be patched: %q != %q", beforeIP, updateIP) + } + } else if beforeIP == updateIP { + t.Errorf("expected clusterIP to not be patched: %q == %q", beforeIP, updateIP) } - if b, u := tc.before.Spec.ClusterIPs, update.Spec.ClusterIPs; tc.expectSameClusterIPs && !cmp.Equal(b, u) { - t.Errorf("expected clusterIPs to be patched: %q != %q", b, u) - } else if !tc.expectSameClusterIPs && cmp.Equal(b, u) { - t.Errorf("expected clusterIPs to not be patched: %q == %q", b, u) + beforeIPs := tc.before.Spec.ClusterIPs + updateIPs := update.Spec.ClusterIPs + if tc.expectSameClusterIPs { + if !cmp.Equal(beforeIPs, updateIPs) { + t.Errorf("expected clusterIPs to be patched: %q != %q", beforeIPs, updateIPs) + } + } else if tc.expectReducedClusterIPs { + if len(updateIPs) != 1 || beforeIPs[0] != updateIPs[0] { + t.Errorf("expected clusterIPs to be trim-patched: %q -> %q", beforeIPs, updateIPs) + } + } else if cmp.Equal(beforeIPs, updateIPs) { + t.Errorf("expected clusterIPs to not be patched: %q == %q", beforeIPs, updateIPs) } if b, u := tc.before.Spec.Ports[0].NodePort, update.Spec.Ports[0].NodePort; tc.expectSameNodePort && b != u {