diff --git a/Gopkg.lock b/Gopkg.lock index de6eefda4..9b98da774 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -1372,6 +1372,7 @@ "k8s.io/apimachinery/pkg/fields", "k8s.io/apimachinery/pkg/labels", "k8s.io/apimachinery/pkg/runtime", + "k8s.io/apimachinery/pkg/runtime/serializer", "k8s.io/apimachinery/pkg/types", "k8s.io/apimachinery/pkg/util/intstr", "k8s.io/apimachinery/pkg/util/net", diff --git a/cmd/virtual-kubelet/commands/root/http.go b/cmd/virtual-kubelet/commands/root/http.go index b61013f7a..14e00c388 100644 --- a/cmd/virtual-kubelet/commands/root/http.go +++ b/cmd/virtual-kubelet/commands/root/http.go @@ -86,7 +86,7 @@ func setupHTTPServer(ctx context.Context, p providers.Provider, cfg *apiServerCo } mux := http.NewServeMux() - vkubelet.AttachPodRoutes(p, mux) + vkubelet.AttachPodRoutes(p, mux, true) s := &http.Server{ Handler: mux, diff --git a/vkubelet/api/pods.go b/vkubelet/api/pods.go new file mode 100644 index 000000000..6d7600128 --- /dev/null +++ b/vkubelet/api/pods.go @@ -0,0 +1,53 @@ +package api + +import ( + "context" + "net/http" + + "github.com/cpuguy83/strongerrors" + "github.com/virtual-kubelet/virtual-kubelet/log" + "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/serializer" +) + +// ContainerLogsBackend is used in place of backend implementations for the provider's pods +type RunningPodsBackend interface { + // GetPods retrieves a list of all pods running on the provider (can be cached). + GetPods(context.Context) ([]*v1.Pod, error) +} + +func RunningPodsHandlerFunc(p RunningPodsBackend) http.HandlerFunc { + scheme := runtime.NewScheme() + v1.SchemeBuilder.AddToScheme(scheme) + codecs := serializer.NewCodecFactory(scheme) + + return handleError(func(w http.ResponseWriter, req *http.Request) error { + ctx := req.Context() + ctx = log.WithLogger(ctx, log.L) + pods, err := p.GetPods(ctx) + if err != nil { + return err + } + + // Borrowed from github.com/kubernetes/kubernetes/pkg/kubelet/server/server.go + // encodePods creates an v1.PodList object from pods and returns the encoded + // PodList. + podList := new(v1.PodList) + for _, pod := range pods { + podList.Items = append(podList.Items, *pod) + } + codec := codecs.LegacyCodec(v1.SchemeGroupVersion) + data, err := runtime.Encode(codec, podList) + if err != nil { + return strongerrors.System(err) + } + + w.Header().Set("Content-Type", "application/json") + _, err = w.Write(data) + if err != nil { + return strongerrors.System(err) + } + return nil + }) +} diff --git a/vkubelet/apiserver.go b/vkubelet/apiserver.go index b4f7251fc..b5b8b824a 100644 --- a/vkubelet/apiserver.go +++ b/vkubelet/apiserver.go @@ -21,9 +21,14 @@ type ServeMux interface { } // PodHandler creates an http handler for interacting with pods/containers. -func PodHandler(p providers.Provider) http.Handler { +func PodHandler(p providers.Provider, debug bool) http.Handler { r := mux.NewRouter() + // This matches the behaviour in the reference kubelet + r.StrictSlash(true) + if debug { + r.HandleFunc("/runningpods/", api.RunningPodsHandlerFunc(p)).Methods("GET") + } r.HandleFunc("/containerLogs/{namespace}/{pod}/{container}", api.PodLogsHandlerFunc(p)).Methods("GET") r.HandleFunc("/exec/{namespace}/{pod}/{container}", api.PodExecHandlerFunc(p)).Methods("POST") r.NotFoundHandler = http.HandlerFunc(NotFound) @@ -58,8 +63,8 @@ func MetricsSummaryHandler(p providers.Provider) http.Handler { // // Callers should take care to namespace the serve mux as they see fit, however // these routes get called by the Kubernetes API server. -func AttachPodRoutes(p providers.Provider, mux ServeMux) { - mux.Handle("/", InstrumentHandler(PodHandler(p))) +func AttachPodRoutes(p providers.Provider, mux ServeMux, debug bool) { + mux.Handle("/", InstrumentHandler(PodHandler(p, debug))) } // AttachMetricsRoutes adds the http routes for pod/node metrics to the passed in serve mux.