1 Star 0 Fork 0

zhuchance / kubernetes

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
client.go 15.82 KB
一键复制 编辑 原始数据 按行查看 历史
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646
// Copyright 2014 go-dockerclient authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package docker provides a client for the Docker remote API.
//
// See http://goo.gl/G3plxW for more details on the remote API.
package docker
import (
"bytes"
"crypto/tls"
"crypto/x509"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
"net/http/httputil"
"net/url"
"reflect"
"strconv"
"strings"
)
const userAgent = "go-dockerclient"
var (
// ErrInvalidEndpoint is returned when the endpoint is not a valid HTTP URL.
ErrInvalidEndpoint = errors.New("invalid endpoint")
// ErrConnectionRefused is returned when the client cannot connect to the given endpoint.
ErrConnectionRefused = errors.New("cannot connect to Docker endpoint")
apiVersion1_12, _ = NewAPIVersion("1.12")
)
// APIVersion is an internal representation of a version of the Remote API.
type APIVersion []int
// NewAPIVersion returns an instance of APIVersion for the given string.
//
// The given string must be in the form <major>.<minor>.<patch>, where <major>,
// <minor> and <patch> are integer numbers.
func NewAPIVersion(input string) (APIVersion, error) {
if !strings.Contains(input, ".") {
return nil, fmt.Errorf("Unable to parse version %q", input)
}
arr := strings.Split(input, ".")
ret := make(APIVersion, len(arr))
var err error
for i, val := range arr {
ret[i], err = strconv.Atoi(val)
if err != nil {
return nil, fmt.Errorf("Unable to parse version %q: %q is not an integer", input, val)
}
}
return ret, nil
}
func (version APIVersion) String() string {
var str string
for i, val := range version {
str += strconv.Itoa(val)
if i < len(version)-1 {
str += "."
}
}
return str
}
// LessThan is a function for comparing APIVersion structs
func (version APIVersion) LessThan(other APIVersion) bool {
return version.compare(other) < 0
}
// LessThanOrEqualTo is a function for comparing APIVersion structs
func (version APIVersion) LessThanOrEqualTo(other APIVersion) bool {
return version.compare(other) <= 0
}
// GreaterThan is a function for comparing APIVersion structs
func (version APIVersion) GreaterThan(other APIVersion) bool {
return version.compare(other) > 0
}
// GreaterThanOrEqualTo is a function for comparing APIVersion structs
func (version APIVersion) GreaterThanOrEqualTo(other APIVersion) bool {
return version.compare(other) >= 0
}
func (version APIVersion) compare(other APIVersion) int {
for i, v := range version {
if i <= len(other)-1 {
otherVersion := other[i]
if v < otherVersion {
return -1
} else if v > otherVersion {
return 1
}
}
}
if len(version) > len(other) {
return 1
}
if len(version) < len(other) {
return -1
}
return 0
}
// Client is the basic type of this package. It provides methods for
// interaction with the API.
type Client struct {
SkipServerVersionCheck bool
HTTPClient *http.Client
TLSConfig *tls.Config
endpoint string
endpointURL *url.URL
eventMonitor *eventMonitoringState
requestedAPIVersion APIVersion
serverAPIVersion APIVersion
expectedAPIVersion APIVersion
}
// NewClient returns a Client instance ready for communication with the given
// server endpoint. It will use the latest remote API version available in the
// server.
func NewClient(endpoint string) (*Client, error) {
client, err := NewVersionedClient(endpoint, "")
if err != nil {
return nil, err
}
client.SkipServerVersionCheck = true
return client, nil
}
// NewTLSClient returns a Client instance ready for TLS communications with the givens
// server endpoint, key and certificates . It will use the latest remote API version
// available in the server.
func NewTLSClient(endpoint string, cert, key, ca string) (*Client, error) {
client, err := NewVersionnedTLSClient(endpoint, cert, key, ca, "")
if err != nil {
return nil, err
}
client.SkipServerVersionCheck = true
return client, nil
}
// NewVersionedClient returns a Client instance ready for communication with
// the given server endpoint, using a specific remote API version.
func NewVersionedClient(endpoint string, apiVersionString string) (*Client, error) {
u, err := parseEndpoint(endpoint)
if err != nil {
return nil, err
}
var requestedAPIVersion APIVersion
if strings.Contains(apiVersionString, ".") {
requestedAPIVersion, err = NewAPIVersion(apiVersionString)
if err != nil {
return nil, err
}
}
return &Client{
HTTPClient: http.DefaultClient,
endpoint: endpoint,
endpointURL: u,
eventMonitor: new(eventMonitoringState),
requestedAPIVersion: requestedAPIVersion,
}, nil
}
// NewVersionnedTLSClient returns a Client instance ready for TLS communications with the givens
// server endpoint, key and certificates, using a specific remote API version.
func NewVersionnedTLSClient(endpoint string, cert, key, ca, apiVersionString string) (*Client, error) {
u, err := parseEndpoint(endpoint)
if err != nil {
return nil, err
}
var requestedAPIVersion APIVersion
if strings.Contains(apiVersionString, ".") {
requestedAPIVersion, err = NewAPIVersion(apiVersionString)
if err != nil {
return nil, err
}
}
if cert == "" || key == "" {
return nil, errors.New("Both cert and key path are required")
}
tlsCert, err := tls.LoadX509KeyPair(cert, key)
if err != nil {
return nil, err
}
tlsConfig := &tls.Config{Certificates: []tls.Certificate{tlsCert}}
if ca == "" {
tlsConfig.InsecureSkipVerify = true
} else {
cert, err := ioutil.ReadFile(ca)
if err != nil {
return nil, err
}
caPool := x509.NewCertPool()
if !caPool.AppendCertsFromPEM(cert) {
return nil, errors.New("Could not add RootCA pem")
}
tlsConfig.RootCAs = caPool
}
tr := &http.Transport{
TLSClientConfig: tlsConfig,
}
if err != nil {
return nil, err
}
return &Client{
HTTPClient: &http.Client{Transport: tr},
TLSConfig: tlsConfig,
endpoint: endpoint,
endpointURL: u,
eventMonitor: new(eventMonitoringState),
requestedAPIVersion: requestedAPIVersion,
}, nil
}
func (c *Client) checkAPIVersion() error {
serverAPIVersionString, err := c.getServerAPIVersionString()
if err != nil {
return err
}
c.serverAPIVersion, err = NewAPIVersion(serverAPIVersionString)
if err != nil {
return err
}
if c.requestedAPIVersion == nil {
c.expectedAPIVersion = c.serverAPIVersion
} else {
c.expectedAPIVersion = c.requestedAPIVersion
}
return nil
}
// Ping pings the docker server
//
// See http://goo.gl/stJENm for more details.
func (c *Client) Ping() error {
path := "/_ping"
body, status, err := c.do("GET", path, nil)
if err != nil {
return err
}
if status != http.StatusOK {
return newError(status, body)
}
return nil
}
func (c *Client) getServerAPIVersionString() (version string, err error) {
body, status, err := c.do("GET", "/version", nil)
if err != nil {
return "", err
}
if status != http.StatusOK {
return "", fmt.Errorf("Received unexpected status %d while trying to retrieve the server version", status)
}
var versionResponse map[string]string
err = json.Unmarshal(body, &versionResponse)
if err != nil {
return "", err
}
version = versionResponse["ApiVersion"]
return version, nil
}
func (c *Client) do(method, path string, data interface{}) ([]byte, int, error) {
var params io.Reader
if data != nil {
buf, err := json.Marshal(data)
if err != nil {
return nil, -1, err
}
params = bytes.NewBuffer(buf)
}
if path != "/version" && !c.SkipServerVersionCheck && c.expectedAPIVersion == nil {
err := c.checkAPIVersion()
if err != nil {
return nil, -1, err
}
}
req, err := http.NewRequest(method, c.getURL(path), params)
if err != nil {
return nil, -1, err
}
req.Header.Set("User-Agent", userAgent)
if data != nil {
req.Header.Set("Content-Type", "application/json")
} else if method == "POST" {
req.Header.Set("Content-Type", "plain/text")
}
var resp *http.Response
protocol := c.endpointURL.Scheme
address := c.endpointURL.Path
if protocol == "unix" {
dial, err := net.Dial(protocol, address)
if err != nil {
return nil, -1, err
}
defer dial.Close()
clientconn := httputil.NewClientConn(dial, nil)
resp, err = clientconn.Do(req)
if err != nil {
return nil, -1, err
}
defer clientconn.Close()
} else {
resp, err = c.HTTPClient.Do(req)
}
if err != nil {
if strings.Contains(err.Error(), "connection refused") {
return nil, -1, ErrConnectionRefused
}
return nil, -1, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, -1, err
}
if resp.StatusCode < 200 || resp.StatusCode >= 400 {
return nil, resp.StatusCode, newError(resp.StatusCode, body)
}
return body, resp.StatusCode, nil
}
func (c *Client) stream(method, path string, setRawTerminal, rawJSONStream bool, headers map[string]string, in io.Reader, stdout, stderr io.Writer) error {
if (method == "POST" || method == "PUT") && in == nil {
in = bytes.NewReader(nil)
}
if path != "/version" && !c.SkipServerVersionCheck && c.expectedAPIVersion == nil {
err := c.checkAPIVersion()
if err != nil {
return err
}
}
req, err := http.NewRequest(method, c.getURL(path), in)
if err != nil {
return err
}
req.Header.Set("User-Agent", userAgent)
if method == "POST" {
req.Header.Set("Content-Type", "plain/text")
}
for key, val := range headers {
req.Header.Set(key, val)
}
var resp *http.Response
protocol := c.endpointURL.Scheme
address := c.endpointURL.Path
if stdout == nil {
stdout = ioutil.Discard
}
if stderr == nil {
stderr = ioutil.Discard
}
if protocol == "unix" {
dial, err := net.Dial(protocol, address)
if err != nil {
return err
}
clientconn := httputil.NewClientConn(dial, nil)
resp, err = clientconn.Do(req)
defer clientconn.Close()
} else {
resp, err = c.HTTPClient.Do(req)
}
if err != nil {
if strings.Contains(err.Error(), "connection refused") {
return ErrConnectionRefused
}
return err
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 400 {
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
return newError(resp.StatusCode, body)
}
if resp.Header.Get("Content-Type") == "application/json" {
// if we want to get raw json stream, just copy it back to output
// without decoding it
if rawJSONStream {
_, err = io.Copy(stdout, resp.Body)
return err
}
dec := json.NewDecoder(resp.Body)
for {
var m jsonMessage
if err := dec.Decode(&m); err == io.EOF {
break
} else if err != nil {
return err
}
if m.Stream != "" {
fmt.Fprint(stdout, m.Stream)
} else if m.Progress != "" {
fmt.Fprintf(stdout, "%s %s\r", m.Status, m.Progress)
} else if m.Error != "" {
return errors.New(m.Error)
}
if m.Status != "" {
fmt.Fprintln(stdout, m.Status)
}
}
} else {
if setRawTerminal {
_, err = io.Copy(stdout, resp.Body)
} else {
_, err = stdCopy(stdout, stderr, resp.Body)
}
return err
}
return nil
}
func (c *Client) hijack(method, path string, success chan struct{}, setRawTerminal bool, in io.Reader, stderr, stdout io.Writer, data interface{}) error {
if path != "/version" && !c.SkipServerVersionCheck && c.expectedAPIVersion == nil {
err := c.checkAPIVersion()
if err != nil {
return err
}
}
var params io.Reader
if data != nil {
buf, err := json.Marshal(data)
if err != nil {
return err
}
params = bytes.NewBuffer(buf)
}
if stdout == nil {
stdout = ioutil.Discard
}
if stderr == nil {
stderr = ioutil.Discard
}
req, err := http.NewRequest(method, c.getURL(path), params)
if err != nil {
return err
}
req.Header.Set("Content-Type", "plain/text")
protocol := c.endpointURL.Scheme
address := c.endpointURL.Path
if protocol != "unix" {
protocol = "tcp"
address = c.endpointURL.Host
}
var dial net.Conn
if c.TLSConfig != nil && protocol != "unix" {
dial, err = tlsDial(protocol, address, c.TLSConfig)
if err != nil {
return err
}
} else {
dial, err = net.Dial(protocol, address)
if err != nil {
return err
}
}
clientconn := httputil.NewClientConn(dial, nil)
defer clientconn.Close()
clientconn.Do(req)
if success != nil {
success <- struct{}{}
<-success
}
rwc, br := clientconn.Hijack()
defer rwc.Close()
errs := make(chan error, 2)
exit := make(chan bool)
go func() {
defer close(exit)
var err error
if setRawTerminal {
// When TTY is ON, use regular copy
_, err = io.Copy(stdout, br)
} else {
_, err = stdCopy(stdout, stderr, br)
}
errs <- err
}()
go func() {
var err error
if in != nil {
_, err = io.Copy(rwc, in)
}
rwc.(interface {
CloseWrite() error
}).CloseWrite()
errs <- err
}()
<-exit
return <-errs
}
func (c *Client) getURL(path string) string {
urlStr := strings.TrimRight(c.endpointURL.String(), "/")
if c.endpointURL.Scheme == "unix" {
urlStr = ""
}
if c.requestedAPIVersion != nil {
return fmt.Sprintf("%s/v%s%s", urlStr, c.requestedAPIVersion, path)
}
return fmt.Sprintf("%s%s", urlStr, path)
}
type jsonMessage struct {
Status string `json:"status,omitempty"`
Progress string `json:"progress,omitempty"`
Error string `json:"error,omitempty"`
Stream string `json:"stream,omitempty"`
}
func queryString(opts interface{}) string {
if opts == nil {
return ""
}
value := reflect.ValueOf(opts)
if value.Kind() == reflect.Ptr {
value = value.Elem()
}
if value.Kind() != reflect.Struct {
return ""
}
items := url.Values(map[string][]string{})
for i := 0; i < value.NumField(); i++ {
field := value.Type().Field(i)
if field.PkgPath != "" {
continue
}
key := field.Tag.Get("qs")
if key == "" {
key = strings.ToLower(field.Name)
} else if key == "-" {
continue
}
v := value.Field(i)
switch v.Kind() {
case reflect.Bool:
if v.Bool() {
items.Add(key, "1")
}
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if v.Int() > 0 {
items.Add(key, strconv.FormatInt(v.Int(), 10))
}
case reflect.Float32, reflect.Float64:
if v.Float() > 0 {
items.Add(key, strconv.FormatFloat(v.Float(), 'f', -1, 64))
}
case reflect.String:
if v.String() != "" {
items.Add(key, v.String())
}
case reflect.Ptr:
if !v.IsNil() {
if b, err := json.Marshal(v.Interface()); err == nil {
items.Add(key, string(b))
}
}
case reflect.Map:
if len(v.MapKeys()) > 0 {
if b, err := json.Marshal(v.Interface()); err == nil {
items.Add(key, string(b))
}
}
}
}
return items.Encode()
}
// Error represents failures in the API. It represents a failure from the API.
type Error struct {
Status int
Message string
}
func newError(status int, body []byte) *Error {
return &Error{Status: status, Message: string(body)}
}
func (e *Error) Error() string {
return fmt.Sprintf("API error (%d): %s", e.Status, e.Message)
}
func parseEndpoint(endpoint string) (*url.URL, error) {
u, err := url.Parse(endpoint)
if err != nil {
return nil, ErrInvalidEndpoint
}
if u.Scheme == "tcp" {
_, port, err := net.SplitHostPort(u.Host)
if err != nil {
if e, ok := err.(*net.AddrError); ok {
if e.Err == "missing port in address" {
return u, nil
}
}
return nil, ErrInvalidEndpoint
}
number, err := strconv.ParseInt(port, 10, 64)
if err == nil && number == 2376 {
u.Scheme = "https"
} else {
u.Scheme = "http"
}
}
if u.Scheme != "http" && u.Scheme != "https" && u.Scheme != "unix" {
return nil, ErrInvalidEndpoint
}
if u.Scheme != "unix" {
_, port, err := net.SplitHostPort(u.Host)
if err != nil {
if e, ok := err.(*net.AddrError); ok {
if e.Err == "missing port in address" {
return u, nil
}
}
return nil, ErrInvalidEndpoint
}
number, err := strconv.ParseInt(port, 10, 64)
if err == nil && number > 0 && number < 65536 {
return u, nil
}
} else {
return u, nil // we don't need port when using a unix socket
}
return nil, ErrInvalidEndpoint
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Go
1
https://gitee.com/meoom/kubernetes.git
git@gitee.com:meoom/kubernetes.git
meoom
kubernetes
kubernetes
v0.11.0

搜索帮助

344bd9b3 5694891 D2dac590 5694891