Change:
[x] two weeks soak end date : 18 March 2021 According to this APIsnoop query, there are still some remaining RESOURCENAME endpoints which are untested. with this query you can filter untested endpoints by their category and eligiblity for conformance. e.g below shows a query to find all conformance eligible untested,stable,core endpoints Note: Community feedback for the e2e test has lead to a number of improvements including extending the test coverage to all outstanding endpoints listed above. As each endpoint is tested a &rsquo;watch&rsquo; confirms that the result is valid before testing the next endpoint. readCoreV1NamespacedServiceStatus (get /status) patchCoreV1NamespacedServiceStatus (patch /status) replaceCoreV1NamespacedServiceStatus (update /status) patchCoreV1NamespacedService Create a Service with a static label Patch the Service with a new label and updated data Get the Service to ensure it&rsquo;s patched Upate the Service with a new label and updated data Get the Service to ensure it&rsquo;s updated Delete Namespaced Service via a Collection with a LabelSelector This query by apisnoop shows that all outstanding endpoints as list at the start of this document have been hit by the e2e test. Note that the results do include other service endpoints that have been addressed in other conformance tests. If a test with these calls gets merged, test coverage will go up by 4 points This test is also created with the goal of conformance promotion. /sig testing /sig architecture /area conformance
Can you please review the proposed test and advise if we could go ahead with a PR
/assign
It looks like it's patching metadata and spec but not status?
Issues go stale after 90d of inactivity. Mark the issue as fresh with . Stale issues rot after an additional 30d of inactivity and eventually close. If this issue is safe to close now please do so with . Send feedback to sig-testing, kubernetes/test-infra and/or . /lifecycle stale
/remove-lifecycle stale
I've been retesting this mock test and apisnoop does log as been hit. From my testing below it looks like the patchStatus is working as expected or have I missed something? Using the following code before the patch request Gives this response Then after running the patch ServiceStatus Then running The response shows the patched changes.
During the Conformance Meeting on 12th of January 2021, reviewed approach with Clayton, and we will start on the test PR this week!
/reopen
Reopened this issue. <detailsIn response to : /reopen </details
/triage accepted

Discussion:
"time" utilnet "k8s.io/apimachinery/pkg/util/net" <ins> utilrand "k8s.io/apimachinery/pkg/util/rand" </ins> "k8s.io/client-go/tools/cache"

discoveryv1beta1 "k8s.io/api/discovery/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" <ins> "k8s.io/apimachinery/pkg/runtime/schema" </ins> "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/sets"

watch "k8s.io/apimachinery/pkg/watch" clientset "k8s.io/client-go/kubernetes" watchtools "k8s.io/client-go/tools/watch" <ins> "k8s.io/client-go/util/retry" </ins> cloudprovider "k8s.io/cloud-provider" "k8s.io/kubernetes/test/e2e/framework" e2edeployment "k8s.io/kubernetes/test/e2e/framework/deployment"

