github.com/galamsiva2020/kubernetes-heapster-monitoring@v0.0.0-20210823134957-3c1baa7c1e70/metrics/sources/kubelet/kubelet_client.go (about)

     1  // Copyright 2014 Google Inc. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // This file implements a cadvisor datasource, that collects metrics from an instance
    16  // of cadvisor running on a specific host.
    17  
    18  package kubelet
    19  
    20  import (
    21  	"bytes"
    22  	"fmt"
    23  	"io/ioutil"
    24  	"net"
    25  	"net/http"
    26  	"net/url"
    27  	"strconv"
    28  	"time"
    29  
    30  	"github.com/golang/glog"
    31  	cadvisor "github.com/google/cadvisor/info/v1"
    32  	jsoniter "github.com/json-iterator/go"
    33  	kubelet_client "k8s.io/heapster/metrics/sources/kubelet/util"
    34  	stats "k8s.io/kubernetes/pkg/kubelet/apis/stats/v1alpha1"
    35  )
    36  
    37  type Host struct {
    38  	IP       net.IP
    39  	Port     int
    40  	Resource string
    41  }
    42  
    43  func (h Host) String() string {
    44  	return net.JoinHostPort(h.IP.String(), strconv.Itoa(h.Port))
    45  }
    46  
    47  type KubeletClient struct {
    48  	config *kubelet_client.KubeletClientConfig
    49  	client *http.Client
    50  }
    51  
    52  type ErrNotFound struct {
    53  	endpoint string
    54  }
    55  
    56  func (err *ErrNotFound) Error() string {
    57  	return fmt.Sprintf("%q not found", err.endpoint)
    58  }
    59  
    60  func IsNotFoundError(err error) bool {
    61  	_, isNotFound := err.(*ErrNotFound)
    62  	return isNotFound
    63  }
    64  
    65  func sampleContainerStats(stats []*cadvisor.ContainerStats) []*cadvisor.ContainerStats {
    66  	if len(stats) == 0 {
    67  		return []*cadvisor.ContainerStats{}
    68  	}
    69  	return []*cadvisor.ContainerStats{stats[len(stats)-1]}
    70  }
    71  
    72  func (self *KubeletClient) postRequestAndGetValue(client *http.Client, req *http.Request, value interface{}) error {
    73  	response, err := client.Do(req)
    74  	if err != nil {
    75  		return err
    76  	}
    77  	defer response.Body.Close()
    78  	body, err := ioutil.ReadAll(response.Body)
    79  	if err != nil {
    80  		return fmt.Errorf("failed to read response body - %v", err)
    81  	}
    82  	if response.StatusCode == http.StatusNotFound {
    83  		return &ErrNotFound{req.URL.String()}
    84  	} else if response.StatusCode != http.StatusOK {
    85  		return fmt.Errorf("request failed - %q, response: %q", response.Status, string(body))
    86  	}
    87  
    88  	kubeletAddr := "[unknown]"
    89  	if req.URL != nil {
    90  		kubeletAddr = req.URL.Host
    91  	}
    92  	glog.V(10).Infof("Raw response from Kubelet at %s: %s", kubeletAddr, string(body))
    93  
    94  	err = jsoniter.ConfigFastest.Unmarshal(body, value)
    95  	if err != nil {
    96  		return fmt.Errorf("failed to parse output. Response: %q. Error: %v", string(body), err)
    97  	}
    98  	return nil
    99  }
   100  
   101  func (self *KubeletClient) parseStat(containerInfo *cadvisor.ContainerInfo) *cadvisor.ContainerInfo {
   102  	containerInfo.Stats = sampleContainerStats(containerInfo.Stats)
   103  	if len(containerInfo.Aliases) > 0 {
   104  		containerInfo.Name = containerInfo.Aliases[0]
   105  	}
   106  	return containerInfo
   107  }
   108  
   109  // TODO(vmarmol): Use Kubernetes' if we export it as an API.
   110  type statsRequest struct {
   111  	// The name of the container for which to request stats.
   112  	// Default: /
   113  	ContainerName string `json:"containerName,omitempty"`
   114  
   115  	// Max number of stats to return.
   116  	// If start and end time are specified this limit is ignored.
   117  	// Default: 60
   118  	NumStats int `json:"num_stats,omitempty"`
   119  
   120  	// Start time for which to query information.
   121  	// If omitted, the beginning of time is assumed.
   122  	Start time.Time `json:"start,omitempty"`
   123  
   124  	// End time for which to query information.
   125  	// If omitted, current time is assumed.
   126  	End time.Time `json:"end,omitempty"`
   127  
   128  	// Whether to also include information from subcontainers.
   129  	// Default: false.
   130  	Subcontainers bool `json:"subcontainers,omitempty"`
   131  }
   132  
   133  func (self *KubeletClient) getScheme() string {
   134  	if self.config != nil && self.config.EnableHttps {
   135  		return "https"
   136  	} else {
   137  		return "http"
   138  	}
   139  }
   140  
   141  func (self *KubeletClient) getUrl(host Host, path string) string {
   142  	url := url.URL{
   143  		Scheme: self.getScheme(),
   144  		Host:   host.String(),
   145  		Path:   path,
   146  	}
   147  
   148  	return url.String()
   149  }
   150  
   151  // Get stats for all non-Kubernetes containers.
   152  func (self *KubeletClient) GetAllRawContainers(host Host, start, end time.Time) ([]cadvisor.ContainerInfo, error) {
   153  	url := self.getUrl(host, "/stats/container/")
   154  
   155  	return self.getAllContainers(url, start, end)
   156  }
   157  
   158  func (self *KubeletClient) GetSummary(host Host) (*stats.Summary, error) {
   159  	url := self.getUrl(host, "/stats/summary/")
   160  
   161  	req, err := http.NewRequest("GET", url, nil)
   162  	if err != nil {
   163  		return nil, err
   164  	}
   165  	summary := &stats.Summary{}
   166  	client := self.client
   167  	if client == nil {
   168  		client = http.DefaultClient
   169  	}
   170  	err = self.postRequestAndGetValue(client, req, summary)
   171  	return summary, err
   172  }
   173  
   174  func (self *KubeletClient) GetPort() int {
   175  	return int(self.config.Port)
   176  }
   177  
   178  func (self *KubeletClient) getAllContainers(url string, start, end time.Time) ([]cadvisor.ContainerInfo, error) {
   179  	// Request data from all subcontainers.
   180  	request := statsRequest{
   181  		ContainerName: "/",
   182  		NumStats:      1,
   183  		Start:         start,
   184  		End:           end,
   185  		Subcontainers: true,
   186  	}
   187  	body, err := jsoniter.ConfigFastest.Marshal(request)
   188  	if err != nil {
   189  		return nil, err
   190  	}
   191  	req, err := http.NewRequest("POST", url, bytes.NewBuffer(body))
   192  	if err != nil {
   193  		return nil, err
   194  	}
   195  	req.Header.Set("Content-Type", "application/json")
   196  
   197  	var containers map[string]cadvisor.ContainerInfo
   198  	client := self.client
   199  	if client == nil {
   200  		client = http.DefaultClient
   201  	}
   202  	err = self.postRequestAndGetValue(client, req, &containers)
   203  	if err != nil {
   204  		return nil, fmt.Errorf("failed to get all container stats from Kubelet URL %q: %v", url, err)
   205  	}
   206  	result := make([]cadvisor.ContainerInfo, 0, len(containers))
   207  	for _, containerInfo := range containers {
   208  		cont := self.parseStat(&containerInfo)
   209  		if cont != nil {
   210  			result = append(result, *cont)
   211  		}
   212  	}
   213  	return result, nil
   214  }
   215  
   216  func NewKubeletClient(kubeletConfig *kubelet_client.KubeletClientConfig) (*KubeletClient, error) {
   217  	transport, err := kubelet_client.MakeTransport(kubeletConfig)
   218  	if err != nil {
   219  		return nil, err
   220  	}
   221  	c := &http.Client{
   222  		Transport: transport,
   223  		Timeout:   kubeletConfig.HTTPTimeout,
   224  	}
   225  	return &KubeletClient{
   226  		config: kubeletConfig,
   227  		client: c,
   228  	}, nil
   229  }