Commit 94cca273 authored by Kubernetes Submit Queue's avatar Kubernetes Submit Queue Committed by GitHub

Merge pull request #36390 from nikhiljindal/cascDelSvc

Automatic merge from submit-queue (batch tested with PRs 38212, 38792, 39641, 36390, 39005) Updating federated service controller to support cascading deletion Ref https://github.com/kubernetes/kubernetes/issues/33612 Service controller is special than other federation controllers because it does not use federatedinformer and updater to sync services (it was written before we had those frameworks). Updating service controller code to instantiate these frameworks and then use deletion helper to perform cascading deletion. Note that, I havent changed the queuing logic in this PR so we still dont use federated informer to manage the queue. Will do that in the next PR. cc @kubernetes/sig-federation-misc @mwielgus @quinton-hoole ```release-note federation: Adding support for DeleteOptions.OrphanDependents for federated services. Setting it to false while deleting a federated service also deletes the corresponding services from all registered clusters. ```
parents 49a0cf7f e7f9f54a
...@@ -26,6 +26,7 @@ go_library( ...@@ -26,6 +26,7 @@ go_library(
"//federation/pkg/dnsprovider:go_default_library", "//federation/pkg/dnsprovider:go_default_library",
"//federation/pkg/dnsprovider/rrstype:go_default_library", "//federation/pkg/dnsprovider/rrstype:go_default_library",
"//federation/pkg/federation-controller/util:go_default_library", "//federation/pkg/federation-controller/util:go_default_library",
"//federation/pkg/federation-controller/util/deletionhelper:go_default_library",
"//pkg/api:go_default_library", "//pkg/api:go_default_library",
"//pkg/api/errors:go_default_library", "//pkg/api/errors:go_default_library",
"//pkg/api/v1:go_default_library", "//pkg/api/v1:go_default_library",
...@@ -35,6 +36,7 @@ go_library( ...@@ -35,6 +36,7 @@ go_library(
"//pkg/client/record:go_default_library", "//pkg/client/record:go_default_library",
"//pkg/client/restclient:go_default_library", "//pkg/client/restclient:go_default_library",
"//pkg/controller:go_default_library", "//pkg/controller:go_default_library",
"//pkg/conversion:go_default_library",
"//pkg/runtime:go_default_library", "//pkg/runtime:go_default_library",
"//pkg/util/runtime:go_default_library", "//pkg/util/runtime:go_default_library",
"//pkg/util/sets:go_default_library", "//pkg/util/sets:go_default_library",
......
...@@ -154,7 +154,7 @@ var _ = framework.KubeDescribe("Federated ingresses [Feature:Federation]", func( ...@@ -154,7 +154,7 @@ var _ = framework.KubeDescribe("Federated ingresses [Feature:Federation]", func(
AfterEach(func() { AfterEach(func() {
deleteBackendPodsOrFail(clusters, ns) deleteBackendPodsOrFail(clusters, ns)
if service != nil { if service != nil {
deleteServiceOrFail(f.FederationClientset_1_5, ns, service.Name) deleteServiceOrFail(f.FederationClientset_1_5, ns, service.Name, nil)
cleanupServiceShardsAndProviderResources(ns, service, clusters) cleanupServiceShardsAndProviderResources(ns, service, clusters)
service = nil service = nil
} else { } else {
......
...@@ -21,11 +21,15 @@ import ( ...@@ -21,11 +21,15 @@ import (
"os" "os"
"reflect" "reflect"
"strconv" "strconv"
"strings"
"time" "time"
fedclientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_clientset"
"k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/api/v1"
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1" metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/util/wait"
"k8s.io/kubernetes/test/e2e/framework" "k8s.io/kubernetes/test/e2e/framework"
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo"
...@@ -116,28 +120,29 @@ var _ = framework.KubeDescribe("[Feature:Federation]", func() { ...@@ -116,28 +120,29 @@ var _ = framework.KubeDescribe("[Feature:Federation]", func() {
waitForServiceShardsOrFail(nsName, service, clusters) waitForServiceShardsOrFail(nsName, service, clusters)
}) })
It("should not be deleted from underlying clusters when it is deleted", func() { It("should be deleted from underlying clusters when OrphanDependents is false", func() {
framework.SkipUnlessFederated(f.ClientSet) framework.SkipUnlessFederated(f.ClientSet)
nsName = f.FederationNamespace.Name nsName := f.FederationNamespace.Name
service = createServiceOrFail(f.FederationClientset_1_5, nsName, FederatedServiceName) orphanDependents := false
By(fmt.Sprintf("Successfully created federated service %q in namespace %q. Waiting for shards to appear in underlying clusters", service.Name, nsName)) verifyCascadingDeletionForService(f.FederationClientset_1_5, clusters, &orphanDependents, nsName)
By(fmt.Sprintf("Verified that services were deleted from underlying clusters"))
})
waitForServiceShardsOrFail(nsName, service, clusters) It("should not be deleted from underlying clusters when OrphanDependents is true", func() {
framework.SkipUnlessFederated(f.ClientSet)
nsName := f.FederationNamespace.Name
orphanDependents := true
verifyCascadingDeletionForService(f.FederationClientset_1_5, clusters, &orphanDependents, nsName)
By(fmt.Sprintf("Verified that services were not deleted from underlying clusters"))
})
By(fmt.Sprintf("Deleting service %s", service.Name)) It("should not be deleted from underlying clusters when OrphanDependents is nil", func() {
err := f.FederationClientset_1_5.Services(nsName).Delete(service.Name, &v1.DeleteOptions{}) framework.SkipUnlessFederated(f.ClientSet)
framework.ExpectNoError(err, "Error deleting service %q in namespace %q", service.Name, service.Namespace) nsName := f.FederationNamespace.Name
By(fmt.Sprintf("Deletion of service %q in namespace %q succeeded.", service.Name, nsName)) verifyCascadingDeletionForService(f.FederationClientset_1_5, clusters, nil, nsName)
By(fmt.Sprintf("Verifying that services in underlying clusters are not deleted")) By(fmt.Sprintf("Verified that services were not deleted from underlying clusters"))
for clusterName, clusterClientset := range clusters {
_, err := clusterClientset.Core().Services(service.Namespace).Get(service.Name, metav1.GetOptions{})
if err != nil {
framework.Failf("Unexpected error in fetching service %s in cluster %s, %s", service.Name, clusterName, err)
}
}
}) })
}) })
var _ = Describe("DNS", func() { var _ = Describe("DNS", func() {
var ( var (
...@@ -211,7 +216,7 @@ var _ = framework.KubeDescribe("[Feature:Federation]", func() { ...@@ -211,7 +216,7 @@ var _ = framework.KubeDescribe("[Feature:Federation]", func() {
deleteBackendPodsOrFail(clusters, nsName) deleteBackendPodsOrFail(clusters, nsName)
if service != nil { if service != nil {
deleteServiceOrFail(f.FederationClientset_1_5, nsName, service.Name) deleteServiceOrFail(f.FederationClientset_1_5, nsName, service.Name, nil)
service = nil service = nil
} else { } else {
By("No service to delete. Service is nil") By("No service to delete. Service is nil")
...@@ -308,6 +313,48 @@ var _ = framework.KubeDescribe("[Feature:Federation]", func() { ...@@ -308,6 +313,48 @@ var _ = framework.KubeDescribe("[Feature:Federation]", func() {
}) })
}) })
// verifyCascadingDeletionForService verifies that services are deleted from
// underlying clusters when orphan dependents is false and they are not
// deleted when orphan dependents is true.
func verifyCascadingDeletionForService(clientset *fedclientset.Clientset, clusters map[string]*cluster, orphanDependents *bool, nsName string) {
service := createServiceOrFail(clientset, nsName, FederatedServiceName)
serviceName := service.Name
// Check subclusters if the service was created there.
By(fmt.Sprintf("Waiting for service %s to be created in all underlying clusters", serviceName))
err := wait.Poll(5*time.Second, 2*time.Minute, func() (bool, error) {
for _, cluster := range clusters {
_, err := cluster.Core().Services(nsName).Get(serviceName, metav1.GetOptions{})
if err != nil {
if !errors.IsNotFound(err) {
return false, err
}
return false, nil
}
}
return true, nil
})
framework.ExpectNoError(err, "Not all services created")
By(fmt.Sprintf("Deleting service %s", serviceName))
deleteServiceOrFail(clientset, nsName, serviceName, orphanDependents)
By(fmt.Sprintf("Verifying services %s in underlying clusters", serviceName))
errMessages := []string{}
// service should be present in underlying clusters unless orphanDependents is false.
shouldExist := orphanDependents == nil || *orphanDependents == true
for clusterName, clusterClientset := range clusters {
_, err := clusterClientset.Core().Services(nsName).Get(serviceName, metav1.GetOptions{})
if shouldExist && errors.IsNotFound(err) {
errMessages = append(errMessages, fmt.Sprintf("unexpected NotFound error for service %s in cluster %s, expected service to exist", serviceName, clusterName))
} else if !shouldExist && !errors.IsNotFound(err) {
errMessages = append(errMessages, fmt.Sprintf("expected NotFound error for service %s in cluster %s, got error: %v", serviceName, clusterName, err))
}
}
if len(errMessages) != 0 {
framework.Failf("%s", strings.Join(errMessages, "; "))
}
}
/* /*
equivalent returns true if the two services are equivalent. Fields which are expected to differ between equivalent returns true if the two services are equivalent. Fields which are expected to differ between
federated services and the underlying cluster services (e.g. ClusterIP, LoadBalancerIP etc) are ignored. federated services and the underlying cluster services (e.g. ClusterIP, LoadBalancerIP etc) are ignored.
......
...@@ -42,7 +42,7 @@ var _ = framework.KubeDescribe("[Feature:Federation]", func() { ...@@ -42,7 +42,7 @@ var _ = framework.KubeDescribe("[Feature:Federation]", func() {
nsName := f.FederationNamespace.Name nsName := f.FederationNamespace.Name
svc := createServiceOrFail(f.FederationClientset_1_5, nsName, FederatedServiceName) svc := createServiceOrFail(f.FederationClientset_1_5, nsName, FederatedServiceName)
deleteServiceOrFail(f.FederationClientset_1_5, nsName, svc.Name) deleteServiceOrFail(f.FederationClientset_1_5, nsName, svc.Name, nil)
}) })
It("should not accept cluster resources when the client has invalid authentication credentials", func() { It("should not accept cluster resources when the client has invalid authentication credentials", func() {
...@@ -62,7 +62,7 @@ var _ = framework.KubeDescribe("[Feature:Federation]", func() { ...@@ -62,7 +62,7 @@ var _ = framework.KubeDescribe("[Feature:Federation]", func() {
svc, err := createService(fcs, nsName, FederatedServiceName) svc, err := createService(fcs, nsName, FederatedServiceName)
Expect(errors.IsUnauthorized(err)).To(BeTrue()) Expect(errors.IsUnauthorized(err)).To(BeTrue())
if err == nil && svc != nil { if err == nil && svc != nil {
deleteServiceOrFail(fcs, nsName, svc.Name) deleteServiceOrFail(fcs, nsName, svc.Name, nil)
} }
}) })
...@@ -76,7 +76,7 @@ var _ = framework.KubeDescribe("[Feature:Federation]", func() { ...@@ -76,7 +76,7 @@ var _ = framework.KubeDescribe("[Feature:Federation]", func() {
svc, err := createService(fcs, nsName, FederatedServiceName) svc, err := createService(fcs, nsName, FederatedServiceName)
Expect(errors.IsUnauthorized(err)).To(BeTrue()) Expect(errors.IsUnauthorized(err)).To(BeTrue())
if err == nil && svc != nil { if err == nil && svc != nil {
deleteServiceOrFail(fcs, nsName, svc.Name) deleteServiceOrFail(fcs, nsName, svc.Name, nil)
} }
}) })
}) })
......
...@@ -283,12 +283,23 @@ func createServiceOrFail(clientset *fedclientset.Clientset, namespace, name stri ...@@ -283,12 +283,23 @@ func createServiceOrFail(clientset *fedclientset.Clientset, namespace, name stri
return service return service
} }
func deleteServiceOrFail(clientset *fedclientset.Clientset, namespace string, serviceName string) { func deleteServiceOrFail(clientset *fedclientset.Clientset, namespace string, serviceName string, orphanDependents *bool) {
if clientset == nil || len(namespace) == 0 || len(serviceName) == 0 { if clientset == nil || len(namespace) == 0 || len(serviceName) == 0 {
Fail(fmt.Sprintf("Internal error: invalid parameters passed to deleteServiceOrFail: clientset: %v, namespace: %v, service: %v", clientset, namespace, serviceName)) Fail(fmt.Sprintf("Internal error: invalid parameters passed to deleteServiceOrFail: clientset: %v, namespace: %v, service: %v", clientset, namespace, serviceName))
} }
err := clientset.Services(namespace).Delete(serviceName, v1.NewDeleteOptions(0)) err := clientset.Services(namespace).Delete(serviceName, &v1.DeleteOptions{OrphanDependents: orphanDependents})
framework.ExpectNoError(err, "Error deleting service %q from namespace %q", serviceName, namespace) framework.ExpectNoError(err, "Error deleting service %q from namespace %q", serviceName, namespace)
// Wait for the service to be deleted.
err = wait.Poll(5*time.Second, 3*wait.ForeverTestTimeout, func() (bool, error) {
_, err := clientset.Core().Services(namespace).Get(serviceName, metav1.GetOptions{})
if err != nil && errors.IsNotFound(err) {
return true, nil
}
return false, err
})
if err != nil {
framework.Failf("Error in deleting service %s: %v", serviceName, err)
}
} }
func cleanupServiceShardsAndProviderResources(namespace string, service *v1.Service, clusters map[string]*cluster) { func cleanupServiceShardsAndProviderResources(namespace string, service *v1.Service, clusters map[string]*cluster) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment