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