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