k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/kubelet/server/server.go (about)

     1  /*
     2  Copyright 2014 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package server
    18  
    19  import (
    20  	"context"
    21  	"crypto/tls"
    22  	"fmt"
    23  	"io"
    24  	"net"
    25  	"net/http"
    26  	"net/http/pprof"
    27  	"net/url"
    28  	"os"
    29  	"reflect"
    30  	goruntime "runtime"
    31  	"strconv"
    32  	"strings"
    33  	"time"
    34  
    35  	"github.com/emicklei/go-restful/v3"
    36  	cadvisormetrics "github.com/google/cadvisor/container"
    37  	cadvisorapi "github.com/google/cadvisor/info/v1"
    38  	cadvisorv2 "github.com/google/cadvisor/info/v2"
    39  	"github.com/google/cadvisor/metrics"
    40  	"go.opentelemetry.io/contrib/instrumentation/github.com/emicklei/go-restful/otelrestful"
    41  	oteltrace "go.opentelemetry.io/otel/trace"
    42  	"google.golang.org/grpc"
    43  	"k8s.io/klog/v2"
    44  	"k8s.io/kubernetes/pkg/kubelet/metrics/collectors"
    45  	"k8s.io/utils/clock"
    46  	netutils "k8s.io/utils/net"
    47  
    48  	v1 "k8s.io/api/core/v1"
    49  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    50  	"k8s.io/apimachinery/pkg/runtime"
    51  	"k8s.io/apimachinery/pkg/runtime/schema"
    52  	"k8s.io/apimachinery/pkg/types"
    53  	"k8s.io/apimachinery/pkg/util/proxy"
    54  	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
    55  	"k8s.io/apimachinery/pkg/util/sets"
    56  	"k8s.io/apiserver/pkg/authentication/authenticator"
    57  	"k8s.io/apiserver/pkg/authorization/authorizer"
    58  	"k8s.io/apiserver/pkg/server/healthz"
    59  	"k8s.io/apiserver/pkg/server/httplog"
    60  	"k8s.io/apiserver/pkg/server/routes"
    61  	utilfeature "k8s.io/apiserver/pkg/util/feature"
    62  	"k8s.io/apiserver/pkg/util/flushwriter"
    63  	"k8s.io/component-base/configz"
    64  	"k8s.io/component-base/logs"
    65  	compbasemetrics "k8s.io/component-base/metrics"
    66  	metricsfeatures "k8s.io/component-base/metrics/features"
    67  	"k8s.io/component-base/metrics/legacyregistry"
    68  	"k8s.io/component-base/metrics/prometheus/slis"
    69  	runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
    70  	"k8s.io/cri-client/pkg/util"
    71  	podresourcesapi "k8s.io/kubelet/pkg/apis/podresources/v1"
    72  	podresourcesapiv1alpha1 "k8s.io/kubelet/pkg/apis/podresources/v1alpha1"
    73  	"k8s.io/kubelet/pkg/cri/streaming"
    74  	"k8s.io/kubelet/pkg/cri/streaming/portforward"
    75  	remotecommandserver "k8s.io/kubelet/pkg/cri/streaming/remotecommand"
    76  	kubelettypes "k8s.io/kubelet/pkg/types"
    77  	"k8s.io/kubernetes/pkg/api/legacyscheme"
    78  	api "k8s.io/kubernetes/pkg/apis/core"
    79  	"k8s.io/kubernetes/pkg/apis/core/v1/validation"
    80  	"k8s.io/kubernetes/pkg/features"
    81  	kubeletconfiginternal "k8s.io/kubernetes/pkg/kubelet/apis/config"
    82  	apisgrpc "k8s.io/kubernetes/pkg/kubelet/apis/grpc"
    83  	"k8s.io/kubernetes/pkg/kubelet/apis/podresources"
    84  	kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
    85  	"k8s.io/kubernetes/pkg/kubelet/prober"
    86  	servermetrics "k8s.io/kubernetes/pkg/kubelet/server/metrics"
    87  	"k8s.io/kubernetes/pkg/kubelet/server/stats"
    88  )
    89  
    90  func init() {
    91  	utilruntime.Must(metricsfeatures.AddFeatureGates(utilfeature.DefaultMutableFeatureGate))
    92  }
    93  
    94  const (
    95  	metricsPath         = "/metrics"
    96  	cadvisorMetricsPath = "/metrics/cadvisor"
    97  	resourceMetricsPath = "/metrics/resource"
    98  	proberMetricsPath   = "/metrics/probes"
    99  	statsPath           = "/stats/"
   100  	logsPath            = "/logs/"
   101  	checkpointPath      = "/checkpoint/"
   102  	pprofBasePath       = "/debug/pprof/"
   103  	debugFlagPath       = "/debug/flags/v"
   104  )
   105  
   106  // Server is a http.Handler which exposes kubelet functionality over HTTP.
   107  type Server struct {
   108  	auth                 AuthInterface
   109  	host                 HostInterface
   110  	restfulCont          containerInterface
   111  	metricsBuckets       sets.String
   112  	metricsMethodBuckets sets.String
   113  	resourceAnalyzer     stats.ResourceAnalyzer
   114  }
   115  
   116  // TLSOptions holds the TLS options.
   117  type TLSOptions struct {
   118  	Config   *tls.Config
   119  	CertFile string
   120  	KeyFile  string
   121  }
   122  
   123  // containerInterface defines the restful.Container functions used on the root container
   124  type containerInterface interface {
   125  	Add(service *restful.WebService) *restful.Container
   126  	Handle(path string, handler http.Handler)
   127  	Filter(filter restful.FilterFunction)
   128  	ServeHTTP(w http.ResponseWriter, r *http.Request)
   129  	RegisteredWebServices() []*restful.WebService
   130  
   131  	// RegisteredHandlePaths returns the paths of handlers registered directly with the container (non-web-services)
   132  	// Used to test filters are being applied on non-web-service handlers
   133  	RegisteredHandlePaths() []string
   134  }
   135  
   136  // filteringContainer delegates all Handle(...) calls to Container.HandleWithFilter(...),
   137  // so we can ensure restful.FilterFunctions are used for all handlers
   138  type filteringContainer struct {
   139  	*restful.Container
   140  
   141  	registeredHandlePaths []string
   142  }
   143  
   144  func (a *filteringContainer) Handle(path string, handler http.Handler) {
   145  	a.HandleWithFilter(path, handler)
   146  	a.registeredHandlePaths = append(a.registeredHandlePaths, path)
   147  }
   148  func (a *filteringContainer) RegisteredHandlePaths() []string {
   149  	return a.registeredHandlePaths
   150  }
   151  
   152  // ListenAndServeKubeletServer initializes a server to respond to HTTP network requests on the Kubelet.
   153  func ListenAndServeKubeletServer(
   154  	host HostInterface,
   155  	resourceAnalyzer stats.ResourceAnalyzer,
   156  	kubeCfg *kubeletconfiginternal.KubeletConfiguration,
   157  	tlsOptions *TLSOptions,
   158  	auth AuthInterface,
   159  	tp oteltrace.TracerProvider) {
   160  
   161  	address := netutils.ParseIPSloppy(kubeCfg.Address)
   162  	port := uint(kubeCfg.Port)
   163  	klog.InfoS("Starting to listen", "address", address, "port", port)
   164  	handler := NewServer(host, resourceAnalyzer, auth, kubeCfg)
   165  
   166  	if utilfeature.DefaultFeatureGate.Enabled(features.KubeletTracing) {
   167  		handler.InstallTracingFilter(tp)
   168  	}
   169  
   170  	s := &http.Server{
   171  		Addr:           net.JoinHostPort(address.String(), strconv.FormatUint(uint64(port), 10)),
   172  		Handler:        &handler,
   173  		IdleTimeout:    90 * time.Second, // matches http.DefaultTransport keep-alive timeout
   174  		ReadTimeout:    4 * 60 * time.Minute,
   175  		WriteTimeout:   4 * 60 * time.Minute,
   176  		MaxHeaderBytes: 1 << 20,
   177  	}
   178  
   179  	if tlsOptions != nil {
   180  		s.TLSConfig = tlsOptions.Config
   181  		// Passing empty strings as the cert and key files means no
   182  		// cert/keys are specified and GetCertificate in the TLSConfig
   183  		// should be called instead.
   184  		if err := s.ListenAndServeTLS(tlsOptions.CertFile, tlsOptions.KeyFile); err != nil {
   185  			klog.ErrorS(err, "Failed to listen and serve")
   186  			os.Exit(1)
   187  		}
   188  	} else if err := s.ListenAndServe(); err != nil {
   189  		klog.ErrorS(err, "Failed to listen and serve")
   190  		os.Exit(1)
   191  	}
   192  }
   193  
   194  // ListenAndServeKubeletReadOnlyServer initializes a server to respond to HTTP network requests on the Kubelet.
   195  func ListenAndServeKubeletReadOnlyServer(
   196  	host HostInterface,
   197  	resourceAnalyzer stats.ResourceAnalyzer,
   198  	address net.IP,
   199  	port uint,
   200  	tp oteltrace.TracerProvider) {
   201  	klog.InfoS("Starting to listen read-only", "address", address, "port", port)
   202  	s := NewServer(host, resourceAnalyzer, nil, nil)
   203  
   204  	if utilfeature.DefaultFeatureGate.Enabled(features.KubeletTracing) {
   205  		s.InstallTracingFilter(tp, otelrestful.WithPublicEndpoint())
   206  	}
   207  
   208  	server := &http.Server{
   209  		Addr:           net.JoinHostPort(address.String(), strconv.FormatUint(uint64(port), 10)),
   210  		Handler:        &s,
   211  		IdleTimeout:    90 * time.Second, // matches http.DefaultTransport keep-alive timeout
   212  		ReadTimeout:    4 * 60 * time.Minute,
   213  		WriteTimeout:   4 * 60 * time.Minute,
   214  		MaxHeaderBytes: 1 << 20,
   215  	}
   216  
   217  	if err := server.ListenAndServe(); err != nil {
   218  		klog.ErrorS(err, "Failed to listen and serve")
   219  		os.Exit(1)
   220  	}
   221  }
   222  
   223  // ListenAndServePodResources initializes a gRPC server to serve the PodResources service
   224  func ListenAndServePodResources(endpoint string, providers podresources.PodResourcesProviders) {
   225  	server := grpc.NewServer(apisgrpc.WithRateLimiter("podresources", podresources.DefaultQPS, podresources.DefaultBurstTokens))
   226  
   227  	podresourcesapiv1alpha1.RegisterPodResourcesListerServer(server, podresources.NewV1alpha1PodResourcesServer(providers))
   228  	podresourcesapi.RegisterPodResourcesListerServer(server, podresources.NewV1PodResourcesServer(providers))
   229  
   230  	l, err := util.CreateListener(endpoint)
   231  	if err != nil {
   232  		klog.ErrorS(err, "Failed to create listener for podResources endpoint")
   233  		os.Exit(1)
   234  	}
   235  
   236  	klog.InfoS("Starting to serve the podresources API", "endpoint", endpoint)
   237  	if err := server.Serve(l); err != nil {
   238  		klog.ErrorS(err, "Failed to serve")
   239  		os.Exit(1)
   240  	}
   241  }
   242  
   243  // AuthInterface contains all methods required by the auth filters
   244  type AuthInterface interface {
   245  	authenticator.Request
   246  	authorizer.RequestAttributesGetter
   247  	authorizer.Authorizer
   248  }
   249  
   250  // HostInterface contains all the kubelet methods required by the server.
   251  // For testability.
   252  type HostInterface interface {
   253  	stats.Provider
   254  	GetVersionInfo() (*cadvisorapi.VersionInfo, error)
   255  	GetCachedMachineInfo() (*cadvisorapi.MachineInfo, error)
   256  	GetRunningPods(ctx context.Context) ([]*v1.Pod, error)
   257  	RunInContainer(ctx context.Context, name string, uid types.UID, container string, cmd []string) ([]byte, error)
   258  	CheckpointContainer(ctx context.Context, podUID types.UID, podFullName, containerName string, options *runtimeapi.CheckpointContainerRequest) error
   259  	GetKubeletContainerLogs(ctx context.Context, podFullName, containerName string, logOptions *v1.PodLogOptions, stdout, stderr io.Writer) error
   260  	ServeLogs(w http.ResponseWriter, req *http.Request)
   261  	ResyncInterval() time.Duration
   262  	GetHostname() string
   263  	LatestLoopEntryTime() time.Time
   264  	GetExec(ctx context.Context, podFullName string, podUID types.UID, containerName string, cmd []string, streamOpts remotecommandserver.Options) (*url.URL, error)
   265  	GetAttach(ctx context.Context, podFullName string, podUID types.UID, containerName string, streamOpts remotecommandserver.Options) (*url.URL, error)
   266  	GetPortForward(ctx context.Context, podName, podNamespace string, podUID types.UID, portForwardOpts portforward.V4Options) (*url.URL, error)
   267  	ListMetricDescriptors(ctx context.Context) ([]*runtimeapi.MetricDescriptor, error)
   268  	ListPodSandboxMetrics(ctx context.Context) ([]*runtimeapi.PodSandboxMetrics, error)
   269  }
   270  
   271  // NewServer initializes and configures a kubelet.Server object to handle HTTP requests.
   272  func NewServer(
   273  	host HostInterface,
   274  	resourceAnalyzer stats.ResourceAnalyzer,
   275  	auth AuthInterface,
   276  	kubeCfg *kubeletconfiginternal.KubeletConfiguration) Server {
   277  
   278  	server := Server{
   279  		host:                 host,
   280  		resourceAnalyzer:     resourceAnalyzer,
   281  		auth:                 auth,
   282  		restfulCont:          &filteringContainer{Container: restful.NewContainer()},
   283  		metricsBuckets:       sets.NewString(),
   284  		metricsMethodBuckets: sets.NewString("OPTIONS", "GET", "HEAD", "POST", "PUT", "DELETE", "TRACE", "CONNECT"),
   285  	}
   286  	if auth != nil {
   287  		server.InstallAuthFilter()
   288  	}
   289  	server.InstallDefaultHandlers()
   290  	if kubeCfg != nil && kubeCfg.EnableDebuggingHandlers {
   291  		server.InstallDebuggingHandlers()
   292  		// To maintain backward compatibility serve logs and pprof only when enableDebuggingHandlers is also enabled
   293  		// see https://github.com/kubernetes/kubernetes/pull/87273
   294  		server.InstallSystemLogHandler(kubeCfg.EnableSystemLogHandler, kubeCfg.EnableSystemLogQuery)
   295  		server.InstallProfilingHandler(kubeCfg.EnableProfilingHandler, kubeCfg.EnableContentionProfiling)
   296  		server.InstallDebugFlagsHandler(kubeCfg.EnableDebugFlagsHandler)
   297  	} else {
   298  		server.InstallDebuggingDisabledHandlers()
   299  	}
   300  	return server
   301  }
   302  
   303  // InstallAuthFilter installs authentication filters with the restful Container.
   304  func (s *Server) InstallAuthFilter() {
   305  	s.restfulCont.Filter(func(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) {
   306  		// Authenticate
   307  		info, ok, err := s.auth.AuthenticateRequest(req.Request)
   308  		if err != nil {
   309  			klog.ErrorS(err, "Unable to authenticate the request due to an error")
   310  			resp.WriteErrorString(http.StatusUnauthorized, "Unauthorized")
   311  			return
   312  		}
   313  		if !ok {
   314  			resp.WriteErrorString(http.StatusUnauthorized, "Unauthorized")
   315  			return
   316  		}
   317  
   318  		// Get authorization attributes
   319  		attrs := s.auth.GetRequestAttributes(info.User, req.Request)
   320  
   321  		// Authorize
   322  		decision, _, err := s.auth.Authorize(req.Request.Context(), attrs)
   323  		if err != nil {
   324  			klog.ErrorS(err, "Authorization error", "user", attrs.GetUser().GetName(), "verb", attrs.GetVerb(), "resource", attrs.GetResource(), "subresource", attrs.GetSubresource())
   325  			msg := fmt.Sprintf("Authorization error (user=%s, verb=%s, resource=%s, subresource=%s)", attrs.GetUser().GetName(), attrs.GetVerb(), attrs.GetResource(), attrs.GetSubresource())
   326  			resp.WriteErrorString(http.StatusInternalServerError, msg)
   327  			return
   328  		}
   329  		if decision != authorizer.DecisionAllow {
   330  			klog.V(2).InfoS("Forbidden", "user", attrs.GetUser().GetName(), "verb", attrs.GetVerb(), "resource", attrs.GetResource(), "subresource", attrs.GetSubresource())
   331  			msg := fmt.Sprintf("Forbidden (user=%s, verb=%s, resource=%s, subresource=%s)", attrs.GetUser().GetName(), attrs.GetVerb(), attrs.GetResource(), attrs.GetSubresource())
   332  			resp.WriteErrorString(http.StatusForbidden, msg)
   333  			return
   334  		}
   335  
   336  		// Continue
   337  		chain.ProcessFilter(req, resp)
   338  	})
   339  }
   340  
   341  // InstallTracingFilter installs OpenTelemetry tracing filter with the restful Container.
   342  func (s *Server) InstallTracingFilter(tp oteltrace.TracerProvider, opts ...otelrestful.Option) {
   343  	s.restfulCont.Filter(otelrestful.OTelFilter("kubelet", append(opts, otelrestful.WithTracerProvider(tp))...))
   344  }
   345  
   346  // addMetricsBucketMatcher adds a regexp matcher and the relevant bucket to use when
   347  // it matches. Please be aware this is not thread safe and should not be used dynamically
   348  func (s *Server) addMetricsBucketMatcher(bucket string) {
   349  	s.metricsBuckets.Insert(bucket)
   350  }
   351  
   352  // getMetricBucket find the appropriate metrics reporting bucket for the given path
   353  func (s *Server) getMetricBucket(path string) string {
   354  	root := getURLRootPath(path)
   355  	if s.metricsBuckets.Has(root) {
   356  		return root
   357  	}
   358  	return "other"
   359  }
   360  
   361  // getMetricMethodBucket checks for unknown or invalid HTTP verbs
   362  func (s *Server) getMetricMethodBucket(method string) string {
   363  	if s.metricsMethodBuckets.Has(method) {
   364  		return method
   365  	}
   366  	return "other"
   367  }
   368  
   369  // InstallDefaultHandlers registers the default set of supported HTTP request
   370  // patterns with the restful Container.
   371  func (s *Server) InstallDefaultHandlers() {
   372  	s.addMetricsBucketMatcher("healthz")
   373  	healthz.InstallHandler(s.restfulCont,
   374  		healthz.PingHealthz,
   375  		healthz.LogHealthz,
   376  		healthz.NamedCheck("syncloop", s.syncLoopHealthCheck),
   377  	)
   378  
   379  	slis.SLIMetricsWithReset{}.Install(s.restfulCont)
   380  
   381  	s.addMetricsBucketMatcher("pods")
   382  	ws := new(restful.WebService)
   383  	ws.
   384  		Path("/pods").
   385  		Produces(restful.MIME_JSON)
   386  	ws.Route(ws.GET("").
   387  		To(s.getPods).
   388  		Operation("getPods"))
   389  	s.restfulCont.Add(ws)
   390  
   391  	s.addMetricsBucketMatcher("stats")
   392  	s.restfulCont.Add(stats.CreateHandlers(statsPath, s.host, s.resourceAnalyzer))
   393  
   394  	s.addMetricsBucketMatcher("metrics")
   395  	s.addMetricsBucketMatcher("metrics/cadvisor")
   396  	s.addMetricsBucketMatcher("metrics/probes")
   397  	s.addMetricsBucketMatcher("metrics/resource")
   398  	s.restfulCont.Handle(metricsPath, legacyregistry.Handler())
   399  
   400  	includedMetrics := cadvisormetrics.MetricSet{
   401  		cadvisormetrics.CpuUsageMetrics:     struct{}{},
   402  		cadvisormetrics.MemoryUsageMetrics:  struct{}{},
   403  		cadvisormetrics.CpuLoadMetrics:      struct{}{},
   404  		cadvisormetrics.DiskIOMetrics:       struct{}{},
   405  		cadvisormetrics.DiskUsageMetrics:    struct{}{},
   406  		cadvisormetrics.NetworkUsageMetrics: struct{}{},
   407  		cadvisormetrics.AppMetrics:          struct{}{},
   408  		cadvisormetrics.ProcessMetrics:      struct{}{},
   409  		cadvisormetrics.OOMMetrics:          struct{}{},
   410  	}
   411  	// cAdvisor metrics are exposed under the secured handler as well
   412  	r := compbasemetrics.NewKubeRegistry()
   413  	r.RawMustRegister(metrics.NewPrometheusMachineCollector(prometheusHostAdapter{s.host}, includedMetrics))
   414  	if utilfeature.DefaultFeatureGate.Enabled(features.PodAndContainerStatsFromCRI) {
   415  		r.CustomRegister(collectors.NewCRIMetricsCollector(context.TODO(), s.host.ListPodSandboxMetrics, s.host.ListMetricDescriptors))
   416  	} else {
   417  		cadvisorOpts := cadvisorv2.RequestOptions{
   418  			IdType:    cadvisorv2.TypeName,
   419  			Count:     1,
   420  			Recursive: true,
   421  		}
   422  		r.RawMustRegister(metrics.NewPrometheusCollector(prometheusHostAdapter{s.host}, containerPrometheusLabelsFunc(s.host), includedMetrics, clock.RealClock{}, cadvisorOpts))
   423  	}
   424  	s.restfulCont.Handle(cadvisorMetricsPath,
   425  		compbasemetrics.HandlerFor(r, compbasemetrics.HandlerOpts{ErrorHandling: compbasemetrics.ContinueOnError}),
   426  	)
   427  
   428  	s.addMetricsBucketMatcher("metrics/resource")
   429  	resourceRegistry := compbasemetrics.NewKubeRegistry()
   430  	resourceRegistry.CustomMustRegister(collectors.NewResourceMetricsCollector(s.resourceAnalyzer))
   431  	s.restfulCont.Handle(resourceMetricsPath,
   432  		compbasemetrics.HandlerFor(resourceRegistry, compbasemetrics.HandlerOpts{ErrorHandling: compbasemetrics.ContinueOnError}),
   433  	)
   434  
   435  	// prober metrics are exposed under a different endpoint
   436  
   437  	s.addMetricsBucketMatcher("metrics/probes")
   438  	p := compbasemetrics.NewKubeRegistry()
   439  	_ = compbasemetrics.RegisterProcessStartTime(p.Register)
   440  	p.MustRegister(prober.ProberResults)
   441  	p.MustRegister(prober.ProberDuration)
   442  	s.restfulCont.Handle(proberMetricsPath,
   443  		compbasemetrics.HandlerFor(p, compbasemetrics.HandlerOpts{ErrorHandling: compbasemetrics.ContinueOnError}),
   444  	)
   445  
   446  	// Only enable checkpoint API if the feature is enabled
   447  	if utilfeature.DefaultFeatureGate.Enabled(features.ContainerCheckpoint) {
   448  		s.addMetricsBucketMatcher("checkpoint")
   449  		ws = &restful.WebService{}
   450  		ws.Path(checkpointPath).Produces(restful.MIME_JSON)
   451  		ws.Route(ws.POST("/{podNamespace}/{podID}/{containerName}").
   452  			To(s.checkpoint).
   453  			Operation("checkpoint"))
   454  		s.restfulCont.Add(ws)
   455  	}
   456  }
   457  
   458  // InstallDebuggingHandlers registers the HTTP request patterns that serve logs or run commands/containers
   459  func (s *Server) InstallDebuggingHandlers() {
   460  	klog.InfoS("Adding debug handlers to kubelet server")
   461  
   462  	s.addMetricsBucketMatcher("run")
   463  	ws := new(restful.WebService)
   464  	ws.
   465  		Path("/run")
   466  	ws.Route(ws.POST("/{podNamespace}/{podID}/{containerName}").
   467  		To(s.getRun).
   468  		Operation("getRun"))
   469  	ws.Route(ws.POST("/{podNamespace}/{podID}/{uid}/{containerName}").
   470  		To(s.getRun).
   471  		Operation("getRun"))
   472  	s.restfulCont.Add(ws)
   473  
   474  	s.addMetricsBucketMatcher("exec")
   475  	ws = new(restful.WebService)
   476  	ws.
   477  		Path("/exec")
   478  	ws.Route(ws.GET("/{podNamespace}/{podID}/{containerName}").
   479  		To(s.getExec).
   480  		Operation("getExec"))
   481  	ws.Route(ws.POST("/{podNamespace}/{podID}/{containerName}").
   482  		To(s.getExec).
   483  		Operation("getExec"))
   484  	ws.Route(ws.GET("/{podNamespace}/{podID}/{uid}/{containerName}").
   485  		To(s.getExec).
   486  		Operation("getExec"))
   487  	ws.Route(ws.POST("/{podNamespace}/{podID}/{uid}/{containerName}").
   488  		To(s.getExec).
   489  		Operation("getExec"))
   490  	s.restfulCont.Add(ws)
   491  
   492  	s.addMetricsBucketMatcher("attach")
   493  	ws = new(restful.WebService)
   494  	ws.
   495  		Path("/attach")
   496  	ws.Route(ws.GET("/{podNamespace}/{podID}/{containerName}").
   497  		To(s.getAttach).
   498  		Operation("getAttach"))
   499  	ws.Route(ws.POST("/{podNamespace}/{podID}/{containerName}").
   500  		To(s.getAttach).
   501  		Operation("getAttach"))
   502  	ws.Route(ws.GET("/{podNamespace}/{podID}/{uid}/{containerName}").
   503  		To(s.getAttach).
   504  		Operation("getAttach"))
   505  	ws.Route(ws.POST("/{podNamespace}/{podID}/{uid}/{containerName}").
   506  		To(s.getAttach).
   507  		Operation("getAttach"))
   508  	s.restfulCont.Add(ws)
   509  
   510  	s.addMetricsBucketMatcher("portForward")
   511  	ws = new(restful.WebService)
   512  	ws.
   513  		Path("/portForward")
   514  	ws.Route(ws.GET("/{podNamespace}/{podID}").
   515  		To(s.getPortForward).
   516  		Operation("getPortForward"))
   517  	ws.Route(ws.POST("/{podNamespace}/{podID}").
   518  		To(s.getPortForward).
   519  		Operation("getPortForward"))
   520  	ws.Route(ws.GET("/{podNamespace}/{podID}/{uid}").
   521  		To(s.getPortForward).
   522  		Operation("getPortForward"))
   523  	ws.Route(ws.POST("/{podNamespace}/{podID}/{uid}").
   524  		To(s.getPortForward).
   525  		Operation("getPortForward"))
   526  	s.restfulCont.Add(ws)
   527  
   528  	s.addMetricsBucketMatcher("containerLogs")
   529  	ws = new(restful.WebService)
   530  	ws.
   531  		Path("/containerLogs")
   532  	ws.Route(ws.GET("/{podNamespace}/{podID}/{containerName}").
   533  		To(s.getContainerLogs).
   534  		Operation("getContainerLogs"))
   535  	s.restfulCont.Add(ws)
   536  
   537  	s.addMetricsBucketMatcher("configz")
   538  	configz.InstallHandler(s.restfulCont)
   539  
   540  	// The /runningpods endpoint is used for testing only.
   541  	s.addMetricsBucketMatcher("runningpods")
   542  	ws = new(restful.WebService)
   543  	ws.
   544  		Path("/runningpods/").
   545  		Produces(restful.MIME_JSON)
   546  	ws.Route(ws.GET("").
   547  		To(s.getRunningPods).
   548  		Operation("getRunningPods"))
   549  	s.restfulCont.Add(ws)
   550  }
   551  
   552  // InstallDebuggingDisabledHandlers registers the HTTP request patterns that provide better error message
   553  func (s *Server) InstallDebuggingDisabledHandlers() {
   554  	h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   555  		http.Error(w, "Debug endpoints are disabled.", http.StatusMethodNotAllowed)
   556  	})
   557  
   558  	s.addMetricsBucketMatcher("run")
   559  	s.addMetricsBucketMatcher("exec")
   560  	s.addMetricsBucketMatcher("attach")
   561  	s.addMetricsBucketMatcher("portForward")
   562  	s.addMetricsBucketMatcher("containerLogs")
   563  	s.addMetricsBucketMatcher("runningpods")
   564  	s.addMetricsBucketMatcher("pprof")
   565  	s.addMetricsBucketMatcher("logs")
   566  	paths := []string{
   567  		"/run/", "/exec/", "/attach/", "/portForward/", "/containerLogs/",
   568  		"/runningpods/", pprofBasePath, logsPath}
   569  	for _, p := range paths {
   570  		s.restfulCont.Handle(p, h)
   571  	}
   572  }
   573  
   574  // InstallSystemLogHandler registers the HTTP request patterns for logs endpoint.
   575  func (s *Server) InstallSystemLogHandler(enableSystemLogHandler bool, enableSystemLogQuery bool) {
   576  	s.addMetricsBucketMatcher("logs")
   577  	if enableSystemLogHandler {
   578  		ws := new(restful.WebService)
   579  		ws.Path(logsPath)
   580  		ws.Route(ws.GET("").
   581  			To(s.getLogs).
   582  			Operation("getLogs"))
   583  		if !enableSystemLogQuery {
   584  			ws.Route(ws.GET("/{logpath:*}").
   585  				To(s.getLogs).
   586  				Operation("getLogs").
   587  				Param(ws.PathParameter("logpath", "path to the log").DataType("string")))
   588  		} else {
   589  			ws.Route(ws.GET("/{logpath:*}").
   590  				To(s.getLogs).
   591  				Operation("getLogs").
   592  				Param(ws.PathParameter("logpath", "path to the log").DataType("string")).
   593  				Param(ws.QueryParameter("query", "query specifies services(s) or files from which to return logs").DataType("string")).
   594  				Param(ws.QueryParameter("sinceTime", "sinceTime is an RFC3339 timestamp from which to show logs").DataType("string")).
   595  				Param(ws.QueryParameter("untilTime", "untilTime is an RFC3339 timestamp until which to show logs").DataType("string")).
   596  				Param(ws.QueryParameter("tailLines", "tailLines is used to retrieve the specified number of lines from the end of the log").DataType("string")).
   597  				Param(ws.QueryParameter("pattern", "pattern filters log entries by the provided regex pattern").DataType("string")).
   598  				Param(ws.QueryParameter("boot", "boot show messages from a specific system boot").DataType("string")))
   599  		}
   600  		s.restfulCont.Add(ws)
   601  	} else {
   602  		s.restfulCont.Handle(logsPath, getHandlerForDisabledEndpoint("logs endpoint is disabled."))
   603  	}
   604  }
   605  
   606  func getHandlerForDisabledEndpoint(errorMessage string) http.HandlerFunc {
   607  	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   608  		http.Error(w, errorMessage, http.StatusMethodNotAllowed)
   609  	})
   610  }
   611  
   612  // InstallDebugFlagsHandler registers the HTTP request patterns for /debug/flags/v endpoint.
   613  func (s *Server) InstallDebugFlagsHandler(enableDebugFlagsHandler bool) {
   614  	if enableDebugFlagsHandler {
   615  		// Setup flags handlers.
   616  		// so far, only logging related endpoints are considered valid to add for these debug flags.
   617  		s.restfulCont.Handle(debugFlagPath, routes.StringFlagPutHandler(logs.GlogSetter))
   618  	} else {
   619  		s.restfulCont.Handle(debugFlagPath, getHandlerForDisabledEndpoint("flags endpoint is disabled."))
   620  		return
   621  	}
   622  }
   623  
   624  // InstallProfilingHandler registers the HTTP request patterns for /debug/pprof endpoint.
   625  func (s *Server) InstallProfilingHandler(enableProfilingLogHandler bool, enableContentionProfiling bool) {
   626  	s.addMetricsBucketMatcher("debug")
   627  	if !enableProfilingLogHandler {
   628  		s.restfulCont.Handle(pprofBasePath, getHandlerForDisabledEndpoint("profiling endpoint is disabled."))
   629  		return
   630  	}
   631  
   632  	handlePprofEndpoint := func(req *restful.Request, resp *restful.Response) {
   633  		name := strings.TrimPrefix(req.Request.URL.Path, pprofBasePath)
   634  		switch name {
   635  		case "profile":
   636  			pprof.Profile(resp, req.Request)
   637  		case "symbol":
   638  			pprof.Symbol(resp, req.Request)
   639  		case "cmdline":
   640  			pprof.Cmdline(resp, req.Request)
   641  		case "trace":
   642  			pprof.Trace(resp, req.Request)
   643  		default:
   644  			pprof.Index(resp, req.Request)
   645  		}
   646  	}
   647  
   648  	// Setup pprof handlers.
   649  	ws := new(restful.WebService).Path(pprofBasePath)
   650  	ws.Route(ws.GET("/{subpath:*}").To(handlePprofEndpoint)).Doc("pprof endpoint")
   651  	s.restfulCont.Add(ws)
   652  
   653  	if enableContentionProfiling {
   654  		goruntime.SetBlockProfileRate(1)
   655  	}
   656  }
   657  
   658  // Checks if kubelet's sync loop  that updates containers is working.
   659  func (s *Server) syncLoopHealthCheck(req *http.Request) error {
   660  	duration := s.host.ResyncInterval() * 2
   661  	minDuration := time.Minute * 5
   662  	if duration < minDuration {
   663  		duration = minDuration
   664  	}
   665  	enterLoopTime := s.host.LatestLoopEntryTime()
   666  	if !enterLoopTime.IsZero() && time.Now().After(enterLoopTime.Add(duration)) {
   667  		return fmt.Errorf("sync Loop took longer than expected")
   668  	}
   669  	return nil
   670  }
   671  
   672  // getContainerLogs handles containerLogs request against the Kubelet
   673  func (s *Server) getContainerLogs(request *restful.Request, response *restful.Response) {
   674  	podNamespace := request.PathParameter("podNamespace")
   675  	podID := request.PathParameter("podID")
   676  	containerName := request.PathParameter("containerName")
   677  	ctx := request.Request.Context()
   678  
   679  	if len(podID) == 0 {
   680  		// TODO: Why return JSON when the rest return plaintext errors?
   681  		// TODO: Why return plaintext errors?
   682  		response.WriteError(http.StatusBadRequest, fmt.Errorf(`{"message": "Missing podID."}`))
   683  		return
   684  	}
   685  	if len(containerName) == 0 {
   686  		// TODO: Why return JSON when the rest return plaintext errors?
   687  		response.WriteError(http.StatusBadRequest, fmt.Errorf(`{"message": "Missing container name."}`))
   688  		return
   689  	}
   690  	if len(podNamespace) == 0 {
   691  		// TODO: Why return JSON when the rest return plaintext errors?
   692  		response.WriteError(http.StatusBadRequest, fmt.Errorf(`{"message": "Missing podNamespace."}`))
   693  		return
   694  	}
   695  
   696  	query := request.Request.URL.Query()
   697  	// backwards compatibility for the "tail" query parameter
   698  	if tail := request.QueryParameter("tail"); len(tail) > 0 {
   699  		query["tailLines"] = []string{tail}
   700  		// "all" is the same as omitting tail
   701  		if tail == "all" {
   702  			delete(query, "tailLines")
   703  		}
   704  	}
   705  	// container logs on the kubelet are locked to the v1 API version of PodLogOptions
   706  	logOptions := &v1.PodLogOptions{}
   707  	if err := legacyscheme.ParameterCodec.DecodeParameters(query, v1.SchemeGroupVersion, logOptions); err != nil {
   708  		response.WriteError(http.StatusBadRequest, fmt.Errorf(`{"message": "Unable to decode query."}`))
   709  		return
   710  	}
   711  	logOptions.TypeMeta = metav1.TypeMeta{}
   712  	if errs := validation.ValidatePodLogOptions(logOptions); len(errs) > 0 {
   713  		response.WriteError(http.StatusUnprocessableEntity, fmt.Errorf(`{"message": "Invalid request."}`))
   714  		return
   715  	}
   716  
   717  	pod, ok := s.host.GetPodByName(podNamespace, podID)
   718  	if !ok {
   719  		response.WriteError(http.StatusNotFound, fmt.Errorf("pod %q does not exist", podID))
   720  		return
   721  	}
   722  	// Check if containerName is valid.
   723  	if kubecontainer.GetContainerSpec(pod, containerName) == nil {
   724  		response.WriteError(http.StatusNotFound, fmt.Errorf("container %q not found in pod %q", containerName, podID))
   725  		return
   726  	}
   727  
   728  	if _, ok := response.ResponseWriter.(http.Flusher); !ok {
   729  		response.WriteError(http.StatusInternalServerError, fmt.Errorf("unable to convert %v into http.Flusher, cannot show logs", reflect.TypeOf(response)))
   730  		return
   731  	}
   732  	fw := flushwriter.Wrap(response.ResponseWriter)
   733  	response.Header().Set("Transfer-Encoding", "chunked")
   734  	if err := s.host.GetKubeletContainerLogs(ctx, kubecontainer.GetPodFullName(pod), containerName, logOptions, fw, fw); err != nil {
   735  		response.WriteError(http.StatusBadRequest, err)
   736  		return
   737  	}
   738  }
   739  
   740  // encodePods creates an v1.PodList object from pods and returns the encoded
   741  // PodList.
   742  func encodePods(pods []*v1.Pod) (data []byte, err error) {
   743  	podList := new(v1.PodList)
   744  	for _, pod := range pods {
   745  		podList.Items = append(podList.Items, *pod)
   746  	}
   747  	// TODO: this needs to be parameterized to the kubelet, not hardcoded. Depends on Kubelet
   748  	//   as API server refactor.
   749  	// TODO: Locked to v1, needs to be made generic
   750  	codec := legacyscheme.Codecs.LegacyCodec(schema.GroupVersion{Group: v1.GroupName, Version: "v1"})
   751  	return runtime.Encode(codec, podList)
   752  }
   753  
   754  // getPods returns a list of pods bound to the Kubelet and their spec.
   755  func (s *Server) getPods(request *restful.Request, response *restful.Response) {
   756  	pods := s.host.GetPods()
   757  	data, err := encodePods(pods)
   758  	if err != nil {
   759  		response.WriteError(http.StatusInternalServerError, err)
   760  		return
   761  	}
   762  	writeJSONResponse(response, data)
   763  }
   764  
   765  // getRunningPods returns a list of pods running on Kubelet. The list is
   766  // provided by the container runtime, and is different from the list returned
   767  // by getPods, which is a set of desired pods to run.
   768  func (s *Server) getRunningPods(request *restful.Request, response *restful.Response) {
   769  	ctx := request.Request.Context()
   770  	pods, err := s.host.GetRunningPods(ctx)
   771  	if err != nil {
   772  		response.WriteError(http.StatusInternalServerError, err)
   773  		return
   774  	}
   775  	data, err := encodePods(pods)
   776  	if err != nil {
   777  		response.WriteError(http.StatusInternalServerError, err)
   778  		return
   779  	}
   780  	writeJSONResponse(response, data)
   781  }
   782  
   783  // getLogs handles logs requests against the Kubelet.
   784  func (s *Server) getLogs(request *restful.Request, response *restful.Response) {
   785  	s.host.ServeLogs(response, request.Request)
   786  }
   787  
   788  type execRequestParams struct {
   789  	podNamespace  string
   790  	podName       string
   791  	podUID        types.UID
   792  	containerName string
   793  	cmd           []string
   794  }
   795  
   796  func getExecRequestParams(req *restful.Request) execRequestParams {
   797  	return execRequestParams{
   798  		podNamespace:  req.PathParameter("podNamespace"),
   799  		podName:       req.PathParameter("podID"),
   800  		podUID:        types.UID(req.PathParameter("uid")),
   801  		containerName: req.PathParameter("containerName"),
   802  		cmd:           req.Request.URL.Query()[api.ExecCommandParam],
   803  	}
   804  }
   805  
   806  type portForwardRequestParams struct {
   807  	podNamespace string
   808  	podName      string
   809  	podUID       types.UID
   810  }
   811  
   812  func getPortForwardRequestParams(req *restful.Request) portForwardRequestParams {
   813  	return portForwardRequestParams{
   814  		podNamespace: req.PathParameter("podNamespace"),
   815  		podName:      req.PathParameter("podID"),
   816  		podUID:       types.UID(req.PathParameter("uid")),
   817  	}
   818  }
   819  
   820  type responder struct{}
   821  
   822  func (r *responder) Error(w http.ResponseWriter, req *http.Request, err error) {
   823  	klog.ErrorS(err, "Error while proxying request")
   824  	http.Error(w, err.Error(), http.StatusInternalServerError)
   825  }
   826  
   827  // proxyStream proxies stream to url.
   828  func proxyStream(w http.ResponseWriter, r *http.Request, url *url.URL) {
   829  	// TODO(random-liu): Set MaxBytesPerSec to throttle the stream.
   830  	handler := proxy.NewUpgradeAwareHandler(url, nil /*transport*/, false /*wrapTransport*/, true /*upgradeRequired*/, &responder{})
   831  	handler.ServeHTTP(w, r)
   832  }
   833  
   834  // getAttach handles requests to attach to a container.
   835  func (s *Server) getAttach(request *restful.Request, response *restful.Response) {
   836  	params := getExecRequestParams(request)
   837  	streamOpts, err := remotecommandserver.NewOptions(request.Request)
   838  	if err != nil {
   839  		utilruntime.HandleError(err)
   840  		response.WriteError(http.StatusBadRequest, err)
   841  		return
   842  	}
   843  	pod, ok := s.host.GetPodByName(params.podNamespace, params.podName)
   844  	if !ok {
   845  		response.WriteError(http.StatusNotFound, fmt.Errorf("pod does not exist"))
   846  		return
   847  	}
   848  
   849  	podFullName := kubecontainer.GetPodFullName(pod)
   850  	url, err := s.host.GetAttach(request.Request.Context(), podFullName, params.podUID, params.containerName, *streamOpts)
   851  	if err != nil {
   852  		streaming.WriteError(err, response.ResponseWriter)
   853  		return
   854  	}
   855  
   856  	proxyStream(response.ResponseWriter, request.Request, url)
   857  }
   858  
   859  // getExec handles requests to run a command inside a container.
   860  func (s *Server) getExec(request *restful.Request, response *restful.Response) {
   861  	params := getExecRequestParams(request)
   862  	streamOpts, err := remotecommandserver.NewOptions(request.Request)
   863  	if err != nil {
   864  		utilruntime.HandleError(err)
   865  		response.WriteError(http.StatusBadRequest, err)
   866  		return
   867  	}
   868  	pod, ok := s.host.GetPodByName(params.podNamespace, params.podName)
   869  	if !ok {
   870  		response.WriteError(http.StatusNotFound, fmt.Errorf("pod does not exist"))
   871  		return
   872  	}
   873  
   874  	podFullName := kubecontainer.GetPodFullName(pod)
   875  	url, err := s.host.GetExec(request.Request.Context(), podFullName, params.podUID, params.containerName, params.cmd, *streamOpts)
   876  	if err != nil {
   877  		streaming.WriteError(err, response.ResponseWriter)
   878  		return
   879  	}
   880  	proxyStream(response.ResponseWriter, request.Request, url)
   881  }
   882  
   883  // getRun handles requests to run a command inside a container.
   884  func (s *Server) getRun(request *restful.Request, response *restful.Response) {
   885  	params := getExecRequestParams(request)
   886  	pod, ok := s.host.GetPodByName(params.podNamespace, params.podName)
   887  	if !ok {
   888  		response.WriteError(http.StatusNotFound, fmt.Errorf("pod does not exist"))
   889  		return
   890  	}
   891  
   892  	// For legacy reasons, run uses different query param than exec.
   893  	params.cmd = strings.Split(request.QueryParameter("cmd"), " ")
   894  	data, err := s.host.RunInContainer(request.Request.Context(), kubecontainer.GetPodFullName(pod), params.podUID, params.containerName, params.cmd)
   895  	if err != nil {
   896  		response.WriteError(http.StatusInternalServerError, err)
   897  		return
   898  	}
   899  	writeJSONResponse(response, data)
   900  }
   901  
   902  // Derived from go-restful writeJSON.
   903  func writeJSONResponse(response *restful.Response, data []byte) {
   904  	if data == nil {
   905  		response.WriteHeader(http.StatusOK)
   906  		// do not write a nil representation
   907  		return
   908  	}
   909  	response.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON)
   910  	response.WriteHeader(http.StatusOK)
   911  	if _, err := response.Write(data); err != nil {
   912  		klog.ErrorS(err, "Error writing response")
   913  	}
   914  }
   915  
   916  // getPortForward handles a new restful port forward request. It determines the
   917  // pod name and uid and then calls ServePortForward.
   918  func (s *Server) getPortForward(request *restful.Request, response *restful.Response) {
   919  	params := getPortForwardRequestParams(request)
   920  
   921  	portForwardOptions, err := portforward.NewV4Options(request.Request)
   922  	if err != nil {
   923  		utilruntime.HandleError(err)
   924  		response.WriteError(http.StatusBadRequest, err)
   925  		return
   926  	}
   927  	pod, ok := s.host.GetPodByName(params.podNamespace, params.podName)
   928  	if !ok {
   929  		response.WriteError(http.StatusNotFound, fmt.Errorf("pod does not exist"))
   930  		return
   931  	}
   932  	if len(params.podUID) > 0 && pod.UID != params.podUID {
   933  		response.WriteError(http.StatusNotFound, fmt.Errorf("pod not found"))
   934  		return
   935  	}
   936  
   937  	url, err := s.host.GetPortForward(request.Request.Context(), pod.Name, pod.Namespace, pod.UID, *portForwardOptions)
   938  	if err != nil {
   939  		streaming.WriteError(err, response.ResponseWriter)
   940  		return
   941  	}
   942  	proxyStream(response.ResponseWriter, request.Request, url)
   943  }
   944  
   945  // checkpoint handles the checkpoint API request. It checks if the requested
   946  // podNamespace, pod and container actually exist and only then calls out
   947  // to the runtime to actually checkpoint the container.
   948  func (s *Server) checkpoint(request *restful.Request, response *restful.Response) {
   949  	ctx := request.Request.Context()
   950  	pod, ok := s.host.GetPodByName(request.PathParameter("podNamespace"), request.PathParameter("podID"))
   951  	if !ok {
   952  		response.WriteError(http.StatusNotFound, fmt.Errorf("pod does not exist"))
   953  		return
   954  	}
   955  
   956  	containerName := request.PathParameter("containerName")
   957  
   958  	found := false
   959  	for _, container := range pod.Spec.Containers {
   960  		if container.Name == containerName {
   961  			found = true
   962  			break
   963  		}
   964  	}
   965  	if !found {
   966  		for _, container := range pod.Spec.InitContainers {
   967  			if container.Name == containerName {
   968  				found = true
   969  				break
   970  			}
   971  		}
   972  	}
   973  	if !found {
   974  		for _, container := range pod.Spec.EphemeralContainers {
   975  			if container.Name == containerName {
   976  				found = true
   977  				break
   978  			}
   979  		}
   980  	}
   981  	if !found {
   982  		response.WriteError(
   983  			http.StatusNotFound,
   984  			fmt.Errorf("container %v does not exist", containerName),
   985  		)
   986  		return
   987  	}
   988  
   989  	options := &runtimeapi.CheckpointContainerRequest{}
   990  	// Query parameter to select an optional timeout. Without the timeout parameter
   991  	// the checkpoint command will use the default CRI timeout.
   992  	timeouts := request.Request.URL.Query()["timeout"]
   993  	if len(timeouts) > 0 {
   994  		// If the user specified one or multiple values for timeouts we
   995  		// are using the last available value.
   996  		timeout, err := strconv.ParseInt(timeouts[len(timeouts)-1], 10, 64)
   997  		if err != nil {
   998  			response.WriteError(
   999  				http.StatusNotFound,
  1000  				fmt.Errorf("cannot parse value of timeout parameter"),
  1001  			)
  1002  			return
  1003  		}
  1004  		options.Timeout = timeout
  1005  	}
  1006  
  1007  	if err := s.host.CheckpointContainer(ctx, pod.UID, kubecontainer.GetPodFullName(pod), containerName, options); err != nil {
  1008  		response.WriteError(
  1009  			http.StatusInternalServerError,
  1010  			fmt.Errorf(
  1011  				"checkpointing of %v/%v/%v failed (%v)",
  1012  				request.PathParameter("podNamespace"),
  1013  				request.PathParameter("podID"),
  1014  				containerName,
  1015  				err,
  1016  			),
  1017  		)
  1018  		return
  1019  	}
  1020  	writeJSONResponse(
  1021  		response,
  1022  		[]byte(fmt.Sprintf("{\"items\":[\"%s\"]}", options.Location)),
  1023  	)
  1024  }
  1025  
  1026  // getURLRootPath trims a URL path.
  1027  // For paths in the format of "/metrics/xxx", "metrics/xxx" is returned;
  1028  // For all other paths, the first part of the path is returned.
  1029  func getURLRootPath(path string) string {
  1030  	parts := strings.SplitN(strings.TrimPrefix(path, "/"), "/", 3)
  1031  	if len(parts) == 0 {
  1032  		return path
  1033  	}
  1034  
  1035  	if parts[0] == "metrics" && len(parts) > 1 {
  1036  		return fmt.Sprintf("%s/%s", parts[0], parts[1])
  1037  
  1038  	}
  1039  	return parts[0]
  1040  }
  1041  
  1042  var longRunningRequestPathMap = map[string]bool{
  1043  	"exec":        true,
  1044  	"attach":      true,
  1045  	"portforward": true,
  1046  	"debug":       true,
  1047  }
  1048  
  1049  // isLongRunningRequest determines whether the request is long-running or not.
  1050  func isLongRunningRequest(path string) bool {
  1051  	_, ok := longRunningRequestPathMap[path]
  1052  	return ok
  1053  }
  1054  
  1055  var statusesNoTracePred = httplog.StatusIsNot(
  1056  	http.StatusOK,
  1057  	http.StatusFound,
  1058  	http.StatusMovedPermanently,
  1059  	http.StatusTemporaryRedirect,
  1060  	http.StatusBadRequest,
  1061  	http.StatusNotFound,
  1062  	http.StatusSwitchingProtocols,
  1063  )
  1064  
  1065  // ServeHTTP responds to HTTP requests on the Kubelet.
  1066  func (s *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
  1067  	handler := httplog.WithLogging(s.restfulCont, statusesNoTracePred)
  1068  
  1069  	// monitor http requests
  1070  	var serverType string
  1071  	if s.auth == nil {
  1072  		serverType = "readonly"
  1073  	} else {
  1074  		serverType = "readwrite"
  1075  	}
  1076  
  1077  	method, path := s.getMetricMethodBucket(req.Method), s.getMetricBucket(req.URL.Path)
  1078  
  1079  	longRunning := strconv.FormatBool(isLongRunningRequest(path))
  1080  
  1081  	servermetrics.HTTPRequests.WithLabelValues(method, path, serverType, longRunning).Inc()
  1082  
  1083  	servermetrics.HTTPInflightRequests.WithLabelValues(method, path, serverType, longRunning).Inc()
  1084  	defer servermetrics.HTTPInflightRequests.WithLabelValues(method, path, serverType, longRunning).Dec()
  1085  
  1086  	startTime := time.Now()
  1087  	defer servermetrics.HTTPRequestsDuration.WithLabelValues(method, path, serverType, longRunning).Observe(servermetrics.SinceInSeconds(startTime))
  1088  
  1089  	handler.ServeHTTP(w, req)
  1090  }
  1091  
  1092  // prometheusHostAdapter adapts the HostInterface to the interface expected by the
  1093  // cAdvisor prometheus collector.
  1094  type prometheusHostAdapter struct {
  1095  	host HostInterface
  1096  }
  1097  
  1098  func (a prometheusHostAdapter) GetRequestedContainersInfo(containerName string, options cadvisorv2.RequestOptions) (map[string]*cadvisorapi.ContainerInfo, error) {
  1099  	return a.host.GetRequestedContainersInfo(containerName, options)
  1100  }
  1101  func (a prometheusHostAdapter) GetVersionInfo() (*cadvisorapi.VersionInfo, error) {
  1102  	return a.host.GetVersionInfo()
  1103  }
  1104  func (a prometheusHostAdapter) GetMachineInfo() (*cadvisorapi.MachineInfo, error) {
  1105  	return a.host.GetCachedMachineInfo()
  1106  }
  1107  
  1108  func containerPrometheusLabelsFunc(s stats.Provider) metrics.ContainerLabelsFunc {
  1109  	// containerPrometheusLabels maps cAdvisor labels to prometheus labels.
  1110  	return func(c *cadvisorapi.ContainerInfo) map[string]string {
  1111  		// Prometheus requires that all metrics in the same family have the same labels,
  1112  		// so we arrange to supply blank strings for missing labels
  1113  		var name, image, podName, namespace, containerName string
  1114  		if len(c.Aliases) > 0 {
  1115  			name = c.Aliases[0]
  1116  		}
  1117  		image = c.Spec.Image
  1118  		if v, ok := c.Spec.Labels[kubelettypes.KubernetesPodNameLabel]; ok {
  1119  			podName = v
  1120  		}
  1121  		if v, ok := c.Spec.Labels[kubelettypes.KubernetesPodNamespaceLabel]; ok {
  1122  			namespace = v
  1123  		}
  1124  		if v, ok := c.Spec.Labels[kubelettypes.KubernetesContainerNameLabel]; ok {
  1125  			containerName = v
  1126  		}
  1127  		// Associate pod cgroup with pod so we have an accurate accounting of sandbox
  1128  		if podName == "" && namespace == "" {
  1129  			if pod, found := s.GetPodByCgroupfs(c.Name); found {
  1130  				podName = pod.Name
  1131  				namespace = pod.Namespace
  1132  			}
  1133  		}
  1134  		set := map[string]string{
  1135  			metrics.LabelID:    c.Name,
  1136  			metrics.LabelName:  name,
  1137  			metrics.LabelImage: image,
  1138  			"pod":              podName,
  1139  			"namespace":        namespace,
  1140  			"container":        containerName,
  1141  		}
  1142  		return set
  1143  	}
  1144  }