Change:
create a service without selector, and manual create endpoints for this service endpointslicemirroring controller will create an endpointslice for this service. after endpoint slice created, and kube-controller-manager restart. during the kube-controller-manager restart, the endpoint was deleted and recreated after kube-controller-manager restart, garbagecollector controller delete this endpointslice and endpointslice not recreate until endpoint/service update event or kube-controller-manager restart endpointslice will be recreated create a service without selector, and manual create endpoints for this service endpointslicemirroring controller will create an endpointslice for this service. after endpoint slice created, and kube-controller-manager restart. during the kube-controller-manager restart, the endpoint was deleted and recreated No response <details</details<details</details<details</details<details</details<details</details<details</details
/sig network
/assign
Trying to understand this, mostly just restating what you've said above to make sure I'm getting it: during the kube-controller-manager restart, the endpoint was deleted and recreated after kube-controller-manager restart, garbagecollector controller delete this endpointslice and endpointslice not recreate until endpoint/service update event or kube-controller-manager restart So an Endpoints resource is being deleted while kube-controller-manager is not running. The EndpointSlice mirroring controller to point to the Endpoints they're mirroring. When kube-controller-manager is restarted, the garbage collector will delete the mirrored EndpointSlice because it's owned by an Endpoints resource that was previously deleted. That deletion by garbage collection would then be , but if the EndpointSliceTracker doesn't think it should have that resource, it will ignore the deletion and not reconcile, resulting in the mirrored EndpointSlice not being recreated until something updates the Service or Endpoints resource.
/triage accepted

Discussion:
totals = totalChanges(existingSlices[0], desiredSet) if totals.added == 0 && totals.updated == 0 && totals.removed == 0 && apiequality.Semantic.DeepEqual(endpoints.Labels, compareLabels) && <del> apiequality.Semantic.DeepEqual(compareAnnotations, existingSlices[0].Annotations) { </del> <ins> apiequality.Semantic.DeepEqual(compareAnnotations, existingSlices[0].Annotations) && !needRebuildExistingSlices(endpoints, existingSlices[0]) { if !r.endpointSliceTracker.Has(existingSlices[0]) { r.endpointSliceTracker.Update(existingSlices[0]) // Always ensure each EndpointSlice is being tracked. } </ins> return slices, totals } }

totals.added = desiredSet.Len() - existingMatches return totals } <ins> func needRebuildExistingSlices(endpoints *corev1.Endpoints, existingSlice *discovery.EndpointSlice) bool { for index := range existingSlice.OwnerReferences { owner := existingSlice.OwnerReferences[index] if owner.Kind == "Endpoints" && owner.Name == endpoints.Name && owner.UID != endpoints.UID { return true } } return false } </ins>

corev1 "k8s.io/api/core/v1" discovery "k8s.io/api/discovery/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" <ins> "k8s.io/apimachinery/pkg/types" </ins> "k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/tools/record"

subsets                  []corev1.EndpointSubset epLabels                 map[string]string epAnnotations            map[string]string <ins> uid                      string </ins> endpointsDeletionPending bool maxEndpointsPerSubset    int32 existingEndpointSlices   []*discovery.EndpointSlice

Hostname: "pod-1", }}, }}, <ins> uid: "d8f2c1f6-5285-4b3c-b3c1-9b89f9e7ed7a", </ins> existingEndpointSlices: []*discovery.EndpointSlice{{ ObjectMeta: metav1.ObjectMeta{ <del> Name: "test-ep-1", </del> <ins> Name:      "test-ep-1", Namespace: "test", OwnerReferences: []metav1.OwnerReference{ { APIVersion: "v1", Kind:       "Endpoints", Name:       "test-ep", UID:        "d8f2c1f6-5285-4b3c-b3c1-9b89f9e7ed7a", }, }, </ins> }, AddressType: discovery.AddressTypeIPv4, Ports: []discovery.EndpointPort{{

expectedNumSlices:     1, expectedClientActions: 0, }, { <ins> testName: "Endpoints with 1 subset, port, and address and existing slice with same fields but different OwnerReferences", subsets: []corev1.EndpointSubset{{ Ports: []corev1.EndpointPort{{ Name:     "http", Port:     80, Protocol: corev1.ProtocolTCP, }}, Addresses: []corev1.EndpointAddress{{ IP:       "10.0.0.1", Hostname: "pod-1", }}, }}, uid: "d8f2c1f6-5285-4b3c-b3c1-9b89f9e7ed7a", existingEndpointSlices: []*discovery.EndpointSlice{{ ObjectMeta: metav1.ObjectMeta{ Name:      "test-ep-1", Namespace: "test", OwnerReferences: []metav1.OwnerReference{ { APIVersion: "v1", Kind:       "Endpoints", Name:       "test-ep", UID:        "fb91e798-1875-4857-b5eb-e2c878157b4d", }, }, }, AddressType: discovery.AddressTypeIPv4, Ports: []discovery.EndpointPort{{ Name:     pointer.String("http"), Port:     pointer.Int32(80), Protocol: &protoTCP, }}, Endpoints: []discovery.Endpoint{{ Addresses:  []string{"10.0.0.1"}, Hostname:   pointer.String("pod-1"), Conditions: discovery.EndpointConditions{Ready: pointer.Bool(true)}, }}, }}, expectedNumSlices:     1, expectedClientActions: 1, }, { </ins> testName: "Endpoints with 1 subset, port, and address and existing slice with an additional annotation", subsets: []corev1.EndpointSubset{{ Ports: []corev1.EndpointPort{{

setupMetrics() namespace := "test" endpoints := corev1.Endpoints{ <del> ObjectMeta: metav1.ObjectMeta{Name: "test-ep", Namespace: namespace, Labels: tc.epLabels, Annotations: tc.epAnnotations}, </del> <ins> ObjectMeta: metav1.ObjectMeta{Name: "test-ep", Namespace: namespace, Labels: tc.epLabels, Annotations: tc.epAnnotations, UID: types.UID(tc.uid)}, </ins> Subsets:    tc.subsets, }