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