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 }