clusterAddonLabelKey   = "k8s-app" kubeAPIServerLabelName = "kube-apiserver" clusterComponentKey    = "component" <ins> svcReadyTimeout = 1 * time.Minute </ins> ) var (

_, err = f.ClientSet.CoreV1().Endpoints(testNamespaceName).Get(context.TODO(), testEndpointName, metav1.GetOptions{}) framework.ExpectError(err, "should not be able to fetch Endpoint") }) <ins> ginkgo.It("should complete a service status lifecycle", func() { ns := f.Namespace.Name svcResource := schema.GroupVersionResource{Group: "", Version: "v1", Resource: "services"} svcClient := f.ClientSet.CoreV1().Services(ns) testSvcName := "test-service-" + utilrand.String(5) testSvcLabels := map[string]string{"test-service-static": "true"} testSvcLabelsFlat := "test-service-static=true" w := &cache.ListWatch{ WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { options.LabelSelector = testSvcLabelsFlat return cs.CoreV1().Services(ns).Watch(context.TODO(), options) }, } svcList, err := cs.CoreV1().Services("").List(context.TODO(), metav1.ListOptions{LabelSelector: testSvcLabelsFlat}) framework.ExpectNoError(err, "failed to list Services") ginkgo.By("creating a Service") testService := v1.Service{ ObjectMeta: metav1.ObjectMeta{ Name:   testSvcName, Labels: testSvcLabels, }, Spec: v1.ServiceSpec{ Type: "ClusterIP", Ports: []v1.ServicePort{{ Name:       "http", Protocol:   v1.ProtocolTCP, Port:       int32(80), TargetPort: intstr.FromInt(80), }}, }, } _, err = cs.CoreV1().Services(ns).Create(context.TODO(), &testService, metav1.CreateOptions{}) framework.ExpectNoError(err, "failed to create Service") ginkgo.By("watching for the Service to be added") ctx, cancel := context.WithTimeout(context.Background(), svcReadyTimeout) defer cancel() _, err = watchtools.Until(ctx, svcList.ResourceVersion, w, func(event watch.Event) (bool, error) { if svc, ok := event.Object.(*v1.Service); ok { found := svc.ObjectMeta.Name == testService.ObjectMeta.Name && svc.ObjectMeta.Namespace == ns && svc.Labels["test-service-static"] == "true" if !found { framework.Logf("observed Service %v in namespace %v with labels: %v & ports %v", svc.ObjectMeta.Name, svc.ObjectMeta.Namespace, svc.Labels, svc.Spec.Ports) return false, nil } framework.Logf("Found Service %v in namespace %v with labels: %v & ports %v", svc.ObjectMeta.Name, svc.ObjectMeta.Namespace, svc.Labels, svc.Spec.Ports) return found, nil } framework.Logf("Observed event: %+v", event.Object) return false, nil }) framework.ExpectNoError(err, "Failed to locate Service %v in namespace %v", testService.ObjectMeta.Name, ns) framework.Logf("Service %s created", testSvcName) ginkgo.By("Getting /status") svcStatusUnstructured, err := f.DynamicClient.Resource(svcResource).Namespace(ns).Get(context.TODO(), testSvcName, metav1.GetOptions{}, "status") framework.ExpectNoError(err, "Failed to fetch ServiceStatus of Service %s in namespace %s", testSvcName, ns) svcStatusBytes, err := json.Marshal(svcStatusUnstructured) framework.ExpectNoError(err, "Failed to marshal unstructured response. %v", err) var svcStatus v1.Service err = json.Unmarshal(svcStatusBytes, &svcStatus) framework.ExpectNoError(err, "Failed to unmarshal JSON bytes to a Service object type") framework.Logf("Service %s has LoadBalancer: %v", testSvcName, svcStatus.Status.LoadBalancer) ginkgo.By("patching the ServiceStatus") lbStatus := v1.LoadBalancerStatus{ Ingress: []v1.LoadBalancerIngress{{IP: "203.0.113.1"}}, } lbStatusJSON, err := json.Marshal(lbStatus) framework.ExpectNoError(err, "Failed to marshal JSON. %v", err) _, err = svcClient.Patch(context.TODO(), testSvcName, types.MergePatchType, []byte(`{"metadata":{"annotations":{"patchedstatus":"true"}},"status":{"loadBalancer":`+string(lbStatusJSON)+`}}`), metav1.PatchOptions{}, "status") framework.ExpectNoError(err, "Could not patch service status", err) ginkgo.By("watching for the Service to be patched") ctx, cancel = context.WithTimeout(context.Background(), svcReadyTimeout) defer cancel() _, err = watchtools.Until(ctx, svcList.ResourceVersion, w, func(event watch.Event) (bool, error) { if svc, ok := event.Object.(*v1.Service); ok { found := svc.ObjectMeta.Name == testService.ObjectMeta.Name && svc.ObjectMeta.Namespace == ns && svc.Annotations["patchedstatus"] == "true" if !found { framework.Logf("observed Service %v in namespace %v with annotations: %v & LoadBalancer: %v", svc.ObjectMeta.Name, svc.ObjectMeta.Namespace, svc.Annotations, svc.Status.LoadBalancer) return false, nil } framework.Logf("Found Service %v in namespace %v with annotations: %v & LoadBalancer: %v", svc.ObjectMeta.Name, svc.ObjectMeta.Namespace, svc.Annotations, svc.Status.LoadBalancer) return found, nil } framework.Logf("Observed event: %+v", event.Object) return false, nil }) framework.ExpectNoError(err, "failed to locate Service %v in namespace %v", testService.ObjectMeta.Name, ns) framework.Logf("Service %s has service status patched", testSvcName) ginkgo.By("updating the ServiceStatus") var statusToUpdate, updatedStatus *v1.Service err = retry.RetryOnConflict(retry.DefaultRetry, func() error { statusToUpdate, err = svcClient.Get(context.TODO(), testSvcName, metav1.GetOptions{}) framework.ExpectNoError(err, "Unable to retrieve service %s", testSvcName) statusToUpdate.Status.Conditions = append(statusToUpdate.Status.Conditions, metav1.Condition{ Type:    "StatusUpdate", Status:  metav1.ConditionTrue, Reason:  "E2E", Message: "Set from e2e test", }) updatedStatus, err = svcClient.UpdateStatus(context.TODO(), statusToUpdate, metav1.UpdateOptions{}) return err }) framework.ExpectNoError(err, "nn Failed to UpdateStatus. %vnn", err) framework.Logf("updatedStatus.Conditions: %#v", updatedStatus.Status.Conditions) ginkgo.By("watching for the Service to be updated") ctx, cancel = context.WithTimeout(context.Background(), svcReadyTimeout) defer cancel() _, err = watchtools.Until(ctx, svcList.ResourceVersion, w, func(event watch.Event) (bool, error) { if svc, ok := event.Object.(*v1.Service); ok { found := svc.ObjectMeta.Name == testService.ObjectMeta.Name && svc.ObjectMeta.Namespace == ns && svc.Annotations["patchedstatus"] == "true" if !found { framework.Logf("Observed Service %v in namespace %v with annotations: %v & Conditions: %v", svc.ObjectMeta.Name, svc.ObjectMeta.Namespace, svc.Annotations, svc.Status.LoadBalancer) return false, nil } for _, cond := range svc.Status.Conditions { if cond.Type == "StatusUpdate" && cond.Reason == "E2E" && cond.Message == "Set from e2e test" { framework.Logf("Found Service %v in namespace %v with annotations: %v & Conditions: %v", svc.ObjectMeta.Name, svc.ObjectMeta.Namespace, svc.Annotations, svc.Status.Conditions) return found, nil } else { framework.Logf("Observed Service %v in namespace %v with annotations: %v & Conditions: %v", svc.ObjectMeta.Name, svc.ObjectMeta.Namespace, svc.Annotations, svc.Status.LoadBalancer) return false, nil } } } framework.Logf("Observed event: %+v", event.Object) return false, nil }) framework.ExpectNoError(err, "failed to locate Service %v in namespace %v", testService.ObjectMeta.Name, ns) framework.Logf("Service %s has service status updated", testSvcName) ginkgo.By("patching the service") servicePatchPayload, err := json.Marshal(v1.Service{ ObjectMeta: metav1.ObjectMeta{ Labels: map[string]string{ "test-service": "patched", }, }, }) _, err = svcClient.Patch(context.TODO(), testSvcName, types.StrategicMergePatchType, []byte(servicePatchPayload), metav1.PatchOptions{}) framework.ExpectNoError(err, "failed to patch service. %v", err) ginkgo.By("watching for the Service to be patched") ctx, cancel = context.WithTimeout(context.Background(), svcReadyTimeout) defer cancel() _, err = watchtools.Until(ctx, svcList.ResourceVersion, w, func(event watch.Event) (bool, error) { if svc, ok := event.Object.(*v1.Service); ok { found := svc.ObjectMeta.Name == testService.ObjectMeta.Name && svc.ObjectMeta.Namespace == ns && svc.Labels["test-service"] == "patched" if !found { framework.Logf("observed Service %v in namespace %v with labels: %v", svc.ObjectMeta.Name, svc.ObjectMeta.Namespace, svc.Labels) return false, nil } framework.Logf("Found Service %v in namespace %v with labels: %v", svc.ObjectMeta.Name, svc.ObjectMeta.Namespace, svc.Labels) return found, nil } framework.Logf("Observed event: %+v", event.Object) return false, nil }) framework.ExpectNoError(err, "failed to locate Service %v in namespace %v", testService.ObjectMeta.Name, ns) framework.Logf("Service %s patched", testSvcName) ginkgo.By("deleting the service") err = cs.CoreV1().Services(ns).Delete(context.TODO(), testSvcName, metav1.DeleteOptions{}) framework.ExpectNoError(err, "failed to delete the Service. %v", err) ginkgo.By("watching for the Service to be deleted") ctx, cancel = context.WithTimeout(context.Background(), svcReadyTimeout) defer cancel() _, err = watchtools.Until(ctx, svcList.ResourceVersion, w, func(event watch.Event) (bool, error) { switch event.Type { case watch.Deleted: if svc, ok := event.Object.(*v1.Service); ok { found := svc.ObjectMeta.Name == testService.ObjectMeta.Name && svc.ObjectMeta.Namespace == ns && svc.Labels["test-service-static"] == "true" if !found { framework.Logf("observed Service %v in namespace %v with labels: %v & annotations: %v", svc.ObjectMeta.Name, svc.ObjectMeta.Namespace, svc.Labels, svc.Annotations) return false, nil } framework.Logf("Found Service %v in namespace %v with labels: %v & annotations: %v", svc.ObjectMeta.Name, svc.ObjectMeta.Namespace, svc.Labels, svc.Annotations) return found, nil } default: framework.Logf("Observed event: %+v", event.Type) } return false, nil }) framework.ExpectNoError(err, "failed to delete Service %v in namespace %v", testService.ObjectMeta.Name, ns) framework.Logf("Service %s deleted", testSvcName) }) </ins> }) // execAffinityTestForSessionAffinityTimeout is a helper function that wrap the logic of