mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-22 11:21:47 +00:00
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:
commit
ae4f2ac43d
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user