blob: 21e3aed45a5d21f78c4d8a15c1354d86ae3c061c [file] [log] [blame]
Matthias Andreas Benkard832a54e2019-01-29 09:27:38 +01001// Copyright 2018 The Kubernetes Authors.
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
15package summary
16
17import (
18 "context"
19 "encoding/json"
20 "fmt"
21 "io/ioutil"
22 "net"
23 "net/http"
24 "net/url"
25 "strconv"
26
27 "github.com/golang/glog"
28 stats "k8s.io/kubernetes/pkg/kubelet/apis/stats/v1alpha1"
29)
30
31// KubeletInterface knows how to fetch metrics from the Kubelet
32type KubeletInterface interface {
33 // GetSummary fetches summary metrics from the given Kubelet
34 GetSummary(ctx context.Context, host string) (*stats.Summary, error)
35}
36
37type kubeletClient struct {
38 port int
39 deprecatedNoTLS bool
40 client *http.Client
41}
42
43type ErrNotFound struct {
44 endpoint string
45}
46
47func (err *ErrNotFound) Error() string {
48 return fmt.Sprintf("%q not found", err.endpoint)
49}
50
51func IsNotFoundError(err error) bool {
52 _, isNotFound := err.(*ErrNotFound)
53 return isNotFound
54}
55
56func (kc *kubeletClient) makeRequestAndGetValue(client *http.Client, req *http.Request, value interface{}) error {
57 // TODO(directxman12): support validating certs by hostname
58 response, err := client.Do(req)
59 if err != nil {
60 return err
61 }
62 defer response.Body.Close()
63 body, err := ioutil.ReadAll(response.Body)
64 if err != nil {
65 return fmt.Errorf("failed to read response body - %v", err)
66 }
67 if response.StatusCode == http.StatusNotFound {
68 return &ErrNotFound{req.URL.String()}
69 } else if response.StatusCode != http.StatusOK {
70 return fmt.Errorf("request failed - %q, response: %q", response.Status, string(body))
71 }
72
73 kubeletAddr := "[unknown]"
74 if req.URL != nil {
75 kubeletAddr = req.URL.Host
76 }
77 glog.V(10).Infof("Raw response from Kubelet at %s: %s", kubeletAddr, string(body))
78
79 err = json.Unmarshal(body, value)
80 if err != nil {
81 return fmt.Errorf("failed to parse output. Response: %q. Error: %v", string(body), err)
82 }
83 return nil
84}
85
86func (kc *kubeletClient) GetSummary(ctx context.Context, host string) (*stats.Summary, error) {
87 scheme := "https"
88 if kc.deprecatedNoTLS {
89 scheme = "http"
90 }
91 url := url.URL{
92 Scheme: scheme,
93 Host: net.JoinHostPort(host, strconv.Itoa(kc.port)),
94 Path: "/stats/summary/",
95 }
96
97 req, err := http.NewRequest("GET", url.String(), nil)
98 if err != nil {
99 return nil, err
100 }
101 summary := &stats.Summary{}
102 client := kc.client
103 if client == nil {
104 client = http.DefaultClient
105 }
106 err = kc.makeRequestAndGetValue(client, req.WithContext(ctx), summary)
107 return summary, err
108}
109
110func NewKubeletClient(transport http.RoundTripper, port int, deprecatedNoTLS bool) (KubeletInterface, error) {
111 c := &http.Client{
112 Transport: transport,
113 }
114 return &kubeletClient{
115 port: port,
116 client: c,
117 deprecatedNoTLS: deprecatedNoTLS,
118 }, nil
119}