deprecate cAdvisor json metrics collected by Kubelet

- remove unused code for cadvisor json metrics collected

Signed-off-by: pacoxu <paco.xu@daocloud.io>
This commit is contained in:
pacoxu 2021-02-19 19:42:50 +08:00
parent 3727aa47d9
commit cd54bd94e9
8 changed files with 36 additions and 441 deletions

View File

@ -167,8 +167,6 @@ type KubeletFlags struct {
// This flag, if set, instructs the kubelet to keep volumes from terminated pods mounted to the node. // This flag, if set, instructs the kubelet to keep volumes from terminated pods mounted to the node.
// This can be useful for debugging volume related issues. // This can be useful for debugging volume related issues.
KeepTerminatedPodVolumes bool KeepTerminatedPodVolumes bool
// EnableCAdvisorJSONEndpoints enables some cAdvisor endpoints that will be removed in future versions
EnableCAdvisorJSONEndpoints bool
} }
// NewKubeletFlags will create a new KubeletFlags with default values // NewKubeletFlags will create a new KubeletFlags with default values
@ -181,20 +179,19 @@ func NewKubeletFlags() *KubeletFlags {
} }
return &KubeletFlags{ return &KubeletFlags{
ContainerRuntimeOptions: *NewContainerRuntimeOptions(), ContainerRuntimeOptions: *NewContainerRuntimeOptions(),
CertDirectory: "/var/lib/kubelet/pki", CertDirectory: "/var/lib/kubelet/pki",
RootDirectory: defaultRootDir, RootDirectory: defaultRootDir,
MasterServiceNamespace: metav1.NamespaceDefault, MasterServiceNamespace: metav1.NamespaceDefault,
MaxContainerCount: -1, MaxContainerCount: -1,
MaxPerPodContainerCount: 1, MaxPerPodContainerCount: 1,
MinimumGCAge: metav1.Duration{Duration: 0}, MinimumGCAge: metav1.Duration{Duration: 0},
NonMasqueradeCIDR: "10.0.0.0/8", NonMasqueradeCIDR: "10.0.0.0/8",
RegisterSchedulable: true, RegisterSchedulable: true,
RemoteRuntimeEndpoint: remoteRuntimeEndpoint, RemoteRuntimeEndpoint: remoteRuntimeEndpoint,
NodeLabels: make(map[string]string), NodeLabels: make(map[string]string),
RegisterNode: true, RegisterNode: true,
SeccompProfileRoot: filepath.Join(defaultRootDir, "seccomp"), SeccompProfileRoot: filepath.Join(defaultRootDir, "seccomp"),
EnableCAdvisorJSONEndpoints: false,
} }
} }
@ -368,9 +365,6 @@ func (f *KubeletFlags) AddFlags(mainfs *pflag.FlagSet) {
fs.MarkDeprecated("non-masquerade-cidr", "will be removed in a future version") fs.MarkDeprecated("non-masquerade-cidr", "will be removed in a future version")
fs.BoolVar(&f.KeepTerminatedPodVolumes, "keep-terminated-pod-volumes", f.KeepTerminatedPodVolumes, "Keep terminated pod volumes mounted to the node after the pod terminates. Can be useful for debugging volume related issues.") fs.BoolVar(&f.KeepTerminatedPodVolumes, "keep-terminated-pod-volumes", f.KeepTerminatedPodVolumes, "Keep terminated pod volumes mounted to the node after the pod terminates. Can be useful for debugging volume related issues.")
fs.MarkDeprecated("keep-terminated-pod-volumes", "will be removed in a future version") fs.MarkDeprecated("keep-terminated-pod-volumes", "will be removed in a future version")
fs.BoolVar(&f.EnableCAdvisorJSONEndpoints, "enable-cadvisor-json-endpoints", f.EnableCAdvisorJSONEndpoints, "Enable cAdvisor json /spec and /stats/* endpoints. This flag has no effect on the /stats/summary endpoint. [default=false]")
// TODO: Remove this flag in 1.20+. https://github.com/kubernetes/kubernetes/issues/68522
fs.MarkDeprecated("enable-cadvisor-json-endpoints", "will be removed in a future version")
fs.BoolVar(&f.ReallyCrashForTesting, "really-crash-for-testing", f.ReallyCrashForTesting, "If true, when panics occur crash. Intended for testing.") fs.BoolVar(&f.ReallyCrashForTesting, "really-crash-for-testing", f.ReallyCrashForTesting, "If true, when panics occur crash. Intended for testing.")
fs.MarkDeprecated("really-crash-for-testing", "will be removed in a future version.") fs.MarkDeprecated("really-crash-for-testing", "will be removed in a future version.")
fs.Float64Var(&f.ChaosChance, "chaos-chance", f.ChaosChance, "If > 0.0, introduce random client errors and latency. Intended for testing.") fs.Float64Var(&f.ChaosChance, "chaos-chance", f.ChaosChance, "If > 0.0, introduce random client errors and latency. Intended for testing.")

View File

@ -1175,22 +1175,22 @@ func RunKubelet(kubeServer *options.KubeletServer, kubeDeps *kubelet.Dependencie
} }
klog.Info("Started kubelet as runonce") klog.Info("Started kubelet as runonce")
} else { } else {
startKubelet(k, podCfg, &kubeServer.KubeletConfiguration, kubeDeps, kubeServer.EnableCAdvisorJSONEndpoints, kubeServer.EnableServer) startKubelet(k, podCfg, &kubeServer.KubeletConfiguration, kubeDeps, kubeServer.EnableServer)
klog.Info("Started kubelet") klog.Info("Started kubelet")
} }
return nil return nil
} }
func startKubelet(k kubelet.Bootstrap, podCfg *config.PodConfig, kubeCfg *kubeletconfiginternal.KubeletConfiguration, kubeDeps *kubelet.Dependencies, enableCAdvisorJSONEndpoints, enableServer bool) { func startKubelet(k kubelet.Bootstrap, podCfg *config.PodConfig, kubeCfg *kubeletconfiginternal.KubeletConfiguration, kubeDeps *kubelet.Dependencies, enableServer bool) {
// start the kubelet // start the kubelet
go k.Run(podCfg.Updates()) go k.Run(podCfg.Updates())
// start the kubelet server // start the kubelet server
if enableServer { if enableServer {
go k.ListenAndServe(kubeCfg, kubeDeps.TLSOptions, kubeDeps.Auth, enableCAdvisorJSONEndpoints) go k.ListenAndServe(kubeCfg, kubeDeps.TLSOptions, kubeDeps.Auth)
} }
if kubeCfg.ReadOnlyPort > 0 { if kubeCfg.ReadOnlyPort > 0 {
go k.ListenAndServeReadOnly(net.ParseIP(kubeCfg.Address), uint(kubeCfg.ReadOnlyPort), enableCAdvisorJSONEndpoints) go k.ListenAndServeReadOnly(net.ParseIP(kubeCfg.Address), uint(kubeCfg.ReadOnlyPort))
} }
if utilfeature.DefaultFeatureGate.Enabled(features.KubeletPodResources) { if utilfeature.DefaultFeatureGate.Enabled(features.KubeletPodResources) {
go k.ListenAndServePodResources() go k.ListenAndServePodResources()

View File

@ -195,9 +195,8 @@ type Bootstrap interface {
GetConfiguration() kubeletconfiginternal.KubeletConfiguration GetConfiguration() kubeletconfiginternal.KubeletConfiguration
BirthCry() BirthCry()
StartGarbageCollection() StartGarbageCollection()
ListenAndServe(kubeCfg *kubeletconfiginternal.KubeletConfiguration, tlsOptions *server.TLSOptions, auth server.AuthInterface, ListenAndServe(kubeCfg *kubeletconfiginternal.KubeletConfiguration, tlsOptions *server.TLSOptions, auth server.AuthInterface)
enableCAdvisorJSONEndpoints bool) ListenAndServeReadOnly(address net.IP, port uint)
ListenAndServeReadOnly(address net.IP, port uint, enableCAdvisorJSONEndpoints bool)
ListenAndServePodResources() ListenAndServePodResources()
Run(<-chan kubetypes.PodUpdate) Run(<-chan kubetypes.PodUpdate)
RunOnce(<-chan kubetypes.PodUpdate) ([]RunPodResult, error) RunOnce(<-chan kubetypes.PodUpdate) ([]RunPodResult, error)
@ -2226,13 +2225,13 @@ func (kl *Kubelet) ResyncInterval() time.Duration {
// ListenAndServe runs the kubelet HTTP server. // ListenAndServe runs the kubelet HTTP server.
func (kl *Kubelet) ListenAndServe(kubeCfg *kubeletconfiginternal.KubeletConfiguration, tlsOptions *server.TLSOptions, func (kl *Kubelet) ListenAndServe(kubeCfg *kubeletconfiginternal.KubeletConfiguration, tlsOptions *server.TLSOptions,
auth server.AuthInterface, enableCAdvisorJSONEndpoints bool) { auth server.AuthInterface) {
server.ListenAndServeKubeletServer(kl, kl.resourceAnalyzer, kubeCfg, tlsOptions, auth, enableCAdvisorJSONEndpoints) server.ListenAndServeKubeletServer(kl, kl.resourceAnalyzer, kubeCfg, tlsOptions, auth)
} }
// ListenAndServeReadOnly runs the kubelet HTTP server in read-only mode. // ListenAndServeReadOnly runs the kubelet HTTP server in read-only mode.
func (kl *Kubelet) ListenAndServeReadOnly(address net.IP, port uint, enableCAdvisorJSONEndpoints bool) { func (kl *Kubelet) ListenAndServeReadOnly(address net.IP, port uint) {
server.ListenAndServeKubeletReadOnlyServer(kl, kl.resourceAnalyzer, address, port, enableCAdvisorJSONEndpoints) server.ListenAndServeKubeletReadOnlyServer(kl, kl.resourceAnalyzer, address, port)
} }
// ListenAndServePodResources runs the kubelet podresources grpc service // ListenAndServePodResources runs the kubelet podresources grpc service

View File

@ -62,7 +62,6 @@ func isSubpath(subpath, path string) bool {
// /stats/* => verb=<api verb from request>, resource=nodes, name=<node name>, subresource=stats // /stats/* => verb=<api verb from request>, resource=nodes, name=<node name>, subresource=stats
// /metrics/* => verb=<api verb from request>, resource=nodes, name=<node name>, subresource=metrics // /metrics/* => verb=<api verb from request>, resource=nodes, name=<node name>, subresource=metrics
// /logs/* => verb=<api verb from request>, resource=nodes, name=<node name>, subresource=log // /logs/* => verb=<api verb from request>, resource=nodes, name=<node name>, subresource=log
// /spec/* => verb=<api verb from request>, resource=nodes, name=<node name>, subresource=spec
func (n nodeAuthorizerAttributesGetter) GetRequestAttributes(u user.Info, r *http.Request) authorizer.Attributes { func (n nodeAuthorizerAttributesGetter) GetRequestAttributes(u user.Info, r *http.Request) authorizer.Attributes {
apiVerb := "" apiVerb := ""
@ -105,8 +104,6 @@ func (n nodeAuthorizerAttributesGetter) GetRequestAttributes(u user.Info, r *htt
case isSubpath(requestPath, logsPath): case isSubpath(requestPath, logsPath):
// "log" to match other log subresources (pods/log, etc) // "log" to match other log subresources (pods/log, etc)
attrs.Subresource = "log" attrs.Subresource = "log"
case isSubpath(requestPath, specPath):
attrs.Subresource = "spec"
} }
klog.V(5).InfoS("Node request attributes", "user", attrs.GetUser().GetName(), "verb", attrs.GetVerb(), "resource", attrs.GetResource(), "subresource", attrs.GetSubresource()) klog.V(5).InfoS("Node request attributes", "user", attrs.GetUser().GetName(), "verb", attrs.GetVerb(), "resource", attrs.GetResource(), "subresource", attrs.GetSubresource())

View File

@ -131,13 +131,9 @@ func AuthzTestCases() []AuthzTestCase {
"/portForward/{podNamespace}/{podID}/{uid}": "proxy", "/portForward/{podNamespace}/{podID}/{uid}": "proxy",
"/run/{podNamespace}/{podID}/{containerName}": "proxy", "/run/{podNamespace}/{podID}/{containerName}": "proxy",
"/run/{podNamespace}/{podID}/{uid}/{containerName}": "proxy", "/run/{podNamespace}/{podID}/{uid}/{containerName}": "proxy",
"/runningpods/": "proxy", "/runningpods/": "proxy",
"/spec/": "spec", "/stats/": "stats",
"/stats/": "stats", "/stats/summary": "stats",
"/stats/container": "stats",
"/stats/summary": "stats",
"/stats/{namespace}/{podName}/{uid}/{containerName}": "stats",
"/stats/{podName}/{containerName}": "stats",
} }
testCases := []AuthzTestCase{} testCases := []AuthzTestCase{}
for path, subresource := range testPaths { for path, subresource := range testPaths {

View File

@ -84,7 +84,6 @@ const (
cadvisorMetricsPath = "/metrics/cadvisor" cadvisorMetricsPath = "/metrics/cadvisor"
resourceMetricsPath = "/metrics/resource" resourceMetricsPath = "/metrics/resource"
proberMetricsPath = "/metrics/probes" proberMetricsPath = "/metrics/probes"
specPath = "/spec/"
statsPath = "/stats/" statsPath = "/stats/"
logsPath = "/logs/" logsPath = "/logs/"
pprofBasePath = "/debug/pprof/" pprofBasePath = "/debug/pprof/"
@ -142,13 +141,12 @@ func ListenAndServeKubeletServer(
resourceAnalyzer stats.ResourceAnalyzer, resourceAnalyzer stats.ResourceAnalyzer,
kubeCfg *kubeletconfiginternal.KubeletConfiguration, kubeCfg *kubeletconfiginternal.KubeletConfiguration,
tlsOptions *TLSOptions, tlsOptions *TLSOptions,
auth AuthInterface, auth AuthInterface) {
enableCAdvisorJSONEndpoints bool) {
address := net.ParseIP(kubeCfg.Address) address := net.ParseIP(kubeCfg.Address)
port := uint(kubeCfg.Port) port := uint(kubeCfg.Port)
klog.InfoS("Starting to listen", "address", address, "port", port) klog.InfoS("Starting to listen", "address", address, "port", port)
handler := NewServer(host, resourceAnalyzer, auth, enableCAdvisorJSONEndpoints, kubeCfg) handler := NewServer(host, resourceAnalyzer, auth, kubeCfg)
s := &http.Server{ s := &http.Server{
Addr: net.JoinHostPort(address.String(), strconv.FormatUint(uint64(port), 10)), Addr: net.JoinHostPort(address.String(), strconv.FormatUint(uint64(port), 10)),
Handler: &handler, Handler: &handler,
@ -168,9 +166,9 @@ func ListenAndServeKubeletServer(
} }
// ListenAndServeKubeletReadOnlyServer initializes a server to respond to HTTP network requests on the Kubelet. // ListenAndServeKubeletReadOnlyServer initializes a server to respond to HTTP network requests on the Kubelet.
func ListenAndServeKubeletReadOnlyServer(host HostInterface, resourceAnalyzer stats.ResourceAnalyzer, address net.IP, port uint, enableCAdvisorJSONEndpoints bool) { func ListenAndServeKubeletReadOnlyServer(host HostInterface, resourceAnalyzer stats.ResourceAnalyzer, address net.IP, port uint) {
klog.InfoS("Starting to listen read-only", "address", address, "port", port) klog.InfoS("Starting to listen read-only", "address", address, "port", port)
s := NewServer(host, resourceAnalyzer, nil, enableCAdvisorJSONEndpoints, nil) s := NewServer(host, resourceAnalyzer, nil, nil)
server := &http.Server{ server := &http.Server{
Addr: net.JoinHostPort(address.String(), strconv.FormatUint(uint64(port), 10)), Addr: net.JoinHostPort(address.String(), strconv.FormatUint(uint64(port), 10)),
@ -222,7 +220,6 @@ func NewServer(
host HostInterface, host HostInterface,
resourceAnalyzer stats.ResourceAnalyzer, resourceAnalyzer stats.ResourceAnalyzer,
auth AuthInterface, auth AuthInterface,
enableCAdvisorJSONEndpoints bool,
kubeCfg *kubeletconfiginternal.KubeletConfiguration) Server { kubeCfg *kubeletconfiginternal.KubeletConfiguration) Server {
server := Server{ server := Server{
host: host, host: host,
@ -235,7 +232,7 @@ func NewServer(
if auth != nil { if auth != nil {
server.InstallAuthFilter() server.InstallAuthFilter()
} }
server.InstallDefaultHandlers(enableCAdvisorJSONEndpoints) server.InstallDefaultHandlers()
if kubeCfg != nil && kubeCfg.EnableDebuggingHandlers { if kubeCfg != nil && kubeCfg.EnableDebuggingHandlers {
server.InstallDebuggingHandlers() server.InstallDebuggingHandlers()
// To maintain backward compatibility serve logs and pprof only when enableDebuggingHandlers is also enabled // To maintain backward compatibility serve logs and pprof only when enableDebuggingHandlers is also enabled
@ -312,7 +309,7 @@ func (s *Server) getMetricMethodBucket(method string) string {
// InstallDefaultHandlers registers the default set of supported HTTP request // InstallDefaultHandlers registers the default set of supported HTTP request
// patterns with the restful Container. // patterns with the restful Container.
func (s *Server) InstallDefaultHandlers(enableCAdvisorJSONEndpoints bool) { func (s *Server) InstallDefaultHandlers() {
s.addMetricsBucketMatcher("healthz") s.addMetricsBucketMatcher("healthz")
healthz.InstallHandler(s.restfulCont, healthz.InstallHandler(s.restfulCont,
healthz.PingHealthz, healthz.PingHealthz,
@ -331,7 +328,7 @@ func (s *Server) InstallDefaultHandlers(enableCAdvisorJSONEndpoints bool) {
s.restfulCont.Add(ws) s.restfulCont.Add(ws)
s.addMetricsBucketMatcher("stats") s.addMetricsBucketMatcher("stats")
s.restfulCont.Add(stats.CreateHandlers(statsPath, s.host, s.resourceAnalyzer, enableCAdvisorJSONEndpoints)) s.restfulCont.Add(stats.CreateHandlers(statsPath, s.host, s.resourceAnalyzer))
s.addMetricsBucketMatcher("metrics") s.addMetricsBucketMatcher("metrics")
s.addMetricsBucketMatcher("metrics/cadvisor") s.addMetricsBucketMatcher("metrics/cadvisor")
@ -387,19 +384,6 @@ func (s *Server) InstallDefaultHandlers(enableCAdvisorJSONEndpoints bool) {
s.restfulCont.Handle(proberMetricsPath, s.restfulCont.Handle(proberMetricsPath,
compbasemetrics.HandlerFor(p, compbasemetrics.HandlerOpts{ErrorHandling: compbasemetrics.ContinueOnError}), compbasemetrics.HandlerFor(p, compbasemetrics.HandlerOpts{ErrorHandling: compbasemetrics.ContinueOnError}),
) )
s.addMetricsBucketMatcher("spec")
if enableCAdvisorJSONEndpoints {
ws := new(restful.WebService)
ws.
Path(specPath).
Produces(restful.MIME_JSON)
ws.Route(ws.GET("").
To(s.getSpec).
Operation("getSpec").
Writes(cadvisorapi.MachineInfo{}))
s.restfulCont.Add(ws)
}
} }
// InstallDebuggingHandlers registers the HTTP request patterns that serve logs or run commands/containers // InstallDebuggingHandlers registers the HTTP request patterns that serve logs or run commands/containers
@ -718,16 +702,6 @@ func (s *Server) getLogs(request *restful.Request, response *restful.Response) {
s.host.ServeLogs(response, request.Request) s.host.ServeLogs(response, request.Request)
} }
// getSpec handles spec requests against the Kubelet.
func (s *Server) getSpec(request *restful.Request, response *restful.Response) {
info, err := s.host.GetCachedMachineInfo()
if err != nil {
response.WriteError(http.StatusInternalServerError, err)
return
}
response.WriteEntity(info)
}
type execRequestParams struct { type execRequestParams struct {
podNamespace string podNamespace string
podName string podName string

View File

@ -17,9 +17,7 @@ limitations under the License.
package server package server
import ( import (
"bytes"
"context" "context"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"io" "io"
@ -57,7 +55,6 @@ import (
_ "k8s.io/kubernetes/pkg/apis/core/install" _ "k8s.io/kubernetes/pkg/apis/core/install"
kubeletconfiginternal "k8s.io/kubernetes/pkg/kubelet/apis/config" kubeletconfiginternal "k8s.io/kubernetes/pkg/kubelet/apis/config"
"k8s.io/kubernetes/pkg/kubelet/cm" "k8s.io/kubernetes/pkg/kubelet/cm"
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
"k8s.io/kubernetes/pkg/kubelet/cri/streaming" "k8s.io/kubernetes/pkg/kubelet/cri/streaming"
"k8s.io/kubernetes/pkg/kubelet/cri/streaming/portforward" "k8s.io/kubernetes/pkg/kubelet/cri/streaming/portforward"
remotecommandserver "k8s.io/kubernetes/pkg/kubelet/cri/streaming/remotecommand" remotecommandserver "k8s.io/kubernetes/pkg/kubelet/cri/streaming/remotecommand"
@ -348,7 +345,6 @@ func newServerTestWithDebuggingHandlers(kubeCfg *kubeletconfiginternal.KubeletCo
fw.fakeKubelet, fw.fakeKubelet,
stats.NewResourceAnalyzer(fw.fakeKubelet, time.Minute), stats.NewResourceAnalyzer(fw.fakeKubelet, time.Minute),
fw.fakeAuth, fw.fakeAuth,
true,
kubeCfg) kubeCfg)
fw.serverUnderTest = &server fw.serverUnderTest = &server
fw.testHTTPServer = httptest.NewServer(fw.serverUnderTest) fw.testHTTPServer = httptest.NewServer(fw.serverUnderTest)
@ -363,185 +359,6 @@ func getPodName(name, namespace string) string {
return name + "_" + namespace return name + "_" + namespace
} }
func TestContainerInfo(t *testing.T) {
fw := newServerTest()
defer fw.testHTTPServer.Close()
expectedInfo := &cadvisorapi.ContainerInfo{}
podID := "somepod"
expectedPodID := getPodName(podID, "")
expectedContainerName := "goodcontainer"
fw.fakeKubelet.containerInfoFunc = func(podID string, uid types.UID, containerName string, req *cadvisorapi.ContainerInfoRequest) (*cadvisorapi.ContainerInfo, error) {
if podID != expectedPodID || containerName != expectedContainerName {
return nil, fmt.Errorf("bad podID or containerName: podID=%v; containerName=%v", podID, containerName)
}
return expectedInfo, nil
}
resp, err := http.Get(fw.testHTTPServer.URL + fmt.Sprintf("/stats/%v/%v", podID, expectedContainerName))
if err != nil {
t.Fatalf("Got error GETing: %v", err)
}
defer resp.Body.Close()
var receivedInfo cadvisorapi.ContainerInfo
err = json.NewDecoder(resp.Body).Decode(&receivedInfo)
if err != nil {
t.Fatalf("received invalid json data: %v", err)
}
if !receivedInfo.Eq(expectedInfo) {
t.Errorf("received wrong data: %#v", receivedInfo)
}
}
func TestContainerInfoWithUidNamespace(t *testing.T) {
fw := newServerTest()
defer fw.testHTTPServer.Close()
expectedInfo := &cadvisorapi.ContainerInfo{}
podID := "somepod"
expectedNamespace := "custom"
expectedPodID := getPodName(podID, expectedNamespace)
expectedContainerName := "goodcontainer"
fw.fakeKubelet.containerInfoFunc = func(podID string, uid types.UID, containerName string, req *cadvisorapi.ContainerInfoRequest) (*cadvisorapi.ContainerInfo, error) {
if podID != expectedPodID || string(uid) != testUID || containerName != expectedContainerName {
return nil, fmt.Errorf("bad podID or uid or containerName: podID=%v; uid=%v; containerName=%v", podID, uid, containerName)
}
return expectedInfo, nil
}
resp, err := http.Get(fw.testHTTPServer.URL + fmt.Sprintf("/stats/%v/%v/%v/%v", expectedNamespace, podID, testUID, expectedContainerName))
if err != nil {
t.Fatalf("Got error GETing: %v", err)
}
defer resp.Body.Close()
var receivedInfo cadvisorapi.ContainerInfo
err = json.NewDecoder(resp.Body).Decode(&receivedInfo)
if err != nil {
t.Fatalf("received invalid json data: %v", err)
}
if !receivedInfo.Eq(expectedInfo) {
t.Errorf("received wrong data: %#v", receivedInfo)
}
}
func TestContainerNotFound(t *testing.T) {
fw := newServerTest()
defer fw.testHTTPServer.Close()
podID := "somepod"
expectedNamespace := "custom"
expectedContainerName := "slowstartcontainer"
fw.fakeKubelet.containerInfoFunc = func(podID string, uid types.UID, containerName string, req *cadvisorapi.ContainerInfoRequest) (*cadvisorapi.ContainerInfo, error) {
return nil, kubecontainer.ErrContainerNotFound
}
resp, err := http.Get(fw.testHTTPServer.URL + fmt.Sprintf("/stats/%v/%v/%v/%v", expectedNamespace, podID, testUID, expectedContainerName))
if err != nil {
t.Fatalf("Got error GETing: %v", err)
}
if resp.StatusCode != http.StatusNotFound {
t.Fatalf("Received status %d expecting %d", resp.StatusCode, http.StatusNotFound)
}
defer resp.Body.Close()
}
func TestRootInfo(t *testing.T) {
fw := newServerTest()
defer fw.testHTTPServer.Close()
expectedInfo := &cadvisorapi.ContainerInfo{
ContainerReference: cadvisorapi.ContainerReference{
Name: "/",
},
}
fw.fakeKubelet.rawInfoFunc = func(req *cadvisorapi.ContainerInfoRequest) (map[string]*cadvisorapi.ContainerInfo, error) {
return map[string]*cadvisorapi.ContainerInfo{
expectedInfo.Name: expectedInfo,
}, nil
}
resp, err := http.Get(fw.testHTTPServer.URL + "/stats")
if err != nil {
t.Fatalf("Got error GETing: %v", err)
}
defer resp.Body.Close()
var receivedInfo cadvisorapi.ContainerInfo
err = json.NewDecoder(resp.Body).Decode(&receivedInfo)
if err != nil {
t.Fatalf("received invalid json data: %v", err)
}
if !receivedInfo.Eq(expectedInfo) {
t.Errorf("received wrong data: %#v, expected %#v", receivedInfo, expectedInfo)
}
}
func TestSubcontainerContainerInfo(t *testing.T) {
fw := newServerTest()
defer fw.testHTTPServer.Close()
const kubeletContainer = "/kubelet"
const kubeletSubContainer = "/kubelet/sub"
expectedInfo := map[string]*cadvisorapi.ContainerInfo{
kubeletContainer: {
ContainerReference: cadvisorapi.ContainerReference{
Name: kubeletContainer,
},
},
kubeletSubContainer: {
ContainerReference: cadvisorapi.ContainerReference{
Name: kubeletSubContainer,
},
},
}
fw.fakeKubelet.rawInfoFunc = func(req *cadvisorapi.ContainerInfoRequest) (map[string]*cadvisorapi.ContainerInfo, error) {
return expectedInfo, nil
}
request := fmt.Sprintf("{\"containerName\":%q, \"subcontainers\": true}", kubeletContainer)
resp, err := http.Post(fw.testHTTPServer.URL+"/stats/container", "application/json", bytes.NewBuffer([]byte(request)))
if err != nil {
t.Fatalf("Got error GETing: %v", err)
}
defer resp.Body.Close()
var receivedInfo map[string]*cadvisorapi.ContainerInfo
err = json.NewDecoder(resp.Body).Decode(&receivedInfo)
if err != nil {
t.Fatalf("Received invalid json data: %v", err)
}
if len(receivedInfo) != len(expectedInfo) {
t.Errorf("Received wrong data: %#v, expected %#v", receivedInfo, expectedInfo)
}
for _, containerName := range []string{kubeletContainer, kubeletSubContainer} {
if _, ok := receivedInfo[containerName]; !ok {
t.Errorf("Expected container %q to be present in result: %#v", containerName, receivedInfo)
}
if !receivedInfo[containerName].Eq(expectedInfo[containerName]) {
t.Errorf("Invalid result for %q: Expected %#v, received %#v", containerName, expectedInfo[containerName], receivedInfo[containerName])
}
}
}
func TestMachineInfo(t *testing.T) {
fw := newServerTest()
defer fw.testHTTPServer.Close()
expectedInfo := &cadvisorapi.MachineInfo{
NumCores: 4,
MemoryCapacity: 1024,
}
fw.fakeKubelet.machineInfoFunc = func() (*cadvisorapi.MachineInfo, error) {
return expectedInfo, nil
}
resp, err := http.Get(fw.testHTTPServer.URL + "/spec")
if err != nil {
t.Fatalf("Got error GETing: %v", err)
}
defer resp.Body.Close()
var receivedInfo cadvisorapi.MachineInfo
err = json.NewDecoder(resp.Body).Decode(&receivedInfo)
if err != nil {
t.Fatalf("received invalid json data: %v", err)
}
if !reflect.DeepEqual(&receivedInfo, expectedInfo) {
t.Errorf("received wrong data: %#v", receivedInfo)
}
}
func TestServeLogs(t *testing.T) { func TestServeLogs(t *testing.T) {
fw := newServerTest() fw := newServerTest()
defer fw.testHTTPServer.Close() defer fw.testHTTPServer.Close()
@ -1470,12 +1287,8 @@ func TestMetricBuckets(t *testing.T) {
"run": {url: "/run/podNamespace/podID/containerName", bucket: "run"}, "run": {url: "/run/podNamespace/podID/containerName", bucket: "run"},
"run with uid": {url: "/run/podNamespace/podID/uid/containerName", bucket: "run"}, "run with uid": {url: "/run/podNamespace/podID/uid/containerName", bucket: "run"},
"runningpods": {url: "/runningpods/", bucket: "runningpods"}, "runningpods": {url: "/runningpods/", bucket: "runningpods"},
"spec": {url: "/spec/", bucket: "spec"},
"stats": {url: "/stats/", bucket: "stats"}, "stats": {url: "/stats/", bucket: "stats"},
"stats container sub": {url: "/stats/container", bucket: "stats"},
"stats summary sub": {url: "/stats/summary", bucket: "stats"}, "stats summary sub": {url: "/stats/summary", bucket: "stats"},
"stats containerName with uid": {url: "/stats/namespace/podName/uid/containerName", bucket: "stats"},
"stats containerName": {url: "/stats/podName/containerName", bucket: "stats"},
"invalid path": {url: "/junk", bucket: "other"}, "invalid path": {url: "/junk", bucket: "other"},
"invalid path starting with good": {url: "/healthzjunk", bucket: "other"}, "invalid path starting with good": {url: "/healthzjunk", bucket: "other"},
} }
@ -1530,34 +1343,6 @@ func TestDebuggingDisabledHandlers(t *testing.T) {
for _, p := range paths { for _, p := range paths {
verifyEndpointResponse(t, fw, p, "Debug endpoints are disabled.\n") verifyEndpointResponse(t, fw, p, "Debug endpoints are disabled.\n")
} }
// test some other paths, make sure they're working
containerInfo := &cadvisorapi.ContainerInfo{
ContainerReference: cadvisorapi.ContainerReference{
Name: "/",
},
}
fw.fakeKubelet.rawInfoFunc = func(req *cadvisorapi.ContainerInfoRequest) (map[string]*cadvisorapi.ContainerInfo, error) {
return map[string]*cadvisorapi.ContainerInfo{
containerInfo.Name: containerInfo,
}, nil
}
resp, err := http.Get(fw.testHTTPServer.URL + "/stats")
require.NoError(t, err)
assert.Equal(t, http.StatusOK, resp.StatusCode)
machineInfo := &cadvisorapi.MachineInfo{
NumCores: 4,
MemoryCapacity: 1024,
}
fw.fakeKubelet.machineInfoFunc = func() (*cadvisorapi.MachineInfo, error) {
return machineInfo, nil
}
resp, err = http.Get(fw.testHTTPServer.URL + "/spec")
require.NoError(t, err)
assert.Equal(t, http.StatusOK, resp.StatusCode)
} }
func TestDisablingLogAndProfilingHandler(t *testing.T) { func TestDisablingLogAndProfilingHandler(t *testing.T) {

View File

@ -17,12 +17,8 @@ limitations under the License.
package stats package stats
import ( import (
"encoding/json"
"fmt" "fmt"
"io"
"net/http" "net/http"
"path"
"time"
restful "github.com/emicklei/go-restful" restful "github.com/emicklei/go-restful"
cadvisorapi "github.com/google/cadvisor/info/v1" cadvisorapi "github.com/google/cadvisor/info/v1"
@ -31,7 +27,6 @@ import (
"k8s.io/klog/v2" "k8s.io/klog/v2"
"k8s.io/api/core/v1" "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
statsapi "k8s.io/kubelet/pkg/apis/stats/v1alpha1" statsapi "k8s.io/kubelet/pkg/apis/stats/v1alpha1"
"k8s.io/kubernetes/pkg/kubelet/cm" "k8s.io/kubernetes/pkg/kubelet/cm"
@ -113,31 +108,20 @@ type handler struct {
} }
// CreateHandlers creates the REST handlers for the stats. // CreateHandlers creates the REST handlers for the stats.
func CreateHandlers(rootPath string, provider Provider, summaryProvider SummaryProvider, enableCAdvisorJSONEndpoints bool) *restful.WebService { func CreateHandlers(rootPath string, provider Provider, summaryProvider SummaryProvider) *restful.WebService {
h := &handler{provider, summaryProvider} h := &handler{provider, summaryProvider}
ws := &restful.WebService{} ws := &restful.WebService{}
ws.Path(rootPath). ws.Path(rootPath).
Produces(restful.MIME_JSON) Produces(restful.MIME_JSON)
type endpoint struct { endpoints := []struct {
path string path string
handler restful.RouteFunction handler restful.RouteFunction
} }{
endpoints := []endpoint{
{"/summary", h.handleSummary}, {"/summary", h.handleSummary},
} }
if enableCAdvisorJSONEndpoints {
endpoints = append(endpoints,
endpoint{"", h.handleStats},
endpoint{"/container", h.handleSystemContainer},
endpoint{"/{podName}/{containerName}", h.handlePodContainer},
endpoint{"/{namespace}/{podName}/{uid}/{containerName}", h.handlePodContainer},
)
}
for _, e := range endpoints { for _, e := range endpoints {
for _, method := range []string{"GET", "POST"} { for _, method := range []string{"GET", "POST"} {
ws.Route(ws. ws.Route(ws.
@ -150,72 +134,6 @@ func CreateHandlers(rootPath string, provider Provider, summaryProvider SummaryP
return ws return ws
} }
type statsRequest struct {
// The name of the container for which to request stats.
// Default: /
// +optional
ContainerName string `json:"containerName,omitempty"`
// Max number of stats to return.
// If start and end time are specified this limit is ignored.
// Default: 60
// +optional
NumStats int `json:"num_stats,omitempty"`
// Start time for which to query information.
// If omitted, the beginning of time is assumed.
// +optional
Start time.Time `json:"start,omitempty"`
// End time for which to query information.
// If omitted, current time is assumed.
// +optional
End time.Time `json:"end,omitempty"`
// Whether to also include information from subcontainers.
// Default: false.
// +optional
Subcontainers bool `json:"subcontainers,omitempty"`
}
func (r *statsRequest) cadvisorRequest() *cadvisorapi.ContainerInfoRequest {
return &cadvisorapi.ContainerInfoRequest{
NumStats: r.NumStats,
Start: r.Start,
End: r.End,
}
}
func parseStatsRequest(request *restful.Request) (statsRequest, error) {
// Default request.
query := statsRequest{
NumStats: 60,
}
err := json.NewDecoder(request.Request.Body).Decode(&query)
if err != nil && err != io.EOF {
return query, err
}
return query, nil
}
// Handles root container stats requests to /stats
func (h *handler) handleStats(request *restful.Request, response *restful.Response) {
query, err := parseStatsRequest(request)
if err != nil {
handleError(response, "/stats", err)
return
}
// Root container stats.
statsMap, err := h.provider.GetRawContainerInfo("/", query.cadvisorRequest(), false)
if err != nil {
handleError(response, fmt.Sprintf("/stats %v", query), err)
return
}
writeResponse(response, statsMap["/"])
}
// Handles stats summary requests to /stats/summary // Handles stats summary requests to /stats/summary
// If "only_cpu_and_memory" GET param is true then only cpu and memory is returned in response. // If "only_cpu_and_memory" GET param is true then only cpu and memory is returned in response.
func (h *handler) handleSummary(request *restful.Request, response *restful.Response) { func (h *handler) handleSummary(request *restful.Request, response *restful.Response) {
@ -244,74 +162,6 @@ func (h *handler) handleSummary(request *restful.Request, response *restful.Resp
} }
} }
// Handles non-kubernetes container stats requests to /stats/container/
func (h *handler) handleSystemContainer(request *restful.Request, response *restful.Response) {
query, err := parseStatsRequest(request)
if err != nil {
handleError(response, "/stats/container", err)
return
}
// Non-Kubernetes container stats.
containerName := path.Join("/", query.ContainerName)
stats, err := h.provider.GetRawContainerInfo(
containerName, query.cadvisorRequest(), query.Subcontainers)
if err != nil {
if _, ok := stats[containerName]; ok {
// If the failure is partial, log it and return a best-effort response.
klog.ErrorS(err, "Partial failure issuing GetRawContainerInfo", "query", query)
} else {
handleError(response, fmt.Sprintf("/stats/container %v", query), err)
return
}
}
writeResponse(response, stats)
}
// Handles kubernetes pod/container stats requests to:
// /stats/<pod name>/<container name>
// /stats/<namespace>/<pod name>/<uid>/<container name>
func (h *handler) handlePodContainer(request *restful.Request, response *restful.Response) {
query, err := parseStatsRequest(request)
if err != nil {
handleError(response, request.Request.URL.String(), err)
return
}
// Default parameters.
params := map[string]string{
"namespace": metav1.NamespaceDefault,
"uid": "",
}
for k, v := range request.PathParameters() {
params[k] = v
}
if params["podName"] == "" || params["containerName"] == "" {
response.WriteErrorString(http.StatusBadRequest,
fmt.Sprintf("Invalid pod container request: %v", params))
return
}
pod, ok := h.provider.GetPodByName(params["namespace"], params["podName"])
if !ok {
klog.V(4).InfoS("Container not found", "pod", klog.KRef(params["namespace"], params["podName"]))
response.WriteError(http.StatusNotFound, kubecontainer.ErrContainerNotFound)
return
}
stats, err := h.provider.GetContainerInfo(
kubecontainer.GetPodFullName(pod),
types.UID(params["uid"]),
params["containerName"],
query.cadvisorRequest())
if err != nil {
handleError(response, fmt.Sprintf("%s %v", request.Request.URL.String(), query), err)
return
}
writeResponse(response, stats)
}
func writeResponse(response *restful.Response, stats interface{}) { func writeResponse(response *restful.Response, stats interface{}) {
if err := response.WriteAsJson(stats); err != nil { if err := response.WriteAsJson(stats); err != nil {
klog.ErrorS(err, "Error writing response") klog.ErrorS(err, "Error writing response")