github.com/google/cadvisor@v0.49.1/client/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 is an implementation of a cAdvisor REST API in Go. 16 // To use it, create a client (replace the URL with your actual cAdvisor REST endpoint): 17 // 18 // client, err := client.NewClient("http://192.168.59.103:8080/") 19 // 20 // Then, the client interface exposes go methods corresponding to the REST endpoints. 21 package client 22 23 import ( 24 "bytes" 25 "encoding/json" 26 "fmt" 27 "io" 28 "net/http" 29 "path" 30 "strings" 31 32 v1 "github.com/google/cadvisor/info/v1" 33 34 "k8s.io/klog/v2" 35 ) 36 37 // Client represents the base URL for a cAdvisor client. 38 type Client struct { 39 baseURL string 40 httpClient *http.Client 41 } 42 43 // NewClient returns a new v1.3 client with the specified base URL. 44 func NewClient(url string) (*Client, error) { 45 return newClient(url, http.DefaultClient) 46 } 47 48 func newClient(url string, client *http.Client) (*Client, error) { 49 if !strings.HasSuffix(url, "/") { 50 url += "/" 51 } 52 53 return &Client{ 54 baseURL: fmt.Sprintf("%sapi/v1.3/", url), 55 httpClient: client, 56 }, nil 57 } 58 59 // Returns all past events that satisfy the request 60 func (c *Client) EventStaticInfo(name string) (einfo []*v1.Event, err error) { 61 u := c.eventsInfoURL(name) 62 ret := new([]*v1.Event) 63 if err = c.httpGetJSONData(ret, nil, u, "event info"); err != nil { 64 return 65 } 66 einfo = *ret 67 return 68 } 69 70 // Streams all events that occur that satisfy the request into the channel 71 // that is passed 72 func (c *Client) EventStreamingInfo(name string, einfo chan *v1.Event) (err error) { 73 u := c.eventsInfoURL(name) 74 if err = c.getEventStreamingData(u, einfo); err != nil { 75 return 76 } 77 return nil 78 } 79 80 // MachineInfo returns the JSON machine information for this client. 81 // A non-nil error result indicates a problem with obtaining 82 // the JSON machine information data. 83 func (c *Client) MachineInfo() (minfo *v1.MachineInfo, err error) { 84 u := c.machineInfoURL() 85 ret := new(v1.MachineInfo) 86 if err = c.httpGetJSONData(ret, nil, u, "machine info"); err != nil { 87 return 88 } 89 minfo = ret 90 return 91 } 92 93 // ContainerInfo returns the JSON container information for the specified 94 // container and request. 95 func (c *Client) ContainerInfo(name string, query *v1.ContainerInfoRequest) (cinfo *v1.ContainerInfo, err error) { 96 u := c.containerInfoURL(name) 97 ret := new(v1.ContainerInfo) 98 if err = c.httpGetJSONData(ret, query, u, fmt.Sprintf("container info for %q", name)); err != nil { 99 return 100 } 101 cinfo = ret 102 return 103 } 104 105 // Returns the information about all subcontainers (recursive) of the specified container (including itself). 106 func (c *Client) SubcontainersInfo(name string, query *v1.ContainerInfoRequest) ([]v1.ContainerInfo, error) { 107 var response []v1.ContainerInfo 108 url := c.subcontainersInfoURL(name) 109 err := c.httpGetJSONData(&response, query, url, fmt.Sprintf("subcontainers container info for %q", name)) 110 if err != nil { 111 return []v1.ContainerInfo{}, err 112 113 } 114 return response, nil 115 } 116 117 // Returns the JSON container information for the specified 118 // Docker container and request. 119 func (c *Client) DockerContainer(name string, query *v1.ContainerInfoRequest) (cinfo v1.ContainerInfo, err error) { 120 u := c.dockerInfoURL(name) 121 ret := make(map[string]v1.ContainerInfo) 122 if err = c.httpGetJSONData(&ret, query, u, fmt.Sprintf("Docker container info for %q", name)); err != nil { 123 return 124 } 125 if len(ret) != 1 { 126 err = fmt.Errorf("expected to only receive 1 Docker container: %+v", ret) 127 return 128 } 129 for _, cont := range ret { 130 cinfo = cont 131 } 132 return 133 } 134 135 // Returns the JSON container information for all Docker containers. 136 func (c *Client) AllDockerContainers(query *v1.ContainerInfoRequest) (cinfo []v1.ContainerInfo, err error) { 137 u := c.dockerInfoURL("/") 138 ret := make(map[string]v1.ContainerInfo) 139 if err = c.httpGetJSONData(&ret, query, u, "all Docker containers info"); err != nil { 140 return 141 } 142 cinfo = make([]v1.ContainerInfo, 0, len(ret)) 143 for _, cont := range ret { 144 cinfo = append(cinfo, cont) 145 } 146 return 147 } 148 149 func (c *Client) machineInfoURL() string { 150 return c.baseURL + path.Join("machine") 151 } 152 153 func (c *Client) containerInfoURL(name string) string { 154 return c.baseURL + path.Join("containers", name) 155 } 156 157 func (c *Client) subcontainersInfoURL(name string) string { 158 return c.baseURL + path.Join("subcontainers", name) 159 } 160 161 func (c *Client) dockerInfoURL(name string) string { 162 return c.baseURL + path.Join("docker", name) 163 } 164 165 func (c *Client) eventsInfoURL(name string) string { 166 return c.baseURL + path.Join("events", name) 167 } 168 169 func (c *Client) httpGetJSONData(data, postData interface{}, url, infoName string) error { 170 var resp *http.Response 171 var err error 172 173 if postData != nil { 174 data, marshalErr := json.Marshal(postData) 175 if marshalErr != nil { 176 return fmt.Errorf("unable to marshal data: %v", marshalErr) 177 } 178 resp, err = c.httpClient.Post(url, "application/json", bytes.NewBuffer(data)) 179 } else { 180 resp, err = c.httpClient.Get(url) 181 } 182 if err != nil { 183 return fmt.Errorf("unable to get %q from %q: %v", infoName, url, err) 184 } 185 if resp == nil { 186 return fmt.Errorf("received empty response for %q from %q", infoName, url) 187 } 188 defer resp.Body.Close() 189 body, err := io.ReadAll(resp.Body) 190 if err != nil { 191 err = fmt.Errorf("unable to read all %q from %q: %v", infoName, url, err) 192 return err 193 } 194 if resp.StatusCode != 200 { 195 return fmt.Errorf("request %q failed with error: %q", url, strings.TrimSpace(string(body))) 196 } 197 if err = json.Unmarshal(body, data); err != nil { 198 err = fmt.Errorf("unable to unmarshal %q (Body: %q) from %q with error: %v", infoName, string(body), url, err) 199 return err 200 } 201 return nil 202 } 203 204 func (c *Client) getEventStreamingData(url string, einfo chan *v1.Event) error { 205 req, err := http.NewRequest("GET", url, nil) 206 if err != nil { 207 return err 208 } 209 resp, err := c.httpClient.Do(req) 210 if err != nil { 211 return err 212 } 213 if resp.StatusCode != http.StatusOK { 214 return fmt.Errorf("Status code is not OK: %v (%s)", resp.StatusCode, resp.Status) 215 } 216 217 dec := json.NewDecoder(resp.Body) 218 var m *v1.Event = &v1.Event{} 219 for { 220 err := dec.Decode(m) 221 if err != nil { 222 if err == io.EOF { 223 break 224 } 225 // if called without &stream=true will not be able to parse event and will trigger fatal 226 klog.Fatalf("Received error %v", err) 227 } 228 einfo <- m 229 } 230 return nil 231 }