Merge pull request #98018 from ii/service-status-life-cycle-test

Write Service Status Life Cycle test - +4 endpoint coverage
This commit is contained in:
Kubernetes Prow Robot 2021-03-04 14:40:22 -08:00 committed by GitHub
commit ae4f2ac43d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -30,6 +30,7 @@ import (
"time"
utilnet "k8s.io/apimachinery/pkg/util/net"
utilrand "k8s.io/apimachinery/pkg/util/rand"
"k8s.io/client-go/tools/cache"
@ -38,6 +39,7 @@ import (
discoveryv1beta1 "k8s.io/api/discovery/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/apimachinery/pkg/util/sets"
@ -45,6 +47,7 @@ import (
watch "k8s.io/apimachinery/pkg/watch"
clientset "k8s.io/client-go/kubernetes"
watchtools "k8s.io/client-go/tools/watch"
"k8s.io/client-go/util/retry"
cloudprovider "k8s.io/cloud-provider"
"k8s.io/kubernetes/test/e2e/framework"
e2edeployment "k8s.io/kubernetes/test/e2e/framework/deployment"
@ -84,6 +87,8 @@ const (
clusterAddonLabelKey = "k8s-app"
kubeAPIServerLabelName = "kube-apiserver"
clusterComponentKey = "component"
svcReadyTimeout = 1 * time.Minute
)
var (
@ -2199,6 +2204,223 @@ var _ = common.SIGDescribe("Services", func() {
_, err = f.ClientSet.CoreV1().Endpoints(testNamespaceName).Get(context.TODO(), testEndpointName, metav1.GetOptions{})
framework.ExpectError(err, "should not be able to fetch Endpoint")
})
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, "\n\n Failed to UpdateStatus. %v\n\n", 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)
})
})
// execAffinityTestForSessionAffinityTimeout is a helper function that wrap the logic of