github.com/timstclair/heapster@v0.20.0-alpha1/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 runing on a specific host. 17 18 package kubelet 19 20 import ( 21 "bytes" 22 "encoding/json" 23 "fmt" 24 "io/ioutil" 25 "net/http" 26 "time" 27 28 cadvisor "github.com/google/cadvisor/info/v1" 29 kube_client "k8s.io/kubernetes/pkg/client/unversioned" 30 ) 31 32 type Host struct { 33 IP string 34 Port int 35 Resource string 36 } 37 38 type KubeletClient struct { 39 config *kube_client.KubeletConfig 40 client *http.Client 41 } 42 43 func sampleContainerStats(stats []*cadvisor.ContainerStats) []*cadvisor.ContainerStats { 44 if len(stats) == 0 { 45 return []*cadvisor.ContainerStats{} 46 } 47 return []*cadvisor.ContainerStats{stats[len(stats)-1]} 48 } 49 50 func (self *KubeletClient) postRequestAndGetValue(client *http.Client, req *http.Request, value interface{}) error { 51 response, err := client.Do(req) 52 if err != nil { 53 return err 54 } 55 defer response.Body.Close() 56 body, err := ioutil.ReadAll(response.Body) 57 if err != nil { 58 return fmt.Errorf("failed to read response body - %v", err) 59 } 60 if response.StatusCode != http.StatusOK { 61 return fmt.Errorf("request failed - %q, response: %q", response.Status, string(body)) 62 } 63 err = json.Unmarshal(body, value) 64 if err != nil { 65 return fmt.Errorf("failed to parse output. Response: %q. Error: %v", string(body), err) 66 } 67 return nil 68 } 69 70 func (self *KubeletClient) parseStat(containerInfo *cadvisor.ContainerInfo) *cadvisor.ContainerInfo { 71 containerInfo.Stats = sampleContainerStats(containerInfo.Stats) 72 if len(containerInfo.Aliases) > 0 { 73 containerInfo.Name = containerInfo.Aliases[0] 74 } 75 return containerInfo 76 } 77 78 // TODO(vmarmol): Use Kubernetes' if we export it as an API. 79 type statsRequest struct { 80 // The name of the container for which to request stats. 81 // Default: / 82 ContainerName string `json:"containerName,omitempty"` 83 84 // Max number of stats to return. 85 // If start and end time are specified this limit is ignored. 86 // Default: 60 87 NumStats int `json:"num_stats,omitempty"` 88 89 // Start time for which to query information. 90 // If ommitted, the beginning of time is assumed. 91 Start time.Time `json:"start,omitempty"` 92 93 // End time for which to query information. 94 // If ommitted, current time is assumed. 95 End time.Time `json:"end,omitempty"` 96 97 // Whether to also include information from subcontainers. 98 // Default: false. 99 Subcontainers bool `json:"subcontainers,omitempty"` 100 } 101 102 // Get stats for all non-Kubernetes containers. 103 func (self *KubeletClient) GetAllRawContainers(host Host, start, end time.Time) ([]cadvisor.ContainerInfo, error) { 104 scheme := "http" 105 if self.config != nil && self.config.EnableHttps { 106 scheme = "https" 107 } 108 109 url := fmt.Sprintf("%s://%s:%d/stats/container/", scheme, host.IP, host.Port) 110 111 return self.getAllContainers(url, start, end) 112 } 113 114 func (self *KubeletClient) GetPort() int { 115 return int(self.config.Port) 116 } 117 118 func (self *KubeletClient) getAllContainers(url string, start, end time.Time) ([]cadvisor.ContainerInfo, error) { 119 // Request data from all subcontainers. 120 request := statsRequest{ 121 ContainerName: "/", 122 NumStats: 1, 123 Start: start, 124 End: end, 125 Subcontainers: true, 126 } 127 body, err := json.Marshal(request) 128 if err != nil { 129 return nil, err 130 } 131 req, err := http.NewRequest("POST", url, bytes.NewBuffer(body)) 132 if err != nil { 133 return nil, err 134 } 135 req.Header.Set("Content-Type", "application/json") 136 137 var containers map[string]cadvisor.ContainerInfo 138 client := self.client 139 if client == nil { 140 client = http.DefaultClient 141 } 142 err = self.postRequestAndGetValue(client, req, &containers) 143 if err != nil { 144 return nil, fmt.Errorf("failed to get all container stats from Kubelet URL %q: %v", url, err) 145 } 146 147 result := make([]cadvisor.ContainerInfo, 0, len(containers)) 148 for _, containerInfo := range containers { 149 cont := self.parseStat(&containerInfo) 150 if cont != nil { 151 result = append(result, *cont) 152 } 153 } 154 return result, nil 155 } 156 157 func NewKubeletClient(kubeletConfig *kube_client.KubeletConfig) (*KubeletClient, error) { 158 transport, err := kube_client.MakeTransport(kubeletConfig) 159 if err != nil { 160 return nil, err 161 } 162 c := &http.Client{ 163 Transport: transport, 164 Timeout: kubeletConfig.HTTPTimeout, 165 } 166 return &KubeletClient{ 167 config: kubeletConfig, 168 client: c, 169 }, nil 170 }