From 5ad12cd476a46cbc50fddf0fa44d898b40be19f8 Mon Sep 17 00:00:00 2001 From: Sargun Dhillon Date: Fri, 20 Mar 2020 03:02:04 -0700 Subject: [PATCH] Add /pods HTTP endpoint --- .../internal/commands/root/http.go | 3 +- .../internal/commands/root/root.go | 4 +- internal/test/e2e/framework/pod.go | 21 ++++++- node/api/server.go | 10 +++- test/e2e/basic.go | 59 ++++++++++++++++--- 5 files changed, 82 insertions(+), 15 deletions(-) diff --git a/cmd/virtual-kubelet/internal/commands/root/http.go b/cmd/virtual-kubelet/internal/commands/root/http.go index 02ac8f4a8..b20a4ff7d 100644 --- a/cmd/virtual-kubelet/internal/commands/root/http.go +++ b/cmd/virtual-kubelet/internal/commands/root/http.go @@ -58,7 +58,7 @@ func loadTLSConfig(certPath, keyPath string) (*tls.Config, error) { }, nil } -func setupHTTPServer(ctx context.Context, p provider.Provider, cfg *apiServerConfig) (_ func(), retErr error) { +func setupHTTPServer(ctx context.Context, p provider.Provider, cfg *apiServerConfig, getPodsFromKubernetes api.PodListerFunc) (_ func(), retErr error) { var closers []io.Closer cancel := func() { for _, c := range closers { @@ -91,6 +91,7 @@ func setupHTTPServer(ctx context.Context, p provider.Provider, cfg *apiServerCon podRoutes := api.PodHandlerConfig{ RunInContainer: p.RunInContainer, GetContainerLogs: p.GetContainerLogs, + GetPodsFromKubernetes: getPodsFromKubernetes, GetPods: p.GetPods, StreamIdleTimeout: cfg.StreamIdleTimeout, StreamCreationTimeout: cfg.StreamCreationTimeout, diff --git a/cmd/virtual-kubelet/internal/commands/root/root.go b/cmd/virtual-kubelet/internal/commands/root/root.go index 5f56a8c21..6cb1dbb22 100644 --- a/cmd/virtual-kubelet/internal/commands/root/root.go +++ b/cmd/virtual-kubelet/internal/commands/root/root.go @@ -193,7 +193,9 @@ func runRootCommand(ctx context.Context, s *provider.Store, c Opts) error { go podInformerFactory.Start(ctx.Done()) go scmInformerFactory.Start(ctx.Done()) - cancelHTTP, err := setupHTTPServer(ctx, p, apiConfig) + cancelHTTP, err := setupHTTPServer(ctx, p, apiConfig, func(context.Context) ([]*corev1.Pod, error) { + return rm.GetPods(), nil + }) if err != nil { return err } diff --git a/internal/test/e2e/framework/pod.go b/internal/test/e2e/framework/pod.go index 124ac8ad8..88ca740bc 100644 --- a/internal/test/e2e/framework/pod.go +++ b/internal/test/e2e/framework/pod.go @@ -164,8 +164,8 @@ func (f *Framework) WaitUntilPodEventWithReason(pod *corev1.Pod, reason string) return nil } -// GetRunningPods gets the running pods from the provider of the virtual kubelet -func (f *Framework) GetRunningPods() (*corev1.PodList, error) { +// GetRunningPodsFromProvider gets the running pods from the provider of the virtual kubelet +func (f *Framework) GetRunningPodsFromProvider() (*corev1.PodList, error) { result := &corev1.PodList{} err := f.KubeClient.CoreV1(). @@ -181,6 +181,23 @@ func (f *Framework) GetRunningPods() (*corev1.PodList, error) { return result, err } +// GetRunningPodsFromProvider gets the running pods from the provider of the virtual kubelet +func (f *Framework) GetRunningPodsFromKubernetes() (*corev1.PodList, error) { + result := &corev1.PodList{} + + err := f.KubeClient.CoreV1(). + RESTClient(). + Get(). + Resource("nodes"). + Name(f.NodeName). + SubResource("proxy"). + Suffix("pods"). + Do(). + Into(result) + + return result, err +} + // stripParentTestName strips out the parent's test name from the input (in the form of 'TestParent/TestChild'). // Some test cases use their name as the pod name for testing purpose, and sometimes it might exceed 63 // characters (Kubernetes's limit for pod name). This function ensures that we strip out the parent's diff --git a/node/api/server.go b/node/api/server.go index 04980667b..7ab0761ad 100644 --- a/node/api/server.go +++ b/node/api/server.go @@ -34,9 +34,12 @@ type ServeMux interface { } type PodHandlerConfig struct { - RunInContainer ContainerExecHandlerFunc - GetContainerLogs ContainerLogsHandlerFunc - GetPods PodListerFunc + RunInContainer ContainerExecHandlerFunc + GetContainerLogs ContainerLogsHandlerFunc + // GetPods is meant to enumerate the pods that the provider knows about + GetPods PodListerFunc + // GetPodsFromKubernetes is meant to enumerate the pods that the node is meant to be running + GetPodsFromKubernetes PodListerFunc StreamIdleTimeout time.Duration StreamCreationTimeout time.Duration } @@ -51,6 +54,7 @@ func PodHandler(p PodHandlerConfig, debug bool) http.Handler { r.HandleFunc("/runningpods/", HandleRunningPods(p.GetPods)).Methods("GET") } + r.HandleFunc("/pods", HandleRunningPods(p.GetPodsFromKubernetes)).Methods("GET") r.HandleFunc("/containerLogs/{namespace}/{pod}/{container}", HandleContainerLogs(p.GetContainerLogs)).Methods("GET") r.HandleFunc( "/exec/{namespace}/{pod}/{container}", diff --git a/test/e2e/basic.go b/test/e2e/basic.go index 01a81b509..9f6c35d28 100644 --- a/test/e2e/basic.go +++ b/test/e2e/basic.go @@ -18,6 +18,49 @@ const ( deleteGracePeriodForProvider = 1 * time.Second ) +// TestGetPods tests that the /pods endpoint works, and only returns pods for our kubelet +func (ts *EndToEndTestSuite) TestGetPods(t *testing.T) { + // Create a pod with prefix "nginx-" having a single container. + podSpec := f.CreateDummyPodObjectWithPrefix(t.Name(), "nginx", "foo") + podSpec.Spec.NodeName = f.NodeName + + nginx, err := f.CreatePod(podSpec) + if err != nil { + t.Fatal(err) + } + // Delete the pod after the test finishes. + defer func() { + if err := f.DeletePodImmediately(nginx.Namespace, nginx.Name); err != nil && !apierrors.IsNotFound(err) { + t.Error(err) + } + }() + t.Logf("Created pod: %s", nginx.Name) + + // Wait for the "nginx-" pod to be reported as running and ready. + if _, err := f.WaitUntilPodReady(nginx.Namespace, nginx.Name); err != nil { + t.Fatal(err) + } + t.Logf("Pod %s ready", nginx.Name) + + k8sPods, err := f.GetRunningPodsFromKubernetes() + if err != nil { + t.Fatal(err) + } + + podFound := false + for _, pod := range k8sPods.Items { + if pod.Spec.NodeName != f.NodeName { + t.Fatalf("Found pod with node name %s, whereas expected %s", pod.Spec.NodeName, f.NodeName) + } + if pod.UID == nginx.UID { + podFound = true + } + } + if !podFound { + t.Fatal("Nginx pod not found") + } +} + // TestGetStatsSummary creates a pod having two containers and queries the /stats/summary endpoint of the virtual-kubelet. // It expects this endpoint to return stats for the current node, as well as for the aforementioned pod and each of its two containers. func (ts *EndToEndTestSuite) TestGetStatsSummary(t *testing.T) { @@ -91,7 +134,7 @@ func (ts *EndToEndTestSuite) TestPodLifecycleGracefulDelete(t *testing.T) { t.Logf("Pod %s ready", pod.Name) // Grab the pods from the provider. - pods, err := f.GetRunningPods() + pods, err := f.GetRunningPodsFromProvider() assert.NilError(t, err) // Check if the pod exists in the slice of PodStats. @@ -125,7 +168,7 @@ func (ts *EndToEndTestSuite) TestPodLifecycleGracefulDelete(t *testing.T) { time.Sleep(deleteGracePeriodForProvider) // Give the provider some time to react to the MODIFIED/DELETED events before proceeding. // Grab the pods from the provider. - pods, err = f.GetRunningPods() + pods, err = f.GetRunningPodsFromProvider() assert.NilError(t, err) // Make sure the pod DOES NOT exist in the provider's set of running pods @@ -162,7 +205,7 @@ func (ts *EndToEndTestSuite) TestPodLifecycleForceDelete(t *testing.T) { t.Logf("Pod %s ready", pod.Name) // Grab the pods from the provider. - pods, err := f.GetRunningPods() + pods, err := f.GetRunningPodsFromProvider() assert.NilError(t, err) // Check if the pod exists in the slice of Pods. @@ -203,7 +246,7 @@ func (ts *EndToEndTestSuite) TestPodLifecycleForceDelete(t *testing.T) { time.Sleep(deleteGracePeriodForProvider) // Grab the pods from the provider. - pods, err = f.GetRunningPods() + pods, err = f.GetRunningPodsFromProvider() assert.NilError(t, err) // Make sure the "nginx-" pod DOES NOT exist in the slice of Pods anymore. @@ -240,7 +283,7 @@ func (ts *EndToEndTestSuite) TestCreatePodWithOptionalInexistentSecrets(t *testi } // Grab the pods from the provider. - pods, err := f.GetRunningPods() + pods, err := f.GetRunningPodsFromProvider() assert.NilError(t, err) // Check if the pod exists in the slice of Pods. @@ -269,7 +312,7 @@ func (ts *EndToEndTestSuite) TestCreatePodWithMandatoryInexistentSecrets(t *test } // Grab the pods from the provider. - pods, err := f.GetRunningPods() + pods, err := f.GetRunningPodsFromProvider() assert.NilError(t, err) // Check if the pod exists in the slice of PodStats. @@ -303,7 +346,7 @@ func (ts *EndToEndTestSuite) TestCreatePodWithOptionalInexistentConfigMap(t *tes } // Grab the pods from the provider. - pods, err := f.GetRunningPods() + pods, err := f.GetRunningPodsFromProvider() assert.NilError(t, err) // Check if the pod exists in the slice of PodStats. @@ -332,7 +375,7 @@ func (ts *EndToEndTestSuite) TestCreatePodWithMandatoryInexistentConfigMap(t *te } // Grab the pods from the provider. - pods, err := f.GetRunningPods() + pods, err := f.GetRunningPodsFromProvider() assert.NilError(t, err) // Check if the pod exists in the slice of PodStats.