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  }