Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
1b82b1148a
3
.gitignore
vendored
3
.gitignore
vendored
@ -42,6 +42,9 @@ Session.vim
|
||||
.vagrant
|
||||
network_closure.sh
|
||||
|
||||
# Local cluster env variables
|
||||
/cluster/env.sh
|
||||
|
||||
# Compiled binaries in third_party
|
||||
/third_party/pkg
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
Please see the [Releases Page](https://github.com/GoogleCloudPlatform/kubernetes/releases)
|
||||
Please see the [Releases Page](https://github.com/kubernetes/kubernetes/releases)
|
||||
|
||||
|
||||
[]()
|
||||
|
8
Godeps/Godeps.json
generated
8
Godeps/Godeps.json
generated
@ -215,8 +215,8 @@
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/emicklei/go-restful",
|
||||
"Comment": "v1.1.3-76-gbfd6ff2",
|
||||
"Rev": "bfd6ff29d2961031cec64346a92bae4cde96c868"
|
||||
"Comment": "v1.1.3-98-g1f9a0ee",
|
||||
"Rev": "1f9a0ee00ff93717a275e15b30cf7df356255877"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/evanphx/json-patch",
|
||||
@ -224,7 +224,7 @@
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/fsouza/go-dockerclient",
|
||||
"Rev": "42d06e2b125654477366c320dcea99107a86e9c2"
|
||||
"Rev": "76fd6c68cf24c48ee6a2b25def997182a29f940e"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/garyburd/redigo/internal",
|
||||
@ -512,7 +512,7 @@
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/spf13/cobra",
|
||||
"Rev": "db0518444643a7b170abb78164bbeaf5a2bb816f"
|
||||
"Rev": "68f5a81a722d56241bd70faf6860ceb05eb27d64"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/spf13/pflag",
|
||||
|
5
Godeps/_workspace/src/github.com/emicklei/go-restful/CHANGES.md
generated
vendored
5
Godeps/_workspace/src/github.com/emicklei/go-restful/CHANGES.md
generated
vendored
@ -1,5 +1,10 @@
|
||||
Change history of go-restful
|
||||
=
|
||||
2015-08-06
|
||||
- add support for reading entities from compressed request content
|
||||
- use sync.Pool for compressors of http response and request body
|
||||
- add Description to Parameter for documentation in Swagger UI
|
||||
|
||||
2015-03-20
|
||||
- add configurable logging
|
||||
|
||||
|
2
Godeps/_workspace/src/github.com/emicklei/go-restful/README.md
generated
vendored
2
Godeps/_workspace/src/github.com/emicklei/go-restful/README.md
generated
vendored
@ -47,7 +47,7 @@ func (u UserResource) findUser(request *restful.Request, response *restful.Respo
|
||||
- Filters for intercepting the request → response flow on Service or Route level
|
||||
- Request-scoped variables using attributes
|
||||
- Containers for WebServices on different HTTP endpoints
|
||||
- Content encoding (gzip,deflate) of responses
|
||||
- Content encoding (gzip,deflate) of request and response payloads
|
||||
- Automatic responses on OPTIONS (using a filter)
|
||||
- Automatic CORS request handling (using a filter)
|
||||
- API declaration for Swagger UI (see swagger package)
|
||||
|
14
Godeps/_workspace/src/github.com/emicklei/go-restful/compress.go
generated
vendored
14
Godeps/_workspace/src/github.com/emicklei/go-restful/compress.go
generated
vendored
@ -73,15 +73,13 @@ func NewCompressingResponseWriter(httpWriter http.ResponseWriter, encoding strin
|
||||
c.writer = httpWriter
|
||||
var err error
|
||||
if ENCODING_GZIP == encoding {
|
||||
c.compressor, err = gzip.NewWriterLevel(httpWriter, gzip.BestSpeed)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
w := GzipWriterPool.Get().(*gzip.Writer)
|
||||
w.Reset(httpWriter)
|
||||
c.compressor = w
|
||||
} else if ENCODING_DEFLATE == encoding {
|
||||
c.compressor, err = zlib.NewWriterLevel(httpWriter, zlib.BestSpeed)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
w := ZlibWriterPool.Get().(*zlib.Writer)
|
||||
w.Reset(httpWriter)
|
||||
c.compressor = w
|
||||
} else {
|
||||
return nil, errors.New("Unknown encoding:" + encoding)
|
||||
}
|
||||
|
74
Godeps/_workspace/src/github.com/emicklei/go-restful/compress_test.go
generated
vendored
74
Godeps/_workspace/src/github.com/emicklei/go-restful/compress_test.go
generated
vendored
@ -1,11 +1,17 @@
|
||||
package restful
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"compress/zlib"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// go test -v -test.run TestGzip ...restful
|
||||
func TestGzip(t *testing.T) {
|
||||
EnableContentEncoding = true
|
||||
httpRequest, _ := http.NewRequest("GET", "/test", nil)
|
||||
@ -27,6 +33,17 @@ func TestGzip(t *testing.T) {
|
||||
if httpWriter.Header().Get("Content-Encoding") != "gzip" {
|
||||
t.Fatal("Missing gzip header")
|
||||
}
|
||||
reader, err := gzip.NewReader(httpWriter.Body)
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
data, err := ioutil.ReadAll(reader)
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
if got, want := string(data), "Hello World"; got != want {
|
||||
t.Errorf("got %v want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeflate(t *testing.T) {
|
||||
@ -50,4 +67,61 @@ func TestDeflate(t *testing.T) {
|
||||
if httpWriter.Header().Get("Content-Encoding") != "deflate" {
|
||||
t.Fatal("Missing deflate header")
|
||||
}
|
||||
reader, err := zlib.NewReader(httpWriter.Body)
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
data, err := ioutil.ReadAll(reader)
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
if got, want := string(data), "Hello World"; got != want {
|
||||
t.Errorf("got %v want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGzipDecompressRequestBody(t *testing.T) {
|
||||
b := new(bytes.Buffer)
|
||||
w := newGzipWriter()
|
||||
w.Reset(b)
|
||||
io.WriteString(w, `{"msg":"hi"}`)
|
||||
w.Flush()
|
||||
w.Close()
|
||||
|
||||
req := new(Request)
|
||||
httpRequest, _ := http.NewRequest("GET", "/", bytes.NewReader(b.Bytes()))
|
||||
httpRequest.Header.Set("Content-Type", "application/json")
|
||||
httpRequest.Header.Set("Content-Encoding", "gzip")
|
||||
req.Request = httpRequest
|
||||
|
||||
doCacheReadEntityBytes = false
|
||||
doc := make(map[string]interface{})
|
||||
req.ReadEntity(&doc)
|
||||
|
||||
if got, want := doc["msg"], "hi"; got != want {
|
||||
t.Errorf("got %v want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestZlibDecompressRequestBody(t *testing.T) {
|
||||
b := new(bytes.Buffer)
|
||||
w := newZlibWriter()
|
||||
w.Reset(b)
|
||||
io.WriteString(w, `{"msg":"hi"}`)
|
||||
w.Flush()
|
||||
w.Close()
|
||||
|
||||
req := new(Request)
|
||||
httpRequest, _ := http.NewRequest("GET", "/", bytes.NewReader(b.Bytes()))
|
||||
httpRequest.Header.Set("Content-Type", "application/json")
|
||||
httpRequest.Header.Set("Content-Encoding", "deflate")
|
||||
req.Request = httpRequest
|
||||
|
||||
doCacheReadEntityBytes = false
|
||||
doc := make(map[string]interface{})
|
||||
req.ReadEntity(&doc)
|
||||
|
||||
if got, want := doc["msg"], "hi"; got != want {
|
||||
t.Errorf("got %v want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
63
Godeps/_workspace/src/github.com/emicklei/go-restful/compressor_pools.go
generated
vendored
Normal file
63
Godeps/_workspace/src/github.com/emicklei/go-restful/compressor_pools.go
generated
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
package restful
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"compress/zlib"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// GzipWriterPool is used to get reusable zippers.
|
||||
// The Get() result must be type asserted to *gzip.Writer.
|
||||
var GzipWriterPool = &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return newGzipWriter()
|
||||
},
|
||||
}
|
||||
|
||||
func newGzipWriter() *gzip.Writer {
|
||||
// create with an empty bytes writer; it will be replaced before using the gzipWriter
|
||||
writer, err := gzip.NewWriterLevel(new(bytes.Buffer), gzip.BestSpeed)
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
return writer
|
||||
}
|
||||
|
||||
// GzipReaderPool is used to get reusable zippers.
|
||||
// The Get() result must be type asserted to *gzip.Reader.
|
||||
var GzipReaderPool = &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return newGzipReader()
|
||||
},
|
||||
}
|
||||
|
||||
func newGzipReader() *gzip.Reader {
|
||||
// create with an empty reader (but with GZIP header); it will be replaced before using the gzipReader
|
||||
w := GzipWriterPool.Get().(*gzip.Writer)
|
||||
b := new(bytes.Buffer)
|
||||
w.Reset(b)
|
||||
w.Flush()
|
||||
w.Close()
|
||||
reader, err := gzip.NewReader(bytes.NewReader(b.Bytes()))
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
return reader
|
||||
}
|
||||
|
||||
// ZlibWriterPool is used to get reusable zippers.
|
||||
// The Get() result must be type asserted to *zlib.Writer.
|
||||
var ZlibWriterPool = &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return newZlibWriter()
|
||||
},
|
||||
}
|
||||
|
||||
func newZlibWriter() *zlib.Writer {
|
||||
writer, err := zlib.NewWriterLevel(new(bytes.Buffer), gzip.BestSpeed)
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
return writer
|
||||
}
|
38
Godeps/_workspace/src/github.com/emicklei/go-restful/container.go
generated
vendored
38
Godeps/_workspace/src/github.com/emicklei/go-restful/container.go
generated
vendored
@ -11,6 +11,7 @@ import (
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/emicklei/go-restful/log"
|
||||
)
|
||||
@ -18,6 +19,7 @@ import (
|
||||
// Container holds a collection of WebServices and a http.ServeMux to dispatch http requests.
|
||||
// The requests are further dispatched to routes of WebServices using a RouteSelector
|
||||
type Container struct {
|
||||
webServicesLock sync.RWMutex
|
||||
webServices []*WebService
|
||||
ServeMux *http.ServeMux
|
||||
isRegisteredOnRoot bool
|
||||
@ -83,6 +85,8 @@ func (c *Container) EnableContentEncoding(enabled bool) {
|
||||
|
||||
// Add a WebService to the Container. It will detect duplicate root paths and panic in that case.
|
||||
func (c *Container) Add(service *WebService) *Container {
|
||||
c.webServicesLock.Lock()
|
||||
defer c.webServicesLock.Unlock()
|
||||
// If registered on root then no additional specific mapping is needed
|
||||
if !c.isRegisteredOnRoot {
|
||||
pattern := c.fixedPrefixPath(service.RootPath())
|
||||
@ -122,6 +126,19 @@ func (c *Container) Add(service *WebService) *Container {
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Container) Remove(ws *WebService) error {
|
||||
c.webServicesLock.Lock()
|
||||
defer c.webServicesLock.Unlock()
|
||||
newServices := []*WebService{}
|
||||
for ix := range c.webServices {
|
||||
if c.webServices[ix].rootPath != ws.rootPath {
|
||||
newServices = append(newServices, c.webServices[ix])
|
||||
}
|
||||
}
|
||||
c.webServices = newServices
|
||||
return nil
|
||||
}
|
||||
|
||||
// logStackOnRecover is the default RecoverHandleFunction and is called
|
||||
// when DoNotRecover is false and the recoverHandleFunc is not set for the container.
|
||||
// Default implementation logs the stacktrace and writes the stacktrace on the response.
|
||||
@ -190,9 +207,16 @@ func (c *Container) dispatch(httpWriter http.ResponseWriter, httpRequest *http.R
|
||||
}
|
||||
}
|
||||
// Find best match Route ; err is non nil if no match was found
|
||||
webService, route, err := c.router.SelectRoute(
|
||||
c.webServices,
|
||||
httpRequest)
|
||||
var webService *WebService
|
||||
var route *Route
|
||||
var err error
|
||||
func() {
|
||||
c.webServicesLock.RLock()
|
||||
defer c.webServicesLock.RUnlock()
|
||||
webService, route, err = c.router.SelectRoute(
|
||||
c.webServices,
|
||||
httpRequest)
|
||||
}()
|
||||
if err != nil {
|
||||
// a non-200 response has already been written
|
||||
// run container filters anyway ; they should not touch the response...
|
||||
@ -272,7 +296,13 @@ func (c *Container) Filter(filter FilterFunction) {
|
||||
|
||||
// RegisteredWebServices returns the collections of added WebServices
|
||||
func (c Container) RegisteredWebServices() []*WebService {
|
||||
return c.webServices
|
||||
c.webServicesLock.RLock()
|
||||
defer c.webServicesLock.RUnlock()
|
||||
result := make([]*WebService, len(c.webServices))
|
||||
for ix := range c.webServices {
|
||||
result[ix] = c.webServices[ix]
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// computeAllowedMethods returns a list of HTTP methods that are valid for a Request
|
||||
|
8
Godeps/_workspace/src/github.com/emicklei/go-restful/parameter.go
generated
vendored
8
Godeps/_workspace/src/github.com/emicklei/go-restful/parameter.go
generated
vendored
@ -95,8 +95,14 @@ func (p *Parameter) DataType(typeName string) *Parameter {
|
||||
return p
|
||||
}
|
||||
|
||||
// DefaultValue sets the default value field and returnw the receiver
|
||||
// DefaultValue sets the default value field and returns the receiver
|
||||
func (p *Parameter) DefaultValue(stringRepresentation string) *Parameter {
|
||||
p.data.DefaultValue = stringRepresentation
|
||||
return p
|
||||
}
|
||||
|
||||
// Description sets the description value field and returns the receiver
|
||||
func (p *Parameter) Description(doc string) *Parameter {
|
||||
p.data.Description = doc
|
||||
return p
|
||||
}
|
||||
|
42
Godeps/_workspace/src/github.com/emicklei/go-restful/request.go
generated
vendored
42
Godeps/_workspace/src/github.com/emicklei/go-restful/request.go
generated
vendored
@ -6,6 +6,8 @@ package restful
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"compress/zlib"
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"io"
|
||||
@ -82,15 +84,17 @@ func (r *Request) HeaderParameter(name string) string {
|
||||
// ReadEntity checks the Accept header and reads the content into the entityPointer
|
||||
// May be called multiple times in the request-response flow
|
||||
func (r *Request) ReadEntity(entityPointer interface{}) (err error) {
|
||||
defer r.Request.Body.Close()
|
||||
contentType := r.Request.Header.Get(HEADER_ContentType)
|
||||
contentEncoding := r.Request.Header.Get(HEADER_ContentEncoding)
|
||||
if doCacheReadEntityBytes {
|
||||
return r.cachingReadEntity(contentType, entityPointer)
|
||||
return r.cachingReadEntity(contentType, contentEncoding, entityPointer)
|
||||
}
|
||||
// unmarshall directly from request Body
|
||||
return r.decodeEntity(r.Request.Body, contentType, entityPointer)
|
||||
return r.decodeEntity(r.Request.Body, contentType, contentEncoding, entityPointer)
|
||||
}
|
||||
|
||||
func (r *Request) cachingReadEntity(contentType string, entityPointer interface{}) (err error) {
|
||||
func (r *Request) cachingReadEntity(contentType string, contentEncoding string, entityPointer interface{}) (err error) {
|
||||
var buffer []byte
|
||||
if r.bodyContent != nil {
|
||||
buffer = *r.bodyContent
|
||||
@ -101,22 +105,38 @@ func (r *Request) cachingReadEntity(contentType string, entityPointer interface{
|
||||
}
|
||||
r.bodyContent = &buffer
|
||||
}
|
||||
return r.decodeEntity(bytes.NewReader(buffer), contentType, entityPointer)
|
||||
return r.decodeEntity(bytes.NewReader(buffer), contentType, contentEncoding, entityPointer)
|
||||
}
|
||||
|
||||
func (r *Request) decodeEntity(reader io.Reader, contentType string, entityPointer interface{}) (err error) {
|
||||
if strings.Contains(contentType, MIME_XML) {
|
||||
return xml.NewDecoder(reader).Decode(entityPointer)
|
||||
func (r *Request) decodeEntity(reader io.Reader, contentType string, contentEncoding string, entityPointer interface{}) (err error) {
|
||||
entityReader := reader
|
||||
|
||||
// check if the request body needs decompression
|
||||
if ENCODING_GZIP == contentEncoding {
|
||||
gzipReader := GzipReaderPool.Get().(*gzip.Reader)
|
||||
gzipReader.Reset(reader)
|
||||
entityReader = gzipReader
|
||||
} else if ENCODING_DEFLATE == contentEncoding {
|
||||
zlibReader, err := zlib.NewReader(reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
entityReader = zlibReader
|
||||
}
|
||||
|
||||
// decode JSON
|
||||
if strings.Contains(contentType, MIME_JSON) || MIME_JSON == defaultRequestContentType {
|
||||
decoder := json.NewDecoder(reader)
|
||||
decoder := json.NewDecoder(entityReader)
|
||||
decoder.UseNumber()
|
||||
return decoder.Decode(entityPointer)
|
||||
}
|
||||
if MIME_XML == defaultRequestContentType {
|
||||
return xml.NewDecoder(reader).Decode(entityPointer)
|
||||
|
||||
// decode XML
|
||||
if strings.Contains(contentType, MIME_XML) || MIME_XML == defaultRequestContentType {
|
||||
return xml.NewDecoder(entityReader).Decode(entityPointer)
|
||||
}
|
||||
return NewError(400, "Unable to unmarshal content of type:"+contentType)
|
||||
|
||||
return NewError(http.StatusBadRequest, "Unable to unmarshal content of type:"+contentType)
|
||||
}
|
||||
|
||||
// SetAttribute adds or replaces the attribute with the given value.
|
||||
|
30
Godeps/_workspace/src/github.com/emicklei/go-restful/response.go
generated
vendored
30
Godeps/_workspace/src/github.com/emicklei/go-restful/response.go
generated
vendored
@ -28,11 +28,12 @@ type Response struct {
|
||||
statusCode int // HTTP status code that has been written explicity (if zero then net/http has written 200)
|
||||
contentLength int // number of bytes written for the response body
|
||||
prettyPrint bool // controls the indentation feature of XML and JSON serialization. It is initialized using var PrettyPrintResponses.
|
||||
err error // err property is kept when WriteError is called
|
||||
}
|
||||
|
||||
// Creates a new response based on a http ResponseWriter.
|
||||
func NewResponse(httpWriter http.ResponseWriter) *Response {
|
||||
return &Response{httpWriter, "", []string{}, http.StatusOK, 0, PrettyPrintResponses} // empty content-types
|
||||
return &Response{httpWriter, "", []string{}, http.StatusOK, 0, PrettyPrintResponses, nil} // empty content-types
|
||||
}
|
||||
|
||||
// If Accept header matching fails, fall back to this type, otherwise
|
||||
@ -182,6 +183,7 @@ func (r *Response) WriteJson(value interface{}, contentType string) error {
|
||||
|
||||
// WriteError write the http status and the error string on the response.
|
||||
func (r *Response) WriteError(httpStatus int, err error) error {
|
||||
r.err = err
|
||||
return r.WriteErrorString(httpStatus, err.Error())
|
||||
}
|
||||
|
||||
@ -203,21 +205,30 @@ func (r *Response) WriteErrorString(status int, errorReason string) error {
|
||||
|
||||
// WriteHeader is overridden to remember the Status Code that has been written.
|
||||
// Note that using this method, the status value is only written when
|
||||
// - calling WriteEntity,
|
||||
// - or directly calling WriteAsXml or WriteAsJson,
|
||||
// - or if the status is one for which no response is allowed (i.e.,
|
||||
// 204 (http.StatusNoContent) or 304 (http.StatusNotModified))
|
||||
// calling WriteEntity,
|
||||
// or directly calling WriteAsXml or WriteAsJson,
|
||||
// or if the status is one for which no response is allowed:
|
||||
//
|
||||
// 202 = http.StatusAccepted
|
||||
// 204 = http.StatusNoContent
|
||||
// 206 = http.StatusPartialContent
|
||||
// 304 = http.StatusNotModified
|
||||
//
|
||||
// If this behavior does not fit your need then you can write to the underlying response, such as:
|
||||
// response.ResponseWriter.WriteHeader(http.StatusAccepted)
|
||||
func (r *Response) WriteHeader(httpStatus int) {
|
||||
r.statusCode = httpStatus
|
||||
// if 201,204,304 then WriteEntity will not be called so we need to pass this code
|
||||
// if 202,204,206,304 then WriteEntity will not be called so we need to pass this code
|
||||
if http.StatusNoContent == httpStatus ||
|
||||
http.StatusNotModified == httpStatus ||
|
||||
http.StatusPartialContent == httpStatus {
|
||||
http.StatusPartialContent == httpStatus ||
|
||||
http.StatusAccepted == httpStatus {
|
||||
r.ResponseWriter.WriteHeader(httpStatus)
|
||||
}
|
||||
}
|
||||
|
||||
// StatusCode returns the code that has been written using WriteHeader.
|
||||
// If WriteHeader, WriteEntity or WriteAsXml has not been called (yet) then return 200 OK.
|
||||
func (r Response) StatusCode() int {
|
||||
if 0 == r.statusCode {
|
||||
// no status code has been written yet; assume OK
|
||||
@ -245,3 +256,8 @@ func (r Response) ContentLength() int {
|
||||
func (r Response) CloseNotify() <-chan bool {
|
||||
return r.ResponseWriter.(http.CloseNotifier).CloseNotify()
|
||||
}
|
||||
|
||||
// Error returns the err created by WriteError
|
||||
func (r Response) Error() error {
|
||||
return r.err
|
||||
}
|
||||
|
28
Godeps/_workspace/src/github.com/emicklei/go-restful/response_test.go
generated
vendored
28
Godeps/_workspace/src/github.com/emicklei/go-restful/response_test.go
generated
vendored
@ -9,7 +9,7 @@ import (
|
||||
|
||||
func TestWriteHeader(t *testing.T) {
|
||||
httpWriter := httptest.NewRecorder()
|
||||
resp := Response{httpWriter, "*/*", []string{"*/*"}, 0, 0, true}
|
||||
resp := Response{httpWriter, "*/*", []string{"*/*"}, 0, 0, true, nil}
|
||||
resp.WriteHeader(123)
|
||||
if resp.StatusCode() != 123 {
|
||||
t.Errorf("Unexpected status code:%d", resp.StatusCode())
|
||||
@ -18,7 +18,7 @@ func TestWriteHeader(t *testing.T) {
|
||||
|
||||
func TestNoWriteHeader(t *testing.T) {
|
||||
httpWriter := httptest.NewRecorder()
|
||||
resp := Response{httpWriter, "*/*", []string{"*/*"}, 0, 0, true}
|
||||
resp := Response{httpWriter, "*/*", []string{"*/*"}, 0, 0, true, nil}
|
||||
if resp.StatusCode() != http.StatusOK {
|
||||
t.Errorf("Unexpected status code:%d", resp.StatusCode())
|
||||
}
|
||||
@ -31,7 +31,7 @@ type food struct {
|
||||
// go test -v -test.run TestMeasureContentLengthXml ...restful
|
||||
func TestMeasureContentLengthXml(t *testing.T) {
|
||||
httpWriter := httptest.NewRecorder()
|
||||
resp := Response{httpWriter, "*/*", []string{"*/*"}, 0, 0, true}
|
||||
resp := Response{httpWriter, "*/*", []string{"*/*"}, 0, 0, true, nil}
|
||||
resp.WriteAsXml(food{"apple"})
|
||||
if resp.ContentLength() != 76 {
|
||||
t.Errorf("Incorrect measured length:%d", resp.ContentLength())
|
||||
@ -41,7 +41,7 @@ func TestMeasureContentLengthXml(t *testing.T) {
|
||||
// go test -v -test.run TestMeasureContentLengthJson ...restful
|
||||
func TestMeasureContentLengthJson(t *testing.T) {
|
||||
httpWriter := httptest.NewRecorder()
|
||||
resp := Response{httpWriter, "*/*", []string{"*/*"}, 0, 0, true}
|
||||
resp := Response{httpWriter, "*/*", []string{"*/*"}, 0, 0, true, nil}
|
||||
resp.WriteAsJson(food{"apple"})
|
||||
if resp.ContentLength() != 22 {
|
||||
t.Errorf("Incorrect measured length:%d", resp.ContentLength())
|
||||
@ -51,7 +51,7 @@ func TestMeasureContentLengthJson(t *testing.T) {
|
||||
// go test -v -test.run TestMeasureContentLengthJsonNotPretty ...restful
|
||||
func TestMeasureContentLengthJsonNotPretty(t *testing.T) {
|
||||
httpWriter := httptest.NewRecorder()
|
||||
resp := Response{httpWriter, "*/*", []string{"*/*"}, 0, 0, false}
|
||||
resp := Response{httpWriter, "*/*", []string{"*/*"}, 0, 0, false, nil}
|
||||
resp.WriteAsJson(food{"apple"})
|
||||
if resp.ContentLength() != 16 {
|
||||
t.Errorf("Incorrect measured length:%d", resp.ContentLength())
|
||||
@ -61,7 +61,7 @@ func TestMeasureContentLengthJsonNotPretty(t *testing.T) {
|
||||
// go test -v -test.run TestMeasureContentLengthWriteErrorString ...restful
|
||||
func TestMeasureContentLengthWriteErrorString(t *testing.T) {
|
||||
httpWriter := httptest.NewRecorder()
|
||||
resp := Response{httpWriter, "*/*", []string{"*/*"}, 0, 0, true}
|
||||
resp := Response{httpWriter, "*/*", []string{"*/*"}, 0, 0, true, nil}
|
||||
resp.WriteErrorString(404, "Invalid")
|
||||
if resp.ContentLength() != len("Invalid") {
|
||||
t.Errorf("Incorrect measured length:%d", resp.ContentLength())
|
||||
@ -79,7 +79,7 @@ func TestStatusIsPassedToResponse(t *testing.T) {
|
||||
{write: 400, read: 200},
|
||||
} {
|
||||
httpWriter := httptest.NewRecorder()
|
||||
resp := Response{httpWriter, "*/*", []string{"*/*"}, 0, 0, true}
|
||||
resp := Response{httpWriter, "*/*", []string{"*/*"}, 0, 0, true, nil}
|
||||
resp.WriteHeader(each.write)
|
||||
if got, want := httpWriter.Code, each.read; got != want {
|
||||
t.Error("got %v want %v", got, want)
|
||||
@ -90,7 +90,7 @@ func TestStatusIsPassedToResponse(t *testing.T) {
|
||||
// go test -v -test.run TestStatusCreatedAndContentTypeJson_Issue54 ...restful
|
||||
func TestStatusCreatedAndContentTypeJson_Issue54(t *testing.T) {
|
||||
httpWriter := httptest.NewRecorder()
|
||||
resp := Response{httpWriter, "application/json", []string{"application/json"}, 0, 0, true}
|
||||
resp := Response{httpWriter, "application/json", []string{"application/json"}, 0, 0, true, nil}
|
||||
resp.WriteHeader(201)
|
||||
resp.WriteAsJson(food{"Juicy"})
|
||||
if httpWriter.HeaderMap.Get("Content-Type") != "application/json" {
|
||||
@ -112,7 +112,7 @@ func (e errorOnWriteRecorder) Write(bytes []byte) (int, error) {
|
||||
// go test -v -test.run TestLastWriteErrorCaught ...restful
|
||||
func TestLastWriteErrorCaught(t *testing.T) {
|
||||
httpWriter := errorOnWriteRecorder{httptest.NewRecorder()}
|
||||
resp := Response{httpWriter, "application/json", []string{"application/json"}, 0, 0, true}
|
||||
resp := Response{httpWriter, "application/json", []string{"application/json"}, 0, 0, true, nil}
|
||||
err := resp.WriteAsJson(food{"Juicy"})
|
||||
if err.Error() != "fail" {
|
||||
t.Errorf("Unexpected error message:%v", err)
|
||||
@ -123,7 +123,7 @@ func TestLastWriteErrorCaught(t *testing.T) {
|
||||
func TestAcceptStarStar_Issue83(t *testing.T) {
|
||||
httpWriter := httptest.NewRecorder()
|
||||
// Accept Produces
|
||||
resp := Response{httpWriter, "application/bogus,*/*;q=0.8", []string{"application/json"}, 0, 0, true}
|
||||
resp := Response{httpWriter, "application/bogus,*/*;q=0.8", []string{"application/json"}, 0, 0, true, nil}
|
||||
resp.WriteEntity(food{"Juicy"})
|
||||
ct := httpWriter.Header().Get("Content-Type")
|
||||
if "application/json" != ct {
|
||||
@ -135,7 +135,7 @@ func TestAcceptStarStar_Issue83(t *testing.T) {
|
||||
func TestAcceptSkipStarStar_Issue83(t *testing.T) {
|
||||
httpWriter := httptest.NewRecorder()
|
||||
// Accept Produces
|
||||
resp := Response{httpWriter, " application/xml ,*/* ; q=0.8", []string{"application/json", "application/xml"}, 0, 0, true}
|
||||
resp := Response{httpWriter, " application/xml ,*/* ; q=0.8", []string{"application/json", "application/xml"}, 0, 0, true, nil}
|
||||
resp.WriteEntity(food{"Juicy"})
|
||||
ct := httpWriter.Header().Get("Content-Type")
|
||||
if "application/xml" != ct {
|
||||
@ -147,7 +147,7 @@ func TestAcceptSkipStarStar_Issue83(t *testing.T) {
|
||||
func TestAcceptXmlBeforeStarStar_Issue83(t *testing.T) {
|
||||
httpWriter := httptest.NewRecorder()
|
||||
// Accept Produces
|
||||
resp := Response{httpWriter, "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", []string{"application/json"}, 0, 0, true}
|
||||
resp := Response{httpWriter, "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", []string{"application/json"}, 0, 0, true, nil}
|
||||
resp.WriteEntity(food{"Juicy"})
|
||||
ct := httpWriter.Header().Get("Content-Type")
|
||||
if "application/json" != ct {
|
||||
@ -158,7 +158,7 @@ func TestAcceptXmlBeforeStarStar_Issue83(t *testing.T) {
|
||||
// go test -v -test.run TestWriteHeaderNoContent_Issue124 ...restful
|
||||
func TestWriteHeaderNoContent_Issue124(t *testing.T) {
|
||||
httpWriter := httptest.NewRecorder()
|
||||
resp := Response{httpWriter, "text/plain", []string{"text/plain"}, 0, 0, true}
|
||||
resp := Response{httpWriter, "text/plain", []string{"text/plain"}, 0, 0, true, nil}
|
||||
resp.WriteHeader(http.StatusNoContent)
|
||||
if httpWriter.Code != http.StatusNoContent {
|
||||
t.Errorf("got %d want %d", httpWriter.Code, http.StatusNoContent)
|
||||
@ -168,7 +168,7 @@ func TestWriteHeaderNoContent_Issue124(t *testing.T) {
|
||||
// go test -v -test.run TestStatusCreatedAndContentTypeJson_Issue163 ...restful
|
||||
func TestStatusCreatedAndContentTypeJson_Issue163(t *testing.T) {
|
||||
httpWriter := httptest.NewRecorder()
|
||||
resp := Response{httpWriter, "application/json", []string{"application/json"}, 0, 0, true}
|
||||
resp := Response{httpWriter, "application/json", []string{"application/json"}, 0, 0, true, nil}
|
||||
resp.WriteHeader(http.StatusNotModified)
|
||||
if httpWriter.Code != http.StatusNotModified {
|
||||
t.Errorf("Got %d want %d", httpWriter.Code, http.StatusNotModified)
|
||||
|
9
Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/swagger_webservice.go
generated
vendored
9
Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/swagger_webservice.go
generated
vendored
@ -173,7 +173,14 @@ func (sws SwaggerService) getDeclarations(req *restful.Request, resp *restful.Re
|
||||
} else {
|
||||
host = hostvalues[0]
|
||||
}
|
||||
(&decl).BasePath = fmt.Sprintf("http://%s", host)
|
||||
// inspect Referer for the scheme (http vs https)
|
||||
scheme := "http"
|
||||
if referer := req.Request.Header["Referer"]; len(referer) > 0 {
|
||||
if strings.HasPrefix(referer[0], "https") {
|
||||
scheme = "https"
|
||||
}
|
||||
}
|
||||
(&decl).BasePath = fmt.Sprintf("%s://%s", scheme, host)
|
||||
}
|
||||
resp.WriteAsJson(decl)
|
||||
}
|
||||
|
1
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/.travis.yml
generated
vendored
1
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/.travis.yml
generated
vendored
@ -3,6 +3,7 @@ sudo: false
|
||||
go:
|
||||
- 1.3.1
|
||||
- 1.4
|
||||
- 1.5
|
||||
- tip
|
||||
env:
|
||||
- GOARCH=amd64
|
||||
|
4
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/AUTHORS
generated
vendored
4
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/AUTHORS
generated
vendored
@ -13,6 +13,7 @@ Brian Lalor <blalor@bravo5.org>
|
||||
Brian Palmer <brianp@instructure.com>
|
||||
Burke Libbey <burke@libbey.me>
|
||||
Carlos Diaz-Padron <cpadron@mozilla.com>
|
||||
Cesar Wong <cewong@redhat.com>
|
||||
Cezar Sa Espinola <cezar.sa@corp.globo.com>
|
||||
Cheah Chu Yeow <chuyeow@gmail.com>
|
||||
cheneydeng <cheneydeng@qq.com>
|
||||
@ -33,6 +34,7 @@ Fabio Rehm <fgrehm@gmail.com>
|
||||
Fatih Arslan <ftharsln@gmail.com>
|
||||
Flavia Missi <flaviamissi@gmail.com>
|
||||
Francisco Souza <f@souza.cc>
|
||||
Grégoire Delattre <gregoire.delattre@gmail.com>
|
||||
Guillermo Álvarez Fernández <guillermo@cientifico.net>
|
||||
He Simei <hesimei@zju.edu.cn>
|
||||
Ivan Mikushin <i.mikushin@gmail.com>
|
||||
@ -66,12 +68,14 @@ Paul Morie <pmorie@gmail.com>
|
||||
Paul Weil <pweil@redhat.com>
|
||||
Peter Edge <peter.edge@gmail.com>
|
||||
Peter Jihoon Kim <raingrove@gmail.com>
|
||||
Phil Lu <lu@stackengine.com>
|
||||
Philippe Lafoucrière <philippe.lafoucriere@tech-angels.com>
|
||||
Rafe Colton <rafael.colton@gmail.com>
|
||||
Rob Miller <rob@kalistra.com>
|
||||
Robert Williamson <williamson.robert@gmail.com>
|
||||
Salvador Gironès <salvadorgirones@gmail.com>
|
||||
Sam Rijs <srijs@airpost.net>
|
||||
Samuel Karp <skarp@amazon.com>
|
||||
Simon Eskildsen <sirup@sirupsen.com>
|
||||
Simon Menke <simon.menke@gmail.com>
|
||||
Skolos <skolos@gopherlab.com>
|
||||
|
11
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/auth.go
generated
vendored
11
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/auth.go
generated
vendored
@ -16,7 +16,8 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
var AuthParseError error = errors.New("Failed to read authentication from dockercfg")
|
||||
// ErrCannotParseDockercfg is the error returned by NewAuthConfigurations when the dockercfg cannot be parsed.
|
||||
var ErrCannotParseDockercfg = errors.New("Failed to read authentication from dockercfg")
|
||||
|
||||
// AuthConfiguration represents authentication options to use in the PushImage
|
||||
// method. It represents the authentication in the Docker index server.
|
||||
@ -33,6 +34,10 @@ type AuthConfigurations struct {
|
||||
Configs map[string]AuthConfiguration `json:"configs"`
|
||||
}
|
||||
|
||||
// AuthConfigurations119 is used to serialize a set of AuthConfigurations
|
||||
// for Docker API >= 1.19.
|
||||
type AuthConfigurations119 map[string]AuthConfiguration
|
||||
|
||||
// dockerConfig represents a registry authentation configuration from the
|
||||
// .dockercfg file.
|
||||
type dockerConfig struct {
|
||||
@ -103,7 +108,7 @@ func authConfigs(confs map[string]dockerConfig) (*AuthConfigurations, error) {
|
||||
}
|
||||
userpass := strings.Split(string(data), ":")
|
||||
if len(userpass) != 2 {
|
||||
return nil, AuthParseError
|
||||
return nil, ErrCannotParseDockercfg
|
||||
}
|
||||
c.Configs[reg] = AuthConfiguration{
|
||||
Email: conf.Email,
|
||||
@ -117,7 +122,7 @@ func authConfigs(confs map[string]dockerConfig) (*AuthConfigurations, error) {
|
||||
|
||||
// AuthCheck validates the given credentials. It returns nil if successful.
|
||||
//
|
||||
// See https://goo.gl/vPoEfJ for more details.
|
||||
// See https://goo.gl/m2SleN for more details.
|
||||
func (c *Client) AuthCheck(conf *AuthConfiguration) error {
|
||||
if conf == nil {
|
||||
return fmt.Errorf("conf is nil")
|
||||
|
2
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/auth_test.go
generated
vendored
2
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/auth_test.go
generated
vendored
@ -41,7 +41,7 @@ func TestAuthBadConfig(t *testing.T) {
|
||||
auth := base64.StdEncoding.EncodeToString([]byte("userpass"))
|
||||
read := strings.NewReader(fmt.Sprintf(`{"docker.io":{"auth":"%s","email":"user@example.com"}}`, auth))
|
||||
ac, err := NewAuthConfigurations(read)
|
||||
if err != AuthParseError {
|
||||
if err != ErrCannotParseDockercfg {
|
||||
t.Errorf("Incorrect error returned %v\n", err)
|
||||
}
|
||||
if ac != nil {
|
||||
|
2
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/change.go
generated
vendored
2
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/change.go
generated
vendored
@ -23,7 +23,7 @@ const (
|
||||
|
||||
// Change represents a change in a container.
|
||||
//
|
||||
// See http://goo.gl/QkW9sH for more details.
|
||||
// See https://goo.gl/9GsTIF for more details.
|
||||
type Change struct {
|
||||
Path string
|
||||
Kind ChangeType
|
||||
|
31
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/client.go
generated
vendored
31
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/client.go
generated
vendored
@ -4,7 +4,7 @@
|
||||
|
||||
// Package docker provides a client for the Docker remote API.
|
||||
//
|
||||
// See http://goo.gl/G3plxW for more details on the remote API.
|
||||
// See https://goo.gl/G3plxW for more details on the remote API.
|
||||
package docker
|
||||
|
||||
import (
|
||||
@ -45,6 +45,8 @@ var (
|
||||
ErrConnectionRefused = errors.New("cannot connect to Docker endpoint")
|
||||
|
||||
apiVersion112, _ = NewAPIVersion("1.12")
|
||||
|
||||
apiVersion119, _ = NewAPIVersion("1.19")
|
||||
)
|
||||
|
||||
// APIVersion is an internal representation of a version of the Remote API.
|
||||
@ -326,7 +328,7 @@ func (c *Client) checkAPIVersion() error {
|
||||
|
||||
// Ping pings the docker server
|
||||
//
|
||||
// See http://goo.gl/stJENm for more details.
|
||||
// See https://goo.gl/kQCfJj for more details.
|
||||
func (c *Client) Ping() error {
|
||||
path := "/_ping"
|
||||
body, status, err := c.do("GET", path, doOptions{})
|
||||
@ -462,9 +464,13 @@ func (c *Client) stream(method, path string, streamOptions streamOptions) error
|
||||
address := c.endpointURL.Path
|
||||
if streamOptions.stdout == nil {
|
||||
streamOptions.stdout = ioutil.Discard
|
||||
} else if t, ok := streamOptions.stdout.(io.Closer); ok {
|
||||
defer t.Close()
|
||||
}
|
||||
if streamOptions.stderr == nil {
|
||||
streamOptions.stderr = ioutil.Discard
|
||||
} else if t, ok := streamOptions.stderr.(io.Closer); ok {
|
||||
defer t.Close()
|
||||
}
|
||||
if protocol == "unix" {
|
||||
dial, err := net.Dial(protocol, address)
|
||||
@ -583,6 +589,8 @@ func (c *Client) hijack(method, path string, hijackOptions hijackOptions) error
|
||||
return err
|
||||
}
|
||||
req.Header.Set("Content-Type", "plain/text")
|
||||
req.Header.Set("Connection", "Upgrade")
|
||||
req.Header.Set("Upgrade", "tcp")
|
||||
protocol := c.endpointURL.Scheme
|
||||
address := c.endpointURL.Path
|
||||
if protocol != "unix" {
|
||||
@ -612,13 +620,16 @@ func (c *Client) hijack(method, path string, hijackOptions hijackOptions) error
|
||||
defer rwc.Close()
|
||||
errChanOut := make(chan error, 1)
|
||||
errChanIn := make(chan error, 1)
|
||||
exit := make(chan bool)
|
||||
go func() {
|
||||
defer close(exit)
|
||||
defer close(errChanOut)
|
||||
defer func() {
|
||||
if hijackOptions.in != nil {
|
||||
if closer, ok := hijackOptions.in.(io.Closer); ok {
|
||||
closer.Close()
|
||||
}
|
||||
}
|
||||
}()
|
||||
var err error
|
||||
if hijackOptions.setRawTerminal {
|
||||
// When TTY is ON, use regular copy
|
||||
_, err = io.Copy(hijackOptions.stdout, br)
|
||||
} else {
|
||||
_, err = stdcopy.StdCopy(hijackOptions.stdout, hijackOptions.stderr, br)
|
||||
@ -626,17 +637,15 @@ func (c *Client) hijack(method, path string, hijackOptions hijackOptions) error
|
||||
errChanOut <- err
|
||||
}()
|
||||
go func() {
|
||||
var err error
|
||||
if hijackOptions.in != nil {
|
||||
_, err := io.Copy(rwc, hijackOptions.in)
|
||||
errChanIn <- err
|
||||
} else {
|
||||
errChanIn <- nil
|
||||
_, err = io.Copy(rwc, hijackOptions.in)
|
||||
}
|
||||
errChanIn <- err
|
||||
rwc.(interface {
|
||||
CloseWrite() error
|
||||
}).CloseWrite()
|
||||
}()
|
||||
<-exit
|
||||
errIn := <-errChanIn
|
||||
errOut := <-errChanOut
|
||||
if errIn != nil {
|
||||
|
175
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/container.go
generated
vendored
175
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/container.go
generated
vendored
@ -23,7 +23,7 @@ var ErrContainerAlreadyExists = errors.New("container already exists")
|
||||
|
||||
// ListContainersOptions specify parameters to the ListContainers function.
|
||||
//
|
||||
// See http://goo.gl/6Y4Gz7 for more details.
|
||||
// See https://goo.gl/47a6tO for more details.
|
||||
type ListContainersOptions struct {
|
||||
All bool
|
||||
Size bool
|
||||
@ -41,24 +41,24 @@ type APIPort struct {
|
||||
IP string `json:"IP,omitempty" yaml:"IP,omitempty"`
|
||||
}
|
||||
|
||||
// APIContainers represents a container.
|
||||
//
|
||||
// See http://goo.gl/QeFH7U for more details.
|
||||
// APIContainers represents each container in the list returned by
|
||||
// ListContainers.
|
||||
type APIContainers struct {
|
||||
ID string `json:"Id" yaml:"Id"`
|
||||
Image string `json:"Image,omitempty" yaml:"Image,omitempty"`
|
||||
Command string `json:"Command,omitempty" yaml:"Command,omitempty"`
|
||||
Created int64 `json:"Created,omitempty" yaml:"Created,omitempty"`
|
||||
Status string `json:"Status,omitempty" yaml:"Status,omitempty"`
|
||||
Ports []APIPort `json:"Ports,omitempty" yaml:"Ports,omitempty"`
|
||||
SizeRw int64 `json:"SizeRw,omitempty" yaml:"SizeRw,omitempty"`
|
||||
SizeRootFs int64 `json:"SizeRootFs,omitempty" yaml:"SizeRootFs,omitempty"`
|
||||
Names []string `json:"Names,omitempty" yaml:"Names,omitempty"`
|
||||
ID string `json:"Id" yaml:"Id"`
|
||||
Image string `json:"Image,omitempty" yaml:"Image,omitempty"`
|
||||
Command string `json:"Command,omitempty" yaml:"Command,omitempty"`
|
||||
Created int64 `json:"Created,omitempty" yaml:"Created,omitempty"`
|
||||
Status string `json:"Status,omitempty" yaml:"Status,omitempty"`
|
||||
Ports []APIPort `json:"Ports,omitempty" yaml:"Ports,omitempty"`
|
||||
SizeRw int64 `json:"SizeRw,omitempty" yaml:"SizeRw,omitempty"`
|
||||
SizeRootFs int64 `json:"SizeRootFs,omitempty" yaml:"SizeRootFs,omitempty"`
|
||||
Names []string `json:"Names,omitempty" yaml:"Names,omitempty"`
|
||||
Labels map[string]string `json:"Labels,omitempty" yaml:"Labels, omitempty"`
|
||||
}
|
||||
|
||||
// ListContainers returns a slice of containers matching the given criteria.
|
||||
//
|
||||
// See http://goo.gl/6Y4Gz7 for more details.
|
||||
// See https://goo.gl/47a6tO for more details.
|
||||
func (c *Client) ListContainers(opts ListContainersOptions) ([]APIContainers, error) {
|
||||
path := "/containers/json?" + queryString(opts)
|
||||
body, _, err := c.do("GET", path, doOptions{})
|
||||
@ -213,9 +213,21 @@ type Config struct {
|
||||
NetworkDisabled bool `json:"NetworkDisabled,omitempty" yaml:"NetworkDisabled,omitempty"`
|
||||
SecurityOpts []string `json:"SecurityOpts,omitempty" yaml:"SecurityOpts,omitempty"`
|
||||
OnBuild []string `json:"OnBuild,omitempty" yaml:"OnBuild,omitempty"`
|
||||
Mounts []Mount `json:"Mounts,omitempty" yaml:"Mounts,omitempty"`
|
||||
Labels map[string]string `json:"Labels,omitempty" yaml:"Labels,omitempty"`
|
||||
}
|
||||
|
||||
// Mount represents a mount point in the container.
|
||||
//
|
||||
// It has been added in the version 1.20 of the Docker API, available since
|
||||
// Docker 1.8.
|
||||
type Mount struct {
|
||||
Source string
|
||||
Destination string
|
||||
Mode string
|
||||
RW bool
|
||||
}
|
||||
|
||||
// LogConfig defines the log driver type and the configuration for it.
|
||||
type LogConfig struct {
|
||||
Type string `json:"Type,omitempty" yaml:"Type,omitempty"`
|
||||
@ -279,7 +291,7 @@ type Container struct {
|
||||
|
||||
// RenameContainerOptions specify parameters to the RenameContainer function.
|
||||
//
|
||||
// See http://goo.gl/L00hoj for more details.
|
||||
// See https://goo.gl/laSOIy for more details.
|
||||
type RenameContainerOptions struct {
|
||||
// ID of container to rename
|
||||
ID string `qs:"-"`
|
||||
@ -290,7 +302,7 @@ type RenameContainerOptions struct {
|
||||
|
||||
// RenameContainer updates and existing containers name
|
||||
//
|
||||
// See http://goo.gl/L00hoj for more details.
|
||||
// See https://goo.gl/laSOIy for more details.
|
||||
func (c *Client) RenameContainer(opts RenameContainerOptions) error {
|
||||
_, _, err := c.do("POST", fmt.Sprintf("/containers/"+opts.ID+"/rename?%s", queryString(opts)), doOptions{})
|
||||
return err
|
||||
@ -298,7 +310,7 @@ func (c *Client) RenameContainer(opts RenameContainerOptions) error {
|
||||
|
||||
// InspectContainer returns information about a container by its ID.
|
||||
//
|
||||
// See http://goo.gl/CxVuJ5 for more details.
|
||||
// See https://goo.gl/RdIq0b for more details.
|
||||
func (c *Client) InspectContainer(id string) (*Container, error) {
|
||||
path := "/containers/" + id + "/json"
|
||||
body, status, err := c.do("GET", path, doOptions{})
|
||||
@ -318,7 +330,7 @@ func (c *Client) InspectContainer(id string) (*Container, error) {
|
||||
|
||||
// ContainerChanges returns changes in the filesystem of the given container.
|
||||
//
|
||||
// See http://goo.gl/QkW9sH for more details.
|
||||
// See https://goo.gl/9GsTIF for more details.
|
||||
func (c *Client) ContainerChanges(id string) ([]Change, error) {
|
||||
path := "/containers/" + id + "/changes"
|
||||
body, status, err := c.do("GET", path, doOptions{})
|
||||
@ -338,7 +350,7 @@ func (c *Client) ContainerChanges(id string) ([]Change, error) {
|
||||
|
||||
// CreateContainerOptions specify parameters to the CreateContainer function.
|
||||
//
|
||||
// See http://goo.gl/2xxQQK for more details.
|
||||
// See https://goo.gl/WxQzrr for more details.
|
||||
type CreateContainerOptions struct {
|
||||
Name string
|
||||
Config *Config `qs:"-"`
|
||||
@ -348,7 +360,7 @@ type CreateContainerOptions struct {
|
||||
// CreateContainer creates a new container, returning the container instance,
|
||||
// or an error in case of failure.
|
||||
//
|
||||
// See http://goo.gl/mErxNp for more details.
|
||||
// See https://goo.gl/WxQzrr for more details.
|
||||
func (c *Client) CreateContainer(opts CreateContainerOptions) (*Container, error) {
|
||||
path := "/containers/create?" + queryString(opts)
|
||||
body, status, err := c.do(
|
||||
@ -434,41 +446,46 @@ type Device struct {
|
||||
// HostConfig contains the container options related to starting a container on
|
||||
// a given host
|
||||
type HostConfig struct {
|
||||
Binds []string `json:"Binds,omitempty" yaml:"Binds,omitempty"`
|
||||
CapAdd []string `json:"CapAdd,omitempty" yaml:"CapAdd,omitempty"`
|
||||
CapDrop []string `json:"CapDrop,omitempty" yaml:"CapDrop,omitempty"`
|
||||
ContainerIDFile string `json:"ContainerIDFile,omitempty" yaml:"ContainerIDFile,omitempty"`
|
||||
LxcConf []KeyValuePair `json:"LxcConf,omitempty" yaml:"LxcConf,omitempty"`
|
||||
Privileged bool `json:"Privileged,omitempty" yaml:"Privileged,omitempty"`
|
||||
PortBindings map[Port][]PortBinding `json:"PortBindings,omitempty" yaml:"PortBindings,omitempty"`
|
||||
Links []string `json:"Links,omitempty" yaml:"Links,omitempty"`
|
||||
PublishAllPorts bool `json:"PublishAllPorts,omitempty" yaml:"PublishAllPorts,omitempty"`
|
||||
DNS []string `json:"Dns,omitempty" yaml:"Dns,omitempty"` // For Docker API v1.10 and above only
|
||||
DNSSearch []string `json:"DnsSearch,omitempty" yaml:"DnsSearch,omitempty"`
|
||||
ExtraHosts []string `json:"ExtraHosts,omitempty" yaml:"ExtraHosts,omitempty"`
|
||||
VolumesFrom []string `json:"VolumesFrom,omitempty" yaml:"VolumesFrom,omitempty"`
|
||||
NetworkMode string `json:"NetworkMode,omitempty" yaml:"NetworkMode,omitempty"`
|
||||
IpcMode string `json:"IpcMode,omitempty" yaml:"IpcMode,omitempty"`
|
||||
PidMode string `json:"PidMode,omitempty" yaml:"PidMode,omitempty"`
|
||||
UTSMode string `json:"UTSMode,omitempty" yaml:"UTSMode,omitempty"`
|
||||
RestartPolicy RestartPolicy `json:"RestartPolicy,omitempty" yaml:"RestartPolicy,omitempty"`
|
||||
Devices []Device `json:"Devices,omitempty" yaml:"Devices,omitempty"`
|
||||
LogConfig LogConfig `json:"LogConfig,omitempty" yaml:"LogConfig,omitempty"`
|
||||
ReadonlyRootfs bool `json:"ReadonlyRootfs,omitempty" yaml:"ReadonlyRootfs,omitempty"`
|
||||
SecurityOpt []string `json:"SecurityOpt,omitempty" yaml:"SecurityOpt,omitempty"`
|
||||
CgroupParent string `json:"CgroupParent,omitempty" yaml:"CgroupParent,omitempty"`
|
||||
Memory int64 `json:"Memory,omitempty" yaml:"Memory,omitempty"`
|
||||
MemorySwap int64 `json:"MemorySwap,omitempty" yaml:"MemorySwap,omitempty"`
|
||||
CPUShares int64 `json:"CpuShares,omitempty" yaml:"CpuShares,omitempty"`
|
||||
CPUSet string `json:"Cpuset,omitempty" yaml:"Cpuset,omitempty"`
|
||||
CPUQuota int64 `json:"CpuQuota,omitempty" yaml:"CpuQuota,omitempty"`
|
||||
CPUPeriod int64 `json:"CpuPeriod,omitempty" yaml:"CpuPeriod,omitempty"`
|
||||
Ulimits []ULimit `json:"Ulimits,omitempty" yaml:"Ulimits,omitempty"`
|
||||
Binds []string `json:"Binds,omitempty" yaml:"Binds,omitempty"`
|
||||
CapAdd []string `json:"CapAdd,omitempty" yaml:"CapAdd,omitempty"`
|
||||
CapDrop []string `json:"CapDrop,omitempty" yaml:"CapDrop,omitempty"`
|
||||
ContainerIDFile string `json:"ContainerIDFile,omitempty" yaml:"ContainerIDFile,omitempty"`
|
||||
LxcConf []KeyValuePair `json:"LxcConf,omitempty" yaml:"LxcConf,omitempty"`
|
||||
Privileged bool `json:"Privileged,omitempty" yaml:"Privileged,omitempty"`
|
||||
PortBindings map[Port][]PortBinding `json:"PortBindings,omitempty" yaml:"PortBindings,omitempty"`
|
||||
Links []string `json:"Links,omitempty" yaml:"Links,omitempty"`
|
||||
PublishAllPorts bool `json:"PublishAllPorts,omitempty" yaml:"PublishAllPorts,omitempty"`
|
||||
DNS []string `json:"Dns,omitempty" yaml:"Dns,omitempty"` // For Docker API v1.10 and above only
|
||||
DNSSearch []string `json:"DnsSearch,omitempty" yaml:"DnsSearch,omitempty"`
|
||||
ExtraHosts []string `json:"ExtraHosts,omitempty" yaml:"ExtraHosts,omitempty"`
|
||||
VolumesFrom []string `json:"VolumesFrom,omitempty" yaml:"VolumesFrom,omitempty"`
|
||||
NetworkMode string `json:"NetworkMode,omitempty" yaml:"NetworkMode,omitempty"`
|
||||
IpcMode string `json:"IpcMode,omitempty" yaml:"IpcMode,omitempty"`
|
||||
PidMode string `json:"PidMode,omitempty" yaml:"PidMode,omitempty"`
|
||||
UTSMode string `json:"UTSMode,omitempty" yaml:"UTSMode,omitempty"`
|
||||
RestartPolicy RestartPolicy `json:"RestartPolicy,omitempty" yaml:"RestartPolicy,omitempty"`
|
||||
Devices []Device `json:"Devices,omitempty" yaml:"Devices,omitempty"`
|
||||
LogConfig LogConfig `json:"LogConfig,omitempty" yaml:"LogConfig,omitempty"`
|
||||
ReadonlyRootfs bool `json:"ReadonlyRootfs,omitempty" yaml:"ReadonlyRootfs,omitempty"`
|
||||
SecurityOpt []string `json:"SecurityOpt,omitempty" yaml:"SecurityOpt,omitempty"`
|
||||
CgroupParent string `json:"CgroupParent,omitempty" yaml:"CgroupParent,omitempty"`
|
||||
Memory int64 `json:"Memory,omitempty" yaml:"Memory,omitempty"`
|
||||
MemorySwap int64 `json:"MemorySwap,omitempty" yaml:"MemorySwap,omitempty"`
|
||||
MemorySwappiness int64 `json:"MemorySwappiness,omitempty" yaml:"MemorySwappiness,omitempty"`
|
||||
OOMKillDisable bool `json:"OomKillDisable,omitempty" yaml:"OomKillDisable"`
|
||||
CPUShares int64 `json:"CpuShares,omitempty" yaml:"CpuShares,omitempty"`
|
||||
CPUSet string `json:"Cpuset,omitempty" yaml:"Cpuset,omitempty"`
|
||||
CPUSetCPUs string `json:"CpusetCpus,omitempty" yaml:"CpusetCpus,omitempty"`
|
||||
CPUSetMEMs string `json:"CpusetMems,omitempty" yaml:"CpusetMems,omitempty"`
|
||||
CPUQuota int64 `json:"CpuQuota,omitempty" yaml:"CpuQuota,omitempty"`
|
||||
CPUPeriod int64 `json:"CpuPeriod,omitempty" yaml:"CpuPeriod,omitempty"`
|
||||
BlkioWeight int64 `json:"BlkioWeight,omitempty" yaml:"BlkioWeight"`
|
||||
Ulimits []ULimit `json:"Ulimits,omitempty" yaml:"Ulimits,omitempty"`
|
||||
}
|
||||
|
||||
// StartContainer starts a container, returning an error in case of failure.
|
||||
//
|
||||
// See http://goo.gl/iM5GYs for more details.
|
||||
// See https://goo.gl/MrBAJv for more details.
|
||||
func (c *Client) StartContainer(id string, hostConfig *HostConfig) error {
|
||||
path := "/containers/" + id + "/start"
|
||||
_, status, err := c.do("POST", path, doOptions{data: hostConfig, forceJSON: true})
|
||||
@ -487,7 +504,7 @@ func (c *Client) StartContainer(id string, hostConfig *HostConfig) error {
|
||||
// StopContainer stops a container, killing it after the given timeout (in
|
||||
// seconds).
|
||||
//
|
||||
// See http://goo.gl/EbcpXt for more details.
|
||||
// See https://goo.gl/USqsFt for more details.
|
||||
func (c *Client) StopContainer(id string, timeout uint) error {
|
||||
path := fmt.Sprintf("/containers/%s/stop?t=%d", id, timeout)
|
||||
_, status, err := c.do("POST", path, doOptions{})
|
||||
@ -506,7 +523,7 @@ func (c *Client) StopContainer(id string, timeout uint) error {
|
||||
// RestartContainer stops a container, killing it after the given timeout (in
|
||||
// seconds), during the stop process.
|
||||
//
|
||||
// See http://goo.gl/VOzR2n for more details.
|
||||
// See https://goo.gl/QzsDnz for more details.
|
||||
func (c *Client) RestartContainer(id string, timeout uint) error {
|
||||
path := fmt.Sprintf("/containers/%s/restart?t=%d", id, timeout)
|
||||
_, status, err := c.do("POST", path, doOptions{})
|
||||
@ -521,7 +538,7 @@ func (c *Client) RestartContainer(id string, timeout uint) error {
|
||||
|
||||
// PauseContainer pauses the given container.
|
||||
//
|
||||
// See http://goo.gl/AM5t42 for more details.
|
||||
// See https://goo.gl/OF7W9X for more details.
|
||||
func (c *Client) PauseContainer(id string) error {
|
||||
path := fmt.Sprintf("/containers/%s/pause", id)
|
||||
_, status, err := c.do("POST", path, doOptions{})
|
||||
@ -536,7 +553,7 @@ func (c *Client) PauseContainer(id string) error {
|
||||
|
||||
// UnpauseContainer unpauses the given container.
|
||||
//
|
||||
// See http://goo.gl/eBrNSL for more details.
|
||||
// See https://goo.gl/7dwyPA for more details.
|
||||
func (c *Client) UnpauseContainer(id string) error {
|
||||
path := fmt.Sprintf("/containers/%s/unpause", id)
|
||||
_, status, err := c.do("POST", path, doOptions{})
|
||||
@ -552,7 +569,7 @@ func (c *Client) UnpauseContainer(id string) error {
|
||||
// TopResult represents the list of processes running in a container, as
|
||||
// returned by /containers/<id>/top.
|
||||
//
|
||||
// See http://goo.gl/qu4gse for more details.
|
||||
// See https://goo.gl/Rb46aY for more details.
|
||||
type TopResult struct {
|
||||
Titles []string
|
||||
Processes [][]string
|
||||
@ -560,7 +577,7 @@ type TopResult struct {
|
||||
|
||||
// TopContainer returns processes running inside a container
|
||||
//
|
||||
// See http://goo.gl/qu4gse for more details.
|
||||
// See https://goo.gl/Rb46aY for more details.
|
||||
func (c *Client) TopContainer(id string, psArgs string) (TopResult, error) {
|
||||
var args string
|
||||
var result TopResult
|
||||
@ -584,7 +601,7 @@ func (c *Client) TopContainer(id string, psArgs string) (TopResult, error) {
|
||||
|
||||
// Stats represents container statistics, returned by /containers/<id>/stats.
|
||||
//
|
||||
// See http://goo.gl/DFMiYD for more details.
|
||||
// See https://goo.gl/GNmLHb for more details.
|
||||
type Stats struct {
|
||||
Read time.Time `json:"read,omitempty" yaml:"read,omitempty"`
|
||||
Network struct {
|
||||
@ -674,7 +691,7 @@ type BlkioStatsEntry struct {
|
||||
|
||||
// StatsOptions specify parameters to the Stats function.
|
||||
//
|
||||
// See http://goo.gl/DFMiYD for more details.
|
||||
// See https://goo.gl/GNmLHb for more details.
|
||||
type StatsOptions struct {
|
||||
ID string
|
||||
Stats chan<- *Stats
|
||||
@ -690,9 +707,10 @@ type StatsOptions struct {
|
||||
// This function is blocking, similar to a streaming call for logs, and should be run
|
||||
// on a separate goroutine from the caller. Note that this function will block until
|
||||
// the given container is removed, not just exited. When finished, this function
|
||||
// will close the given channel. Alternatively, function can be stopped by signaling on the Done channel
|
||||
// will close the given channel. Alternatively, function can be stopped by
|
||||
// signaling on the Done channel.
|
||||
//
|
||||
// See http://goo.gl/DFMiYD for more details.
|
||||
// See https://goo.gl/GNmLHb for more details.
|
||||
func (c *Client) Stats(opts StatsOptions) (retErr error) {
|
||||
errC := make(chan error, 1)
|
||||
readCloser, writeCloser := io.Pipe()
|
||||
@ -763,7 +781,7 @@ func (c *Client) Stats(opts StatsOptions) (retErr error) {
|
||||
// KillContainerOptions represents the set of options that can be used in a
|
||||
// call to KillContainer.
|
||||
//
|
||||
// See http://goo.gl/TFkECx for more details.
|
||||
// See https://goo.gl/hkS9i8 for more details.
|
||||
type KillContainerOptions struct {
|
||||
// The ID of the container.
|
||||
ID string `qs:"-"`
|
||||
@ -773,9 +791,10 @@ type KillContainerOptions struct {
|
||||
Signal Signal
|
||||
}
|
||||
|
||||
// KillContainer kills a container, returning an error in case of failure.
|
||||
// KillContainer sends a signal to a container, returning an error in case of
|
||||
// failure.
|
||||
//
|
||||
// See http://goo.gl/TFkECx for more details.
|
||||
// See https://goo.gl/hkS9i8 for more details.
|
||||
func (c *Client) KillContainer(opts KillContainerOptions) error {
|
||||
path := "/containers/" + opts.ID + "/kill" + "?" + queryString(opts)
|
||||
_, status, err := c.do("POST", path, doOptions{})
|
||||
@ -790,7 +809,7 @@ func (c *Client) KillContainer(opts KillContainerOptions) error {
|
||||
|
||||
// RemoveContainerOptions encapsulates options to remove a container.
|
||||
//
|
||||
// See http://goo.gl/ZB83ji for more details.
|
||||
// See https://goo.gl/RQyX62 for more details.
|
||||
type RemoveContainerOptions struct {
|
||||
// The ID of the container.
|
||||
ID string `qs:"-"`
|
||||
@ -806,7 +825,7 @@ type RemoveContainerOptions struct {
|
||||
|
||||
// RemoveContainer removes a container, returning an error in case of failure.
|
||||
//
|
||||
// See http://goo.gl/ZB83ji for more details.
|
||||
// See https://goo.gl/RQyX62 for more details.
|
||||
func (c *Client) RemoveContainer(opts RemoveContainerOptions) error {
|
||||
path := "/containers/" + opts.ID + "?" + queryString(opts)
|
||||
_, status, err := c.do("DELETE", path, doOptions{})
|
||||
@ -822,7 +841,7 @@ func (c *Client) RemoveContainer(opts RemoveContainerOptions) error {
|
||||
// CopyFromContainerOptions is the set of options that can be used when copying
|
||||
// files or folders from a container.
|
||||
//
|
||||
// See http://goo.gl/rINMlw for more details.
|
||||
// See https://goo.gl/4L7b07 for more details.
|
||||
type CopyFromContainerOptions struct {
|
||||
OutputStream io.Writer `json:"-"`
|
||||
Container string `json:"-"`
|
||||
@ -832,7 +851,7 @@ type CopyFromContainerOptions struct {
|
||||
// CopyFromContainer copy files or folders from a container, using a given
|
||||
// resource.
|
||||
//
|
||||
// See http://goo.gl/rINMlw for more details.
|
||||
// See https://goo.gl/4L7b07 for more details.
|
||||
func (c *Client) CopyFromContainer(opts CopyFromContainerOptions) error {
|
||||
if opts.Container == "" {
|
||||
return &NoSuchContainer{ID: opts.Container}
|
||||
@ -852,7 +871,7 @@ func (c *Client) CopyFromContainer(opts CopyFromContainerOptions) error {
|
||||
// WaitContainer blocks until the given container stops, return the exit code
|
||||
// of the container status.
|
||||
//
|
||||
// See http://goo.gl/J88DHU for more details.
|
||||
// See https://goo.gl/Gc1rge for more details.
|
||||
func (c *Client) WaitContainer(id string) (int, error) {
|
||||
body, status, err := c.do("POST", "/containers/"+id+"/wait", doOptions{})
|
||||
if status == http.StatusNotFound {
|
||||
@ -871,7 +890,7 @@ func (c *Client) WaitContainer(id string) (int, error) {
|
||||
|
||||
// CommitContainerOptions aggregates parameters to the CommitContainer method.
|
||||
//
|
||||
// See http://goo.gl/Jn8pe8 for more details.
|
||||
// See https://goo.gl/mqfoCw for more details.
|
||||
type CommitContainerOptions struct {
|
||||
Container string
|
||||
Repository string `qs:"repo"`
|
||||
@ -883,7 +902,7 @@ type CommitContainerOptions struct {
|
||||
|
||||
// CommitContainer creates a new image from a container's changes.
|
||||
//
|
||||
// See http://goo.gl/Jn8pe8 for more details.
|
||||
// See https://goo.gl/mqfoCw for more details.
|
||||
func (c *Client) CommitContainer(opts CommitContainerOptions) (*Image, error) {
|
||||
path := "/commit?" + queryString(opts)
|
||||
body, status, err := c.do("POST", path, doOptions{data: opts.Run})
|
||||
@ -904,7 +923,7 @@ func (c *Client) CommitContainer(opts CommitContainerOptions) (*Image, error) {
|
||||
// AttachToContainerOptions is the set of options that can be used when
|
||||
// attaching to a container.
|
||||
//
|
||||
// See http://goo.gl/RRAhws for more details.
|
||||
// See https://goo.gl/NKpkFk for more details.
|
||||
type AttachToContainerOptions struct {
|
||||
Container string `qs:"-"`
|
||||
InputStream io.Reader `qs:"-"`
|
||||
@ -939,7 +958,7 @@ type AttachToContainerOptions struct {
|
||||
|
||||
// AttachToContainer attaches to a container, using the given options.
|
||||
//
|
||||
// See http://goo.gl/RRAhws for more details.
|
||||
// See https://goo.gl/NKpkFk for more details.
|
||||
func (c *Client) AttachToContainer(opts AttachToContainerOptions) error {
|
||||
if opts.Container == "" {
|
||||
return &NoSuchContainer{ID: opts.Container}
|
||||
@ -957,7 +976,7 @@ func (c *Client) AttachToContainer(opts AttachToContainerOptions) error {
|
||||
// LogsOptions represents the set of options used when getting logs from a
|
||||
// container.
|
||||
//
|
||||
// See http://goo.gl/rLhKSU for more details.
|
||||
// See https://goo.gl/yl8PGm for more details.
|
||||
type LogsOptions struct {
|
||||
Container string `qs:"-"`
|
||||
OutputStream io.Writer `qs:"-"`
|
||||
@ -975,7 +994,7 @@ type LogsOptions struct {
|
||||
|
||||
// Logs gets stdout and stderr logs from the specified container.
|
||||
//
|
||||
// See http://goo.gl/rLhKSU for more details.
|
||||
// See https://goo.gl/yl8PGm for more details.
|
||||
func (c *Client) Logs(opts LogsOptions) error {
|
||||
if opts.Container == "" {
|
||||
return &NoSuchContainer{ID: opts.Container}
|
||||
@ -992,6 +1011,8 @@ func (c *Client) Logs(opts LogsOptions) error {
|
||||
}
|
||||
|
||||
// ResizeContainerTTY resizes the terminal to the given height and width.
|
||||
//
|
||||
// See https://goo.gl/xERhCc for more details.
|
||||
func (c *Client) ResizeContainerTTY(id string, height, width int) error {
|
||||
params := make(url.Values)
|
||||
params.Set("h", strconv.Itoa(height))
|
||||
@ -1003,7 +1024,7 @@ func (c *Client) ResizeContainerTTY(id string, height, width int) error {
|
||||
// ExportContainerOptions is the set of parameters to the ExportContainer
|
||||
// method.
|
||||
//
|
||||
// See http://goo.gl/hnzE62 for more details.
|
||||
// See https://goo.gl/dOkTyk for more details.
|
||||
type ExportContainerOptions struct {
|
||||
ID string
|
||||
OutputStream io.Writer
|
||||
@ -1012,7 +1033,7 @@ type ExportContainerOptions struct {
|
||||
// ExportContainer export the contents of container id as tar archive
|
||||
// and prints the exported contents to stdout.
|
||||
//
|
||||
// See http://goo.gl/hnzE62 for more details.
|
||||
// See https://goo.gl/dOkTyk for more details.
|
||||
func (c *Client) ExportContainer(opts ExportContainerOptions) error {
|
||||
if opts.ID == "" {
|
||||
return &NoSuchContainer{ID: opts.ID}
|
||||
|
5
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/container_test.go
generated
vendored
5
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/container_test.go
generated
vendored
@ -1122,10 +1122,7 @@ func TestAttachToContainerRawTerminalFalse(t *testing.T) {
|
||||
Stream: true,
|
||||
RawTerminal: false,
|
||||
}
|
||||
err := client.AttachToContainer(opts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
client.AttachToContainer(opts)
|
||||
expected := map[string][]string{
|
||||
"stdin": {"1"},
|
||||
"stdout": {"1"},
|
||||
|
122
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/exec.go
generated
vendored
122
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/exec.go
generated
vendored
@ -2,8 +2,6 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Docs can currently be found at https://github.com/docker/docker/blob/master/docs/sources/reference/api/docker_remote_api_v1.15.md#exec-create
|
||||
|
||||
package docker
|
||||
|
||||
import (
|
||||
@ -15,9 +13,15 @@ import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Exec is the type representing a `docker exec` instance and containing the
|
||||
// instance ID
|
||||
type Exec struct {
|
||||
ID string `json:"Id,omitempty" yaml:"Id,omitempty"`
|
||||
}
|
||||
|
||||
// CreateExecOptions specify parameters to the CreateExecContainer function.
|
||||
//
|
||||
// See http://goo.gl/8izrzI for more details
|
||||
// See https://goo.gl/1KSIb7 for more details
|
||||
type CreateExecOptions struct {
|
||||
AttachStdin bool `json:"AttachStdin,omitempty" yaml:"AttachStdin,omitempty"`
|
||||
AttachStdout bool `json:"AttachStdout,omitempty" yaml:"AttachStdout,omitempty"`
|
||||
@ -28,9 +32,31 @@ type CreateExecOptions struct {
|
||||
User string `json:"User,omitempty" yaml:"User,omitempty"`
|
||||
}
|
||||
|
||||
// CreateExec sets up an exec instance in a running container `id`, returning the exec
|
||||
// instance, or an error in case of failure.
|
||||
//
|
||||
// See https://goo.gl/1KSIb7 for more details
|
||||
func (c *Client) CreateExec(opts CreateExecOptions) (*Exec, error) {
|
||||
path := fmt.Sprintf("/containers/%s/exec", opts.Container)
|
||||
body, status, err := c.do("POST", path, doOptions{data: opts})
|
||||
if status == http.StatusNotFound {
|
||||
return nil, &NoSuchContainer{ID: opts.Container}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var exec Exec
|
||||
err = json.Unmarshal(body, &exec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &exec, nil
|
||||
}
|
||||
|
||||
// StartExecOptions specify parameters to the StartExecContainer function.
|
||||
//
|
||||
// See http://goo.gl/JW8Lxl for more details
|
||||
// See https://goo.gl/iQCnto for more details
|
||||
type StartExecOptions struct {
|
||||
Detach bool `json:"Detach,omitempty" yaml:"Detach,omitempty"`
|
||||
|
||||
@ -51,67 +77,11 @@ type StartExecOptions struct {
|
||||
Success chan struct{} `json:"-"`
|
||||
}
|
||||
|
||||
// Exec is the type representing a `docker exec` instance and containing the
|
||||
// instance ID
|
||||
type Exec struct {
|
||||
ID string `json:"Id,omitempty" yaml:"Id,omitempty"`
|
||||
}
|
||||
|
||||
// ExecProcessConfig is a type describing the command associated to a Exec
|
||||
// instance. It's used in the ExecInspect type.
|
||||
//
|
||||
// See http://goo.gl/ypQULN for more details
|
||||
type ExecProcessConfig struct {
|
||||
Privileged bool `json:"privileged,omitempty" yaml:"privileged,omitempty"`
|
||||
User string `json:"user,omitempty" yaml:"user,omitempty"`
|
||||
Tty bool `json:"tty,omitempty" yaml:"tty,omitempty"`
|
||||
EntryPoint string `json:"entrypoint,omitempty" yaml:"entrypoint,omitempty"`
|
||||
Arguments []string `json:"arguments,omitempty" yaml:"arguments,omitempty"`
|
||||
}
|
||||
|
||||
// ExecInspect is a type with details about a exec instance, including the
|
||||
// exit code if the command has finished running. It's returned by a api
|
||||
// call to /exec/(id)/json
|
||||
//
|
||||
// See http://goo.gl/ypQULN for more details
|
||||
type ExecInspect struct {
|
||||
ID string `json:"ID,omitempty" yaml:"ID,omitempty"`
|
||||
Running bool `json:"Running,omitempty" yaml:"Running,omitempty"`
|
||||
ExitCode int `json:"ExitCode,omitempty" yaml:"ExitCode,omitempty"`
|
||||
OpenStdin bool `json:"OpenStdin,omitempty" yaml:"OpenStdin,omitempty"`
|
||||
OpenStderr bool `json:"OpenStderr,omitempty" yaml:"OpenStderr,omitempty"`
|
||||
OpenStdout bool `json:"OpenStdout,omitempty" yaml:"OpenStdout,omitempty"`
|
||||
ProcessConfig ExecProcessConfig `json:"ProcessConfig,omitempty" yaml:"ProcessConfig,omitempty"`
|
||||
Container Container `json:"Container,omitempty" yaml:"Container,omitempty"`
|
||||
}
|
||||
|
||||
// CreateExec sets up an exec instance in a running container `id`, returning the exec
|
||||
// instance, or an error in case of failure.
|
||||
//
|
||||
// See http://goo.gl/8izrzI for more details
|
||||
func (c *Client) CreateExec(opts CreateExecOptions) (*Exec, error) {
|
||||
path := fmt.Sprintf("/containers/%s/exec", opts.Container)
|
||||
body, status, err := c.do("POST", path, doOptions{data: opts})
|
||||
if status == http.StatusNotFound {
|
||||
return nil, &NoSuchContainer{ID: opts.Container}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var exec Exec
|
||||
err = json.Unmarshal(body, &exec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &exec, nil
|
||||
}
|
||||
|
||||
// StartExec starts a previously set up exec instance id. If opts.Detach is
|
||||
// true, it returns after starting the exec command. Otherwise, it sets up an
|
||||
// interactive session with the exec command.
|
||||
//
|
||||
// See http://goo.gl/JW8Lxl for more details
|
||||
// See https://goo.gl/iQCnto for more details
|
||||
func (c *Client) StartExec(id string, opts StartExecOptions) error {
|
||||
if id == "" {
|
||||
return &NoSuchExec{ID: id}
|
||||
@ -144,7 +114,7 @@ func (c *Client) StartExec(id string, opts StartExecOptions) error {
|
||||
// is valid only if Tty was specified as part of creating and starting the exec
|
||||
// command.
|
||||
//
|
||||
// See http://goo.gl/YDSx1f for more details
|
||||
// See https://goo.gl/e1JpsA for more details
|
||||
func (c *Client) ResizeExecTTY(id string, height, width int) error {
|
||||
params := make(url.Values)
|
||||
params.Set("h", strconv.Itoa(height))
|
||||
@ -155,9 +125,35 @@ func (c *Client) ResizeExecTTY(id string, height, width int) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// ExecProcessConfig is a type describing the command associated to a Exec
|
||||
// instance. It's used in the ExecInspect type.
|
||||
type ExecProcessConfig struct {
|
||||
Privileged bool `json:"privileged,omitempty" yaml:"privileged,omitempty"`
|
||||
User string `json:"user,omitempty" yaml:"user,omitempty"`
|
||||
Tty bool `json:"tty,omitempty" yaml:"tty,omitempty"`
|
||||
EntryPoint string `json:"entrypoint,omitempty" yaml:"entrypoint,omitempty"`
|
||||
Arguments []string `json:"arguments,omitempty" yaml:"arguments,omitempty"`
|
||||
}
|
||||
|
||||
// ExecInspect is a type with details about a exec instance, including the
|
||||
// exit code if the command has finished running. It's returned by a api
|
||||
// call to /exec/(id)/json
|
||||
//
|
||||
// See https://goo.gl/gPtX9R for more details
|
||||
type ExecInspect struct {
|
||||
ID string `json:"ID,omitempty" yaml:"ID,omitempty"`
|
||||
Running bool `json:"Running,omitempty" yaml:"Running,omitempty"`
|
||||
ExitCode int `json:"ExitCode,omitempty" yaml:"ExitCode,omitempty"`
|
||||
OpenStdin bool `json:"OpenStdin,omitempty" yaml:"OpenStdin,omitempty"`
|
||||
OpenStderr bool `json:"OpenStderr,omitempty" yaml:"OpenStderr,omitempty"`
|
||||
OpenStdout bool `json:"OpenStdout,omitempty" yaml:"OpenStdout,omitempty"`
|
||||
ProcessConfig ExecProcessConfig `json:"ProcessConfig,omitempty" yaml:"ProcessConfig,omitempty"`
|
||||
Container Container `json:"Container,omitempty" yaml:"Container,omitempty"`
|
||||
}
|
||||
|
||||
// InspectExec returns low-level information about the exec command id.
|
||||
//
|
||||
// See http://goo.gl/ypQULN for more details
|
||||
// See https://goo.gl/gPtX9R for more details
|
||||
func (c *Client) InspectExec(id string) (*ExecInspect, error) {
|
||||
path := fmt.Sprintf("/exec/%s/json", id)
|
||||
body, status, err := c.do("GET", path, doOptions{})
|
||||
|
111
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/image.go
generated
vendored
111
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/image.go
generated
vendored
@ -11,7 +11,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
@ -46,16 +45,6 @@ type Image struct {
|
||||
VirtualSize int64 `json:"VirtualSize,omitempty" yaml:"VirtualSize,omitempty"`
|
||||
}
|
||||
|
||||
// ImageHistory represent a layer in an image's history returned by the
|
||||
// ImageHistory call.
|
||||
type ImageHistory struct {
|
||||
ID string `json:"Id" yaml:"Id"`
|
||||
Tags []string `json:"Tags,omitempty" yaml:"Tags,omitempty"`
|
||||
Created int64 `json:"Created,omitempty" yaml:"Created,omitempty"`
|
||||
CreatedBy string `json:"CreatedBy,omitempty" yaml:"CreatedBy,omitempty"`
|
||||
Size int64 `json:"Size,omitempty" yaml:"Size,omitempty"`
|
||||
}
|
||||
|
||||
// ImagePre012 serves the same purpose as the Image type except that it is for
|
||||
// earlier versions of the Docker API (pre-012 to be specific)
|
||||
type ImagePre012 struct {
|
||||
@ -72,15 +61,6 @@ type ImagePre012 struct {
|
||||
Size int64 `json:"size,omitempty"`
|
||||
}
|
||||
|
||||
// ListImagesOptions specify parameters to the ListImages function.
|
||||
//
|
||||
// See http://goo.gl/HRVN1Z for more details.
|
||||
type ListImagesOptions struct {
|
||||
All bool
|
||||
Filters map[string][]string
|
||||
Digests bool
|
||||
}
|
||||
|
||||
var (
|
||||
// ErrNoSuchImage is the error returned when the image does not exist.
|
||||
ErrNoSuchImage = errors.New("no such image")
|
||||
@ -102,9 +82,18 @@ var (
|
||||
ErrMustSpecifyNames = errors.New("must specify at least one name to export")
|
||||
)
|
||||
|
||||
// ListImagesOptions specify parameters to the ListImages function.
|
||||
//
|
||||
// See https://goo.gl/xBe1u3 for more details.
|
||||
type ListImagesOptions struct {
|
||||
All bool
|
||||
Filters map[string][]string
|
||||
Digests bool
|
||||
}
|
||||
|
||||
// ListImages returns the list of available images in the server.
|
||||
//
|
||||
// See http://goo.gl/HRVN1Z for more details.
|
||||
// See https://goo.gl/xBe1u3 for more details.
|
||||
func (c *Client) ListImages(opts ListImagesOptions) ([]APIImages, error) {
|
||||
path := "/images/json?" + queryString(opts)
|
||||
body, _, err := c.do("GET", path, doOptions{})
|
||||
@ -119,9 +108,19 @@ func (c *Client) ListImages(opts ListImagesOptions) ([]APIImages, error) {
|
||||
return images, nil
|
||||
}
|
||||
|
||||
// ImageHistory represent a layer in an image's history returned by the
|
||||
// ImageHistory call.
|
||||
type ImageHistory struct {
|
||||
ID string `json:"Id" yaml:"Id"`
|
||||
Tags []string `json:"Tags,omitempty" yaml:"Tags,omitempty"`
|
||||
Created int64 `json:"Created,omitempty" yaml:"Created,omitempty"`
|
||||
CreatedBy string `json:"CreatedBy,omitempty" yaml:"CreatedBy,omitempty"`
|
||||
Size int64 `json:"Size,omitempty" yaml:"Size,omitempty"`
|
||||
}
|
||||
|
||||
// ImageHistory returns the history of the image by its name or ID.
|
||||
//
|
||||
// See http://goo.gl/2oJmNs for more details.
|
||||
// See https://goo.gl/8bnTId for more details.
|
||||
func (c *Client) ImageHistory(name string) ([]ImageHistory, error) {
|
||||
body, status, err := c.do("GET", "/images/"+name+"/history", doOptions{})
|
||||
if status == http.StatusNotFound {
|
||||
@ -140,7 +139,7 @@ func (c *Client) ImageHistory(name string) ([]ImageHistory, error) {
|
||||
|
||||
// RemoveImage removes an image by its name or ID.
|
||||
//
|
||||
// See http://goo.gl/znj0wM for more details.
|
||||
// See https://goo.gl/V3ZWnK for more details.
|
||||
func (c *Client) RemoveImage(name string) error {
|
||||
_, status, err := c.do("DELETE", "/images/"+name, doOptions{})
|
||||
if status == http.StatusNotFound {
|
||||
@ -152,7 +151,7 @@ func (c *Client) RemoveImage(name string) error {
|
||||
// RemoveImageOptions present the set of options available for removing an image
|
||||
// from a registry.
|
||||
//
|
||||
// See http://goo.gl/6V48bF for more details.
|
||||
// See https://goo.gl/V3ZWnK for more details.
|
||||
type RemoveImageOptions struct {
|
||||
Force bool `qs:"force"`
|
||||
NoPrune bool `qs:"noprune"`
|
||||
@ -161,7 +160,7 @@ type RemoveImageOptions struct {
|
||||
// RemoveImageExtended removes an image by its name or ID.
|
||||
// Extra params can be passed, see RemoveImageOptions
|
||||
//
|
||||
// See http://goo.gl/znj0wM for more details.
|
||||
// See https://goo.gl/V3ZWnK for more details.
|
||||
func (c *Client) RemoveImageExtended(name string, opts RemoveImageOptions) error {
|
||||
uri := fmt.Sprintf("/images/%s?%s", name, queryString(&opts))
|
||||
_, status, err := c.do("DELETE", uri, doOptions{})
|
||||
@ -173,7 +172,7 @@ func (c *Client) RemoveImageExtended(name string, opts RemoveImageOptions) error
|
||||
|
||||
// InspectImage returns an image by its name or ID.
|
||||
//
|
||||
// See http://goo.gl/Q112NY for more details.
|
||||
// See https://goo.gl/jHPcg6 for more details.
|
||||
func (c *Client) InspectImage(name string) (*Image, error) {
|
||||
body, status, err := c.do("GET", "/images/"+name+"/json", doOptions{})
|
||||
if status == http.StatusNotFound {
|
||||
@ -216,7 +215,7 @@ func (c *Client) InspectImage(name string) (*Image, error) {
|
||||
|
||||
// PushImageOptions represents options to use in the PushImage method.
|
||||
//
|
||||
// See http://goo.gl/pN8A3P for more details.
|
||||
// See https://goo.gl/zPtZaT for more details.
|
||||
type PushImageOptions struct {
|
||||
// Name of the image
|
||||
Name string
|
||||
@ -236,7 +235,7 @@ type PushImageOptions struct {
|
||||
// An empty instance of AuthConfiguration may be used for unauthenticated
|
||||
// pushes.
|
||||
//
|
||||
// See http://goo.gl/pN8A3P for more details.
|
||||
// See https://goo.gl/zPtZaT for more details.
|
||||
func (c *Client) PushImage(opts PushImageOptions, auth AuthConfiguration) error {
|
||||
if opts.Name == "" {
|
||||
return ErrNoSuchImage
|
||||
@ -259,7 +258,7 @@ func (c *Client) PushImage(opts PushImageOptions, auth AuthConfiguration) error
|
||||
// PullImageOptions present the set of options available for pulling an image
|
||||
// from a registry.
|
||||
//
|
||||
// See http://goo.gl/ACyYNS for more details.
|
||||
// See https://goo.gl/iJkZjD for more details.
|
||||
type PullImageOptions struct {
|
||||
Repository string `qs:"fromImage"`
|
||||
Registry string
|
||||
@ -268,9 +267,10 @@ type PullImageOptions struct {
|
||||
RawJSONStream bool `qs:"-"`
|
||||
}
|
||||
|
||||
// PullImage pulls an image from a remote registry, logging progress to opts.OutputStream.
|
||||
// PullImage pulls an image from a remote registry, logging progress to
|
||||
// opts.OutputStream.
|
||||
//
|
||||
// See http://goo.gl/ACyYNS for more details.
|
||||
// See https://goo.gl/iJkZjD for more details.
|
||||
func (c *Client) PullImage(opts PullImageOptions, auth AuthConfiguration) error {
|
||||
if opts.Repository == "" {
|
||||
return ErrNoSuchImage
|
||||
@ -296,14 +296,14 @@ func (c *Client) createImage(qs string, headers map[string]string, in io.Reader,
|
||||
|
||||
// LoadImageOptions represents the options for LoadImage Docker API Call
|
||||
//
|
||||
// See http://goo.gl/Y8NNCq for more details.
|
||||
// See https://goo.gl/JyClMX for more details.
|
||||
type LoadImageOptions struct {
|
||||
InputStream io.Reader
|
||||
}
|
||||
|
||||
// LoadImage imports a tarball docker image
|
||||
//
|
||||
// See http://goo.gl/Y8NNCq for more details.
|
||||
// See https://goo.gl/JyClMX for more details.
|
||||
func (c *Client) LoadImage(opts LoadImageOptions) error {
|
||||
return c.stream("POST", "/images/load", streamOptions{
|
||||
setRawTerminal: true,
|
||||
@ -311,17 +311,17 @@ func (c *Client) LoadImage(opts LoadImageOptions) error {
|
||||
})
|
||||
}
|
||||
|
||||
// ExportImageOptions represent the options for ExportImage Docker API call
|
||||
// ExportImageOptions represent the options for ExportImage Docker API call.
|
||||
//
|
||||
// See http://goo.gl/mi6kvk for more details.
|
||||
// See https://goo.gl/le7vK8 for more details.
|
||||
type ExportImageOptions struct {
|
||||
Name string
|
||||
OutputStream io.Writer
|
||||
}
|
||||
|
||||
// ExportImage exports an image (as a tar file) into the stream
|
||||
// ExportImage exports an image (as a tar file) into the stream.
|
||||
//
|
||||
// See http://goo.gl/mi6kvk for more details.
|
||||
// See https://goo.gl/le7vK8 for more details.
|
||||
func (c *Client) ExportImage(opts ExportImageOptions) error {
|
||||
return c.stream("GET", fmt.Sprintf("/images/%s/get", opts.Name), streamOptions{
|
||||
setRawTerminal: true,
|
||||
@ -331,7 +331,7 @@ func (c *Client) ExportImage(opts ExportImageOptions) error {
|
||||
|
||||
// ExportImagesOptions represent the options for ExportImages Docker API call
|
||||
//
|
||||
// See http://goo.gl/YeZzQK for more details.
|
||||
// See https://goo.gl/huC7HA for more details.
|
||||
type ExportImagesOptions struct {
|
||||
Names []string
|
||||
OutputStream io.Writer `qs:"-"`
|
||||
@ -339,7 +339,7 @@ type ExportImagesOptions struct {
|
||||
|
||||
// ExportImages exports one or more images (as a tar file) into the stream
|
||||
//
|
||||
// See http://goo.gl/YeZzQK for more details.
|
||||
// See https://goo.gl/huC7HA for more details.
|
||||
func (c *Client) ExportImages(opts ExportImagesOptions) error {
|
||||
if opts.Names == nil || len(opts.Names) == 0 {
|
||||
return ErrMustSpecifyNames
|
||||
@ -353,7 +353,7 @@ func (c *Client) ExportImages(opts ExportImagesOptions) error {
|
||||
// ImportImageOptions present the set of informations available for importing
|
||||
// an image from a source file or the stdin.
|
||||
//
|
||||
// See http://goo.gl/PhBKnS for more details.
|
||||
// See https://goo.gl/iJkZjD for more details.
|
||||
type ImportImageOptions struct {
|
||||
Repository string `qs:"repo"`
|
||||
Source string `qs:"fromSrc"`
|
||||
@ -366,7 +366,7 @@ type ImportImageOptions struct {
|
||||
|
||||
// ImportImage imports an image from a url, a file or stdin
|
||||
//
|
||||
// See http://goo.gl/PhBKnS for more details.
|
||||
// See https://goo.gl/iJkZjD for more details.
|
||||
func (c *Client) ImportImage(opts ImportImageOptions) error {
|
||||
if opts.Repository == "" {
|
||||
return ErrNoSuchImage
|
||||
@ -379,8 +379,7 @@ func (c *Client) ImportImage(opts ImportImageOptions) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b, err := ioutil.ReadAll(f)
|
||||
opts.InputStream = bytes.NewBuffer(b)
|
||||
opts.InputStream = f
|
||||
opts.Source = "-"
|
||||
}
|
||||
return c.createImage(queryString(&opts), nil, opts.InputStream, opts.OutputStream, opts.RawJSONStream)
|
||||
@ -415,12 +414,12 @@ type BuildImageOptions struct {
|
||||
// BuildImage builds an image from a tarball's url or a Dockerfile in the input
|
||||
// stream.
|
||||
//
|
||||
// See http://goo.gl/7nuGXa for more details.
|
||||
// See https://goo.gl/xySxCe for more details.
|
||||
func (c *Client) BuildImage(opts BuildImageOptions) error {
|
||||
if opts.OutputStream == nil {
|
||||
return ErrMissingOutputStream
|
||||
}
|
||||
headers, err := headersWithAuth(opts.Auth, opts.AuthConfigs)
|
||||
headers, err := headersWithAuth(opts.Auth, c.versionedAuthConfigs(opts.AuthConfigs))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -452,9 +451,19 @@ func (c *Client) BuildImage(opts BuildImageOptions) error {
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Client) versionedAuthConfigs(authConfigs AuthConfigurations) interface{} {
|
||||
if c.serverAPIVersion == nil {
|
||||
c.checkAPIVersion()
|
||||
}
|
||||
if c.serverAPIVersion != nil && c.serverAPIVersion.GreaterThanOrEqualTo(apiVersion119) {
|
||||
return AuthConfigurations119(authConfigs.Configs)
|
||||
}
|
||||
return authConfigs
|
||||
}
|
||||
|
||||
// TagImageOptions present the set of options to tag an image.
|
||||
//
|
||||
// See http://goo.gl/5g6qFy for more details.
|
||||
// See https://goo.gl/98ZzkU for more details.
|
||||
type TagImageOptions struct {
|
||||
Repo string
|
||||
Tag string
|
||||
@ -463,7 +472,7 @@ type TagImageOptions struct {
|
||||
|
||||
// TagImage adds a tag to the image identified by the given name.
|
||||
//
|
||||
// See http://goo.gl/5g6qFy for more details.
|
||||
// See https://goo.gl/98ZzkU for more details.
|
||||
func (c *Client) TagImage(name string, opts TagImageOptions) error {
|
||||
if name == "" {
|
||||
return ErrNoSuchImage
|
||||
@ -497,7 +506,7 @@ func headersWithAuth(auths ...interface{}) (map[string]string, error) {
|
||||
return nil, err
|
||||
}
|
||||
headers["X-Registry-Auth"] = base64.URLEncoding.EncodeToString(buf.Bytes())
|
||||
case AuthConfigurations:
|
||||
case AuthConfigurations, AuthConfigurations119:
|
||||
var buf bytes.Buffer
|
||||
if err := json.NewEncoder(&buf).Encode(auth); err != nil {
|
||||
return nil, err
|
||||
@ -509,9 +518,9 @@ func headersWithAuth(auths ...interface{}) (map[string]string, error) {
|
||||
return headers, nil
|
||||
}
|
||||
|
||||
// APIImageSearch reflect the result of a search on the dockerHub
|
||||
// APIImageSearch reflect the result of a search on the Docker Hub.
|
||||
//
|
||||
// See http://goo.gl/xI5lLZ for more details.
|
||||
// See https://goo.gl/AYjyrF for more details.
|
||||
type APIImageSearch struct {
|
||||
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||
IsOfficial bool `json:"is_official,omitempty" yaml:"is_official,omitempty"`
|
||||
@ -522,7 +531,7 @@ type APIImageSearch struct {
|
||||
|
||||
// SearchImages search the docker hub with a specific given term.
|
||||
//
|
||||
// See http://goo.gl/xI5lLZ for more details.
|
||||
// See https://goo.gl/AYjyrF for more details.
|
||||
func (c *Client) SearchImages(term string) ([]APIImageSearch, error) {
|
||||
body, _, err := c.do("GET", "/images/search?term="+term, doOptions{})
|
||||
if err != nil {
|
||||
|
2
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/image_test.go
generated
vendored
2
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/image_test.go
generated
vendored
@ -21,11 +21,13 @@ import (
|
||||
func newTestClient(rt *FakeRoundTripper) Client {
|
||||
endpoint := "http://localhost:4243"
|
||||
u, _ := parseEndpoint("http://localhost:4243", false)
|
||||
testAPIVersion, _ := NewAPIVersion("1.17")
|
||||
client := Client{
|
||||
HTTPClient: &http.Client{Transport: rt},
|
||||
endpoint: endpoint,
|
||||
endpointURL: u,
|
||||
SkipServerVersionCheck: true,
|
||||
serverAPIVersion: testAPIVersion,
|
||||
}
|
||||
return client
|
||||
}
|
||||
|
4
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/misc.go
generated
vendored
4
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/misc.go
generated
vendored
@ -11,7 +11,7 @@ import (
|
||||
|
||||
// Version returns version information about the docker server.
|
||||
//
|
||||
// See http://goo.gl/BOZrF5 for more details.
|
||||
// See https://goo.gl/ND9R8L for more details.
|
||||
func (c *Client) Version() (*Env, error) {
|
||||
body, _, err := c.do("GET", "/version", doOptions{})
|
||||
if err != nil {
|
||||
@ -26,7 +26,7 @@ func (c *Client) Version() (*Env, error) {
|
||||
|
||||
// Info returns system-wide information about the Docker server.
|
||||
//
|
||||
// See http://goo.gl/wmqZsW for more details.
|
||||
// See https://goo.gl/ElTHi2 for more details.
|
||||
func (c *Client) Info() (*Env, error) {
|
||||
body, _, err := c.do("GET", "/info", doOptions{})
|
||||
if err != nil {
|
||||
|
27
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/server.go
generated
vendored
27
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/server.go
generated
vendored
@ -12,6 +12,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
mathrand "math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
@ -532,7 +533,7 @@ func (s *DockerServer) startContainer(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
container.HostConfig = &hostConfig
|
||||
if container.State.Running {
|
||||
http.Error(w, "Container already running", http.StatusBadRequest)
|
||||
http.Error(w, "", http.StatusNotModified)
|
||||
return
|
||||
}
|
||||
container.State.Running = true
|
||||
@ -610,14 +611,34 @@ func (s *DockerServer) attachContainer(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
wg := sync.WaitGroup{}
|
||||
if r.URL.Query().Get("stdin") == "1" {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
ioutil.ReadAll(conn)
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
outStream := stdcopy.NewStdWriter(conn, stdcopy.Stdout)
|
||||
if container.State.Running {
|
||||
fmt.Fprintf(outStream, "Container %q is running\n", container.ID)
|
||||
fmt.Fprintf(outStream, "Container is running\n")
|
||||
} else {
|
||||
fmt.Fprintf(outStream, "Container %q is not running\n", container.ID)
|
||||
fmt.Fprintf(outStream, "Container is not running\n")
|
||||
}
|
||||
fmt.Fprintln(outStream, "What happened?")
|
||||
fmt.Fprintln(outStream, "Something happened")
|
||||
wg.Wait()
|
||||
if r.URL.Query().Get("stream") == "1" {
|
||||
for {
|
||||
time.Sleep(1e6)
|
||||
s.cMut.RLock()
|
||||
if !container.State.Running {
|
||||
s.cMut.RUnlock()
|
||||
break
|
||||
}
|
||||
s.cMut.RUnlock()
|
||||
}
|
||||
}
|
||||
conn.Close()
|
||||
}
|
||||
|
||||
|
77
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/server_test.go
generated
vendored
77
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/server_test.go
generated
vendored
@ -5,9 +5,11 @@
|
||||
package testing
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
@ -624,8 +626,8 @@ func TestStartContainerAlreadyRunning(t *testing.T) {
|
||||
path := fmt.Sprintf("/containers/%s/start", server.containers[0].ID)
|
||||
request, _ := http.NewRequest("POST", path, bytes.NewBuffer([]byte("null")))
|
||||
server.ServeHTTP(recorder, request)
|
||||
if recorder.Code != http.StatusBadRequest {
|
||||
t.Errorf("StartContainer: wrong status code. Want %d. Got %d.", http.StatusBadRequest, recorder.Code)
|
||||
if recorder.Code != http.StatusNotModified {
|
||||
t.Errorf("StartContainer: wrong status code. Want %d. Got %d.", http.StatusNotModified, recorder.Code)
|
||||
}
|
||||
}
|
||||
|
||||
@ -845,22 +847,41 @@ func TestWaitContainerNotFound(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
type HijackableResponseRecorder struct {
|
||||
httptest.ResponseRecorder
|
||||
readCh chan []byte
|
||||
}
|
||||
|
||||
func (r *HijackableResponseRecorder) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||
myConn, otherConn := net.Pipe()
|
||||
r.readCh = make(chan []byte)
|
||||
go func() {
|
||||
data, _ := ioutil.ReadAll(myConn)
|
||||
r.readCh <- data
|
||||
}()
|
||||
return otherConn, nil, nil
|
||||
}
|
||||
|
||||
func (r *HijackableResponseRecorder) HijackBuffer() string {
|
||||
return string(<-r.readCh)
|
||||
}
|
||||
|
||||
func TestAttachContainer(t *testing.T) {
|
||||
server := DockerServer{}
|
||||
addContainers(&server, 1)
|
||||
server.containers[0].State.Running = true
|
||||
server.buildMuxer()
|
||||
recorder := httptest.NewRecorder()
|
||||
recorder := &HijackableResponseRecorder{}
|
||||
path := fmt.Sprintf("/containers/%s/attach?logs=1", server.containers[0].ID)
|
||||
request, _ := http.NewRequest("POST", path, nil)
|
||||
server.ServeHTTP(recorder, request)
|
||||
lines := []string{
|
||||
fmt.Sprintf("\x01\x00\x00\x00\x03\x00\x00\x00Container %q is running", server.containers[0].ID),
|
||||
"What happened?",
|
||||
"Something happened",
|
||||
"\x01\x00\x00\x00\x00\x00\x00\x15Container is running",
|
||||
"\x01\x00\x00\x00\x00\x00\x00\x0fWhat happened?",
|
||||
"\x01\x00\x00\x00\x00\x00\x00\x13Something happened",
|
||||
}
|
||||
expected := strings.Join(lines, "\n") + "\n"
|
||||
if body := recorder.Body.String(); body == expected {
|
||||
if body := recorder.HijackBuffer(); body != expected {
|
||||
t.Errorf("AttachContainer: wrong body. Want %q. Got %q.", expected, body)
|
||||
}
|
||||
}
|
||||
@ -868,7 +889,7 @@ func TestAttachContainer(t *testing.T) {
|
||||
func TestAttachContainerNotFound(t *testing.T) {
|
||||
server := DockerServer{}
|
||||
server.buildMuxer()
|
||||
recorder := httptest.NewRecorder()
|
||||
recorder := &HijackableResponseRecorder{}
|
||||
path := "/containers/abc123/attach?logs=1"
|
||||
request, _ := http.NewRequest("POST", path, nil)
|
||||
server.ServeHTTP(recorder, request)
|
||||
@ -877,6 +898,44 @@ func TestAttachContainerNotFound(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAttachContainerWithStreamBlocks(t *testing.T) {
|
||||
server := DockerServer{}
|
||||
addContainers(&server, 1)
|
||||
server.containers[0].State.Running = true
|
||||
server.buildMuxer()
|
||||
path := fmt.Sprintf("/containers/%s/attach?logs=1&stdout=1&stream=1", server.containers[0].ID)
|
||||
request, _ := http.NewRequest("POST", path, nil)
|
||||
done := make(chan string)
|
||||
go func() {
|
||||
recorder := &HijackableResponseRecorder{}
|
||||
server.ServeHTTP(recorder, request)
|
||||
done <- recorder.HijackBuffer()
|
||||
}()
|
||||
select {
|
||||
case <-done:
|
||||
t.Fatalf("attach stream returned before container is stopped")
|
||||
case <-time.After(500 * time.Millisecond):
|
||||
}
|
||||
server.cMut.Lock()
|
||||
server.containers[0].State.Running = false
|
||||
server.cMut.Unlock()
|
||||
var body string
|
||||
select {
|
||||
case body = <-done:
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Fatalf("timed out waiting for attach to finish")
|
||||
}
|
||||
lines := []string{
|
||||
"\x01\x00\x00\x00\x00\x00\x00\x15Container is running",
|
||||
"\x01\x00\x00\x00\x00\x00\x00\x0fWhat happened?",
|
||||
"\x01\x00\x00\x00\x00\x00\x00\x13Something happened",
|
||||
}
|
||||
expected := strings.Join(lines, "\n") + "\n"
|
||||
if body != expected {
|
||||
t.Errorf("AttachContainer: wrong body. Want %q. Got %q.", expected, body)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveContainer(t *testing.T) {
|
||||
server := DockerServer{}
|
||||
addContainers(&server, 1)
|
||||
@ -1690,7 +1749,7 @@ func addNetworks(server *DockerServer, n int) {
|
||||
ID: fmt.Sprintf("%x", rand.Int()%10000),
|
||||
Type: "bridge",
|
||||
Endpoints: []*docker.Endpoint{
|
||||
&docker.Endpoint{
|
||||
{
|
||||
Name: "blah",
|
||||
ID: fmt.Sprintf("%x", rand.Int()%10000),
|
||||
Network: netid,
|
||||
|
118
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/volume.go
generated
vendored
Normal file
118
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/volume.go
generated
vendored
Normal file
@ -0,0 +1,118 @@
|
||||
// Copyright 2015 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
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNoSuchVolume is the error returned when the volume does not exist.
|
||||
ErrNoSuchVolume = errors.New("no such volume")
|
||||
|
||||
// ErrVolumeInUse is the error returned when the volume requested to be removed is still in use.
|
||||
ErrVolumeInUse = errors.New("volume in use and cannot be removed")
|
||||
)
|
||||
|
||||
// Volume represents a volume.
|
||||
//
|
||||
// See https://goo.gl/FZA4BK for more details.
|
||||
type Volume struct {
|
||||
Name string `json:"Name" yaml:"Name"`
|
||||
Driver string `json:"Driver,omitempty" yaml:"Driver,omitempty"`
|
||||
Mountpoint string `json:"Mountpoint,omitempty" yaml:"Mountpoint,omitempty"`
|
||||
}
|
||||
|
||||
// ListVolumesOptions specify parameters to the ListVolumes function.
|
||||
//
|
||||
// See https://goo.gl/FZA4BK for more details.
|
||||
type ListVolumesOptions struct {
|
||||
Filters map[string][]string
|
||||
}
|
||||
|
||||
// ListVolumes returns a list of available volumes in the server.
|
||||
//
|
||||
// See https://goo.gl/FZA4BK for more details.
|
||||
func (c *Client) ListVolumes(opts ListVolumesOptions) ([]Volume, error) {
|
||||
body, _, err := c.do("GET", "/volumes?"+queryString(opts), doOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m := make(map[string]interface{})
|
||||
if err := json.Unmarshal(body, &m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var volumes []Volume
|
||||
volumesJSON, ok := m["Volumes"]
|
||||
if !ok {
|
||||
return volumes, nil
|
||||
}
|
||||
data, err := json.Marshal(volumesJSON)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := json.Unmarshal(data, &volumes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return volumes, nil
|
||||
}
|
||||
|
||||
// CreateVolumeOptions specify parameters to the CreateVolume function.
|
||||
//
|
||||
// See https://goo.gl/pBUbZ9 for more details.
|
||||
type CreateVolumeOptions struct {
|
||||
Name string
|
||||
Driver string
|
||||
DriverOpts map[string]string
|
||||
}
|
||||
|
||||
// CreateVolume creates a volume on the server.
|
||||
//
|
||||
// See https://goo.gl/pBUbZ9 for more details.
|
||||
func (c *Client) CreateVolume(opts CreateVolumeOptions) (*Volume, error) {
|
||||
body, _, err := c.do("POST", "/volumes", doOptions{data: opts})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var volume Volume
|
||||
if err := json.Unmarshal(body, &volume); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &volume, nil
|
||||
}
|
||||
|
||||
// InspectVolume returns a volume by its name.
|
||||
//
|
||||
// See https://goo.gl/0g9A6i for more details.
|
||||
func (c *Client) InspectVolume(name string) (*Volume, error) {
|
||||
body, status, err := c.do("GET", "/volumes/"+name, doOptions{})
|
||||
if status == http.StatusNotFound {
|
||||
return nil, ErrNoSuchVolume
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var volume Volume
|
||||
if err := json.Unmarshal(body, &volume); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &volume, nil
|
||||
}
|
||||
|
||||
// RemoveVolume removes a volume by its name.
|
||||
//
|
||||
// See https://goo.gl/79GNQz for more details.
|
||||
func (c *Client) RemoveVolume(name string) error {
|
||||
_, status, err := c.do("DELETE", "/volumes/"+name, doOptions{})
|
||||
if status == http.StatusNotFound {
|
||||
return ErrNoSuchVolume
|
||||
}
|
||||
if status == http.StatusConflict {
|
||||
return ErrVolumeInUse
|
||||
}
|
||||
return err
|
||||
}
|
142
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/volume_test.go
generated
vendored
Normal file
142
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/volume_test.go
generated
vendored
Normal file
@ -0,0 +1,142 @@
|
||||
// Copyright 2015 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
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestListVolumes(t *testing.T) {
|
||||
volumesData := `[
|
||||
{
|
||||
"Name": "tardis",
|
||||
"Driver": "local",
|
||||
"Mountpoint": "/var/lib/docker/volumes/tardis"
|
||||
},
|
||||
{
|
||||
"Name": "foo",
|
||||
"Driver": "bar",
|
||||
"Mountpoint": "/var/lib/docker/volumes/bar"
|
||||
}
|
||||
]`
|
||||
body := `{ "Volumes": ` + volumesData + ` }`
|
||||
var expected []Volume
|
||||
if err := json.Unmarshal([]byte(volumesData), &expected); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
client := newTestClient(&FakeRoundTripper{message: body, status: http.StatusOK})
|
||||
volumes, err := client.ListVolumes(ListVolumesOptions{})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if !reflect.DeepEqual(volumes, expected) {
|
||||
t.Errorf("ListVolumes: Wrong return value. Want %#v. Got %#v.", expected, volumes)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateVolume(t *testing.T) {
|
||||
body := `{
|
||||
"Name": "tardis",
|
||||
"Driver": "local",
|
||||
"Mountpoint": "/var/lib/docker/volumes/tardis"
|
||||
}`
|
||||
var expected Volume
|
||||
if err := json.Unmarshal([]byte(body), &expected); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fakeRT := &FakeRoundTripper{message: body, status: http.StatusOK}
|
||||
client := newTestClient(fakeRT)
|
||||
volume, err := client.CreateVolume(
|
||||
CreateVolumeOptions{
|
||||
Name: "tardis",
|
||||
Driver: "local",
|
||||
DriverOpts: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(volume, &expected) {
|
||||
t.Errorf("CreateVolume: Wrong return value. Want %#v. Got %#v.", expected, volume)
|
||||
}
|
||||
req := fakeRT.requests[0]
|
||||
expectedMethod := "POST"
|
||||
if req.Method != expectedMethod {
|
||||
t.Errorf("CreateVolume(): Wrong HTTP method. Want %s. Got %s.", expectedMethod, req.Method)
|
||||
}
|
||||
u, _ := url.Parse(client.getURL("/volumes"))
|
||||
if req.URL.Path != u.Path {
|
||||
t.Errorf("CreateVolume(): Wrong request path. Want %q. Got %q.", u.Path, req.URL.Path)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInspectVolume(t *testing.T) {
|
||||
body := `{
|
||||
"Name": "tardis",
|
||||
"Driver": "local",
|
||||
"Mountpoint": "/var/lib/docker/volumes/tardis"
|
||||
}`
|
||||
var expected Volume
|
||||
if err := json.Unmarshal([]byte(body), &expected); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fakeRT := &FakeRoundTripper{message: body, status: http.StatusOK}
|
||||
client := newTestClient(fakeRT)
|
||||
name := "tardis"
|
||||
volume, err := client.InspectVolume(name)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(volume, &expected) {
|
||||
t.Errorf("InspectVolume: Wrong return value. Want %#v. Got %#v.", expected, volume)
|
||||
}
|
||||
req := fakeRT.requests[0]
|
||||
expectedMethod := "GET"
|
||||
if req.Method != expectedMethod {
|
||||
t.Errorf("InspectVolume(%q): Wrong HTTP method. Want %s. Got %s.", name, expectedMethod, req.Method)
|
||||
}
|
||||
u, _ := url.Parse(client.getURL("/volumes/" + name))
|
||||
if req.URL.Path != u.Path {
|
||||
t.Errorf("CreateVolume(%q): Wrong request path. Want %q. Got %q.", name, u.Path, req.URL.Path)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveVolume(t *testing.T) {
|
||||
name := "test"
|
||||
fakeRT := &FakeRoundTripper{message: "", status: http.StatusNoContent}
|
||||
client := newTestClient(fakeRT)
|
||||
if err := client.RemoveVolume(name); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
req := fakeRT.requests[0]
|
||||
expectedMethod := "DELETE"
|
||||
if req.Method != expectedMethod {
|
||||
t.Errorf("RemoveVolume(%q): Wrong HTTP method. Want %s. Got %s.", name, expectedMethod, req.Method)
|
||||
}
|
||||
u, _ := url.Parse(client.getURL("/volumes/" + name))
|
||||
if req.URL.Path != u.Path {
|
||||
t.Errorf("RemoveVolume(%q): Wrong request path. Want %q. Got %q.", name, u.Path, req.URL.Path)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveVolumeNotFound(t *testing.T) {
|
||||
client := newTestClient(&FakeRoundTripper{message: "no such volume", status: http.StatusNotFound})
|
||||
if err := client.RemoveVolume("test:"); err != ErrNoSuchVolume {
|
||||
t.Errorf("RemoveVolume: wrong error. Want %#v. Got %#v.", ErrNoSuchVolume, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveVolumeInUse(t *testing.T) {
|
||||
client := newTestClient(&FakeRoundTripper{message: "volume in use and cannot be removed", status: http.StatusConflict})
|
||||
if err := client.RemoveVolume("test:"); err != ErrVolumeInUse {
|
||||
t.Errorf("RemoveVolume: wrong error. Want %#v. Got %#v.", ErrVolumeInUse, err)
|
||||
}
|
||||
}
|
4
Godeps/_workspace/src/github.com/spf13/cobra/README.md
generated
vendored
4
Godeps/_workspace/src/github.com/spf13/cobra/README.md
generated
vendored
@ -422,6 +422,10 @@ func main() {
|
||||
|
||||
Cobra can generate a markdown formatted document based on the subcommands, flags, etc. A simple example of how to do this for your command can be found in [Markdown Docs](md_docs.md)
|
||||
|
||||
## Generating man pages for your command
|
||||
|
||||
Cobra can generate a man page based on the subcommands, flags, etc. A simple example of how to do this for your command can be found in [Man Docs](man_docs.md)
|
||||
|
||||
## Generating bash completions for your command
|
||||
|
||||
Cobra can generate a bash completions file. If you add more information to your command these completions can be amazingly powerful and flexible. Read more about [Bash Completions](bash_completions.md)
|
||||
|
21
Godeps/_workspace/src/github.com/spf13/cobra/bash_completions.go
generated
vendored
21
Godeps/_workspace/src/github.com/spf13/cobra/bash_completions.go
generated
vendored
@ -19,7 +19,6 @@ const (
|
||||
func preamble(out *bytes.Buffer) {
|
||||
fmt.Fprintf(out, `#!/bin/bash
|
||||
|
||||
|
||||
__debug()
|
||||
{
|
||||
if [[ -n ${BASH_COMP_DEBUG_FILE} ]]; then
|
||||
@ -27,6 +26,14 @@ __debug()
|
||||
fi
|
||||
}
|
||||
|
||||
# Homebrew on Macs have version 1.3 of bash-completion which doesn't include
|
||||
# _init_completion. This is a very minimal version of that function.
|
||||
__my_init_completion()
|
||||
{
|
||||
COMPREPLY=()
|
||||
_get_comp_words_by_ref cur prev words cword
|
||||
}
|
||||
|
||||
__index_of_word()
|
||||
{
|
||||
local w word=$1
|
||||
@ -188,7 +195,11 @@ func postscript(out *bytes.Buffer, name string) {
|
||||
fmt.Fprintf(out, "__start_%s()\n", name)
|
||||
fmt.Fprintf(out, `{
|
||||
local cur prev words cword
|
||||
_init_completion -s || return
|
||||
if declare -F _init_completions >/dev/null 2>&1; then
|
||||
_init_completion -s || return
|
||||
else
|
||||
__my_init_completion || return
|
||||
fi
|
||||
|
||||
local c=0
|
||||
local flags=()
|
||||
@ -212,7 +223,7 @@ func postscript(out *bytes.Buffer, name string) {
|
||||
func writeCommands(cmd *Command, out *bytes.Buffer) {
|
||||
fmt.Fprintf(out, " commands=()\n")
|
||||
for _, c := range cmd.Commands() {
|
||||
if len(c.Deprecated) > 0 {
|
||||
if len(c.Deprecated) > 0 || c == cmd.helpCommand {
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(out, " commands+=(%q)\n", c.Name())
|
||||
@ -292,7 +303,7 @@ func writeRequiredFlag(cmd *Command, out *bytes.Buffer) {
|
||||
fmt.Fprintf(out, " must_have_one_flag=()\n")
|
||||
flags := cmd.NonInheritedFlags()
|
||||
flags.VisitAll(func(flag *pflag.Flag) {
|
||||
for key, _ := range flag.Annotations {
|
||||
for key := range flag.Annotations {
|
||||
switch key {
|
||||
case BashCompOneRequiredFlag:
|
||||
format := " must_have_one_flag+=(\"--%s"
|
||||
@ -321,7 +332,7 @@ func writeRequiredNoun(cmd *Command, out *bytes.Buffer) {
|
||||
|
||||
func gen(cmd *Command, out *bytes.Buffer) {
|
||||
for _, c := range cmd.Commands() {
|
||||
if len(c.Deprecated) > 0 {
|
||||
if len(c.Deprecated) > 0 || c == cmd.helpCommand {
|
||||
continue
|
||||
}
|
||||
gen(c, out)
|
||||
|
28
Godeps/_workspace/src/github.com/spf13/cobra/cobra.go
generated
vendored
28
Godeps/_workspace/src/github.com/spf13/cobra/cobra.go
generated
vendored
@ -25,6 +25,13 @@ import (
|
||||
"text/template"
|
||||
)
|
||||
|
||||
var templateFuncs template.FuncMap = template.FuncMap{
|
||||
"trim": strings.TrimSpace,
|
||||
"rpad": rpad,
|
||||
"gt": Gt,
|
||||
"eq": Eq,
|
||||
}
|
||||
|
||||
var initializers []func()
|
||||
|
||||
// automatic prefix matching can be a dangerous thing to automatically enable in CLI tools.
|
||||
@ -39,6 +46,20 @@ var MousetrapHelpText string = `This is a command line tool
|
||||
You need to open cmd.exe and run it from there.
|
||||
`
|
||||
|
||||
//AddTemplateFunc adds a template function that's available to Usage and Help
|
||||
//template generation.
|
||||
func AddTemplateFunc(name string, tmplFunc interface{}) {
|
||||
templateFuncs[name] = tmplFunc
|
||||
}
|
||||
|
||||
//AddTemplateFuncs adds multiple template functions availalble to Usage and
|
||||
//Help template generation.
|
||||
func AddTemplateFuncs(tmplFuncs template.FuncMap) {
|
||||
for k, v := range tmplFuncs {
|
||||
templateFuncs[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
//OnInitialize takes a series of func() arguments and appends them to a slice of func().
|
||||
func OnInitialize(y ...func()) {
|
||||
for _, x := range y {
|
||||
@ -101,12 +122,7 @@ func rpad(s string, padding int) string {
|
||||
// tmpl executes the given template text on data, writing the result to w.
|
||||
func tmpl(w io.Writer, text string, data interface{}) error {
|
||||
t := template.New("top")
|
||||
t.Funcs(template.FuncMap{
|
||||
"trim": strings.TrimSpace,
|
||||
"rpad": rpad,
|
||||
"gt": Gt,
|
||||
"eq": Eq,
|
||||
})
|
||||
t.Funcs(templateFuncs)
|
||||
template.Must(t.Parse(text))
|
||||
return t.Execute(w, data)
|
||||
}
|
||||
|
18
Godeps/_workspace/src/github.com/spf13/cobra/cobra_test.go
generated
vendored
18
Godeps/_workspace/src/github.com/spf13/cobra/cobra_test.go
generated
vendored
@ -8,6 +8,7 @@ import (
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
"text/template"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
@ -971,3 +972,20 @@ func TestFlagOnPflagCommandLine(t *testing.T) {
|
||||
|
||||
checkResultContains(t, r, flagName)
|
||||
}
|
||||
|
||||
func TestAddTemplateFunctions(t *testing.T) {
|
||||
AddTemplateFunc("t", func() bool { return true })
|
||||
AddTemplateFuncs(template.FuncMap{
|
||||
"f": func() bool { return false },
|
||||
"h": func() string { return "Hello," },
|
||||
"w": func() string { return "world." }})
|
||||
|
||||
const usage = "Hello, world."
|
||||
|
||||
c := &Command{}
|
||||
c.SetUsageTemplate(`{{if t}}{{h}}{{end}}{{if f}}{{h}}{{end}} {{w}}`)
|
||||
|
||||
if us := c.UsageString(); us != usage {
|
||||
t.Errorf("c.UsageString() != \"%s\", is \"%s\"", usage, us)
|
||||
}
|
||||
}
|
||||
|
127
Godeps/_workspace/src/github.com/spf13/cobra/command.go
generated
vendored
127
Godeps/_workspace/src/github.com/spf13/cobra/command.go
generated
vendored
@ -66,14 +66,24 @@ type Command struct {
|
||||
// All functions get the same args, the arguments after the command name
|
||||
// PersistentPreRun: children of this command will inherit and execute
|
||||
PersistentPreRun func(cmd *Command, args []string)
|
||||
// PersistentPreRunE: PersistentPreRun but returns an error
|
||||
PersistentPreRunE func(cmd *Command, args []string) error
|
||||
// PreRun: children of this command will not inherit.
|
||||
PreRun func(cmd *Command, args []string)
|
||||
// PreRunE: PreRun but returns an error
|
||||
PreRunE func(cmd *Command, args []string) error
|
||||
// Run: Typically the actual work function. Most commands will only implement this
|
||||
Run func(cmd *Command, args []string)
|
||||
// RunE: Run but returns an error
|
||||
RunE func(cmd *Command, args []string) error
|
||||
// PostRun: run after the Run command.
|
||||
PostRun func(cmd *Command, args []string)
|
||||
// PostRunE: PostRun but returns an error
|
||||
PostRunE func(cmd *Command, args []string) error
|
||||
// PersistentPostRun: children of this command will inherit and execute after PostRun
|
||||
PersistentPostRun func(cmd *Command, args []string)
|
||||
// PersistentPostRunE: PersistentPostRun but returns an error
|
||||
PersistentPostRunE func(cmd *Command, args []string) error
|
||||
// Commands is the list of commands supported by this program.
|
||||
commands []*Command
|
||||
// Parent Command for this command
|
||||
@ -92,7 +102,6 @@ type Command struct {
|
||||
helpTemplate string // Can be defined by Application
|
||||
helpFunc func(*Command, []string) // Help can be defined by application
|
||||
helpCommand *Command // The help command
|
||||
helpFlagVal bool
|
||||
// The global normalization function that we can use on every pFlag set and children commands
|
||||
globNormFunc func(f *flag.FlagSet, name string) flag.NormalizedName
|
||||
}
|
||||
@ -179,32 +188,21 @@ func (c *Command) UsageFunc() (f func(*Command) error) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// HelpFunc returns either the function set by SetHelpFunc for this command
|
||||
// or a parent, or it returns a function which calls c.Help()
|
||||
func (c *Command) HelpFunc() func(*Command, []string) {
|
||||
if c.helpFunc != nil {
|
||||
return c.helpFunc
|
||||
cmd := c
|
||||
for cmd != nil {
|
||||
if cmd.helpFunc != nil {
|
||||
return cmd.helpFunc
|
||||
}
|
||||
cmd = cmd.parent
|
||||
}
|
||||
|
||||
if c.HasParent() {
|
||||
return c.parent.HelpFunc()
|
||||
} else {
|
||||
return func(c *Command, args []string) {
|
||||
if len(args) == 0 {
|
||||
// Help called without any topic, calling on root
|
||||
c.Root().Help()
|
||||
return
|
||||
}
|
||||
|
||||
cmd, _, e := c.Root().Find(args)
|
||||
if cmd == nil || e != nil {
|
||||
c.Printf("Unknown help topic %#q.", args)
|
||||
|
||||
c.Root().Usage()
|
||||
} else {
|
||||
err := cmd.Help()
|
||||
if err != nil {
|
||||
c.Println(err)
|
||||
}
|
||||
}
|
||||
return func(*Command, []string) {
|
||||
err := c.Help()
|
||||
if err != nil {
|
||||
c.Println(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -270,7 +268,7 @@ Global Flags:
|
||||
{{.InheritedFlags.FlagUsages}}{{end}}{{if .HasHelpSubCommands}}
|
||||
|
||||
Additional help topics: {{range .Commands}}{{if .IsHelpCommand}}
|
||||
{{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}}{{end}}{{end}}{{ if .HasSubCommands }}
|
||||
{{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{ if .HasSubCommands }}
|
||||
|
||||
Use "{{.CommandPath}} [command] --help" for more information about a command.
|
||||
{{end}}`
|
||||
@ -450,13 +448,24 @@ func (c *Command) execute(a []string) (err error) {
|
||||
c.Printf("Command %q is deprecated, %s\n", c.Name(), c.Deprecated)
|
||||
}
|
||||
|
||||
// initialize help flag as the last point possible to allow for user
|
||||
// overriding
|
||||
c.initHelpFlag()
|
||||
|
||||
err = c.ParseFlags(a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// If help is called, regardless of other flags, return we want help
|
||||
// Also say we need help if c.Run is nil.
|
||||
if c.helpFlagVal || !c.Runnable() {
|
||||
// Also say we need help if the command isn't runnable.
|
||||
helpVal, err := c.Flags().GetBool("help")
|
||||
if err != nil {
|
||||
// should be impossible to get here as we always declare a help
|
||||
// flag in initHelpFlag()
|
||||
c.Println("\"help\" flag declared as non-bool. Please correct your code")
|
||||
return err
|
||||
}
|
||||
if helpVal || !c.Runnable() {
|
||||
return flag.ErrHelp
|
||||
}
|
||||
|
||||
@ -464,22 +473,45 @@ func (c *Command) execute(a []string) (err error) {
|
||||
argWoFlags := c.Flags().Args()
|
||||
|
||||
for p := c; p != nil; p = p.Parent() {
|
||||
if p.PersistentPreRun != nil {
|
||||
if p.PersistentPreRunE != nil {
|
||||
if err := p.PersistentPostRunE(c, argWoFlags); err != nil {
|
||||
return err
|
||||
}
|
||||
break
|
||||
} else if p.PersistentPreRun != nil {
|
||||
p.PersistentPreRun(c, argWoFlags)
|
||||
break
|
||||
}
|
||||
}
|
||||
if c.PreRun != nil {
|
||||
if c.PreRunE != nil {
|
||||
if err := c.PreRunE(c, argWoFlags); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if c.PreRun != nil {
|
||||
c.PreRun(c, argWoFlags)
|
||||
}
|
||||
|
||||
c.Run(c, argWoFlags)
|
||||
|
||||
if c.PostRun != nil {
|
||||
if c.RunE != nil {
|
||||
if err := c.RunE(c, argWoFlags); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
c.Run(c, argWoFlags)
|
||||
}
|
||||
if c.PostRunE != nil {
|
||||
if err := c.PostRunE(c, argWoFlags); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if c.PostRun != nil {
|
||||
c.PostRun(c, argWoFlags)
|
||||
}
|
||||
for p := c; p != nil; p = p.Parent() {
|
||||
if p.PersistentPostRun != nil {
|
||||
if p.PersistentPostRunE != nil {
|
||||
if err := p.PersistentPostRunE(c, argWoFlags); err != nil {
|
||||
return err
|
||||
}
|
||||
break
|
||||
} else if p.PersistentPostRun != nil {
|
||||
p.PersistentPostRun(c, argWoFlags)
|
||||
break
|
||||
}
|
||||
@ -526,7 +558,7 @@ func (c *Command) Execute() (err error) {
|
||||
|
||||
// initialize help as the last point possible to allow for user
|
||||
// overriding
|
||||
c.initHelp()
|
||||
c.initHelpCmd()
|
||||
|
||||
var args []string
|
||||
|
||||
@ -550,7 +582,7 @@ func (c *Command) Execute() (err error) {
|
||||
err = cmd.execute(flags)
|
||||
if err != nil {
|
||||
if err == flag.ErrHelp {
|
||||
cmd.Help()
|
||||
cmd.HelpFunc()(cmd, args)
|
||||
return nil
|
||||
}
|
||||
c.Println(cmd.UsageString())
|
||||
@ -560,7 +592,13 @@ func (c *Command) Execute() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Command) initHelp() {
|
||||
func (c *Command) initHelpFlag() {
|
||||
if c.Flags().Lookup("help") == nil {
|
||||
c.Flags().BoolP("help", "h", false, "help for "+c.Name())
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Command) initHelpCmd() {
|
||||
if c.helpCommand == nil {
|
||||
if !c.HasSubCommands() {
|
||||
return
|
||||
@ -571,9 +609,19 @@ func (c *Command) initHelp() {
|
||||
Short: "Help about any command",
|
||||
Long: `Help provides help for any command in the application.
|
||||
Simply type ` + c.Name() + ` help [path to command] for full details.`,
|
||||
Run: c.HelpFunc(),
|
||||
PersistentPreRun: func(cmd *Command, args []string) {},
|
||||
PersistentPostRun: func(cmd *Command, args []string) {},
|
||||
|
||||
Run: func(c *Command, args []string) {
|
||||
cmd, _, e := c.Root().Find(args)
|
||||
if cmd == nil || e != nil {
|
||||
c.Printf("Unknown help topic %#q.", args)
|
||||
c.Root().Usage()
|
||||
} else {
|
||||
helpFunc := cmd.HelpFunc()
|
||||
helpFunc(cmd, args)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
c.AddCommand(c.helpCommand)
|
||||
@ -794,7 +842,7 @@ func (c *Command) HasExample() bool {
|
||||
|
||||
// Determine if the command is itself runnable
|
||||
func (c *Command) Runnable() bool {
|
||||
return c.Run != nil
|
||||
return c.Run != nil || c.RunE != nil
|
||||
}
|
||||
|
||||
// Determine if the command has children commands
|
||||
@ -859,7 +907,6 @@ func (c *Command) Flags() *flag.FlagSet {
|
||||
c.flagErrorBuf = new(bytes.Buffer)
|
||||
}
|
||||
c.flags.SetOutput(c.flagErrorBuf)
|
||||
c.PersistentFlags().BoolVarP(&c.helpFlagVal, "help", "h", false, "help for "+c.Name())
|
||||
}
|
||||
return c.flags
|
||||
}
|
||||
|
36
Godeps/_workspace/src/github.com/spf13/cobra/doc_util.go
generated
vendored
Normal file
36
Godeps/_workspace/src/github.com/spf13/cobra/doc_util.go
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
// Copyright 2015 Red Hat 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.
|
||||
|
||||
package cobra
|
||||
|
||||
import ()
|
||||
|
||||
// Test to see if we have a reason to print See Also information in docs
|
||||
// Basically this is a test for a parent commend or a subcommand which is
|
||||
// both not deprecated and not the autogenerated help command.
|
||||
func (cmd *Command) hasSeeAlso() bool {
|
||||
if cmd.HasParent() {
|
||||
return true
|
||||
}
|
||||
children := cmd.Commands()
|
||||
if len(children) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, c := range children {
|
||||
if len(c.Deprecated) != 0 || c == cmd.helpCommand {
|
||||
continue
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
164
Godeps/_workspace/src/github.com/spf13/cobra/man_docs.go
generated
vendored
Normal file
164
Godeps/_workspace/src/github.com/spf13/cobra/man_docs.go
generated
vendored
Normal file
@ -0,0 +1,164 @@
|
||||
// Copyright 2015 Red Hat 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.
|
||||
|
||||
package cobra
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
mangen "github.com/cpuguy83/go-md2man/md2man"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
func GenManTree(cmd *Command, projectName, dir string) {
|
||||
cmd.GenManTree(projectName, dir)
|
||||
}
|
||||
|
||||
func (cmd *Command) GenManTree(projectName, dir string) {
|
||||
for _, c := range cmd.Commands() {
|
||||
if len(c.Deprecated) != 0 || c == cmd.helpCommand {
|
||||
continue
|
||||
}
|
||||
GenManTree(c, projectName, dir)
|
||||
}
|
||||
out := new(bytes.Buffer)
|
||||
|
||||
cmd.GenMan(projectName, out)
|
||||
|
||||
filename := cmd.CommandPath()
|
||||
filename = dir + strings.Replace(filename, " ", "-", -1) + ".1"
|
||||
outFile, err := os.Create(filename)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer outFile.Close()
|
||||
_, err = outFile.Write(out.Bytes())
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func GenMan(cmd *Command, projectName string, out *bytes.Buffer) {
|
||||
cmd.GenMan(projectName, out)
|
||||
}
|
||||
|
||||
func (cmd *Command) GenMan(projectName string, out *bytes.Buffer) {
|
||||
|
||||
buf := genMarkdown(cmd, projectName)
|
||||
final := mangen.Render(buf)
|
||||
out.Write(final)
|
||||
}
|
||||
|
||||
func manPreamble(out *bytes.Buffer, projectName, name, short, long string) {
|
||||
fmt.Fprintf(out, `%% %s(1)
|
||||
# NAME
|
||||
`, projectName)
|
||||
fmt.Fprintf(out, "%s \\- %s\n\n", name, short)
|
||||
fmt.Fprintf(out, "# SYNOPSIS\n")
|
||||
fmt.Fprintf(out, "**%s** [OPTIONS]\n\n", name)
|
||||
fmt.Fprintf(out, "# DESCRIPTION\n")
|
||||
fmt.Fprintf(out, "%s\n\n", long)
|
||||
}
|
||||
|
||||
func manPrintFlags(out *bytes.Buffer, flags *pflag.FlagSet) {
|
||||
flags.VisitAll(func(flag *pflag.Flag) {
|
||||
if len(flag.Deprecated) > 0 {
|
||||
return
|
||||
}
|
||||
format := ""
|
||||
if len(flag.Shorthand) > 0 {
|
||||
format = "**-%s**, **--%s**"
|
||||
} else {
|
||||
format = "%s**--%s**"
|
||||
}
|
||||
if len(flag.NoOptDefVal) > 0 {
|
||||
format = format + "["
|
||||
}
|
||||
if flag.Value.Type() == "string" {
|
||||
// put quotes on the value
|
||||
format = format + "=%q"
|
||||
} else {
|
||||
format = format + "=%s"
|
||||
}
|
||||
if len(flag.NoOptDefVal) > 0 {
|
||||
format = format + "]"
|
||||
}
|
||||
format = format + "\n\t%s\n\n"
|
||||
fmt.Fprintf(out, format, flag.Shorthand, flag.Name, flag.DefValue, flag.Usage)
|
||||
})
|
||||
}
|
||||
|
||||
func manPrintOptions(out *bytes.Buffer, command *Command) {
|
||||
flags := command.NonInheritedFlags()
|
||||
if flags.HasFlags() {
|
||||
fmt.Fprintf(out, "# OPTIONS\n")
|
||||
manPrintFlags(out, flags)
|
||||
fmt.Fprintf(out, "\n")
|
||||
}
|
||||
flags = command.InheritedFlags()
|
||||
if flags.HasFlags() {
|
||||
fmt.Fprintf(out, "# OPTIONS INHERITED FROM PARENT COMMANDS\n")
|
||||
manPrintFlags(out, flags)
|
||||
fmt.Fprintf(out, "\n")
|
||||
}
|
||||
}
|
||||
|
||||
func genMarkdown(cmd *Command, projectName string) []byte {
|
||||
// something like `rootcmd subcmd1 subcmd2`
|
||||
commandName := cmd.CommandPath()
|
||||
// something like `rootcmd-subcmd1-subcmd2`
|
||||
dashCommandName := strings.Replace(commandName, " ", "-", -1)
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
short := cmd.Short
|
||||
long := cmd.Long
|
||||
if len(long) == 0 {
|
||||
long = short
|
||||
}
|
||||
|
||||
manPreamble(buf, projectName, commandName, short, long)
|
||||
manPrintOptions(buf, cmd)
|
||||
|
||||
if len(cmd.Example) > 0 {
|
||||
fmt.Fprintf(buf, "# EXAMPLE\n")
|
||||
fmt.Fprintf(buf, "```\n%s\n```\n", cmd.Example)
|
||||
}
|
||||
|
||||
if cmd.hasSeeAlso() {
|
||||
fmt.Fprintf(buf, "# SEE ALSO\n")
|
||||
if cmd.HasParent() {
|
||||
fmt.Fprintf(buf, "**%s(1)**, ", cmd.Parent().CommandPath())
|
||||
}
|
||||
|
||||
children := cmd.Commands()
|
||||
sort.Sort(byName(children))
|
||||
for _, c := range children {
|
||||
if len(c.Deprecated) != 0 || c == cmd.helpCommand {
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(buf, "**%s-%s(1)**, ", dashCommandName, c.Name())
|
||||
}
|
||||
fmt.Fprintf(buf, "\n")
|
||||
}
|
||||
|
||||
fmt.Fprintf(buf, "# HISTORY\n%s Auto generated by spf13/cobra\n", time.Now().UTC())
|
||||
return buf.Bytes()
|
||||
}
|
21
Godeps/_workspace/src/github.com/spf13/cobra/man_docs.md
generated
vendored
Normal file
21
Godeps/_workspace/src/github.com/spf13/cobra/man_docs.md
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
# Generating Man Pages For Your Own cobra.Command
|
||||
|
||||
Generating bash completions from a cobra command is incredibly easy. An example is as follows:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cmd := &cobra.Command{
|
||||
Use: "test",
|
||||
Short: "my test program",
|
||||
}
|
||||
cmd.GenManTree("/tmp")
|
||||
}
|
||||
```
|
||||
|
||||
That will get you a man page `/tmp/test.1`
|
71
Godeps/_workspace/src/github.com/spf13/cobra/man_docs_test.go
generated
vendored
Normal file
71
Godeps/_workspace/src/github.com/spf13/cobra/man_docs_test.go
generated
vendored
Normal file
@ -0,0 +1,71 @@
|
||||
package cobra
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var _ = fmt.Println
|
||||
var _ = os.Stderr
|
||||
|
||||
func translate(in string) string {
|
||||
return strings.Replace(in, "-", "\\-", -1)
|
||||
}
|
||||
|
||||
func TestGenManDoc(t *testing.T) {
|
||||
c := initializeWithRootCmd()
|
||||
// Need two commands to run the command alphabetical sort
|
||||
cmdEcho.AddCommand(cmdTimes, cmdEchoSub, cmdDeprecated)
|
||||
c.AddCommand(cmdPrint, cmdEcho)
|
||||
cmdRootWithRun.PersistentFlags().StringVarP(&flags2a, "rootflag", "r", "two", strtwoParentHelp)
|
||||
|
||||
out := new(bytes.Buffer)
|
||||
|
||||
// We generate on a subcommand so we have both subcommands and parents
|
||||
cmdEcho.GenMan("PROJECT", out)
|
||||
found := out.String()
|
||||
|
||||
// Our description
|
||||
expected := translate(cmdEcho.Name())
|
||||
if !strings.Contains(found, expected) {
|
||||
t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found)
|
||||
}
|
||||
|
||||
// Better have our example
|
||||
expected = translate(cmdEcho.Name())
|
||||
if !strings.Contains(found, expected) {
|
||||
t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found)
|
||||
}
|
||||
|
||||
// A local flag
|
||||
expected = "boolone"
|
||||
if !strings.Contains(found, expected) {
|
||||
t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found)
|
||||
}
|
||||
|
||||
// persistent flag on parent
|
||||
expected = "rootflag"
|
||||
if !strings.Contains(found, expected) {
|
||||
t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found)
|
||||
}
|
||||
|
||||
// We better output info about our parent
|
||||
expected = translate(cmdRootWithRun.Name())
|
||||
if !strings.Contains(found, expected) {
|
||||
t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found)
|
||||
}
|
||||
|
||||
// And about subcommands
|
||||
expected = translate(cmdEchoSub.Name())
|
||||
if !strings.Contains(found, expected) {
|
||||
t.Errorf("Unexpected response.\nExpecting to contain: \n %q\nGot:\n %q\n", expected, found)
|
||||
}
|
||||
|
||||
unexpected := translate(cmdDeprecated.Name())
|
||||
if strings.Contains(found, unexpected) {
|
||||
t.Errorf("Unexpected response.\nFound: %v\nBut should not have!!\n", unexpected)
|
||||
}
|
||||
}
|
31
Godeps/_workspace/src/github.com/spf13/cobra/md_docs.go
generated
vendored
31
Godeps/_workspace/src/github.com/spf13/cobra/md_docs.go
generated
vendored
@ -47,10 +47,18 @@ func (s byName) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
func (s byName) Less(i, j int) bool { return s[i].Name() < s[j].Name() }
|
||||
|
||||
func GenMarkdown(cmd *Command, out *bytes.Buffer) {
|
||||
GenMarkdownCustom(cmd, out, func(s string) string { return s })
|
||||
cmd.GenMarkdown(out)
|
||||
}
|
||||
|
||||
func (cmd *Command) GenMarkdown(out *bytes.Buffer) {
|
||||
cmd.GenMarkdownCustom(out, func(s string) string { return s })
|
||||
}
|
||||
|
||||
func GenMarkdownCustom(cmd *Command, out *bytes.Buffer, linkHandler func(string) string) {
|
||||
cmd.GenMarkdownCustom(out, linkHandler)
|
||||
}
|
||||
|
||||
func (cmd *Command) GenMarkdownCustom(out *bytes.Buffer, linkHandler func(string) string) {
|
||||
name := cmd.CommandPath()
|
||||
|
||||
short := cmd.Short
|
||||
@ -75,7 +83,7 @@ func GenMarkdownCustom(cmd *Command, out *bytes.Buffer, linkHandler func(string)
|
||||
|
||||
printOptions(out, cmd, name)
|
||||
|
||||
if len(cmd.Commands()) > 0 || cmd.HasParent() {
|
||||
if cmd.hasSeeAlso() {
|
||||
fmt.Fprintf(out, "### SEE ALSO\n")
|
||||
if cmd.HasParent() {
|
||||
parent := cmd.Parent()
|
||||
@ -89,7 +97,7 @@ func GenMarkdownCustom(cmd *Command, out *bytes.Buffer, linkHandler func(string)
|
||||
sort.Sort(byName(children))
|
||||
|
||||
for _, child := range children {
|
||||
if len(child.Deprecated) > 0 {
|
||||
if len(child.Deprecated) > 0 || child == cmd.helpCommand {
|
||||
continue
|
||||
}
|
||||
cname := name + " " + child.Name()
|
||||
@ -104,18 +112,29 @@ func GenMarkdownCustom(cmd *Command, out *bytes.Buffer, linkHandler func(string)
|
||||
}
|
||||
|
||||
func GenMarkdownTree(cmd *Command, dir string) {
|
||||
cmd.GenMarkdownTree(dir)
|
||||
}
|
||||
|
||||
func (cmd *Command) GenMarkdownTree(dir string) {
|
||||
identity := func(s string) string { return s }
|
||||
emptyStr := func(s string) string { return "" }
|
||||
GenMarkdownTreeCustom(cmd, dir, emptyStr, identity)
|
||||
cmd.GenMarkdownTreeCustom(dir, emptyStr, identity)
|
||||
}
|
||||
|
||||
func GenMarkdownTreeCustom(cmd *Command, dir string, filePrepender func(string) string, linkHandler func(string) string) {
|
||||
cmd.GenMarkdownTreeCustom(dir, filePrepender, linkHandler)
|
||||
}
|
||||
|
||||
func (cmd *Command) GenMarkdownTreeCustom(dir string, filePrepender func(string) string, linkHandler func(string) string) {
|
||||
for _, c := range cmd.Commands() {
|
||||
GenMarkdownTreeCustom(c, dir, filePrepender, linkHandler)
|
||||
if len(c.Deprecated) != 0 || c == cmd.helpCommand {
|
||||
continue
|
||||
}
|
||||
c.GenMarkdownTreeCustom(dir, filePrepender, linkHandler)
|
||||
}
|
||||
out := new(bytes.Buffer)
|
||||
|
||||
GenMarkdownCustom(cmd, out, linkHandler)
|
||||
cmd.GenMarkdownCustom(out, linkHandler)
|
||||
|
||||
filename := cmd.CommandPath()
|
||||
filename = dir + strings.Replace(filename, " ", "_", -1) + ".md"
|
||||
|
@ -114,7 +114,7 @@ You can also view recordings of past events and presentations on our [Media page
|
||||
For Q&A, our threads are at:
|
||||
|
||||
* [Stack Overflow](http://stackoverflow.com/questions/tagged/kubernetes)
|
||||
* [BotBot.me (IRC)](https://botbot.me/freenode/google-containers/)
|
||||
* [Slack](/docs/troubleshooting.md#slack)
|
||||
|
||||
#### Want to do more than just 'discuss' Kubernetes?
|
||||
|
||||
|
@ -11879,6 +11879,9 @@
|
||||
"v1.PersistentVolumeClaimList": {
|
||||
"id": "v1.PersistentVolumeClaimList",
|
||||
"description": "PersistentVolumeClaimList is a list of PersistentVolumeClaim items.",
|
||||
"required": [
|
||||
"items"
|
||||
],
|
||||
"properties": {
|
||||
"kind": {
|
||||
"type": "string",
|
||||
@ -11990,6 +11993,9 @@
|
||||
"v1.PersistentVolumeList": {
|
||||
"id": "v1.PersistentVolumeList",
|
||||
"description": "PersistentVolumeList is a list of PersistentVolume items.",
|
||||
"required": [
|
||||
"items"
|
||||
],
|
||||
"properties": {
|
||||
"kind": {
|
||||
"type": "string",
|
||||
|
@ -1,22 +1,22 @@
|
||||
apiVersion: v1
|
||||
kind: ReplicationController
|
||||
metadata:
|
||||
name: kube-dns-v8
|
||||
name: kube-dns-v9
|
||||
namespace: kube-system
|
||||
labels:
|
||||
k8s-app: kube-dns
|
||||
version: v8
|
||||
version: v9
|
||||
kubernetes.io/cluster-service: "true"
|
||||
spec:
|
||||
replicas: {{ pillar['dns_replicas'] }}
|
||||
selector:
|
||||
k8s-app: kube-dns
|
||||
version: v8
|
||||
version: v9
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
k8s-app: kube-dns
|
||||
version: v8
|
||||
version: v9
|
||||
kubernetes.io/cluster-service: "true"
|
||||
spec:
|
||||
containers:
|
||||
@ -73,6 +73,13 @@ spec:
|
||||
scheme: HTTP
|
||||
initialDelaySeconds: 30
|
||||
timeoutSeconds: 5
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: 8080
|
||||
scheme: HTTP
|
||||
initialDelaySeconds: 1
|
||||
timeoutSeconds: 5
|
||||
- name: healthz
|
||||
image: gcr.io/google_containers/exechealthz:1.0
|
||||
resources:
|
||||
|
@ -2,7 +2,7 @@
|
||||
This directory contains the source files needed to make a Docker image
|
||||
that collects Docker container log files using [Fluentd](http://www.fluentd.org/)
|
||||
and sends them to an instance of [Elasticsearch](http://www.elasticsearch.org/).
|
||||
This image is designed to be used as part of the [Kubernetes](https://github.com/GoogleCloudPlatform/kubernetes)
|
||||
This image is designed to be used as part of the [Kubernetes](https://github.com/kubernetes/kubernetes)
|
||||
cluster bring up process. The image resides at DockerHub under the name
|
||||
[kubernetes/fluentd-eslasticsearch](https://registry.hub.docker.com/u/kubernetes/fluentd-elasticsearch/).
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
This directory contains the source files needed to make a Docker image
|
||||
that collects Docker container log files using [Fluentd](http://www.fluentd.org/)
|
||||
and sends them to GCP.
|
||||
This image is designed to be used as part of the [Kubernetes](https://github.com/GoogleCloudPlatform/kubernetes)
|
||||
This image is designed to be used as part of the [Kubernetes](https://github.com/kubernetes/kubernetes)
|
||||
cluster bring up process. The image resides at DockerHub under the name
|
||||
[kubernetes/fluentd-gcp](https://registry.hub.docker.com/u/kubernetes/fluentd-gcp/).
|
||||
|
||||
|
@ -87,7 +87,7 @@ DNS_REPLICAS=1
|
||||
ENABLE_CLUSTER_UI="${KUBE_ENABLE_CLUSTER_UI:-true}"
|
||||
|
||||
# Admission Controllers to invoke prior to persisting objects in cluster
|
||||
ADMISSION_CONTROL=NamespaceLifecycle,NamespaceExists,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota
|
||||
ADMISSION_CONTROL=NamespaceLifecycle,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota
|
||||
|
||||
# Optional: Enable/disable public IP assignment for minions.
|
||||
# Important Note: disable only if you have setup a NAT instance for internet access and configured appropriate routes!
|
||||
|
@ -83,7 +83,7 @@ DNS_REPLICAS=1
|
||||
ENABLE_CLUSTER_UI="${KUBE_ENABLE_CLUSTER_UI:-true}"
|
||||
|
||||
# Admission Controllers to invoke prior to persisting objects in cluster
|
||||
ADMISSION_CONTROL=NamespaceLifecycle,NamespaceExists,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota
|
||||
ADMISSION_CONTROL=NamespaceLifecycle,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota
|
||||
|
||||
# Optional: Enable/disable public IP assignment for minions.
|
||||
# Important Note: disable only if you have setup a NAT instance for internet access and configured appropriate routes!
|
||||
|
@ -55,4 +55,4 @@ ENABLE_CLUSTER_MONITORING="${KUBE_ENABLE_CLUSTER_MONITORING:-influxdb}"
|
||||
ENABLE_CLUSTER_UI="${KUBE_ENABLE_CLUSTER_UI:-true}"
|
||||
|
||||
# Admission Controllers to invoke prior to persisting objects in cluster
|
||||
ADMISSION_CONTROL=NamespaceLifecycle,NamespaceExists,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota
|
||||
ADMISSION_CONTROL=NamespaceLifecycle,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota
|
||||
|
@ -117,6 +117,15 @@ function clear-kubeconfig() {
|
||||
echo "Cleared config for ${CONTEXT} from ${KUBECONFIG}"
|
||||
}
|
||||
|
||||
|
||||
function tear_down_alive_resources() {
|
||||
local kubectl="${KUBE_ROOT}/cluster/kubectl.sh"
|
||||
"${kubectl}" delete rc --all
|
||||
"${kubectl}" delete pods --all
|
||||
"${kubectl}" delete svc --all
|
||||
"${kubectl}" delete pvc --all
|
||||
}
|
||||
|
||||
# Gets username, password for the current-context in kubeconfig, if they exist.
|
||||
# Assumed vars:
|
||||
# KUBECONFIG # if unset, defaults to global
|
||||
@ -278,3 +287,4 @@ function tars_from_version() {
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
|
@ -96,7 +96,7 @@ if [[ "${ENABLE_NODE_AUTOSCALER}" == "true" ]]; then
|
||||
fi
|
||||
|
||||
# Admission Controllers to invoke prior to persisting objects in cluster
|
||||
ADMISSION_CONTROL=NamespaceLifecycle,NamespaceExists,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota
|
||||
ADMISSION_CONTROL=NamespaceLifecycle,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota
|
||||
|
||||
# Optional: if set to true kube-up will automatically check for existing resources and clean them up.
|
||||
KUBE_UP_AUTOMATIC_CLEANUP=${KUBE_UP_AUTOMATIC_CLEANUP:-false}
|
||||
|
@ -18,9 +18,9 @@
|
||||
# gcloud multiplexing for shared GCE/GKE tests.
|
||||
GCLOUD=gcloud
|
||||
ZONE=${KUBE_GCE_ZONE:-us-central1-b}
|
||||
MASTER_SIZE=${MASTER_SIZE:-n1-standard-1}
|
||||
MINION_SIZE=${MINION_SIZE:-n1-standard-1}
|
||||
NUM_MINIONS=${NUM_MINIONS:-2}
|
||||
MASTER_SIZE=${MASTER_SIZE:-n1-standard-2}
|
||||
MINION_SIZE=${MINION_SIZE:-n1-standard-2}
|
||||
NUM_MINIONS=${NUM_MINIONS:-3}
|
||||
MASTER_DISK_TYPE=pd-ssd
|
||||
MASTER_DISK_SIZE=${MASTER_DISK_SIZE:-20GB}
|
||||
MINION_DISK_TYPE=pd-standard
|
||||
@ -100,7 +100,7 @@ if [[ "${ENABLE_NODE_AUTOSCALER}" == "true" ]]; then
|
||||
TARGET_NODE_UTILIZATION="${KUBE_TARGET_NODE_UTILIZATION:-0.7}"
|
||||
fi
|
||||
|
||||
ADMISSION_CONTROL=NamespaceLifecycle,NamespaceExists,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota
|
||||
ADMISSION_CONTROL=NamespaceLifecycle,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota
|
||||
|
||||
# Optional: if set to true kube-up will automatically check for existing resources and clean them up.
|
||||
KUBE_UP_AUTOMATIC_CLEANUP=${KUBE_UP_AUTOMATIC_CLEANUP:-false}
|
||||
|
@ -540,10 +540,11 @@ grains:
|
||||
- kubernetes-master
|
||||
cloud: gce
|
||||
EOF
|
||||
if ! [[ -z "${PROJECT_ID:-}" ]] && ! [[ -z "${TOKEN_URL:-}" ]] && ! [[ -z "${NODE_NETWORK:-}" ]] ; then
|
||||
if ! [[ -z "${PROJECT_ID:-}" ]] && ! [[ -z "${TOKEN_URL:-}" ]] && ! [[ -z "${TOKEN_BODY:-}" ]] && ! [[ -z "${NODE_NETWORK:-}" ]] ; then
|
||||
cat <<EOF >/etc/gce.conf
|
||||
[global]
|
||||
token-url = ${TOKEN_URL}
|
||||
token-body = ${TOKEN_BODY}
|
||||
project-id = ${PROJECT_ID}
|
||||
network-name = ${NODE_NETWORK}
|
||||
EOF
|
||||
|
@ -21,6 +21,11 @@ set -o nounset
|
||||
set -o pipefail
|
||||
|
||||
KUBE_ROOT=$(dirname "${BASH_SOURCE}")/..
|
||||
|
||||
if [ -f "${KUBE_ROOT}/cluster/env.sh" ]; then
|
||||
source "${KUBE_ROOT}/cluster/env.sh"
|
||||
fi
|
||||
|
||||
source "${KUBE_ROOT}/cluster/kube-env.sh"
|
||||
source "${KUBE_ROOT}/cluster/kube-util.sh"
|
||||
|
||||
|
@ -24,6 +24,11 @@ set -o nounset
|
||||
set -o pipefail
|
||||
|
||||
KUBE_ROOT=$(dirname "${BASH_SOURCE}")/..
|
||||
|
||||
if [ -f "${KUBE_ROOT}/cluster/env.sh" ]; then
|
||||
source "${KUBE_ROOT}/cluster/env.sh"
|
||||
fi
|
||||
|
||||
source "${KUBE_ROOT}/cluster/kube-env.sh"
|
||||
source "${KUBE_ROOT}/cluster/kube-util.sh"
|
||||
|
||||
|
@ -25,6 +25,11 @@ set -o nounset
|
||||
set -o pipefail
|
||||
|
||||
KUBE_ROOT=$(dirname "${BASH_SOURCE}")/..
|
||||
|
||||
if [ -f "${KUBE_ROOT}/cluster/env.sh" ]; then
|
||||
source "${KUBE_ROOT}/cluster/env.sh"
|
||||
fi
|
||||
|
||||
source "${KUBE_ROOT}/cluster/kube-env.sh"
|
||||
source "${KUBE_ROOT}/cluster/kube-util.sh"
|
||||
|
||||
|
@ -195,7 +195,7 @@ function wait-cluster-readiness {
|
||||
|
||||
local timeout=120
|
||||
while [[ $timeout -ne 0 ]]; do
|
||||
nb_ready_minions=$("${kubectl}" get nodes -o template -t "{{range.items}}{{range.status.conditions}}{{.type}}{{end}}:{{end}}" --api-version=v1 2>/dev/null | tr ':' '\n' | grep -c Ready || true)
|
||||
nb_ready_minions=$("${kubectl}" get nodes -o go-template="{{range.items}}{{range.status.conditions}}{{.type}}{{end}}:{{end}}" --api-version=v1 2>/dev/null | tr ':' '\n' | grep -c Ready || true)
|
||||
echo "Nb ready minions: $nb_ready_minions / $NUM_MINIONS"
|
||||
if [[ "$nb_ready_minions" -eq "$NUM_MINIONS" ]]; then
|
||||
return 0
|
||||
@ -294,7 +294,7 @@ function upload-server-tars {
|
||||
tar -x -C "$POOL_PATH/kubernetes" -f "$SERVER_BINARY_TAR" kubernetes
|
||||
rm -rf "$POOL_PATH/kubernetes/bin"
|
||||
mv "$POOL_PATH/kubernetes/kubernetes/server/bin" "$POOL_PATH/kubernetes/bin"
|
||||
rmdir "$POOL_PATH/kubernetes/kubernetes/server" "$POOL_PATH/kubernetes/kubernetes"
|
||||
rm -fr "$POOL_PATH/kubernetes/kubernetes"
|
||||
}
|
||||
|
||||
# Update a kubernetes cluster with latest source
|
||||
|
@ -89,7 +89,7 @@ apiserver:
|
||||
--external-hostname=apiserver
|
||||
--etcd-servers=http://etcd:4001
|
||||
--port=8888
|
||||
--admission-control=NamespaceLifecycle,NamespaceExists,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota
|
||||
--admission-control=NamespaceLifecycle,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota
|
||||
--authorization-mode=AlwaysAllow
|
||||
--token-auth-file=/var/run/kubernetes/auth/token-users
|
||||
--basic-auth-file=/var/run/kubernetes/auth/basic-users
|
||||
|
@ -198,7 +198,7 @@ function run-until-success() {
|
||||
# returns a list of <namespace>/<name> pairs (nsnames)
|
||||
function get-addon-nsnames-from-server() {
|
||||
local -r obj_type=$1
|
||||
"${KUBECTL}" get "${obj_type}" --all-namespaces -o template -t "{{range.items}}{{.metadata.namespace}}/{{.metadata.name}} {{end}}" --api-version=v1 -l kubernetes.io/cluster-service=true
|
||||
"${KUBECTL}" get "${obj_type}" --all-namespaces -o go-template="{{range.items}}{{.metadata.namespace}}/{{.metadata.name}} {{end}}" --api-version=v1 -l kubernetes.io/cluster-service=true
|
||||
}
|
||||
|
||||
# returns the characters after the last separator (including)
|
||||
|
@ -174,7 +174,7 @@ start_addon /etc/kubernetes/addons/namespace.yaml 100 10 "" &
|
||||
token_found=""
|
||||
while [ -z "${token_found}" ]; do
|
||||
sleep .5
|
||||
token_found=$(${KUBECTL} get --namespace="${SYSTEM_NAMESPACE}" serviceaccount default -o template -t "{{with index .secrets 0}}{{.name}}{{end}}" || true)
|
||||
token_found=$(${KUBECTL} get --namespace="${SYSTEM_NAMESPACE}" serviceaccount default -o go-template="{{with index .secrets 0}}{{.name}}{{end}}" || true)
|
||||
done
|
||||
|
||||
echo "== default service account in the ${SYSTEM_NAMESPACE} namespace has token ${token_found} =="
|
||||
|
@ -97,10 +97,15 @@
|
||||
{% set pod_cidr = "--pod-cidr=" + grains['cbr-cidr'] %}
|
||||
{% endif %}
|
||||
|
||||
{% set cpu_cfs_quota = "" %}
|
||||
{% if pillar['enable_cpu_cfs_quota'] is defined -%}
|
||||
{% set cpu_cfs_quota = "--cpu-cfs-quota=" + pillar['enable_cpu_cfs_quota'] -%}
|
||||
{% endif -%}
|
||||
|
||||
{% set test_args = "" -%}
|
||||
{% if pillar['kubelet_test_args'] is defined -%}
|
||||
{% set test_args=pillar['kubelet_test_args'] %}
|
||||
{% endif -%}
|
||||
|
||||
# test_args has to be kept at the end, so they'll overwrite any prior configuration
|
||||
DAEMON_ARGS="{{daemon_args}} {{api_servers_with_port}} {{debugging_handlers}} {{hostname_override}} {{cloud_provider}} {{config}} {{manifest_url}} --allow-privileged={{pillar['allow_privileged']}} {{pillar['log_level']}} {{cluster_dns}} {{cluster_domain}} {{docker_root}} {{kubelet_root}} {{configure_cbr0}} {{cgroup_root}} {{system_container}} {{pod_cidr}} {{test_args}}"
|
||||
DAEMON_ARGS="{{daemon_args}} {{api_servers_with_port}} {{debugging_handlers}} {{hostname_override}} {{cloud_provider}} {{config}} {{manifest_url}} --allow-privileged={{pillar['allow_privileged']}} {{pillar['log_level']}} {{cluster_dns}} {{cluster_domain}} {{docker_root}} {{kubelet_root}} {{configure_cbr0}} {{cgroup_root}} {{system_container}} {{pod_cidr}} {{cpu_cfs_quota}} {{test_args}}"
|
||||
|
@ -51,7 +51,6 @@ if [ ! -f etcd.tar.gz ] ; then
|
||||
tar xzf etcd.tar.gz
|
||||
fi
|
||||
cp $ETCD/etcd $ETCD/etcdctl binaries/master
|
||||
cp $ETCD/etcd $ETCD/etcdctl binaries/minion
|
||||
|
||||
# k8s
|
||||
echo "Download kubernetes release ..."
|
||||
|
@ -35,7 +35,7 @@ export SERVICE_CLUSTER_IP_RANGE=${SERVICE_CLUSTER_IP_RANGE:-192.168.3.0/24} # f
|
||||
export FLANNEL_NET=${FLANNEL_NET:-172.16.0.0/16}
|
||||
|
||||
# Admission Controllers to invoke prior to persisting objects in cluster
|
||||
export ADMISSION_CONTROL=NamespaceLifecycle,NamespaceExists,LimitRanger,ServiceAccount,ResourceQuota,SecurityContextDeny
|
||||
export ADMISSION_CONTROL=NamespaceLifecycle,LimitRanger,ServiceAccount,ResourceQuota,SecurityContextDeny
|
||||
|
||||
SERVICE_NODE_PORT_RANGE=${SERVICE_NODE_PORT_RANGE:-"30000-32767"}
|
||||
|
||||
|
@ -1,31 +0,0 @@
|
||||
description "Etcd service"
|
||||
author "@jainvipin"
|
||||
|
||||
start on (net-device-up
|
||||
and local-filesystems
|
||||
and runlevel [2345])
|
||||
|
||||
respawn
|
||||
|
||||
pre-start script
|
||||
# see also https://github.com/jainvipin/kubernetes-ubuntu-start
|
||||
ETCD=/opt/bin/$UPSTART_JOB
|
||||
if [ -f /etc/default/$UPSTART_JOB ]; then
|
||||
. /etc/default/$UPSTART_JOB
|
||||
fi
|
||||
if [ -f $ETCD ]; then
|
||||
exit 0
|
||||
fi
|
||||
echo "$ETCD binary not found, exiting"
|
||||
exit 22
|
||||
end script
|
||||
|
||||
script
|
||||
# modify these in /etc/default/$UPSTART_JOB (/etc/default/docker)
|
||||
ETCD=/opt/bin/$UPSTART_JOB
|
||||
ETCD_OPTS=""
|
||||
if [ -f /etc/default/$UPSTART_JOB ]; then
|
||||
. /etc/default/$UPSTART_JOB
|
||||
fi
|
||||
exec "$ETCD" $ETCD_OPTS
|
||||
end script
|
@ -3,10 +3,9 @@ author "@chenxingyu"
|
||||
|
||||
respawn
|
||||
|
||||
# start in conjunction with etcd
|
||||
start on started etcd
|
||||
stop on stopping etcd
|
||||
|
||||
start on (net-device-up
|
||||
and local-filesystems
|
||||
and runlevel [2345])
|
||||
pre-start script
|
||||
FLANNEL=/opt/bin/$UPSTART_JOB
|
||||
if [ -f /etc/default/$UPSTART_JOB ]; then
|
||||
|
@ -3,9 +3,9 @@ author "@jainvipin"
|
||||
|
||||
respawn
|
||||
|
||||
# start in conjunction with etcd
|
||||
start on started etcd
|
||||
stop on stopping etcd
|
||||
# start in conjunction with flanneld
|
||||
start on started flanneld
|
||||
stop on stopping flanneld
|
||||
|
||||
limit nofile 65536 65536
|
||||
|
||||
|
@ -3,9 +3,9 @@ author "@jainvipin"
|
||||
|
||||
respawn
|
||||
|
||||
# start in conjunction with etcd
|
||||
start on started etcd
|
||||
stop on stopping etcd
|
||||
# start in conjunction with flanneld
|
||||
start on started flanneld
|
||||
stop on stopping flanneld
|
||||
|
||||
pre-start script
|
||||
# see also https://github.com/jainvipin/kubernetes-ubuntu-start
|
||||
|
@ -1,100 +0,0 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
### BEGIN INIT INFO
|
||||
# Provides: etcd
|
||||
# Required-Start: $docker
|
||||
# Required-Stop:
|
||||
# Should-Start:
|
||||
# Should-Stop:
|
||||
# Default-Start:
|
||||
# Default-Stop:
|
||||
# Short-Description: Start distrubted key/value pair service
|
||||
# Description:
|
||||
# http://www.github.com/coreos/etcd
|
||||
### END INIT INFO
|
||||
|
||||
export PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin:/opt/bin:
|
||||
|
||||
BASE=$(basename $0)
|
||||
|
||||
# modify these in /etc/default/$BASE (/etc/default/etcd)
|
||||
ETCD=/opt/bin/$BASE
|
||||
# This is the pid file managed by etcd itself
|
||||
ETCD_PIDFILE=/var/run/$BASE.pid
|
||||
ETCD_LOGFILE=/var/log/$BASE.log
|
||||
ETCD_OPTS=""
|
||||
ETCD_DESC="Etcd"
|
||||
|
||||
# Get lsb functions
|
||||
. /lib/lsb/init-functions
|
||||
|
||||
if [ -f /etc/default/$BASE ]; then
|
||||
. /etc/default/$BASE
|
||||
fi
|
||||
|
||||
# see also init_is_upstart in /lib/lsb/init-functions (which isn't available in Ubuntu 12.04, or we'd use it)
|
||||
if false && [ -x /sbin/initctl ] && /sbin/initctl version 2>/dev/null | grep -q upstart; then
|
||||
log_failure_msg "$ETCD_DESC is managed via upstart, try using service $BASE $1"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check etcd is present
|
||||
if [ ! -x $ETCD ]; then
|
||||
log_failure_msg "$ETCD not present or not executable"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
fail_unless_root() {
|
||||
if [ "$(id -u)" != '0' ]; then
|
||||
log_failure_msg "$ETCD_DESC must be run as root"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
ETCD_START="start-stop-daemon \
|
||||
--start \
|
||||
--background \
|
||||
--quiet \
|
||||
--exec $ETCD \
|
||||
--make-pidfile \
|
||||
--pidfile $ETCD_PIDFILE \
|
||||
-- $ETCD_OPTS \
|
||||
>> $ETCD_LOGFILE 2>&1"
|
||||
|
||||
ETCD_STOP="start-stop-daemon \
|
||||
--stop \
|
||||
--pidfile $ETCD_PIDFILE"
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
fail_unless_root
|
||||
log_begin_msg "Starting $ETCD_DESC: $BASE"
|
||||
$ETCD_START
|
||||
log_end_msg $?
|
||||
;;
|
||||
|
||||
stop)
|
||||
fail_unless_root
|
||||
log_begin_msg "Stopping $ETCD_DESC: $BASE"
|
||||
$ETCD_STOP
|
||||
log_end_msg $?
|
||||
;;
|
||||
|
||||
restart | force-reload)
|
||||
fail_unless_root
|
||||
log_begin_msg "Restarting $ETCD_DESC: $BASE"
|
||||
$ETCD_STOP
|
||||
$ETCD_START
|
||||
log_end_msg $?
|
||||
;;
|
||||
|
||||
status)
|
||||
status_of_proc -p "$ETCD_PIDFILE" "$ETCD" "$ETCD_DESC"
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Usage: $0 {start|stop|restart|status}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
@ -24,7 +24,7 @@ KUBELET=/opt/bin/$BASE
|
||||
KUBELET_PIDFILE=/var/run/$BASE.pid
|
||||
KUBELET_LOGFILE=/var/log/$BASE.log
|
||||
KUBELET_OPTS=""
|
||||
KUBELET_DESC="Kube-Apiserver"
|
||||
KUBELET_DESC="Kubelet"
|
||||
|
||||
# Get lsb functions
|
||||
. /lib/lsb/init-functions
|
||||
|
@ -21,33 +21,48 @@ if [ "$(id -u)" != "0" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
source ~/kube/config-default.sh
|
||||
|
||||
attempt=0
|
||||
while true; do
|
||||
/opt/bin/etcdctl get /coreos.com/network/config
|
||||
if [[ "$?" == 0 ]]; then
|
||||
break
|
||||
else
|
||||
# enough timeout??
|
||||
if (( attempt > 600 )); then
|
||||
echo "timeout for waiting network config" > ~/kube/err.log
|
||||
exit 2
|
||||
function config_etcd {
|
||||
|
||||
source ~/kube/config-default.sh
|
||||
|
||||
attempt=0
|
||||
while true; do
|
||||
/opt/bin/etcdctl get /coreos.com/network/config
|
||||
if [[ "$?" == 0 ]]; then
|
||||
break
|
||||
else
|
||||
# enough timeout??
|
||||
if (( attempt > 600 )); then
|
||||
echo "timeout for waiting network config" > ~/kube/err.log
|
||||
exit 2
|
||||
fi
|
||||
|
||||
/opt/bin/etcdctl mk /coreos.com/network/config "{\"Network\":\"${FLANNEL_NET}\"}"
|
||||
attempt=$((attempt+1))
|
||||
sleep 3
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
/opt/bin/etcdctl mk /coreos.com/network/config "{\"Network\":\"${FLANNEL_NET}\"}"
|
||||
attempt=$((attempt+1))
|
||||
sleep 3
|
||||
fi
|
||||
done
|
||||
function restart_docker {
|
||||
#wait some secs for /run/flannel/subnet.env ready
|
||||
sleep 15
|
||||
sudo ip link set dev docker0 down
|
||||
sudo brctl delbr docker0
|
||||
|
||||
#wait some secs for /run/flannel/subnet.env ready
|
||||
sleep 15
|
||||
sudo ip link set dev docker0 down
|
||||
sudo brctl delbr docker0
|
||||
source /run/flannel/subnet.env
|
||||
|
||||
source /run/flannel/subnet.env
|
||||
echo DOCKER_OPTS=\"${DOCKER_OPTS} -H tcp://127.0.0.1:4243 -H unix:///var/run/docker.sock \
|
||||
--bip=${FLANNEL_SUBNET} --mtu=${FLANNEL_MTU}\" > /etc/default/docker
|
||||
sudo service docker restart
|
||||
}
|
||||
|
||||
echo DOCKER_OPTS=\"${DOCKER_OPTS} -H tcp://127.0.0.1:4243 -H unix:///var/run/docker.sock \
|
||||
--bip=${FLANNEL_SUBNET} --mtu=${FLANNEL_MTU}\" > /etc/default/docker
|
||||
sudo service docker restart
|
||||
if [[ $1 == "i" ]]; then
|
||||
restart_docker
|
||||
elif [[ $1 == "ai" ]]; then
|
||||
config_etcd
|
||||
restart_docker
|
||||
elif [[ $1 == "a" ]]; then
|
||||
config_etcd
|
||||
fi
|
||||
|
@ -21,7 +21,6 @@ SSH_OPTS="-oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null -oLogLevel=E
|
||||
|
||||
# use an array to record name and ip
|
||||
declare -A mm
|
||||
CLUSTER=""
|
||||
MASTER=""
|
||||
MASTER_IP=""
|
||||
MINION_IPS=""
|
||||
@ -44,28 +43,18 @@ function setClusterInfo() {
|
||||
MINION_IPS=""
|
||||
|
||||
ii=0
|
||||
for i in $nodes
|
||||
do
|
||||
name="infra"$ii
|
||||
for i in $nodes; do
|
||||
nodeIP=${i#*@}
|
||||
|
||||
item="$name=http://$nodeIP:2380"
|
||||
if [ "$ii" == 0 ]; then
|
||||
CLUSTER=$item
|
||||
else
|
||||
CLUSTER="$CLUSTER,$item"
|
||||
fi
|
||||
mm[$nodeIP]=$name
|
||||
|
||||
if [ "${roles[${ii}]}" == "ai" ]; then
|
||||
if [[ "${roles[${ii}]}" == "ai" ]]; then
|
||||
MASTER_IP=$nodeIP
|
||||
MASTER=$i
|
||||
MINION_IPS="$nodeIP"
|
||||
elif [ "${roles[${ii}]}" == "a" ]; then
|
||||
elif [[ "${roles[${ii}]}" == "a" ]]; then
|
||||
MASTER_IP=$nodeIP
|
||||
MASTER=$i
|
||||
elif [ "${roles[${ii}]}" == "i" ]; then
|
||||
if [ -z "${MINION_IPS}" ];then
|
||||
elif [[ "${roles[${ii}]}" == "i" ]]; then
|
||||
if [[ -z "${MINION_IPS}" ]];then
|
||||
MINION_IPS="$nodeIP"
|
||||
else
|
||||
MINION_IPS="$MINION_IPS,$nodeIP"
|
||||
@ -191,12 +180,9 @@ function verify-minion(){
|
||||
|
||||
function create-etcd-opts(){
|
||||
cat <<EOF > ~/kube/default/etcd
|
||||
ETCD_OPTS="-name $1 \
|
||||
-initial-advertise-peer-urls http://$2:2380 \
|
||||
-listen-peer-urls http://$2:2380 \
|
||||
-initial-cluster-token etcd-cluster-1 \
|
||||
-initial-cluster $3 \
|
||||
-initial-cluster-state new"
|
||||
ETCD_OPTS="-name infra
|
||||
-listen-client-urls http://0.0.0.0:4001 \
|
||||
-advertise-client-urls http://127.0.0.1:4001"
|
||||
EOF
|
||||
}
|
||||
|
||||
@ -256,7 +242,7 @@ EOF
|
||||
|
||||
function create-flanneld-opts(){
|
||||
cat <<EOF > ~/kube/default/flanneld
|
||||
FLANNEL_OPTS=""
|
||||
FLANNEL_OPTS="--etcd-endpoints=http://${1}:4001"
|
||||
EOF
|
||||
}
|
||||
|
||||
@ -324,10 +310,10 @@ function kube-up() {
|
||||
{
|
||||
if [ "${roles[${ii}]}" == "a" ]; then
|
||||
provision-master
|
||||
elif [ "${roles[${ii}]}" == "i" ]; then
|
||||
provision-minion $i
|
||||
elif [ "${roles[${ii}]}" == "ai" ]; then
|
||||
provision-masterandminion
|
||||
elif [ "${roles[${ii}]}" == "i" ]; then
|
||||
provision-minion $i
|
||||
else
|
||||
echo "unsupported role for ${i}. please check"
|
||||
exit 1
|
||||
@ -356,21 +342,22 @@ function provision-master() {
|
||||
echo "Deploying master on machine ${MASTER_IP}"
|
||||
echo
|
||||
ssh $SSH_OPTS $MASTER "mkdir -p ~/kube/default"
|
||||
scp -r $SSH_OPTS saltbase/salt/generate-cert/make-ca-cert.sh ubuntu/config-default.sh ubuntu/util.sh ubuntu/master/* ubuntu/binaries/master/ "${MASTER}:~/kube"
|
||||
scp -r $SSH_OPTS saltbase/salt/generate-cert/make-ca-cert.sh ubuntu/reconfDocker.sh ubuntu/config-default.sh ubuntu/util.sh ubuntu/master/* ubuntu/binaries/master/ "${MASTER}:~/kube"
|
||||
|
||||
# remote login to MASTER and use sudo to configue k8s master
|
||||
ssh $SSH_OPTS -t $MASTER "source ~/kube/util.sh; \
|
||||
setClusterInfo; \
|
||||
create-etcd-opts "${mm[${MASTER_IP}]}" "${MASTER_IP}" "${CLUSTER}"; \
|
||||
create-etcd-opts; \
|
||||
create-kube-apiserver-opts "${SERVICE_CLUSTER_IP_RANGE}" "${ADMISSION_CONTROL}" "${SERVICE_NODE_PORT_RANGE}"; \
|
||||
create-kube-controller-manager-opts "${MINION_IPS}"; \
|
||||
create-kube-scheduler-opts; \
|
||||
create-flanneld-opts; \
|
||||
create-flanneld-opts "127.0.0.1"; \
|
||||
sudo -p '[sudo] password to copy files and start master: ' cp ~/kube/default/* /etc/default/ && sudo cp ~/kube/init_conf/* /etc/init/ && sudo cp ~/kube/init_scripts/* /etc/init.d/ ;\
|
||||
sudo groupadd -f -r kube-cert; \
|
||||
sudo ~/kube/make-ca-cert.sh ${MASTER_IP} IP:${MASTER_IP},IP:${SERVICE_CLUSTER_IP_RANGE%.*}.1,DNS:kubernetes,DNS:kubernetes.default,DNS:kubernetes.default.svc,DNS:kubernetes.default.svc.cluster.local; \
|
||||
sudo mkdir -p /opt/bin/ && sudo cp ~/kube/master/* /opt/bin/; \
|
||||
sudo service etcd start;"
|
||||
sudo service etcd start; \
|
||||
sudo FLANNEL_NET=${FLANNEL_NET} -b ~/kube/reconfDocker.sh "a";"
|
||||
}
|
||||
|
||||
function provision-minion() {
|
||||
@ -383,14 +370,13 @@ function provision-minion() {
|
||||
# remote login to MASTER and use sudo to configue k8s master
|
||||
ssh $SSH_OPTS -t $1 "source ~/kube/util.sh; \
|
||||
setClusterInfo; \
|
||||
create-etcd-opts "${mm[${1#*@}]}" "${1#*@}" "${CLUSTER}"; \
|
||||
create-kubelet-opts "${1#*@}" "${MASTER_IP}" "${DNS_SERVER_IP}" "${DNS_DOMAIN}";
|
||||
create-kubelet-opts "${1#*@}" "${MASTER_IP}" "${DNS_SERVER_IP}" "${DNS_DOMAIN}"; \
|
||||
create-kube-proxy-opts "${MASTER_IP}"; \
|
||||
create-flanneld-opts; \
|
||||
create-flanneld-opts "${MASTER_IP}"; \
|
||||
sudo -p '[sudo] password to copy files and start minion: ' cp ~/kube/default/* /etc/default/ && sudo cp ~/kube/init_conf/* /etc/init/ && sudo cp ~/kube/init_scripts/* /etc/init.d/ \
|
||||
&& sudo mkdir -p /opt/bin/ && sudo cp ~/kube/minion/* /opt/bin; \
|
||||
sudo service etcd start; \
|
||||
sudo FLANNEL_NET=${FLANNEL_NET} -b ~/kube/reconfDocker.sh"
|
||||
sudo service flanneld start; \
|
||||
sudo -b ~/kube/reconfDocker.sh "i";"
|
||||
}
|
||||
|
||||
function provision-masterandminion() {
|
||||
@ -398,24 +384,25 @@ function provision-masterandminion() {
|
||||
echo "Deploying master and minion on machine ${MASTER_IP}"
|
||||
echo
|
||||
ssh $SSH_OPTS $MASTER "mkdir -p ~/kube/default"
|
||||
scp -r $SSH_OPTS saltbase/salt/generate-cert/make-ca-cert.sh ubuntu/config-default.sh ubuntu/util.sh ubuntu/master/* ubuntu/reconfDocker.sh ubuntu/minion/* ubuntu/binaries/master/ ubuntu/binaries/minion "${MASTER}:~/kube"
|
||||
# scp order matters
|
||||
scp -r $SSH_OPTS saltbase/salt/generate-cert/make-ca-cert.sh ubuntu/config-default.sh ubuntu/util.sh ubuntu/minion/* ubuntu/master/* ubuntu/reconfDocker.sh ubuntu/binaries/master/ ubuntu/binaries/minion "${MASTER}:~/kube"
|
||||
|
||||
# remote login to the node and use sudo to configue k8s
|
||||
ssh $SSH_OPTS -t $MASTER "source ~/kube/util.sh; \
|
||||
setClusterInfo; \
|
||||
create-etcd-opts "${mm[${MASTER_IP}]}" "${MASTER_IP}" "${CLUSTER}"; \
|
||||
create-etcd-opts; \
|
||||
create-kube-apiserver-opts "${SERVICE_CLUSTER_IP_RANGE}" "${ADMISSION_CONTROL}" "${SERVICE_NODE_PORT_RANGE}"; \
|
||||
create-kube-controller-manager-opts "${MINION_IPS}"; \
|
||||
create-kube-scheduler-opts; \
|
||||
create-kubelet-opts "${MASTER_IP}" "${MASTER_IP}" "${DNS_SERVER_IP}" "${DNS_DOMAIN}";
|
||||
create-kube-proxy-opts "${MASTER_IP}";\
|
||||
create-flanneld-opts; \
|
||||
create-flanneld-opts "127.0.0.1"; \
|
||||
sudo -p '[sudo] password to copy files and start node: ' cp ~/kube/default/* /etc/default/ && sudo cp ~/kube/init_conf/* /etc/init/ && sudo cp ~/kube/init_scripts/* /etc/init.d/ ; \
|
||||
sudo groupadd -f -r kube-cert; \
|
||||
sudo ~/kube/make-ca-cert.sh ${MASTER_IP} IP:${MASTER_IP},IP:${SERVICE_CLUSTER_IP_RANGE%.*}.1,DNS:kubernetes,DNS:kubernetes.default,DNS:kubernetes.default.svc,DNS:kubernetes.default.svc.cluster.local; \
|
||||
sudo mkdir -p /opt/bin/ && sudo cp ~/kube/master/* /opt/bin/ && sudo cp ~/kube/minion/* /opt/bin/; \
|
||||
sudo service etcd start; \
|
||||
sudo FLANNEL_NET=${FLANNEL_NET} -b ~/kube/reconfDocker.sh"
|
||||
sudo FLANNEL_NET=${FLANNEL_NET} -b ~/kube/reconfDocker.sh "ai";"
|
||||
}
|
||||
|
||||
# Delete a kubernetes cluster
|
||||
@ -423,20 +410,116 @@ function kube-down {
|
||||
KUBE_ROOT=$(dirname "${BASH_SOURCE}")/../..
|
||||
source "${KUBE_ROOT}/cluster/ubuntu/${KUBE_CONFIG_FILE-"config-default.sh"}"
|
||||
|
||||
source "${KUBE_ROOT}/cluster/common.sh"
|
||||
tear_down_alive_resources
|
||||
|
||||
ii=0
|
||||
for i in ${nodes}; do
|
||||
{
|
||||
echo "Cleaning on node ${i#*@}"
|
||||
ssh -t $i 'pgrep etcd && sudo -p "[sudo] password for cleaning etcd data: " service etcd stop && sudo rm -rf /infra*'
|
||||
# Delete the files in order to generate a clean environment, so you can change each node's role at next deployment.
|
||||
ssh -t $i 'sudo rm -f /opt/bin/kube* /etc/init/kube* /etc/init.d/kube* /etc/default/kube*; sudo rm -rf ~/kube /var/lib/kubelet'
|
||||
if [[ "${roles[${ii}]}" == "ai" || "${roles[${ii}]}" == "a" ]]; then
|
||||
ssh -t $i 'pgrep etcd && sudo -p "[sudo] password for cleaning etcd data: " service etcd stop && sudo rm -rf /infra*;
|
||||
sudo rm -rf /opt/bin/etcd* /etc/init/etcd.conf /etc/init.d/etcd /etc/default/etcd'
|
||||
elif [[ "${roles[${ii}]}" == "i" ]]; then
|
||||
ssh -t $i 'pgrep flanneld && sudo -p "[sudo] password for stopping flanneld: " service flanneld stop'
|
||||
else
|
||||
echo "unsupported role for ${i}"
|
||||
fi
|
||||
# Delete the files in order to generate a clean environment, so you can change each node's role at next deployment.
|
||||
ssh -t $i 'sudo rm -f /opt/bin/kube* /opt/bin/flanneld;
|
||||
sudo rm -rf /etc/init/kube* /etc/init/flanneld.conf /etc/init.d/kube* /etc/init.d/flanneld;
|
||||
sudo rm -rf /etc/default/kube* /etc/default/flanneld;
|
||||
sudo rm -rf ~/kube /var/lib/kubelet'
|
||||
}
|
||||
((ii=ii+1))
|
||||
done
|
||||
wait
|
||||
}
|
||||
|
||||
|
||||
# Perform common upgrade setup tasks
|
||||
function prepare-push() {
|
||||
#Not yet support upgrading by using local binaries.
|
||||
if [[ $KUBE_VERSION == "" ]]; then
|
||||
echo "Upgrading nodes to local binaries is not yet supported.Please specify the version"
|
||||
exit 1
|
||||
fi
|
||||
# Run build.sh to get the latest release
|
||||
source "${KUBE_ROOT}/cluster/ubuntu/build.sh"
|
||||
}
|
||||
|
||||
# Update a kubernetes master with latest release
|
||||
function push-master {
|
||||
source "${KUBE_ROOT}/cluster/ubuntu/${KUBE_CONFIG_FILE-"config-default.sh"}"
|
||||
setClusterInfo
|
||||
ii=0
|
||||
for i in ${nodes}; do
|
||||
if [[ "${roles[${ii}]}" == "a" || "${roles[${ii}]}" == "ai" ]]; then
|
||||
echo "Cleaning on master ${i#*@}"
|
||||
ssh -t $i 'sudo -p "[sudo] stop the all process: " service etcd stop' || true
|
||||
provision-master
|
||||
elif [[ "${roles[${ii}]}" == "i" ]]; then
|
||||
continue
|
||||
else
|
||||
echo "unsupported role for ${i}. please check"
|
||||
exit 1
|
||||
fi
|
||||
((ii=ii+1))
|
||||
done
|
||||
verify-cluster
|
||||
}
|
||||
|
||||
# Update a kubernetes node with latest release
|
||||
function push-node() {
|
||||
source "${KUBE_ROOT}/cluster/ubuntu/${KUBE_CONFIG_FILE-"config-default.sh"}"
|
||||
node=${1}
|
||||
setClusterInfo
|
||||
ii=0
|
||||
for i in ${nodes}; do
|
||||
if [[ "${roles[${ii}]}" == "i" || "${roles[${ii}]}" == "ai" && $i == *$node ]]; then
|
||||
echo "Cleaning on node ${i#*@}"
|
||||
ssh -t $i 'sudo -p "[sudo] stop the all process: " service etcd stop' || true
|
||||
provision-minion $i
|
||||
else
|
||||
echo "unsupported role for ${i}, or nodes ${i} don't exist. please check"
|
||||
exit 1
|
||||
fi
|
||||
((ii=ii+1))
|
||||
done
|
||||
verify-cluster
|
||||
}
|
||||
|
||||
# Update a kubernetes cluster with latest source
|
||||
function kube-push {
|
||||
echo "not implemented"
|
||||
prepare-push
|
||||
#stop all the kube's process & etcd
|
||||
source "${KUBE_ROOT}/cluster/ubuntu/${KUBE_CONFIG_FILE-"config-default.sh"}"
|
||||
for i in ${nodes}; do
|
||||
echo "Cleaning on node ${i#*@}"
|
||||
ssh -t $i 'sudo -p "[sudo] stop all process: " service etcd stop' || true
|
||||
ssh -t $i 'rm -f /opt/bin/kube* /etc/init/kube* /etc/init.d/kube* /etc/default/kube*; rm -rf ~/kube' || true
|
||||
done
|
||||
#Update all nodes with the lasted release
|
||||
if [[ ! -f "ubuntu/binaries/master/kube-apiserver" ]]; then
|
||||
echo "There is no latest release of kubernetes,please check first"
|
||||
exit 1
|
||||
fi
|
||||
#provision all nodes,include master&nodes
|
||||
setClusterInfo
|
||||
ii=0
|
||||
for i in ${nodes}; do
|
||||
if [[ "${roles[${ii}]}" == "a" ]]; then
|
||||
provision-master
|
||||
elif [[ "${roles[${ii}]}" == "i" ]]; then
|
||||
provision-minion $i
|
||||
elif [[ "${roles[${ii}]}" == "ai" ]]; then
|
||||
provision-masterandminion
|
||||
else
|
||||
echo "unsupported role for ${i}. please check"
|
||||
exit 1
|
||||
fi
|
||||
((ii=ii+1))
|
||||
done
|
||||
verify-cluster
|
||||
}
|
||||
|
||||
# Perform preparations required to run e2e tests
|
||||
|
@ -49,7 +49,7 @@ declare -a resources=(
|
||||
)
|
||||
|
||||
# Find all the namespaces.
|
||||
namespaces=( $("${KUBECTL}" get namespaces -o template -t "{{range.items}}{{.metadata.name}} {{end}}"))
|
||||
namespaces=( $("${KUBECTL}" get namespaces -o go-template="{{range.items}}{{.metadata.name}} {{end}}"))
|
||||
if [ -z "${namespaces:-}" ]
|
||||
then
|
||||
echo "Unexpected: No namespace found. Nothing to do."
|
||||
@ -59,7 +59,7 @@ for resource in "${resources[@]}"
|
||||
do
|
||||
for namespace in "${namespaces[@]}"
|
||||
do
|
||||
instances=( $("${KUBECTL}" get "${resource}" --namespace="${namespace}" -o template -t "{{range.items}}{{.metadata.name}} {{end}}"))
|
||||
instances=( $("${KUBECTL}" get "${resource}" --namespace="${namespace}" -o go-template="{{range.items}}{{.metadata.name}} {{end}}"))
|
||||
# Nothing to do if there is no instance of that resource.
|
||||
if [[ -z "${instances:-}" ]]
|
||||
then
|
||||
@ -84,7 +84,7 @@ do
|
||||
echo "Looks like ${instance} got deleted. Ignoring it"
|
||||
continue
|
||||
fi
|
||||
output=$("${KUBECTL}" update -f "${filename}" --namespace="${namespace}") || true
|
||||
output=$("${KUBECTL}" replace -f "${filename}" --namespace="${namespace}") || true
|
||||
rm "${filename}"
|
||||
if [ -n "${output:-}" ]
|
||||
then
|
||||
|
@ -53,7 +53,7 @@ MASTER_USER=vagrant
|
||||
MASTER_PASSWD=vagrant
|
||||
|
||||
# Admission Controllers to invoke prior to persisting objects in cluster
|
||||
ADMISSION_CONTROL=NamespaceLifecycle,NamespaceExists,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota
|
||||
ADMISSION_CONTROL=NamespaceLifecycle,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota
|
||||
|
||||
# Optional: Enable node logging.
|
||||
ENABLE_NODE_LOGGING=false
|
||||
@ -76,6 +76,9 @@ ENABLE_CLUSTER_MONITORING="${KUBE_ENABLE_CLUSTER_MONITORING:-influxdb}"
|
||||
#EXTRA_DOCKER_OPTS="-b=cbr0 --selinux-enabled --insecure-registry 10.0.0.0/8"
|
||||
EXTRA_DOCKER_OPTS="-b=cbr0 --insecure-registry 10.0.0.0/8"
|
||||
|
||||
# Flag to tell the kubelet to enable CFS quota support
|
||||
ENABLE_CPU_CFS_QUOTA="${KUBE_ENABLE_CPU_CFS_QUOTA:-true}"
|
||||
|
||||
# Optional: Install cluster DNS.
|
||||
ENABLE_CLUSTER_DNS="${KUBE_ENABLE_CLUSTER_DNS:-true}"
|
||||
DNS_SERVER_IP="10.247.0.10"
|
||||
|
@ -126,6 +126,7 @@ cat <<EOF >/srv/salt-overlay/pillar/cluster-params.sls
|
||||
dns_domain: '$(echo "$DNS_DOMAIN" | sed -e "s/'/''/g")'
|
||||
instance_prefix: '$(echo "$INSTANCE_PREFIX" | sed -e "s/'/''/g")'
|
||||
admission_control: '$(echo "$ADMISSION_CONTROL" | sed -e "s/'/''/g")'
|
||||
enable_cpu_cfs_quota: '$(echo "$ENABLE_CPU_CFS_QUOTA" | sed -e "s/'/''/g")'
|
||||
EOF
|
||||
|
||||
# Configure the salt-master
|
||||
|
@ -155,6 +155,10 @@ grains:
|
||||
docker_opts: '$(echo "$DOCKER_OPTS" | sed -e "s/'/''/g")'
|
||||
EOF
|
||||
|
||||
# QoS support requires that swap memory is disabled on each of the minions
|
||||
echo "Disable swap memory to ensure proper QoS"
|
||||
swapoff -a
|
||||
|
||||
# we will run provision to update code each time we test, so we do not want to do salt install each time
|
||||
if ! which salt-minion >/dev/null 2>&1; then
|
||||
# Install Salt
|
||||
|
@ -153,6 +153,7 @@ function create-provision-scripts {
|
||||
echo "KUBELET_TOKEN='${KUBELET_TOKEN:-}'"
|
||||
echo "KUBE_PROXY_TOKEN='${KUBE_PROXY_TOKEN:-}'"
|
||||
echo "MASTER_EXTRA_SANS='${MASTER_EXTRA_SANS:-}'"
|
||||
echo "ENABLE_CPU_CFS_QUOTA='${ENABLE_CPU_CFS_QUOTA}'"
|
||||
awk '!/^#/' "${KUBE_ROOT}/cluster/vagrant/provision-network.sh"
|
||||
awk '!/^#/' "${KUBE_ROOT}/cluster/vagrant/provision-master.sh"
|
||||
) > "${KUBE_TEMP}/master-start.sh"
|
||||
@ -198,6 +199,9 @@ function verify-cluster {
|
||||
local machine="master"
|
||||
local -a required_daemon=("salt-master" "salt-minion" "kubelet")
|
||||
local validated="1"
|
||||
# This is a hack, but sometimes the salt-minion gets stuck on the master, so we just restart it
|
||||
# to ensure that users never wait forever
|
||||
vagrant ssh "$machine" -c "sudo systemctl restart salt-minion"
|
||||
until [[ "$validated" == "0" ]]; do
|
||||
validated="0"
|
||||
local daemon
|
||||
@ -237,7 +241,7 @@ function verify-cluster {
|
||||
local count="0"
|
||||
until [[ "$count" == "1" ]]; do
|
||||
local minions
|
||||
minions=$("${KUBE_ROOT}/cluster/kubectl.sh" get nodes -o template --template '{{range.items}}{{.metadata.name}}:{{end}}' --api-version=v1)
|
||||
minions=$("${KUBE_ROOT}/cluster/kubectl.sh" get nodes -o go-template='{{range.items}}{{.metadata.name}}:{{end}}' --api-version=v1)
|
||||
count=$(echo $minions | grep -c "${MINION_IPS[i]}") || {
|
||||
printf "."
|
||||
sleep 2
|
||||
|
@ -38,6 +38,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
apierrors "k8s.io/kubernetes/pkg/api/errors"
|
||||
"k8s.io/kubernetes/pkg/api/latest"
|
||||
"k8s.io/kubernetes/pkg/api/testapi"
|
||||
"k8s.io/kubernetes/pkg/apiserver"
|
||||
client "k8s.io/kubernetes/pkg/client/unversioned"
|
||||
"k8s.io/kubernetes/pkg/client/unversioned/record"
|
||||
@ -69,8 +70,6 @@ import (
|
||||
|
||||
var (
|
||||
fakeDocker1, fakeDocker2 dockertools.FakeDockerClient
|
||||
// API version that should be used by the client to talk to the server.
|
||||
apiVersion string
|
||||
// Limit the number of concurrent tests.
|
||||
maxConcurrency int
|
||||
)
|
||||
@ -93,7 +92,7 @@ func (h *delegateHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
|
||||
func startComponents(firstManifestURL, secondManifestURL, apiVersion string) (string, string) {
|
||||
func startComponents(firstManifestURL, secondManifestURL string) (string, string) {
|
||||
// Setup
|
||||
servers := []string{}
|
||||
glog.Infof("Creating etcd client pointing to %v", servers)
|
||||
@ -126,13 +125,17 @@ func startComponents(firstManifestURL, secondManifestURL, apiVersion string) (st
|
||||
glog.Fatalf("Failed to connect to etcd")
|
||||
}
|
||||
|
||||
cl := client.NewOrDie(&client.Config{Host: apiServer.URL, Version: apiVersion})
|
||||
cl := client.NewOrDie(&client.Config{Host: apiServer.URL, Version: testapi.Default.Version()})
|
||||
|
||||
etcdStorage, err := master.NewEtcdStorage(etcdClient, latest.InterfacesFor, latest.Version, etcdtest.PathPrefix())
|
||||
// TODO: caesarxuchao: hacky way to specify version of Experimental client.
|
||||
// We will fix this by supporting multiple group versions in Config
|
||||
cl.ExperimentalClient = client.NewExperimentalOrDie(&client.Config{Host: apiServer.URL, Version: testapi.Experimental.Version()})
|
||||
|
||||
etcdStorage, err := master.NewEtcdStorage(etcdClient, latest.InterfacesFor, testapi.Default.Version(), etcdtest.PathPrefix())
|
||||
if err != nil {
|
||||
glog.Fatalf("Unable to get etcd storage: %v", err)
|
||||
}
|
||||
expEtcdStorage, err := master.NewEtcdStorage(etcdClient, explatest.InterfacesFor, explatest.Version, etcdtest.PathPrefix())
|
||||
expEtcdStorage, err := master.NewEtcdStorage(etcdClient, explatest.InterfacesFor, testapi.Experimental.Version(), etcdtest.PathPrefix())
|
||||
if err != nil {
|
||||
glog.Fatalf("Unable to get etcd storage for experimental: %v", err)
|
||||
}
|
||||
@ -891,7 +894,6 @@ func runSchedulerNoPhantomPodsTest(client *client.Client) {
|
||||
type testFunc func(*client.Client)
|
||||
|
||||
func addFlags(fs *pflag.FlagSet) {
|
||||
fs.StringVar(&apiVersion, "api-version", latest.Version, "API version that should be used by the client for communicating with the server")
|
||||
fs.IntVar(
|
||||
&maxConcurrency, "max-concurrency", -1, "Maximum number of tests to be run simultaneously. Unlimited if set to negative.")
|
||||
}
|
||||
@ -911,18 +913,21 @@ func main() {
|
||||
glog.Fatalf("This test has timed out.")
|
||||
}()
|
||||
|
||||
glog.Infof("Running tests for APIVersion: %s", apiVersion)
|
||||
glog.Infof("Running tests for APIVersion: %s", os.Getenv("KUBE_TEST_API"))
|
||||
|
||||
firstManifestURL := ServeCachedManifestFile(testPodSpecFile)
|
||||
secondManifestURL := ServeCachedManifestFile(testPodSpecFile)
|
||||
apiServerURL, _ := startComponents(firstManifestURL, secondManifestURL, apiVersion)
|
||||
apiServerURL, _ := startComponents(firstManifestURL, secondManifestURL)
|
||||
|
||||
// Ok. we're good to go.
|
||||
glog.Infof("API Server started on %s", apiServerURL)
|
||||
// Wait for the synchronization threads to come up.
|
||||
time.Sleep(time.Second * 10)
|
||||
|
||||
kubeClient := client.NewOrDie(&client.Config{Host: apiServerURL, Version: apiVersion})
|
||||
kubeClient := client.NewOrDie(&client.Config{Host: apiServerURL, Version: testapi.Default.Version()})
|
||||
// TODO: caesarxuchao: hacky way to specify version of Experimental client.
|
||||
// We will fix this by supporting multiple group versions in Config
|
||||
kubeClient.ExperimentalClient = client.NewExperimentalOrDie(&client.Config{Host: apiServerURL, Version: testapi.Experimental.Version()})
|
||||
|
||||
// Run tests in parallel
|
||||
testFuncs := []testFunc{
|
||||
|
@ -68,8 +68,6 @@ type APIServer struct {
|
||||
AdvertiseAddress net.IP
|
||||
SecurePort int
|
||||
ExternalHost string
|
||||
APIRate float32
|
||||
APIBurst int
|
||||
TLSCertFile string
|
||||
TLSPrivateKeyFile string
|
||||
CertDirectory string
|
||||
@ -107,6 +105,7 @@ type APIServer struct {
|
||||
KubeletConfig client.KubeletConfig
|
||||
ClusterName string
|
||||
EnableProfiling bool
|
||||
EnableWatchCache bool
|
||||
MaxRequestsInFlight int
|
||||
MinRequestTimeout int
|
||||
LongRunningRequestRE string
|
||||
@ -122,8 +121,6 @@ func NewAPIServer() *APIServer {
|
||||
InsecureBindAddress: net.ParseIP("127.0.0.1"),
|
||||
BindAddress: net.ParseIP("0.0.0.0"),
|
||||
SecurePort: 6443,
|
||||
APIRate: 10.0,
|
||||
APIBurst: 200,
|
||||
APIPrefix: "/api",
|
||||
ExpAPIPrefix: "/experimental",
|
||||
EventTTL: 1 * time.Hour,
|
||||
@ -161,7 +158,7 @@ func (s *APIServer) AddFlags(fs *pflag.FlagSet) {
|
||||
"The IP address on which to serve the --insecure-port (set to 0.0.0.0 for all interfaces). "+
|
||||
"Defaults to localhost.")
|
||||
fs.IPVar(&s.InsecureBindAddress, "address", s.InsecureBindAddress, "DEPRECATED: see --insecure-bind-address instead")
|
||||
fs.MarkDeprecated("address", "see --insecure-bind-address instread")
|
||||
fs.MarkDeprecated("address", "see --insecure-bind-address instead")
|
||||
fs.IPVar(&s.BindAddress, "bind-address", s.BindAddress, ""+
|
||||
"The IP address on which to serve the --read-only-port and --secure-port ports. The "+
|
||||
"associated interface(s) must be reachable by the rest of the cluster, and by CLI/web "+
|
||||
@ -176,8 +173,6 @@ func (s *APIServer) AddFlags(fs *pflag.FlagSet) {
|
||||
fs.IntVar(&s.SecurePort, "secure-port", s.SecurePort, ""+
|
||||
"The port on which to serve HTTPS with authentication and authorization. If 0, "+
|
||||
"don't serve HTTPS at all.")
|
||||
fs.Float32Var(&s.APIRate, "api-rate", s.APIRate, "API rate limit as QPS for the read only port")
|
||||
fs.IntVar(&s.APIBurst, "api-burst", s.APIBurst, "API burst amount for the read only port")
|
||||
fs.StringVar(&s.TLSCertFile, "tls-cert-file", s.TLSCertFile, ""+
|
||||
"File containing x509 Certificate for HTTPS. (CA cert, if any, concatenated after server cert). "+
|
||||
"If HTTPS serving is enabled, and --tls-cert-file and --tls-private-key-file are not provided, "+
|
||||
@ -203,7 +198,7 @@ func (s *APIServer) AddFlags(fs *pflag.FlagSet) {
|
||||
fs.StringVar(&s.ServiceAccountKeyFile, "service-account-key-file", s.ServiceAccountKeyFile, "File containing PEM-encoded x509 RSA private or public key, used to verify ServiceAccount tokens. If unspecified, --tls-private-key-file is used.")
|
||||
fs.BoolVar(&s.ServiceAccountLookup, "service-account-lookup", s.ServiceAccountLookup, "If true, validate ServiceAccount tokens exist in etcd as part of authentication.")
|
||||
fs.StringVar(&s.KeystoneURL, "experimental-keystone-url", s.KeystoneURL, "If passed, activates the keystone authentication plugin")
|
||||
fs.StringVar(&s.AuthorizationMode, "authorization-mode", s.AuthorizationMode, "Selects how to do authorization on the secure port. One of: "+strings.Join(apiserver.AuthorizationModeChoices, ","))
|
||||
fs.StringVar(&s.AuthorizationMode, "authorization-mode", s.AuthorizationMode, "Ordered list of plug-ins to do authorization on secure port. Comma-delimited list of: "+strings.Join(apiserver.AuthorizationModeChoices, ","))
|
||||
fs.StringVar(&s.AuthorizationPolicyFile, "authorization-policy-file", s.AuthorizationPolicyFile, "File with authorization policy in csv format, used with --authorization-mode=ABAC, on the secure port.")
|
||||
fs.StringVar(&s.AdmissionControl, "admission-control", s.AdmissionControl, "Ordered list of plug-ins to do admission control of resources into cluster. Comma-delimited list of: "+strings.Join(admission.GetPlugins(), ", "))
|
||||
fs.StringVar(&s.AdmissionControlConfigFile, "admission-control-config-file", s.AdmissionControlConfigFile, "File with admission control configuration.")
|
||||
@ -222,6 +217,8 @@ func (s *APIServer) AddFlags(fs *pflag.FlagSet) {
|
||||
fs.Var(&s.RuntimeConfig, "runtime-config", "A set of key=value pairs that describe runtime configuration that may be passed to the apiserver. api/<version> key can be used to turn on/off specific api versions. api/all and api/legacy are special keys to control all and legacy api versions respectively.")
|
||||
fs.StringVar(&s.ClusterName, "cluster-name", s.ClusterName, "The instance prefix for the cluster")
|
||||
fs.BoolVar(&s.EnableProfiling, "profiling", true, "Enable profiling via web interface host:port/debug/pprof/")
|
||||
// TODO: enable cache in integration tests.
|
||||
fs.BoolVar(&s.EnableWatchCache, "watch-cache", true, "Enable watch caching in the apiserver")
|
||||
fs.StringVar(&s.ExternalHost, "external-hostname", "", "The hostname to use when generating externalized URLs for this master (e.g. Swagger API Docs.)")
|
||||
fs.IntVar(&s.MaxRequestsInFlight, "max-requests-inflight", 400, "The maximum number of requests in flight at a given time. When the server exceeds this, it rejects requests. Zero for no limit.")
|
||||
fs.IntVar(&s.MinRequestTimeout, "min-request-timeout", 1800, "An optional field indicating the minimum number of seconds a handler must keep a request open before timing it out. Currently only honored by the watch request handler, which picks a randomized value above this number as the connection timeout, to spread out load.")
|
||||
@ -233,7 +230,7 @@ func (s *APIServer) AddFlags(fs *pflag.FlagSet) {
|
||||
fs.BoolVar(&s.KubeletConfig.EnableHttps, "kubelet-https", s.KubeletConfig.EnableHttps, "Use https for kubelet connections")
|
||||
fs.UintVar(&s.KubeletConfig.Port, "kubelet-port", s.KubeletConfig.Port, "Kubelet port")
|
||||
fs.DurationVar(&s.KubeletConfig.HTTPTimeout, "kubelet-timeout", s.KubeletConfig.HTTPTimeout, "Timeout for kubelet operations")
|
||||
fs.StringVar(&s.KubeletConfig.CertFile, "kubelet-client-certificate", s.KubeletConfig.CertFile, "Path to a client key file for TLS.")
|
||||
fs.StringVar(&s.KubeletConfig.CertFile, "kubelet-client-certificate", s.KubeletConfig.CertFile, "Path to a client cert file for TLS.")
|
||||
fs.StringVar(&s.KubeletConfig.KeyFile, "kubelet-client-key", s.KubeletConfig.KeyFile, "Path to a client key file for TLS.")
|
||||
fs.StringVar(&s.KubeletConfig.CAFile, "kubelet-certificate-authority", s.KubeletConfig.CAFile, "Path to a cert. file for the certificate authority.")
|
||||
}
|
||||
@ -380,7 +377,8 @@ func (s *APIServer) Run(_ []string) error {
|
||||
glog.Fatalf("Invalid Authentication Config: %v", err)
|
||||
}
|
||||
|
||||
authorizer, err := apiserver.NewAuthorizerFromAuthorizationConfig(s.AuthorizationMode, s.AuthorizationPolicyFile)
|
||||
authorizationModeNames := strings.Split(s.AuthorizationMode, ",")
|
||||
authorizer, err := apiserver.NewAuthorizerFromAuthorizationConfig(authorizationModeNames, s.AuthorizationPolicyFile)
|
||||
if err != nil {
|
||||
glog.Fatalf("Invalid Authorization Config: %v", err)
|
||||
}
|
||||
@ -429,6 +427,7 @@ func (s *APIServer) Run(_ []string) error {
|
||||
EnableUISupport: true,
|
||||
EnableSwaggerSupport: true,
|
||||
EnableProfiling: s.EnableProfiling,
|
||||
EnableWatchCache: s.EnableWatchCache,
|
||||
EnableIndex: true,
|
||||
APIPrefix: s.APIPrefix,
|
||||
ExpAPIPrefix: s.ExpAPIPrefix,
|
||||
|
@ -36,6 +36,7 @@ import (
|
||||
clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
|
||||
"k8s.io/kubernetes/pkg/cloudprovider"
|
||||
"k8s.io/kubernetes/pkg/controller/autoscaler"
|
||||
"k8s.io/kubernetes/pkg/controller/autoscaler/metrics"
|
||||
"k8s.io/kubernetes/pkg/controller/endpoint"
|
||||
"k8s.io/kubernetes/pkg/controller/namespace"
|
||||
"k8s.io/kubernetes/pkg/controller/node"
|
||||
@ -291,7 +292,8 @@ func (s *CMServer) Run(_ []string) error {
|
||||
if err != nil {
|
||||
glog.Fatalf("Invalid API configuration: %v", err)
|
||||
}
|
||||
horizontalPodAutoscalerController := autoscalercontroller.New(kubeClient, expClient)
|
||||
horizontalPodAutoscalerController := autoscalercontroller.New(kubeClient, expClient,
|
||||
metrics.NewHeapsterMetricsClient(kubeClient))
|
||||
horizontalPodAutoscalerController.Run(s.HorizontalPodAutoscalerSyncPeriod)
|
||||
}
|
||||
|
||||
|
@ -159,9 +159,6 @@ func (s *ProxyServer) Run(_ []string) error {
|
||||
Namespace: "",
|
||||
}
|
||||
|
||||
// Birth Cry
|
||||
s.birthCry()
|
||||
|
||||
serviceConfig := config.NewServiceConfig()
|
||||
endpointsConfig := config.NewEndpointsConfig()
|
||||
|
||||
@ -200,7 +197,7 @@ func (s *ProxyServer) Run(_ []string) error {
|
||||
ipt := utiliptables.New(execer, protocol)
|
||||
proxierUserspace, err := userspace.NewProxier(loadBalancer, s.BindAddress, ipt, s.PortRange, s.SyncPeriod)
|
||||
if err != nil {
|
||||
glog.Fatalf("Unable to create proxer: %v", err)
|
||||
glog.Fatalf("Unable to create proxier: %v", err)
|
||||
}
|
||||
proxier = proxierUserspace
|
||||
// Remove artifacts from the pure-iptables Proxier.
|
||||
@ -208,6 +205,9 @@ func (s *ProxyServer) Run(_ []string) error {
|
||||
iptables.CleanupLeftovers(ipt)
|
||||
}
|
||||
|
||||
// Birth Cry after the birth is successful
|
||||
s.birthCry()
|
||||
|
||||
// Wire proxier to handle changes to services
|
||||
serviceConfig.RegisterHandler(proxier)
|
||||
// And wire endpointsHandler to handle changes to endpoints to services
|
||||
|
@ -124,7 +124,7 @@ type KubeletServer struct {
|
||||
MaxPods int
|
||||
DockerExecHandlerName string
|
||||
ResolverConfig string
|
||||
|
||||
CPUCFSQuota bool
|
||||
// Flags intended for testing
|
||||
|
||||
// Crash immediately, rather than eating panics.
|
||||
@ -189,6 +189,7 @@ func NewKubeletServer() *KubeletServer {
|
||||
SystemContainer: "",
|
||||
ConfigureCBR0: false,
|
||||
DockerExecHandlerName: "native",
|
||||
CPUCFSQuota: false,
|
||||
}
|
||||
}
|
||||
|
||||
@ -255,6 +256,7 @@ func (s *KubeletServer) AddFlags(fs *pflag.FlagSet) {
|
||||
fs.StringVar(&s.DockerExecHandlerName, "docker-exec-handler", s.DockerExecHandlerName, "Handler to use when executing a command in a container. Valid values are 'native' and 'nsenter'. Defaults to 'native'.")
|
||||
fs.StringVar(&s.PodCIDR, "pod-cidr", "", "The CIDR to use for pod IP addresses, only used in standalone mode. In cluster mode, this is obtained from the master.")
|
||||
fs.StringVar(&s.ResolverConfig, "resolv-conf", kubelet.ResolvConfDefault, "Resolver configuration file used as the basis for the container DNS resolution configuration.")
|
||||
fs.BoolVar(&s.CPUCFSQuota, "cpu-cfs-quota", s.CPUCFSQuota, "Enable CPU CFS quota enforcement for containers that specify CPU limits")
|
||||
// Flags intended for testing, not recommended used in production environments.
|
||||
fs.BoolVar(&s.ReallyCrashForTesting, "really-crash-for-testing", s.ReallyCrashForTesting, "If true, when panics occur crash. Intended for testing.")
|
||||
fs.Float64Var(&s.ChaosChance, "chaos-chance", s.ChaosChance, "If > 0.0, introduce random client errors and latency. Intended for testing. [default=0.0]")
|
||||
@ -362,6 +364,7 @@ func (s *KubeletServer) KubeletConfig() (*KubeletConfig, error) {
|
||||
MaxPods: s.MaxPods,
|
||||
DockerExecHandler: dockerExecHandler,
|
||||
ResolverConfig: s.ResolverConfig,
|
||||
CPUCFSQuota: s.CPUCFSQuota,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -604,6 +607,7 @@ func SimpleKubelet(client *client.Client,
|
||||
MaxPods: 32,
|
||||
DockerExecHandler: &dockertools.NativeExecHandler{},
|
||||
ResolverConfig: kubelet.ResolvConfDefault,
|
||||
CPUCFSQuota: false,
|
||||
}
|
||||
return &kcfg
|
||||
}
|
||||
@ -774,6 +778,7 @@ type KubeletConfig struct {
|
||||
MaxPods int
|
||||
DockerExecHandler dockertools.ExecHandler
|
||||
ResolverConfig string
|
||||
CPUCFSQuota bool
|
||||
}
|
||||
|
||||
func createAndInitKubelet(kc *KubeletConfig) (k KubeletBootstrap, pc *config.PodConfig, err error) {
|
||||
@ -833,7 +838,8 @@ func createAndInitKubelet(kc *KubeletConfig) (k KubeletBootstrap, pc *config.Pod
|
||||
kc.PodCIDR,
|
||||
kc.MaxPods,
|
||||
kc.DockerExecHandler,
|
||||
kc.ResolverConfig)
|
||||
kc.ResolverConfig,
|
||||
kc.CPUCFSQuota)
|
||||
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
|
@ -1,6 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
|
||||
__debug()
|
||||
{
|
||||
if [[ -n ${BASH_COMP_DEBUG_FILE} ]]; then
|
||||
@ -8,6 +7,14 @@ __debug()
|
||||
fi
|
||||
}
|
||||
|
||||
# Homebrew on Macs have version 1.3 of bash-completion which doesn't include
|
||||
# _init_completion. This is a very minimal version of that function.
|
||||
__my_init_completion()
|
||||
{
|
||||
COMPREPLY=()
|
||||
_get_comp_words_by_ref cur prev words cword
|
||||
}
|
||||
|
||||
__index_of_word()
|
||||
{
|
||||
local w word=$1
|
||||
@ -256,12 +263,10 @@ _kubectl_get()
|
||||
flags+=("--all-namespaces")
|
||||
flags+=("--filename=")
|
||||
flags_with_completion+=("--filename")
|
||||
flags_completion+=("__handle_filename_extension_flag json|yaml|yml")
|
||||
flags_completion+=("__handle_filename_extension_flag json|stdin|yaml|yml")
|
||||
two_word_flags+=("-f")
|
||||
flags_with_completion+=("-f")
|
||||
flags_completion+=("__handle_filename_extension_flag json|yaml|yml")
|
||||
flags+=("--help")
|
||||
flags+=("-h")
|
||||
flags_completion+=("__handle_filename_extension_flag json|stdin|yaml|yml")
|
||||
flags+=("--label-columns=")
|
||||
two_word_flags+=("-L")
|
||||
flags+=("--no-headers")
|
||||
@ -285,6 +290,7 @@ _kubectl_get()
|
||||
must_have_one_noun+=("deployment")
|
||||
must_have_one_noun+=("endpoints")
|
||||
must_have_one_noun+=("event")
|
||||
must_have_one_noun+=("horizontalpodautoscaler")
|
||||
must_have_one_noun+=("limitrange")
|
||||
must_have_one_noun+=("namespace")
|
||||
must_have_one_noun+=("node")
|
||||
@ -312,12 +318,10 @@ _kubectl_describe()
|
||||
|
||||
flags+=("--filename=")
|
||||
flags_with_completion+=("--filename")
|
||||
flags_completion+=("__handle_filename_extension_flag json|yaml|yml")
|
||||
flags_completion+=("__handle_filename_extension_flag json|stdin|yaml|yml")
|
||||
two_word_flags+=("-f")
|
||||
flags_with_completion+=("-f")
|
||||
flags_completion+=("__handle_filename_extension_flag json|yaml|yml")
|
||||
flags+=("--help")
|
||||
flags+=("-h")
|
||||
flags_completion+=("__handle_filename_extension_flag json|stdin|yaml|yml")
|
||||
flags+=("--selector=")
|
||||
two_word_flags+=("-l")
|
||||
|
||||
@ -349,12 +353,10 @@ _kubectl_create()
|
||||
|
||||
flags+=("--filename=")
|
||||
flags_with_completion+=("--filename")
|
||||
flags_completion+=("__handle_filename_extension_flag json|yaml|yml")
|
||||
flags_completion+=("__handle_filename_extension_flag json|stdin|yaml|yml")
|
||||
two_word_flags+=("-f")
|
||||
flags_with_completion+=("-f")
|
||||
flags_completion+=("__handle_filename_extension_flag json|yaml|yml")
|
||||
flags+=("--help")
|
||||
flags+=("-h")
|
||||
flags_completion+=("__handle_filename_extension_flag json|stdin|yaml|yml")
|
||||
flags+=("--output=")
|
||||
two_word_flags+=("-o")
|
||||
flags+=("--validate")
|
||||
@ -378,14 +380,12 @@ _kubectl_replace()
|
||||
flags+=("--cascade")
|
||||
flags+=("--filename=")
|
||||
flags_with_completion+=("--filename")
|
||||
flags_completion+=("__handle_filename_extension_flag json|yaml|yml")
|
||||
flags_completion+=("__handle_filename_extension_flag json|stdin|yaml|yml")
|
||||
two_word_flags+=("-f")
|
||||
flags_with_completion+=("-f")
|
||||
flags_completion+=("__handle_filename_extension_flag json|yaml|yml")
|
||||
flags_completion+=("__handle_filename_extension_flag json|stdin|yaml|yml")
|
||||
flags+=("--force")
|
||||
flags+=("--grace-period=")
|
||||
flags+=("--help")
|
||||
flags+=("-h")
|
||||
flags+=("--output=")
|
||||
two_word_flags+=("-o")
|
||||
flags+=("--timeout=")
|
||||
@ -409,12 +409,10 @@ _kubectl_patch()
|
||||
|
||||
flags+=("--filename=")
|
||||
flags_with_completion+=("--filename")
|
||||
flags_completion+=("__handle_filename_extension_flag json|yaml|yml")
|
||||
flags_completion+=("__handle_filename_extension_flag json|stdin|yaml|yml")
|
||||
two_word_flags+=("-f")
|
||||
flags_with_completion+=("-f")
|
||||
flags_completion+=("__handle_filename_extension_flag json|yaml|yml")
|
||||
flags+=("--help")
|
||||
flags+=("-h")
|
||||
flags_completion+=("__handle_filename_extension_flag json|stdin|yaml|yml")
|
||||
flags+=("--output=")
|
||||
two_word_flags+=("-o")
|
||||
flags+=("--patch=")
|
||||
@ -440,13 +438,11 @@ _kubectl_delete()
|
||||
flags+=("--cascade")
|
||||
flags+=("--filename=")
|
||||
flags_with_completion+=("--filename")
|
||||
flags_completion+=("__handle_filename_extension_flag json|yaml|yml")
|
||||
flags_completion+=("__handle_filename_extension_flag json|stdin|yaml|yml")
|
||||
two_word_flags+=("-f")
|
||||
flags_with_completion+=("-f")
|
||||
flags_completion+=("__handle_filename_extension_flag json|yaml|yml")
|
||||
flags_completion+=("__handle_filename_extension_flag json|stdin|yaml|yml")
|
||||
flags+=("--grace-period=")
|
||||
flags+=("--help")
|
||||
flags+=("-h")
|
||||
flags+=("--ignore-not-found")
|
||||
flags+=("--output=")
|
||||
two_word_flags+=("-o")
|
||||
@ -460,6 +456,7 @@ _kubectl_delete()
|
||||
must_have_one_noun+=("deployment")
|
||||
must_have_one_noun+=("endpoints")
|
||||
must_have_one_noun+=("event")
|
||||
must_have_one_noun+=("horizontalpodautoscaler")
|
||||
must_have_one_noun+=("limitrange")
|
||||
must_have_one_noun+=("namespace")
|
||||
must_have_one_noun+=("node")
|
||||
@ -485,8 +482,6 @@ _kubectl_namespace()
|
||||
flags_with_completion=()
|
||||
flags_completion=()
|
||||
|
||||
flags+=("--help")
|
||||
flags+=("-h")
|
||||
|
||||
must_have_one_flag=()
|
||||
must_have_one_noun=()
|
||||
@ -506,8 +501,6 @@ _kubectl_logs()
|
||||
two_word_flags+=("-c")
|
||||
flags+=("--follow")
|
||||
flags+=("-f")
|
||||
flags+=("--help")
|
||||
flags+=("-h")
|
||||
flags+=("--interactive")
|
||||
flags+=("--previous")
|
||||
flags+=("-p")
|
||||
@ -530,12 +523,10 @@ _kubectl_rolling-update()
|
||||
flags+=("--dry-run")
|
||||
flags+=("--filename=")
|
||||
flags_with_completion+=("--filename")
|
||||
flags_completion+=("__handle_filename_extension_flag json|yaml|yml")
|
||||
flags_completion+=("__handle_filename_extension_flag json|stdin|yaml|yml")
|
||||
two_word_flags+=("-f")
|
||||
flags_with_completion+=("-f")
|
||||
flags_completion+=("__handle_filename_extension_flag json|yaml|yml")
|
||||
flags+=("--help")
|
||||
flags+=("-h")
|
||||
flags_completion+=("__handle_filename_extension_flag json|stdin|yaml|yml")
|
||||
flags+=("--image=")
|
||||
flags+=("--no-headers")
|
||||
flags+=("--output=")
|
||||
@ -572,12 +563,10 @@ _kubectl_scale()
|
||||
flags+=("--current-replicas=")
|
||||
flags+=("--filename=")
|
||||
flags_with_completion+=("--filename")
|
||||
flags_completion+=("__handle_filename_extension_flag json|yaml|yml")
|
||||
flags_completion+=("__handle_filename_extension_flag json|stdin|yaml|yml")
|
||||
two_word_flags+=("-f")
|
||||
flags_with_completion+=("-f")
|
||||
flags_completion+=("__handle_filename_extension_flag json|yaml|yml")
|
||||
flags+=("--help")
|
||||
flags+=("-h")
|
||||
flags_completion+=("__handle_filename_extension_flag json|stdin|yaml|yml")
|
||||
flags+=("--output=")
|
||||
two_word_flags+=("-o")
|
||||
flags+=("--replicas=")
|
||||
@ -601,8 +590,6 @@ _kubectl_attach()
|
||||
|
||||
flags+=("--container=")
|
||||
two_word_flags+=("-c")
|
||||
flags+=("--help")
|
||||
flags+=("-h")
|
||||
flags+=("--stdin")
|
||||
flags+=("-i")
|
||||
flags+=("--tty")
|
||||
@ -624,8 +611,6 @@ _kubectl_exec()
|
||||
|
||||
flags+=("--container=")
|
||||
two_word_flags+=("-c")
|
||||
flags+=("--help")
|
||||
flags+=("-h")
|
||||
flags+=("--pod=")
|
||||
two_word_flags+=("-p")
|
||||
flags+=("--stdin")
|
||||
@ -647,8 +632,6 @@ _kubectl_port-forward()
|
||||
flags_with_completion=()
|
||||
flags_completion=()
|
||||
|
||||
flags+=("--help")
|
||||
flags+=("-h")
|
||||
flags+=("--pod=")
|
||||
two_word_flags+=("-p")
|
||||
|
||||
@ -670,8 +653,6 @@ _kubectl_proxy()
|
||||
flags+=("--accept-paths=")
|
||||
flags+=("--api-prefix=")
|
||||
flags+=("--disable-filter")
|
||||
flags+=("--help")
|
||||
flags+=("-h")
|
||||
flags+=("--port=")
|
||||
two_word_flags+=("-p")
|
||||
flags+=("--reject-methods=")
|
||||
@ -700,9 +681,8 @@ _kubectl_run()
|
||||
flags+=("--attach")
|
||||
flags+=("--command")
|
||||
flags+=("--dry-run")
|
||||
flags+=("--env=")
|
||||
flags+=("--generator=")
|
||||
flags+=("--help")
|
||||
flags+=("-h")
|
||||
flags+=("--hostport=")
|
||||
flags+=("--image=")
|
||||
flags+=("--labels=")
|
||||
@ -743,13 +723,11 @@ _kubectl_stop()
|
||||
flags+=("--all")
|
||||
flags+=("--filename=")
|
||||
flags_with_completion+=("--filename")
|
||||
flags_completion+=("__handle_filename_extension_flag json|yaml|yml")
|
||||
flags_completion+=("__handle_filename_extension_flag json|stdin|yaml|yml")
|
||||
two_word_flags+=("-f")
|
||||
flags_with_completion+=("-f")
|
||||
flags_completion+=("__handle_filename_extension_flag json|yaml|yml")
|
||||
flags_completion+=("__handle_filename_extension_flag json|stdin|yaml|yml")
|
||||
flags+=("--grace-period=")
|
||||
flags+=("--help")
|
||||
flags+=("-h")
|
||||
flags+=("--ignore-not-found")
|
||||
flags+=("--output=")
|
||||
two_word_flags+=("-o")
|
||||
@ -777,13 +755,11 @@ _kubectl_expose()
|
||||
flags+=("--external-ip=")
|
||||
flags+=("--filename=")
|
||||
flags_with_completion+=("--filename")
|
||||
flags_completion+=("__handle_filename_extension_flag json|yaml|yml")
|
||||
flags_completion+=("__handle_filename_extension_flag json|stdin|yaml|yml")
|
||||
two_word_flags+=("-f")
|
||||
flags_with_completion+=("-f")
|
||||
flags_completion+=("__handle_filename_extension_flag json|yaml|yml")
|
||||
flags_completion+=("__handle_filename_extension_flag json|stdin|yaml|yml")
|
||||
flags+=("--generator=")
|
||||
flags+=("--help")
|
||||
flags+=("-h")
|
||||
flags+=("--labels=")
|
||||
two_word_flags+=("-l")
|
||||
flags+=("--name=")
|
||||
@ -820,14 +796,13 @@ _kubectl_label()
|
||||
flags_completion=()
|
||||
|
||||
flags+=("--all")
|
||||
flags+=("--dry-run")
|
||||
flags+=("--filename=")
|
||||
flags_with_completion+=("--filename")
|
||||
flags_completion+=("__handle_filename_extension_flag json|yaml|yml")
|
||||
flags_completion+=("__handle_filename_extension_flag json|stdin|yaml|yml")
|
||||
two_word_flags+=("-f")
|
||||
flags_with_completion+=("-f")
|
||||
flags_completion+=("__handle_filename_extension_flag json|yaml|yml")
|
||||
flags+=("--help")
|
||||
flags+=("-h")
|
||||
flags_completion+=("__handle_filename_extension_flag json|stdin|yaml|yml")
|
||||
flags+=("--no-headers")
|
||||
flags+=("--output=")
|
||||
two_word_flags+=("-o")
|
||||
@ -848,6 +823,7 @@ _kubectl_label()
|
||||
must_have_one_noun+=("deployment")
|
||||
must_have_one_noun+=("endpoints")
|
||||
must_have_one_noun+=("event")
|
||||
must_have_one_noun+=("horizontalpodautoscaler")
|
||||
must_have_one_noun+=("limitrange")
|
||||
must_have_one_noun+=("namespace")
|
||||
must_have_one_noun+=("node")
|
||||
@ -876,12 +852,10 @@ _kubectl_annotate()
|
||||
flags+=("--all")
|
||||
flags+=("--filename=")
|
||||
flags_with_completion+=("--filename")
|
||||
flags_completion+=("__handle_filename_extension_flag json|yaml|yml")
|
||||
flags_completion+=("__handle_filename_extension_flag json|stdin|yaml|yml")
|
||||
two_word_flags+=("-f")
|
||||
flags_with_completion+=("-f")
|
||||
flags_completion+=("__handle_filename_extension_flag json|yaml|yml")
|
||||
flags+=("--help")
|
||||
flags+=("-h")
|
||||
flags_completion+=("__handle_filename_extension_flag json|stdin|yaml|yml")
|
||||
flags+=("--overwrite")
|
||||
flags+=("--resource-version=")
|
||||
|
||||
@ -900,8 +874,6 @@ _kubectl_config_view()
|
||||
flags_completion=()
|
||||
|
||||
flags+=("--flatten")
|
||||
flags+=("--help")
|
||||
flags+=("-h")
|
||||
flags+=("--merge")
|
||||
flags+=("--minify")
|
||||
flags+=("--no-headers")
|
||||
@ -932,8 +904,6 @@ _kubectl_config_set-cluster()
|
||||
flags+=("--api-version=")
|
||||
flags+=("--certificate-authority=")
|
||||
flags+=("--embed-certs")
|
||||
flags+=("--help")
|
||||
flags+=("-h")
|
||||
flags+=("--insecure-skip-tls-verify")
|
||||
flags+=("--server=")
|
||||
|
||||
@ -954,8 +924,6 @@ _kubectl_config_set-credentials()
|
||||
flags+=("--client-certificate=")
|
||||
flags+=("--client-key=")
|
||||
flags+=("--embed-certs")
|
||||
flags+=("--help")
|
||||
flags+=("-h")
|
||||
flags+=("--password=")
|
||||
flags+=("--token=")
|
||||
flags+=("--username=")
|
||||
@ -975,8 +943,6 @@ _kubectl_config_set-context()
|
||||
flags_completion=()
|
||||
|
||||
flags+=("--cluster=")
|
||||
flags+=("--help")
|
||||
flags+=("-h")
|
||||
flags+=("--namespace=")
|
||||
flags+=("--user=")
|
||||
|
||||
@ -994,8 +960,6 @@ _kubectl_config_set()
|
||||
flags_with_completion=()
|
||||
flags_completion=()
|
||||
|
||||
flags+=("--help")
|
||||
flags+=("-h")
|
||||
|
||||
must_have_one_flag=()
|
||||
must_have_one_noun=()
|
||||
@ -1011,8 +975,6 @@ _kubectl_config_unset()
|
||||
flags_with_completion=()
|
||||
flags_completion=()
|
||||
|
||||
flags+=("--help")
|
||||
flags+=("-h")
|
||||
|
||||
must_have_one_flag=()
|
||||
must_have_one_noun=()
|
||||
@ -1028,8 +990,6 @@ _kubectl_config_use-context()
|
||||
flags_with_completion=()
|
||||
flags_completion=()
|
||||
|
||||
flags+=("--help")
|
||||
flags+=("-h")
|
||||
|
||||
must_have_one_flag=()
|
||||
must_have_one_noun=()
|
||||
@ -1052,8 +1012,6 @@ _kubectl_config()
|
||||
flags_with_completion=()
|
||||
flags_completion=()
|
||||
|
||||
flags+=("--help")
|
||||
flags+=("-h")
|
||||
flags+=("--kubeconfig=")
|
||||
|
||||
must_have_one_flag=()
|
||||
@ -1070,8 +1028,6 @@ _kubectl_cluster-info()
|
||||
flags_with_completion=()
|
||||
flags_completion=()
|
||||
|
||||
flags+=("--help")
|
||||
flags+=("-h")
|
||||
|
||||
must_have_one_flag=()
|
||||
must_have_one_noun=()
|
||||
@ -1087,8 +1043,6 @@ _kubectl_api-versions()
|
||||
flags_with_completion=()
|
||||
flags_completion=()
|
||||
|
||||
flags+=("--help")
|
||||
flags+=("-h")
|
||||
|
||||
must_have_one_flag=()
|
||||
must_have_one_noun=()
|
||||
@ -1106,8 +1060,6 @@ _kubectl_version()
|
||||
|
||||
flags+=("--client")
|
||||
flags+=("-c")
|
||||
flags+=("--help")
|
||||
flags+=("-h")
|
||||
|
||||
must_have_one_flag=()
|
||||
must_have_one_noun=()
|
||||
@ -1153,8 +1105,6 @@ _kubectl()
|
||||
flags+=("--client-key=")
|
||||
flags+=("--cluster=")
|
||||
flags+=("--context=")
|
||||
flags+=("--help")
|
||||
flags+=("-h")
|
||||
flags+=("--insecure-skip-tls-verify")
|
||||
flags+=("--kubeconfig=")
|
||||
flags+=("--log-backtrace-at=")
|
||||
@ -1180,7 +1130,11 @@ _kubectl()
|
||||
__start_kubectl()
|
||||
{
|
||||
local cur prev words cword
|
||||
_init_completion -s || return
|
||||
if declare -F _init_completions >/dev/null 2>&1; then
|
||||
_init_completion -s || return
|
||||
else
|
||||
__my_init_completion || return
|
||||
fi
|
||||
|
||||
local c=0
|
||||
local flags=()
|
||||
|
@ -318,7 +318,7 @@ func TestExecutorLaunchAndKillTask(t *testing.T) {
|
||||
Updates: updates,
|
||||
APIClient: client.NewOrDie(&client.Config{
|
||||
Host: testApiServer.server.URL,
|
||||
Version: testapi.Version(),
|
||||
Version: testapi.Default.Version(),
|
||||
}),
|
||||
Kubelet: &fakeKubelet{
|
||||
Kubelet: &kubelet.Kubelet{},
|
||||
@ -355,7 +355,7 @@ func TestExecutorLaunchAndKillTask(t *testing.T) {
|
||||
assert.Equal(t, nil, err, "must be able to create a task from a pod")
|
||||
|
||||
taskInfo := podTask.BuildTaskInfo()
|
||||
data, err := testapi.Codec().Encode(pod)
|
||||
data, err := testapi.Default.Codec().Encode(pod)
|
||||
assert.Equal(t, nil, err, "must be able to encode a pod's spec data")
|
||||
taskInfo.Data = data
|
||||
var statusUpdateCalls sync.WaitGroup
|
||||
@ -484,7 +484,7 @@ func TestExecutorStaticPods(t *testing.T) {
|
||||
Updates: make(chan interface{}, 1), // allow kube-executor source to proceed past init
|
||||
APIClient: client.NewOrDie(&client.Config{
|
||||
Host: testApiServer.server.URL,
|
||||
Version: testapi.Version(),
|
||||
Version: testapi.Default.Version(),
|
||||
}),
|
||||
Kubelet: &kubelet.Kubelet{},
|
||||
PodStatusFunc: func(kl KubeletInterface, pod *api.Pod) (*api.PodStatus, error) {
|
||||
@ -565,7 +565,7 @@ func TestExecutorFrameworkMessage(t *testing.T) {
|
||||
Updates: make(chan interface{}, 1024),
|
||||
APIClient: client.NewOrDie(&client.Config{
|
||||
Host: testApiServer.server.URL,
|
||||
Version: testapi.Version(),
|
||||
Version: testapi.Default.Version(),
|
||||
}),
|
||||
Kubelet: &fakeKubelet{
|
||||
Kubelet: &kubelet.Kubelet{},
|
||||
@ -602,7 +602,7 @@ func TestExecutorFrameworkMessage(t *testing.T) {
|
||||
*pod, &mesosproto.ExecutorInfo{})
|
||||
|
||||
taskInfo := podTask.BuildTaskInfo()
|
||||
data, _ := testapi.Codec().Encode(pod)
|
||||
data, _ := testapi.Default.Codec().Encode(pod)
|
||||
taskInfo.Data = data
|
||||
|
||||
mockDriver.On(
|
||||
@ -660,11 +660,11 @@ func TestExecutorFrameworkMessage(t *testing.T) {
|
||||
func NewTestPod(i int) *api.Pod {
|
||||
name := fmt.Sprintf("pod%d", i)
|
||||
return &api.Pod{
|
||||
TypeMeta: api.TypeMeta{APIVersion: testapi.Version()},
|
||||
TypeMeta: api.TypeMeta{APIVersion: testapi.Default.Version()},
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: api.NamespaceDefault,
|
||||
SelfLink: testapi.SelfLink("pods", string(i)),
|
||||
SelfLink: testapi.Default.SelfLink("pods", string(i)),
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
Containers: []api.Container{
|
||||
@ -710,7 +710,7 @@ func NewTestServer(t *testing.T, namespace string, pods *api.PodList) *TestServe
|
||||
}
|
||||
mux := http.NewServeMux()
|
||||
|
||||
mux.HandleFunc(testapi.ResourcePath("bindings", namespace, ""), func(w http.ResponseWriter, r *http.Request) {
|
||||
mux.HandleFunc(testapi.Default.ResourcePath("bindings", namespace, ""), func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
})
|
||||
|
||||
|
@ -228,6 +228,7 @@ func (s *KubeletExecutorServer) Run(hks hyperkube.Interface, _ []string) error {
|
||||
MaxPods: s.MaxPods,
|
||||
DockerExecHandler: dockerExecHandler,
|
||||
ResolverConfig: s.ResolverConfig,
|
||||
CPUCFSQuota: s.CPUCFSQuota,
|
||||
}
|
||||
|
||||
kcfg.NodeName = kcfg.Hostname
|
||||
@ -330,6 +331,7 @@ func (ks *KubeletExecutorServer) createAndInitKubelet(
|
||||
kc.MaxPods,
|
||||
kc.DockerExecHandler,
|
||||
kc.ResolverConfig,
|
||||
kc.CPUCFSQuota,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
|
@ -21,15 +21,15 @@ import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
"syscall"
|
||||
|
||||
exservice "k8s.io/kubernetes/contrib/mesos/pkg/executor/service"
|
||||
"k8s.io/kubernetes/contrib/mesos/pkg/hyperkube"
|
||||
"k8s.io/kubernetes/contrib/mesos/pkg/minion/config"
|
||||
"k8s.io/kubernetes/contrib/mesos/pkg/runtime"
|
||||
"k8s.io/kubernetes/contrib/mesos/pkg/minion/tasks"
|
||||
"k8s.io/kubernetes/pkg/api/resource"
|
||||
client "k8s.io/kubernetes/pkg/client/unversioned"
|
||||
|
||||
@ -39,6 +39,11 @@ import (
|
||||
"gopkg.in/natefinch/lumberjack.v2"
|
||||
)
|
||||
|
||||
const (
|
||||
proxyLogFilename = "proxy.log"
|
||||
executorLogFilename = "executor.log"
|
||||
)
|
||||
|
||||
type MinionServer struct {
|
||||
// embed the executor server to be able to use its flags
|
||||
// TODO(sttts): get rid of this mixing of the minion and the executor server with a multiflags implementation for km
|
||||
@ -48,16 +53,18 @@ type MinionServer struct {
|
||||
hks hyperkube.Interface
|
||||
clientConfig *client.Config
|
||||
kmBinary string
|
||||
done chan struct{} // closed when shutting down
|
||||
exit chan error // to signal fatal errors
|
||||
tasks []*tasks.Task
|
||||
|
||||
pathOverride string // the PATH environment for the sub-processes
|
||||
cgroupPrefix string // e.g. mesos
|
||||
cgroupRoot string // e.g. /mesos/{container-id}, determined at runtime
|
||||
pathOverride string // the PATH environment for the sub-processes
|
||||
cgroupPrefix string // e.g. mesos
|
||||
cgroupRoot string // the cgroupRoot that we pass to the kubelet-executor, depends on containPodResources
|
||||
mesosCgroup string // discovered mesos cgroup root, e.g. /mesos/{container-id}
|
||||
containPodResources bool
|
||||
|
||||
logMaxSize resource.Quantity
|
||||
logMaxBackups int
|
||||
logMaxAgeInDays int
|
||||
logVerbosity int32 // see glog.Level
|
||||
|
||||
runProxy bool
|
||||
proxyLogV int
|
||||
@ -69,15 +76,12 @@ func NewMinionServer() *MinionServer {
|
||||
s := &MinionServer{
|
||||
KubeletExecutorServer: exservice.NewKubeletExecutorServer(),
|
||||
privateMountNS: false, // disabled until Docker supports customization of the parent mount namespace
|
||||
done: make(chan struct{}),
|
||||
exit: make(chan error),
|
||||
|
||||
cgroupPrefix: config.DefaultCgroupPrefix,
|
||||
logMaxSize: config.DefaultLogMaxSize(),
|
||||
logMaxBackups: config.DefaultLogMaxBackups,
|
||||
logMaxAgeInDays: config.DefaultLogMaxAgeInDays,
|
||||
|
||||
runProxy: true,
|
||||
cgroupPrefix: config.DefaultCgroupPrefix,
|
||||
containPodResources: true,
|
||||
logMaxSize: config.DefaultLogMaxSize(),
|
||||
logMaxBackups: config.DefaultLogMaxBackups,
|
||||
logMaxAgeInDays: config.DefaultLogMaxAgeInDays,
|
||||
runProxy: true,
|
||||
}
|
||||
|
||||
// cache this for later use
|
||||
@ -131,7 +135,7 @@ func (ms *MinionServer) launchProxyServer() {
|
||||
fmt.Sprintf("--bind-address=%s", bindAddress),
|
||||
fmt.Sprintf("--v=%d", ms.proxyLogV),
|
||||
"--logtostderr=true",
|
||||
"--resource-container=" + path.Join("/", ms.cgroupRoot, "kube-proxy"),
|
||||
"--resource-container=" + path.Join("/", ms.mesosCgroup, "kube-proxy"),
|
||||
}
|
||||
|
||||
if ms.clientConfig.Host != "" {
|
||||
@ -141,10 +145,13 @@ func (ms *MinionServer) launchProxyServer() {
|
||||
args = append(args, fmt.Sprintf("--hostname-override=%s", ms.KubeletExecutorServer.HostnameOverride))
|
||||
}
|
||||
|
||||
ms.launchHyperkubeServer(hyperkube.CommandProxy, &args, "proxy.log")
|
||||
ms.launchHyperkubeServer(hyperkube.CommandProxy, args, proxyLogFilename, nil)
|
||||
}
|
||||
|
||||
func (ms *MinionServer) launchExecutorServer() {
|
||||
// launchExecutorServer returns a chan that closes upon kubelet-executor death. since the kubelet-
|
||||
// executor doesn't support failover right now, the right thing to do is to fail completely since all
|
||||
// pods will be lost upon restart and we want mesos to recover the resources from them.
|
||||
func (ms *MinionServer) launchExecutorServer() <-chan struct{} {
|
||||
allArgs := os.Args[1:]
|
||||
|
||||
// filter out minion flags, leaving those for the executor
|
||||
@ -153,117 +160,71 @@ func (ms *MinionServer) launchExecutorServer() {
|
||||
ms.AddExecutorFlags(executorFlags)
|
||||
executorArgs, _ := filterArgsByFlagSet(allArgs, executorFlags)
|
||||
|
||||
executorArgs = append(executorArgs, "--resource-container="+path.Join("/", ms.cgroupRoot, "kubelet"))
|
||||
executorArgs = append(executorArgs, "--resource-container="+path.Join("/", ms.mesosCgroup, "kubelet"))
|
||||
if ms.cgroupRoot != "" {
|
||||
executorArgs = append(executorArgs, "--cgroup-root="+ms.cgroupRoot)
|
||||
}
|
||||
|
||||
// run executor and quit minion server when this exits cleanly
|
||||
err := ms.launchHyperkubeServer(hyperkube.CommandExecutor, &executorArgs, "executor.log")
|
||||
if err != nil {
|
||||
// just return, executor will be restarted on error
|
||||
log.Error(err)
|
||||
return
|
||||
execDied := make(chan struct{})
|
||||
decorator := func(t *tasks.Task) *tasks.Task {
|
||||
t.Finished = func(_ bool) bool {
|
||||
// this func implements the task.finished spec, so when the executor exits
|
||||
// we return false to indicate that it should not be restarted. we also
|
||||
// close execDied to signal interested listeners.
|
||||
close(execDied)
|
||||
return false
|
||||
}
|
||||
// since we only expect to die once, and there is no restart; don't delay any longer than needed
|
||||
t.RestartDelay = 0
|
||||
return t
|
||||
}
|
||||
|
||||
log.Info("Executor exited cleanly, stopping the minion")
|
||||
ms.exit <- nil
|
||||
ms.launchHyperkubeServer(hyperkube.CommandExecutor, executorArgs, executorLogFilename, decorator)
|
||||
return execDied
|
||||
}
|
||||
|
||||
func (ms *MinionServer) launchHyperkubeServer(server string, args *[]string, logFileName string) error {
|
||||
func (ms *MinionServer) launchHyperkubeServer(server string, args []string, logFileName string, decorator func(*tasks.Task) *tasks.Task) {
|
||||
log.V(2).Infof("Spawning hyperkube %v with args '%+v'", server, args)
|
||||
|
||||
// prepare parameters
|
||||
kmArgs := []string{server}
|
||||
for _, arg := range *args {
|
||||
kmArgs = append(kmArgs, arg)
|
||||
}
|
||||
|
||||
// create command
|
||||
cmd := exec.Command(ms.kmBinary, kmArgs...)
|
||||
if _, err := cmd.StdoutPipe(); err != nil {
|
||||
// fatal error => terminate minion
|
||||
err = fmt.Errorf("error getting stdout of %v: %v", server, err)
|
||||
ms.exit <- err
|
||||
return err
|
||||
}
|
||||
stderrLogs, err := cmd.StderrPipe()
|
||||
if err != nil {
|
||||
// fatal error => terminate minion
|
||||
err = fmt.Errorf("error getting stderr of %v: %v", server, err)
|
||||
ms.exit <- err
|
||||
return err
|
||||
}
|
||||
|
||||
ch := make(chan struct{})
|
||||
go func() {
|
||||
defer func() {
|
||||
select {
|
||||
case <-ch:
|
||||
log.Infof("killing %v process...", server)
|
||||
if err = cmd.Process.Kill(); err != nil {
|
||||
log.Errorf("failed to kill %v process: %v", server, err)
|
||||
}
|
||||
default:
|
||||
}
|
||||
}()
|
||||
|
||||
maxSize := ms.logMaxSize.Value()
|
||||
if maxSize > 0 {
|
||||
// convert to MB
|
||||
maxSize = maxSize / 1024 / 1024
|
||||
if maxSize == 0 {
|
||||
log.Warning("maximal log file size is rounded to 1 MB")
|
||||
maxSize = 1
|
||||
}
|
||||
kmArgs := append([]string{server}, args...)
|
||||
maxSize := ms.logMaxSize.Value()
|
||||
if maxSize > 0 {
|
||||
// convert to MB
|
||||
maxSize = maxSize / 1024 / 1024
|
||||
if maxSize == 0 {
|
||||
log.Warning("maximal log file size is rounded to 1 MB")
|
||||
maxSize = 1
|
||||
}
|
||||
writer := &lumberjack.Logger{
|
||||
}
|
||||
|
||||
writerFunc := func() io.WriteCloser {
|
||||
return &lumberjack.Logger{
|
||||
Filename: logFileName,
|
||||
MaxSize: int(maxSize),
|
||||
MaxBackups: ms.logMaxBackups,
|
||||
MaxAge: ms.logMaxAgeInDays,
|
||||
}
|
||||
defer writer.Close()
|
||||
|
||||
log.V(2).Infof("Starting logging for %v: max log file size %d MB, keeping %d backups, for %d days", server, maxSize, ms.logMaxBackups, ms.logMaxAgeInDays)
|
||||
|
||||
<-ch
|
||||
written, err := io.Copy(writer, stderrLogs)
|
||||
if err != nil {
|
||||
log.Errorf("error writing data to %v: %v", logFileName, err)
|
||||
}
|
||||
|
||||
log.Infof("wrote %d bytes to %v", written, logFileName)
|
||||
}()
|
||||
}
|
||||
|
||||
// use given environment, but add /usr/sbin to the path for the iptables binary used in kube-proxy
|
||||
var kmEnv []string
|
||||
if ms.pathOverride != "" {
|
||||
env := os.Environ()
|
||||
cmd.Env = make([]string, 0, len(env))
|
||||
kmEnv = make([]string, 0, len(env))
|
||||
for _, e := range env {
|
||||
if !strings.HasPrefix(e, "PATH=") {
|
||||
cmd.Env = append(cmd.Env, e)
|
||||
kmEnv = append(kmEnv, e)
|
||||
}
|
||||
}
|
||||
cmd.Env = append(cmd.Env, "PATH="+ms.pathOverride)
|
||||
kmEnv = append(kmEnv, "PATH="+ms.pathOverride)
|
||||
}
|
||||
|
||||
// if the server fails to start then we exit the executor, otherwise
|
||||
// wait for the proxy process to end (and release resources after).
|
||||
if err := cmd.Start(); err != nil {
|
||||
// fatal error => terminate minion
|
||||
err = fmt.Errorf("error starting %v: %v", server, err)
|
||||
ms.exit <- err
|
||||
return err
|
||||
t := tasks.New(server, ms.kmBinary, kmArgs, kmEnv, writerFunc)
|
||||
if decorator != nil {
|
||||
t = decorator(t)
|
||||
}
|
||||
close(ch)
|
||||
if err := cmd.Wait(); err != nil {
|
||||
log.Errorf("%v exited with error: %v", server, err)
|
||||
err = fmt.Errorf("%v exited with error: %v", server, err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
go t.Start()
|
||||
ms.tasks = append(ms.tasks, t)
|
||||
}
|
||||
|
||||
// runs the main kubelet loop, closing the kubeletFinished chan when the loop exits.
|
||||
@ -284,30 +245,79 @@ func (ms *MinionServer) Run(hks hyperkube.Interface, _ []string) error {
|
||||
ms.clientConfig = clientConfig
|
||||
|
||||
// derive the executor cgroup and use it as:
|
||||
// - pod container cgroup root (e.g. docker cgroup-parent)
|
||||
// - pod container cgroup root (e.g. docker cgroup-parent, optionally; see comments below)
|
||||
// - parent of kubelet container
|
||||
// - parent of kube-proxy container
|
||||
ms.cgroupRoot = findMesosCgroup(ms.cgroupPrefix)
|
||||
ms.mesosCgroup = findMesosCgroup(ms.cgroupPrefix)
|
||||
log.Infof("discovered mesos cgroup at %q", ms.mesosCgroup)
|
||||
|
||||
// hack alert, this helps to work around systemd+docker+mesos integration problems
|
||||
// when docker's cgroup-parent flag is used (!containPodResources = don't use the docker flag)
|
||||
if ms.containPodResources {
|
||||
ms.cgroupRoot = ms.mesosCgroup
|
||||
}
|
||||
|
||||
cgroupLogger := log.Infof
|
||||
if ms.cgroupRoot == "" {
|
||||
cgroupLogger = log.Warningf
|
||||
}
|
||||
|
||||
cgroupLogger("using cgroup-root %q", ms.cgroupRoot)
|
||||
|
||||
// run subprocesses until ms.done is closed on return of this function
|
||||
defer close(ms.done)
|
||||
if ms.runProxy {
|
||||
go runtime.Until(ms.launchProxyServer, 5*time.Second, ms.done)
|
||||
ms.launchProxyServer()
|
||||
}
|
||||
go runtime.Until(ms.launchExecutorServer, 5*time.Second, ms.done)
|
||||
|
||||
// wait until minion exit is requested
|
||||
// don't close ms.exit here to avoid panics of go routines writing an error to it
|
||||
return <-ms.exit
|
||||
// abort closes when the kubelet-executor dies
|
||||
abort := ms.launchExecutorServer()
|
||||
shouldQuit := termSignalListener(abort)
|
||||
te := tasks.MergeOutput(ms.tasks, shouldQuit)
|
||||
|
||||
// TODO(jdef) do something fun here, such as reporting task completion to the apiserver
|
||||
|
||||
<-te.Close().Done() // we don't listen for any specific events yet; wait for all tasks to finish
|
||||
return nil
|
||||
}
|
||||
|
||||
// termSignalListener returns a signal chan that closes when either (a) the process receives a termination
|
||||
// signal: SIGTERM, SIGINT, or SIGHUP; or (b) the abort chan closes.
|
||||
func termSignalListener(abort <-chan struct{}) <-chan struct{} {
|
||||
shouldQuit := make(chan struct{})
|
||||
sigCh := make(chan os.Signal, 1)
|
||||
signal.Notify(sigCh)
|
||||
|
||||
go func() {
|
||||
defer close(shouldQuit)
|
||||
for {
|
||||
select {
|
||||
case <-abort:
|
||||
log.Infof("executor died, aborting")
|
||||
return
|
||||
case s, ok := <-sigCh:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
switch s {
|
||||
case os.Interrupt, os.Signal(syscall.SIGTERM), os.Signal(syscall.SIGINT), os.Signal(syscall.SIGHUP):
|
||||
log.Infof("received signal %q, aborting", s)
|
||||
return
|
||||
case os.Signal(syscall.SIGCHLD): // who cares?
|
||||
default:
|
||||
log.Errorf("unexpected signal: %T %#v", s, s)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}()
|
||||
return shouldQuit
|
||||
}
|
||||
|
||||
func (ms *MinionServer) AddExecutorFlags(fs *pflag.FlagSet) {
|
||||
ms.KubeletExecutorServer.AddFlags(fs)
|
||||
|
||||
// hack to forward log verbosity flag to the executor
|
||||
fs.Int32Var(&ms.logVerbosity, "v", ms.logVerbosity, "log level for V logs")
|
||||
}
|
||||
|
||||
func (ms *MinionServer) AddMinionFlags(fs *pflag.FlagSet) {
|
||||
@ -315,6 +325,7 @@ func (ms *MinionServer) AddMinionFlags(fs *pflag.FlagSet) {
|
||||
fs.StringVar(&ms.cgroupPrefix, "mesos-cgroup-prefix", ms.cgroupPrefix, "The cgroup prefix concatenated with MESOS_DIRECTORY must give the executor cgroup set by Mesos")
|
||||
fs.BoolVar(&ms.privateMountNS, "private-mountns", ms.privateMountNS, "Enter a private mount NS before spawning procs (linux only). Experimental, not yet compatible with k8s volumes.")
|
||||
fs.StringVar(&ms.pathOverride, "path-override", ms.pathOverride, "Override the PATH in the environment of the sub-processes.")
|
||||
fs.BoolVar(&ms.containPodResources, "contain-pod-resources", ms.containPodResources, "Allocate pod CPU and memory resources from offers and reparent pod containers into mesos cgroups; disable if you're having strange mesos/docker/systemd interactions.")
|
||||
|
||||
// log file flags
|
||||
fs.Var(resource.NewQuantityFlagValue(&ms.logMaxSize), "max-log-size", "Maximum log file size for the executor and proxy before rotation")
|
||||
|
20
contrib/mesos/pkg/minion/tasks/doc.go
Normal file
20
contrib/mesos/pkg/minion/tasks/doc.go
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors 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.
|
||||
*/
|
||||
|
||||
// Package tasks provides an API for supervising system processes as Task's.
|
||||
// It provides stronger guarantees with respect to process lifecycle than a
|
||||
// standalone kubelet running static pods.
|
||||
package tasks
|
98
contrib/mesos/pkg/minion/tasks/events.go
Normal file
98
contrib/mesos/pkg/minion/tasks/events.go
Normal file
@ -0,0 +1,98 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors 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.
|
||||
*/
|
||||
|
||||
package tasks
|
||||
|
||||
type Events interface {
|
||||
// Close stops delivery of events in the completion and errors channels; callers must close this when they intend to no longer read from completion() or errors()
|
||||
Close() Events
|
||||
|
||||
// Completion reports Completion events as they happen
|
||||
Completion() <-chan *Completion
|
||||
|
||||
// Done returns a signal chan that closes when all tasks have completed and there are no more events to deliver
|
||||
Done() <-chan struct{}
|
||||
}
|
||||
|
||||
type eventsImpl struct {
|
||||
tc chan *Completion
|
||||
stopForwarding chan struct{}
|
||||
done <-chan struct{}
|
||||
}
|
||||
|
||||
func newEventsImpl(tcin <-chan *Completion, done <-chan struct{}) *eventsImpl {
|
||||
ei := &eventsImpl{
|
||||
tc: make(chan *Completion),
|
||||
stopForwarding: make(chan struct{}),
|
||||
done: done,
|
||||
}
|
||||
go func() {
|
||||
defer close(ei.tc)
|
||||
forwardCompletionUntil(tcin, ei.tc, ei.stopForwarding, done, nil)
|
||||
}()
|
||||
return ei
|
||||
}
|
||||
|
||||
func (e *eventsImpl) Close() Events { close(e.stopForwarding); return e }
|
||||
func (e *eventsImpl) Completion() <-chan *Completion { return e.tc }
|
||||
func (e *eventsImpl) Done() <-chan struct{} { return e.done }
|
||||
|
||||
// forwardCompletionUntil is a generic pipe that forwards objects between channels.
|
||||
// if discard is closed, objects are silently dropped.
|
||||
// if tap != nil then it's invoked for each object as it's read from tin, but before it's written to tch.
|
||||
// returns when either reading from tin completes (no more objects, and is closed), or else
|
||||
// abort is closed, which ever happens first.
|
||||
func forwardCompletionUntil(tin <-chan *Completion, tch chan<- *Completion, discard <-chan struct{}, abort <-chan struct{}, tap func(*Completion, bool)) {
|
||||
var tc *Completion
|
||||
var ok bool
|
||||
forwardLoop:
|
||||
for {
|
||||
select {
|
||||
case tc, ok = <-tin:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if tap != nil {
|
||||
tap(tc, false)
|
||||
}
|
||||
select {
|
||||
case <-abort:
|
||||
break forwardLoop
|
||||
case <-discard:
|
||||
case tch <- tc:
|
||||
}
|
||||
case <-abort:
|
||||
// best effort
|
||||
select {
|
||||
case tc, ok = <-tin:
|
||||
if ok {
|
||||
if tap != nil {
|
||||
tap(tc, true)
|
||||
}
|
||||
break forwardLoop
|
||||
}
|
||||
default:
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
// best effort
|
||||
select {
|
||||
case tch <- tc:
|
||||
case <-discard:
|
||||
default:
|
||||
}
|
||||
}
|
348
contrib/mesos/pkg/minion/tasks/task.go
Normal file
348
contrib/mesos/pkg/minion/tasks/task.go
Normal file
@ -0,0 +1,348 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors 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.
|
||||
*/
|
||||
|
||||
package tasks
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os/exec"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
log "github.com/golang/glog"
|
||||
"k8s.io/kubernetes/contrib/mesos/pkg/runtime"
|
||||
)
|
||||
|
||||
const defaultTaskRestartDelay = 5 * time.Second
|
||||
|
||||
// Completion represents the termination of a Task process. Each process execution should
|
||||
// yield (barring drops because of an abort signal) exactly one Completion.
|
||||
type Completion struct {
|
||||
name string // name of the task
|
||||
code int // exit code that the task process completed with
|
||||
err error // process management errors are reported here
|
||||
}
|
||||
|
||||
// systemProcess is a useful abstraction for testing
|
||||
type systemProcess interface {
|
||||
// Wait works like exec.Cmd.Wait()
|
||||
Wait() error
|
||||
|
||||
// Kill returns the pid of the process that was killed
|
||||
Kill() (int, error)
|
||||
}
|
||||
|
||||
type cmdProcess struct {
|
||||
delegate *exec.Cmd
|
||||
}
|
||||
|
||||
func (cp *cmdProcess) Wait() error {
|
||||
return cp.delegate.Wait()
|
||||
}
|
||||
|
||||
func (cp *cmdProcess) Kill() (int, error) {
|
||||
// kill the entire process group, not just the one process
|
||||
pid := cp.delegate.Process.Pid
|
||||
processGroup := 0 - pid
|
||||
|
||||
// we send a SIGTERM here for a graceful stop. users of this package should
|
||||
// wait for tasks to complete normally. as a fallback/safeguard, child procs
|
||||
// are spawned in notStartedTask to receive a SIGKILL when this process dies.
|
||||
rc := syscall.Kill(processGroup, syscall.SIGTERM)
|
||||
return pid, rc
|
||||
}
|
||||
|
||||
// task is a specification for running a system process; it provides hooks for customizing
|
||||
// logging and restart handling as well as provides event channels for communicating process
|
||||
// termination and errors related to process management.
|
||||
type Task struct {
|
||||
Finished func(restarting bool) bool // callback invoked when a task process has completed; if `restarting` then it will be restarted if it returns true
|
||||
RestartDelay time.Duration // interval between repeated task restarts
|
||||
|
||||
name string // required: unique name for this task
|
||||
bin string // required: path to executable
|
||||
args []string // optional: process arguments
|
||||
env []string // optional: process environment override
|
||||
createLogger func() io.WriteCloser // factory func that builds a log writer
|
||||
cmd systemProcess // process that we started
|
||||
completedCh chan *Completion // reports exit codes encountered when task processes exit, or errors during process management
|
||||
shouldQuit chan struct{} // shouldQuit is closed to indicate that the task should stop its running process, if any
|
||||
done chan struct{} // done closes when all processes related to the task have terminated
|
||||
initialState taskStateFn // prepare and start a new live process, defaults to notStartedTask; should be set by run()
|
||||
runLatch int32 // guard against multiple Task.run calls
|
||||
}
|
||||
|
||||
// New builds a newly initialized task object but does not start any processes for it. callers
|
||||
// are expected to invoke task.run(...) on their own.
|
||||
func New(name, bin string, args, env []string, cl func() io.WriteCloser) *Task {
|
||||
return &Task{
|
||||
name: name,
|
||||
bin: bin,
|
||||
args: args,
|
||||
env: env,
|
||||
createLogger: cl,
|
||||
completedCh: make(chan *Completion),
|
||||
shouldQuit: make(chan struct{}),
|
||||
done: make(chan struct{}),
|
||||
RestartDelay: defaultTaskRestartDelay,
|
||||
Finished: func(restarting bool) bool { return restarting },
|
||||
}
|
||||
}
|
||||
|
||||
// Start spawns a goroutine to execute the Task. Panics if invoked more than once.
|
||||
func (t *Task) Start() {
|
||||
go t.run(notStartedTask)
|
||||
}
|
||||
|
||||
// run executes the state machine responsible for starting, monitoring, and possibly restarting
|
||||
// a system process for the task. The initialState func is the entry point of the state machine.
|
||||
// Upon returning the done and completedCh chans are all closed.
|
||||
func (t *Task) run(initialState taskStateFn) {
|
||||
if !atomic.CompareAndSwapInt32(&t.runLatch, 0, 1) {
|
||||
panic("Task.run() may only be invoked once")
|
||||
}
|
||||
t.initialState = initialState
|
||||
|
||||
defer close(t.done)
|
||||
defer close(t.completedCh)
|
||||
|
||||
state := initialState
|
||||
for state != nil {
|
||||
next := state(t)
|
||||
state = next
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Task) tryComplete(tc *Completion) {
|
||||
select {
|
||||
case <-t.shouldQuit:
|
||||
// best effort
|
||||
select {
|
||||
case t.completedCh <- tc:
|
||||
default:
|
||||
}
|
||||
case t.completedCh <- tc:
|
||||
}
|
||||
}
|
||||
|
||||
// tryError is a convenience func that invokes tryComplete with a completion error
|
||||
func (t *Task) tryError(err error) {
|
||||
t.tryComplete(&Completion{err: err})
|
||||
}
|
||||
|
||||
type taskStateFn func(*Task) taskStateFn
|
||||
|
||||
func taskShouldRestart(t *Task) taskStateFn {
|
||||
// make our best effort to stop here if signalled (shouldQuit). not doing so here
|
||||
// could add cost later (a process might be launched).
|
||||
|
||||
// sleep for a bit; then return t.initialState
|
||||
tm := time.NewTimer(t.RestartDelay)
|
||||
defer tm.Stop()
|
||||
select {
|
||||
case <-tm.C:
|
||||
select {
|
||||
case <-t.shouldQuit:
|
||||
default:
|
||||
if t.Finished(true) {
|
||||
select {
|
||||
case <-t.shouldQuit:
|
||||
// the world has changed, die
|
||||
return nil
|
||||
default:
|
||||
}
|
||||
return t.initialState
|
||||
}
|
||||
// finish call decided not to respawn, so die
|
||||
return nil
|
||||
}
|
||||
case <-t.shouldQuit:
|
||||
}
|
||||
|
||||
// we're quitting, tell the Finished callback and then die
|
||||
t.Finished(false)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Task) initLogging(r io.Reader) {
|
||||
writer := t.createLogger()
|
||||
go func() {
|
||||
defer writer.Close()
|
||||
_, err := io.Copy(writer, r)
|
||||
if err != nil && err != io.EOF {
|
||||
// using tryComplete is racy because the state machine closes completedCh and
|
||||
// so we don't want to attempt to write to a closed/closing chan. so
|
||||
// just log this for now.
|
||||
log.Errorf("logger for task %q crashed: %v", t.bin, err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// notStartedTask spawns the given task and transitions to a startedTask state
|
||||
func notStartedTask(t *Task) taskStateFn {
|
||||
log.Infof("starting task process %q with args '%+v'", t.bin, t.args)
|
||||
|
||||
// create command
|
||||
cmd := exec.Command(t.bin, t.args...)
|
||||
if _, err := cmd.StdoutPipe(); err != nil {
|
||||
t.tryError(fmt.Errorf("error getting stdout of %v: %v", t.name, err))
|
||||
return taskShouldRestart
|
||||
}
|
||||
stderrLogs, err := cmd.StderrPipe()
|
||||
if err != nil {
|
||||
t.tryError(fmt.Errorf("error getting stderr of %v: %v", t.name, err))
|
||||
return taskShouldRestart
|
||||
}
|
||||
|
||||
t.initLogging(stderrLogs)
|
||||
if len(t.env) > 0 {
|
||||
cmd.Env = t.env
|
||||
}
|
||||
cmd.SysProcAttr = sysProcAttr()
|
||||
|
||||
// last min check for shouldQuit here
|
||||
select {
|
||||
case <-t.shouldQuit:
|
||||
t.tryError(fmt.Errorf("task execution canceled, aborting process launch"))
|
||||
return taskShouldRestart
|
||||
default:
|
||||
}
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
t.tryError(fmt.Errorf("failed to start task process %q: %v", t.bin, err))
|
||||
return taskShouldRestart
|
||||
}
|
||||
log.Infoln("task started", t.name)
|
||||
t.cmd = &cmdProcess{delegate: cmd}
|
||||
return taskRunning
|
||||
}
|
||||
|
||||
type exitError interface {
|
||||
error
|
||||
|
||||
// see os.ProcessState.Sys: returned value can be converted to something like syscall.WaitStatus
|
||||
Sys() interface{}
|
||||
}
|
||||
|
||||
func taskRunning(t *Task) taskStateFn {
|
||||
waiter := t.cmd.Wait
|
||||
var sendOnce sync.Once
|
||||
trySend := func(wr *Completion) {
|
||||
// guarded with once because we're only allowed to send a single "result" for each
|
||||
// process termination. for example, if Kill() results in an error because Wait()
|
||||
// already completed we only want to return a single result for the process.
|
||||
sendOnce.Do(func() {
|
||||
t.tryComplete(wr)
|
||||
})
|
||||
}
|
||||
// listen for normal process completion in a goroutine; don't block because we need to listen for shouldQuit
|
||||
waitCh := make(chan *Completion, 1)
|
||||
go func() {
|
||||
wr := &Completion{}
|
||||
defer func() {
|
||||
waitCh <- wr
|
||||
close(waitCh)
|
||||
}()
|
||||
|
||||
if err := waiter(); err != nil {
|
||||
if exitError, ok := err.(exitError); ok {
|
||||
if waitStatus, ok := exitError.Sys().(syscall.WaitStatus); ok {
|
||||
wr.name = t.name
|
||||
wr.code = waitStatus.ExitStatus()
|
||||
return
|
||||
}
|
||||
}
|
||||
wr.err = fmt.Errorf("task wait ended strangely for %q: %v", t.bin, err)
|
||||
} else {
|
||||
wr.name = t.name
|
||||
}
|
||||
}()
|
||||
|
||||
// wait for the process to complete, or else for a "quit" signal on the task (at which point we'll attempt to kill manually)
|
||||
select {
|
||||
case <-t.shouldQuit:
|
||||
// check for tie
|
||||
select {
|
||||
case wr := <-waitCh:
|
||||
// we got a signal to quit, but we're already finished; attempt best effort delvery
|
||||
trySend(wr)
|
||||
default:
|
||||
// Wait() has not exited yet, kill the process
|
||||
log.Infof("killing %s : %s", t.name, t.bin)
|
||||
pid, err := t.cmd.Kill()
|
||||
if err != nil {
|
||||
trySend(&Completion{err: fmt.Errorf("failed to kill process: %q pid %d: %v", t.bin, pid, err)})
|
||||
}
|
||||
// else, Wait() should complete and send a completion event
|
||||
}
|
||||
case wr := <-waitCh:
|
||||
// task has completed before we were told to quit, pass along completion and error information
|
||||
trySend(wr)
|
||||
}
|
||||
return taskShouldRestart
|
||||
}
|
||||
|
||||
// forwardUntil forwards task process completion status and errors to the given output
|
||||
// chans until either the task terminates or abort is closed.
|
||||
func (t *Task) forwardUntil(tch chan<- *Completion, abort <-chan struct{}) {
|
||||
// merge task completion and error until we're told to die, then
|
||||
// tell the task to stop
|
||||
defer close(t.shouldQuit)
|
||||
forwardCompletionUntil(t.completedCh, tch, nil, abort, nil)
|
||||
}
|
||||
|
||||
// MergeOutput waits for the given tasks to complete. meanwhile it logs each time a task
|
||||
// process completes or generates an error. when shouldQuit closes, tasks are canceled and this
|
||||
// func eventually returns once all ongoing event handlers have completed running.
|
||||
func MergeOutput(tasks []*Task, shouldQuit <-chan struct{}) Events {
|
||||
tc := make(chan *Completion)
|
||||
|
||||
var waitForTasks sync.WaitGroup
|
||||
waitForTasks.Add(len(tasks))
|
||||
|
||||
for _, t := range tasks {
|
||||
t := t
|
||||
// translate task dead signal into Done
|
||||
go func() {
|
||||
<-t.done
|
||||
waitForTasks.Done()
|
||||
}()
|
||||
// fan-in task completion and error events to tc, ec
|
||||
go t.forwardUntil(tc, shouldQuit)
|
||||
}
|
||||
|
||||
tclistener := make(chan *Completion)
|
||||
done := runtime.After(func() {
|
||||
completionFinished := runtime.After(func() {
|
||||
defer close(tclistener)
|
||||
forwardCompletionUntil(tc, tclistener, nil, shouldQuit, func(tt *Completion, shutdown bool) {
|
||||
prefix := ""
|
||||
if shutdown {
|
||||
prefix = "(shutdown) "
|
||||
}
|
||||
log.Infof(prefix+"task %q exited with status %d", tt.name, tt.code)
|
||||
})
|
||||
})
|
||||
waitForTasks.Wait()
|
||||
close(tc)
|
||||
<-completionFinished
|
||||
})
|
||||
ei := newEventsImpl(tclistener, done)
|
||||
return ei
|
||||
}
|
28
contrib/mesos/pkg/minion/tasks/task_linux.go
Normal file
28
contrib/mesos/pkg/minion/tasks/task_linux.go
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors 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.
|
||||
*/
|
||||
|
||||
package tasks
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func sysProcAttr() *syscall.SysProcAttr {
|
||||
return &syscall.SysProcAttr{
|
||||
Setpgid: true,
|
||||
Pdeathsig: syscall.SIGKILL, // see cmdProcess.Kill
|
||||
}
|
||||
}
|
38
contrib/mesos/pkg/minion/tasks/task_other.go
Normal file
38
contrib/mesos/pkg/minion/tasks/task_other.go
Normal file
@ -0,0 +1,38 @@
|
||||
// +build !linux
|
||||
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors 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.
|
||||
*/
|
||||
|
||||
package tasks
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func sysProcAttr() *syscall.SysProcAttr {
|
||||
// TODO(jdef)
|
||||
// Consequence of not having Pdeathdig is that on non-Linux systems,
|
||||
// if SIGTERM doesn't stop child procs then they may "leak" and be
|
||||
// reparented 'up the chain' somewhere when the minion process
|
||||
// terminates. For example, such child procs end up living indefinitely
|
||||
// as children of the mesos slave process (I think the slave could handle
|
||||
// this case, but currently doesn't do it very well). Pdeathsig on Linux
|
||||
// was a fallback/failsafe mechanism implemented to guard against this. I
|
||||
// don't know if OS X has any syscalls that do something similar.
|
||||
return &syscall.SysProcAttr{
|
||||
Setpgid: true,
|
||||
}
|
||||
}
|
222
contrib/mesos/pkg/minion/tasks/task_test.go
Normal file
222
contrib/mesos/pkg/minion/tasks/task_test.go
Normal file
@ -0,0 +1,222 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors 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.
|
||||
*/
|
||||
|
||||
package tasks
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
"syscall"
|
||||
"testing"
|
||||
|
||||
log "github.com/golang/glog"
|
||||
)
|
||||
|
||||
type badWriteCloser struct {
|
||||
err error
|
||||
}
|
||||
|
||||
func (b *badWriteCloser) Write(_ []byte) (int, error) { return 0, b.err }
|
||||
func (b *badWriteCloser) Close() error { return b.err }
|
||||
|
||||
type discardCloser int
|
||||
|
||||
func (d discardCloser) Write(b []byte) (int, error) { return len(b), nil }
|
||||
func (d discardCloser) Close() error { return nil }
|
||||
|
||||
var devNull = func() io.WriteCloser { return discardCloser(0) }
|
||||
|
||||
type fakeExitError uint32
|
||||
|
||||
func (f fakeExitError) Sys() interface{} { return syscall.WaitStatus(f << 8) }
|
||||
func (f fakeExitError) Error() string { return fmt.Sprintf("fake-exit-error: %d", f) }
|
||||
|
||||
type fakeProcess struct {
|
||||
done chan struct{}
|
||||
pid int
|
||||
err error
|
||||
}
|
||||
|
||||
func (f *fakeProcess) Wait() error {
|
||||
<-f.done
|
||||
return f.err
|
||||
}
|
||||
func (f *fakeProcess) Kill() (int, error) {
|
||||
close(f.done)
|
||||
return f.pid, f.err
|
||||
}
|
||||
func (f *fakeProcess) exit(code int) {
|
||||
f.err = fakeExitError(code)
|
||||
close(f.done)
|
||||
}
|
||||
|
||||
func newFakeProcess() *fakeProcess {
|
||||
return &fakeProcess{
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
func TestBadLogger(t *testing.T) {
|
||||
err := errors.New("qux")
|
||||
fp := newFakeProcess()
|
||||
tt := New("foo", "bar", nil, nil, func() io.WriteCloser {
|
||||
defer func() {
|
||||
fp.pid = 123 // sanity check
|
||||
fp.Kill() // this causes Wait() to return
|
||||
}()
|
||||
return &badWriteCloser{err}
|
||||
})
|
||||
|
||||
tt.RestartDelay = 0 // don't slow the test down for no good reason
|
||||
|
||||
finishCalled := make(chan struct{})
|
||||
tt.Finished = func(ok bool) bool {
|
||||
log.Infof("tt.Finished: ok %t", ok)
|
||||
if ok {
|
||||
close(finishCalled)
|
||||
}
|
||||
return false // never respawn, this causes t.done to close
|
||||
}
|
||||
|
||||
// abuse eventsImpl: we're not going to listen on the task completion or event chans,
|
||||
// and we don't want to block the state machine, so discard all task events as they happen
|
||||
ei := newEventsImpl(tt.completedCh, tt.done)
|
||||
ei.Close()
|
||||
|
||||
go tt.run(func(_ *Task) taskStateFn {
|
||||
log.Infof("tt initialized")
|
||||
tt.initLogging(bytes.NewBuffer(([]byte)("unlogged bytes")))
|
||||
tt.cmd = fp
|
||||
return taskRunning
|
||||
})
|
||||
|
||||
// if the logger fails the task will be killed
|
||||
// badWriteLogger generates an error immediately and results in a task kill
|
||||
<-finishCalled
|
||||
<-tt.done
|
||||
|
||||
// this should never data race since the state machine is dead at this point
|
||||
if fp.pid != 123 {
|
||||
t.Fatalf("incorrect pid, expected 123 not %d", fp.pid)
|
||||
}
|
||||
|
||||
// TODO(jdef) would be nice to check for a specific error that indicates the logger died
|
||||
}
|
||||
|
||||
func TestMergeOutput(t *testing.T) {
|
||||
var tasksStarted, tasksDone sync.WaitGroup
|
||||
tasksDone.Add(2)
|
||||
tasksStarted.Add(2)
|
||||
|
||||
t1 := New("foo", "", nil, nil, devNull)
|
||||
t1exited := make(chan struct{})
|
||||
t1.RestartDelay = 0 // don't slow the test down for no good reason
|
||||
t1.Finished = func(ok bool) bool {
|
||||
// we expect each of these cases to happen exactly once
|
||||
if !ok {
|
||||
tasksDone.Done()
|
||||
} else {
|
||||
close(t1exited)
|
||||
}
|
||||
return ok
|
||||
}
|
||||
go t1.run(func(t *Task) taskStateFn {
|
||||
defer tasksStarted.Done()
|
||||
t.initLogging(bytes.NewBuffer([]byte{}))
|
||||
t.cmd = newFakeProcess()
|
||||
return taskRunning
|
||||
})
|
||||
|
||||
t2 := New("bar", "", nil, nil, devNull)
|
||||
t2exited := make(chan struct{})
|
||||
t2.RestartDelay = 0 // don't slow the test down for no good reason
|
||||
t2.Finished = func(ok bool) bool {
|
||||
// we expect each of these cases to happen exactly once
|
||||
if !ok {
|
||||
tasksDone.Done()
|
||||
} else {
|
||||
close(t2exited)
|
||||
}
|
||||
return ok
|
||||
}
|
||||
go t2.run(func(t *Task) taskStateFn {
|
||||
defer tasksStarted.Done()
|
||||
t.initLogging(bytes.NewBuffer([]byte{}))
|
||||
t.cmd = newFakeProcess()
|
||||
return taskRunning
|
||||
})
|
||||
|
||||
shouldQuit := make(chan struct{})
|
||||
te := MergeOutput([]*Task{t1, t2}, shouldQuit)
|
||||
|
||||
tasksStarted.Wait()
|
||||
tasksStarted.Add(2) // recycle the barrier
|
||||
|
||||
// kill each task once, let it restart; make sure that we get the completion status?
|
||||
t1.cmd.(*fakeProcess).exit(1)
|
||||
t2.cmd.(*fakeProcess).exit(2)
|
||||
|
||||
codes := map[int]struct{}{}
|
||||
for i := 0; i < 2; i++ {
|
||||
switch tc := <-te.Completion(); tc.code {
|
||||
case 1, 2:
|
||||
codes[tc.code] = struct{}{}
|
||||
default:
|
||||
if tc.err != nil {
|
||||
t.Errorf("unexpected task completion error: %v", tc.err)
|
||||
} else {
|
||||
t.Errorf("unexpected task completion code: %d", tc.code)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
te.Close() // we're not going to read any other completion or error events
|
||||
|
||||
if len(codes) != 2 {
|
||||
t.Fatalf("expected each task process to exit once")
|
||||
}
|
||||
|
||||
// each task invokes Finished() once
|
||||
<-t1exited
|
||||
<-t2exited
|
||||
|
||||
log.Infoln("each task process has completed one round")
|
||||
tasksStarted.Wait() // tasks will auto-restart their exited procs
|
||||
|
||||
// assert that the tasks are not dead; TODO(jdef) not sure that these checks are useful
|
||||
select {
|
||||
case <-t1.done:
|
||||
t.Fatalf("t1 is unexpectedly dead")
|
||||
default:
|
||||
}
|
||||
select {
|
||||
case <-t2.done:
|
||||
t.Fatalf("t2 is unexpectedly dead")
|
||||
default:
|
||||
}
|
||||
|
||||
log.Infoln("firing quit signal")
|
||||
close(shouldQuit) // fire shouldQuit, and everything should terminate gracefully
|
||||
|
||||
log.Infoln("waiting for tasks to die")
|
||||
tasksDone.Wait() // our tasks should die
|
||||
|
||||
log.Infoln("waiting for merge to complete")
|
||||
<-te.Done() // wait for the merge to complete
|
||||
}
|
@ -24,8 +24,42 @@ import (
|
||||
"k8s.io/kubernetes/contrib/mesos/pkg/scheduler/podtask"
|
||||
)
|
||||
|
||||
type allocationStrategy struct {
|
||||
fitPredicate podtask.FitPredicate
|
||||
procurement podtask.Procurement
|
||||
}
|
||||
|
||||
func (a *allocationStrategy) FitPredicate() podtask.FitPredicate {
|
||||
return a.fitPredicate
|
||||
}
|
||||
|
||||
func (a *allocationStrategy) Procurement() podtask.Procurement {
|
||||
return a.procurement
|
||||
}
|
||||
|
||||
func NewAllocationStrategy(fitPredicate podtask.FitPredicate, procurement podtask.Procurement) AllocationStrategy {
|
||||
if fitPredicate == nil {
|
||||
panic("fitPredicate is required")
|
||||
}
|
||||
if procurement == nil {
|
||||
panic("procurement is required")
|
||||
}
|
||||
return &allocationStrategy{
|
||||
fitPredicate: fitPredicate,
|
||||
procurement: procurement,
|
||||
}
|
||||
}
|
||||
|
||||
type fcfsPodScheduler struct {
|
||||
AllocationStrategy
|
||||
}
|
||||
|
||||
func NewFCFSPodScheduler(as AllocationStrategy) PodScheduler {
|
||||
return &fcfsPodScheduler{as}
|
||||
}
|
||||
|
||||
// A first-come-first-serve scheduler: acquires the first offer that can support the task
|
||||
func FCFSScheduleFunc(r offers.Registry, unused SlaveIndex, task *podtask.T) (offers.Perishable, error) {
|
||||
func (fps *fcfsPodScheduler) SchedulePod(r offers.Registry, unused SlaveIndex, task *podtask.T) (offers.Perishable, error) {
|
||||
podName := fmt.Sprintf("%s/%s", task.Pod.Namespace, task.Pod.Name)
|
||||
var acceptedOffer offers.Perishable
|
||||
err := r.Walk(func(p offers.Perishable) (bool, error) {
|
||||
@ -33,7 +67,7 @@ func FCFSScheduleFunc(r offers.Registry, unused SlaveIndex, task *podtask.T) (of
|
||||
if offer == nil {
|
||||
return false, fmt.Errorf("nil offer while scheduling task %v", task.ID)
|
||||
}
|
||||
if task.AcceptOffer(offer) {
|
||||
if fps.FitPredicate()(task, offer) {
|
||||
if p.Acquire() {
|
||||
acceptedOffer = p
|
||||
log.V(3).Infof("Pod %s accepted offer %v", podName, offer.Id.GetValue())
|
||||
|
@ -42,11 +42,11 @@ func (m *MockScheduler) slaveFor(id string) (slave *Slave, ok bool) {
|
||||
ok = args.Bool(1)
|
||||
return
|
||||
}
|
||||
func (m *MockScheduler) algorithm() (f PodScheduleFunc) {
|
||||
func (m *MockScheduler) algorithm() (f PodScheduler) {
|
||||
args := m.Called()
|
||||
x := args.Get(0)
|
||||
if x != nil {
|
||||
f = x.(PodScheduleFunc)
|
||||
f = x.(PodScheduler)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -33,7 +33,6 @@ import (
|
||||
"k8s.io/kubernetes/contrib/mesos/pkg/runtime"
|
||||
annotation "k8s.io/kubernetes/contrib/mesos/pkg/scheduler/meta"
|
||||
"k8s.io/kubernetes/contrib/mesos/pkg/scheduler/podtask"
|
||||
mresource "k8s.io/kubernetes/contrib/mesos/pkg/scheduler/resource"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/errors"
|
||||
client "k8s.io/kubernetes/pkg/client/unversioned"
|
||||
@ -56,8 +55,9 @@ const (
|
||||
// scheduler abstraction to allow for easier unit testing
|
||||
type schedulerInterface interface {
|
||||
sync.Locker // synchronize scheduler plugin operations
|
||||
|
||||
SlaveIndex
|
||||
algorithm() PodScheduleFunc // see types.go
|
||||
algorithm() PodScheduler
|
||||
offers() offers.Registry
|
||||
tasks() podtask.Registry
|
||||
|
||||
@ -76,8 +76,8 @@ type k8smScheduler struct {
|
||||
internal *KubernetesScheduler
|
||||
}
|
||||
|
||||
func (k *k8smScheduler) algorithm() PodScheduleFunc {
|
||||
return k.internal.scheduleFunc
|
||||
func (k *k8smScheduler) algorithm() PodScheduler {
|
||||
return k.internal
|
||||
}
|
||||
|
||||
func (k *k8smScheduler) offers() offers.Registry {
|
||||
@ -231,10 +231,8 @@ func (b *binder) prepareTaskForLaunch(ctx api.Context, machine string, task *pod
|
||||
}
|
||||
|
||||
type kubeScheduler struct {
|
||||
api schedulerInterface
|
||||
podUpdates queue.FIFO
|
||||
defaultContainerCPULimit mresource.CPUShares
|
||||
defaultContainerMemLimit mresource.MegaBytes
|
||||
api schedulerInterface
|
||||
podUpdates queue.FIFO
|
||||
}
|
||||
|
||||
// recoverAssignedSlave recovers the assigned Mesos slave from a pod by searching
|
||||
@ -318,7 +316,7 @@ func (k *kubeScheduler) doSchedule(task *podtask.T, err error) (string, error) {
|
||||
}
|
||||
}
|
||||
if err == nil && offer == nil {
|
||||
offer, err = k.api.algorithm()(k.api.offers(), k.api, task)
|
||||
offer, err = k.api.algorithm().SchedulePod(k.api.offers(), k.api, task)
|
||||
}
|
||||
if err != nil {
|
||||
return "", err
|
||||
@ -338,18 +336,8 @@ func (k *kubeScheduler) doSchedule(task *podtask.T, err error) (string, error) {
|
||||
return "", fmt.Errorf("task.offer assignment must be idempotent, task %+v: offer %+v", task, offer)
|
||||
}
|
||||
|
||||
// write resource limits into the pod spec which is transferred to the executor. From here
|
||||
// on we can expect that the pod spec of a task has proper limits for CPU and memory.
|
||||
// TODO(sttts): For a later separation of the kubelet and the executor also patch the pod on the apiserver
|
||||
if unlimitedCPU := mresource.LimitPodCPU(&task.Pod, k.defaultContainerCPULimit); unlimitedCPU {
|
||||
log.Warningf("Pod %s/%s without cpu limits is admitted %.2f cpu shares", task.Pod.Namespace, task.Pod.Name, mresource.PodCPULimit(&task.Pod))
|
||||
}
|
||||
if unlimitedMem := mresource.LimitPodMem(&task.Pod, k.defaultContainerMemLimit); unlimitedMem {
|
||||
log.Warningf("Pod %s/%s without memory limits is admitted %.2f MB", task.Pod.Namespace, task.Pod.Name, mresource.PodMemLimit(&task.Pod))
|
||||
}
|
||||
|
||||
task.Offer = offer
|
||||
task.FillFromDetails(details)
|
||||
k.api.algorithm().Procurement()(task, details) // TODO(jdef) why is nothing checking the error returned here?
|
||||
|
||||
if err := k.api.tasks().Update(task); err != nil {
|
||||
offer.Release()
|
||||
@ -556,7 +544,7 @@ func (k *errorHandler) handleSchedulingError(pod *api.Pod, schedulingErr error)
|
||||
defer k.api.Unlock()
|
||||
switch task, state := k.api.tasks().Get(task.ID); state {
|
||||
case podtask.StatePending:
|
||||
return !task.Has(podtask.Launched) && task.AcceptOffer(offer)
|
||||
return !task.Has(podtask.Launched) && k.api.algorithm().FitPredicate()(task, offer)
|
||||
default:
|
||||
// no point in continuing to check for matching offers
|
||||
return true
|
||||
@ -698,10 +686,8 @@ func (k *KubernetesScheduler) NewPluginConfig(terminate <-chan struct{}, mux *ht
|
||||
Config: &plugin.Config{
|
||||
MinionLister: nil,
|
||||
Algorithm: &kubeScheduler{
|
||||
api: kapi,
|
||||
podUpdates: podUpdates,
|
||||
defaultContainerCPULimit: k.defaultContainerCPULimit,
|
||||
defaultContainerMemLimit: k.defaultContainerMemLimit,
|
||||
api: kapi,
|
||||
podUpdates: podUpdates,
|
||||
},
|
||||
Binder: &binder{api: kapi},
|
||||
NextPod: q.yield,
|
||||
|
@ -61,13 +61,13 @@ func NewTestServer(t *testing.T, namespace string, mockPodListWatch *MockPodsLis
|
||||
}
|
||||
mux := http.NewServeMux()
|
||||
|
||||
mux.HandleFunc(testapi.ResourcePath("pods", namespace, ""), func(w http.ResponseWriter, r *http.Request) {
|
||||
mux.HandleFunc(testapi.Default.ResourcePath("pods", namespace, ""), func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
pods := mockPodListWatch.Pods()
|
||||
w.Write([]byte(runtime.EncodeOrDie(testapi.Codec(), &pods)))
|
||||
w.Write([]byte(runtime.EncodeOrDie(testapi.Default.Codec(), &pods)))
|
||||
})
|
||||
|
||||
podsPrefix := testapi.ResourcePath("pods", namespace, "") + "/"
|
||||
podsPrefix := testapi.Default.ResourcePath("pods", namespace, "") + "/"
|
||||
mux.HandleFunc(podsPrefix, func(w http.ResponseWriter, r *http.Request) {
|
||||
name := r.URL.Path[len(podsPrefix):]
|
||||
|
||||
@ -79,13 +79,13 @@ func NewTestServer(t *testing.T, namespace string, mockPodListWatch *MockPodsLis
|
||||
p := mockPodListWatch.GetPod(name)
|
||||
if p != nil {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte(runtime.EncodeOrDie(testapi.Codec(), p)))
|
||||
w.Write([]byte(runtime.EncodeOrDie(testapi.Default.Codec(), p)))
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
})
|
||||
|
||||
mux.HandleFunc(testapi.ResourcePath("events", namespace, ""), func(w http.ResponseWriter, r *http.Request) {
|
||||
mux.HandleFunc(testapi.Default.ResourcePath("events", namespace, ""), func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
})
|
||||
|
||||
@ -196,7 +196,7 @@ func NewTestPod() (*api.Pod, int) {
|
||||
currentPodNum = currentPodNum + 1
|
||||
name := fmt.Sprintf("pod%d", currentPodNum)
|
||||
return &api.Pod{
|
||||
TypeMeta: api.TypeMeta{APIVersion: testapi.Version()},
|
||||
TypeMeta: api.TypeMeta{APIVersion: testapi.Default.Version()},
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: api.NamespaceDefault,
|
||||
@ -393,13 +393,14 @@ func TestPlugin_LifeCycle(t *testing.T) {
|
||||
executor.Data = []byte{0, 1, 2}
|
||||
|
||||
// create scheduler
|
||||
as := NewAllocationStrategy(
|
||||
podtask.DefaultPredicate,
|
||||
podtask.NewDefaultProcurement(mresource.DefaultDefaultContainerCPULimit, mresource.DefaultDefaultContainerMemLimit))
|
||||
testScheduler := New(Config{
|
||||
Executor: executor,
|
||||
Client: client.NewOrDie(&client.Config{Host: testApiServer.server.URL, Version: testapi.Version()}),
|
||||
ScheduleFunc: FCFSScheduleFunc,
|
||||
Schedcfg: *schedcfg.CreateDefaultConfig(),
|
||||
DefaultContainerCPULimit: mresource.DefaultDefaultContainerCPULimit,
|
||||
DefaultContainerMemLimit: mresource.DefaultDefaultContainerMemLimit,
|
||||
Executor: executor,
|
||||
Client: client.NewOrDie(&client.Config{Host: testApiServer.server.URL, Version: testapi.Default.Version()}),
|
||||
Scheduler: NewFCFSPodScheduler(as),
|
||||
Schedcfg: *schedcfg.CreateDefaultConfig(),
|
||||
})
|
||||
|
||||
assert.NotNil(testScheduler.client, "client is nil")
|
||||
|
73
contrib/mesos/pkg/scheduler/podtask/minimal.go
Normal file
73
contrib/mesos/pkg/scheduler/podtask/minimal.go
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors 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.
|
||||
*/
|
||||
|
||||
package podtask
|
||||
|
||||
import (
|
||||
log "github.com/golang/glog"
|
||||
mesos "github.com/mesos/mesos-go/mesosproto"
|
||||
)
|
||||
|
||||
// bogus numbers that we use to make sure that there's some set of minimal offered resources on the slave
|
||||
const (
|
||||
minimalCpus = 0.01
|
||||
minimalMem = 0.25
|
||||
)
|
||||
|
||||
var (
|
||||
DefaultMinimalPredicate = RequireAllPredicate([]FitPredicate{
|
||||
ValidationPredicate,
|
||||
NodeSelectorPredicate,
|
||||
MinimalPodResourcesPredicate,
|
||||
PortsPredicate,
|
||||
}).Fit
|
||||
|
||||
DefaultMinimalProcurement = AllOrNothingProcurement([]Procurement{
|
||||
ValidateProcurement,
|
||||
NodeProcurement,
|
||||
MinimalPodResourcesProcurement,
|
||||
PortsProcurement,
|
||||
}).Procure
|
||||
)
|
||||
|
||||
func MinimalPodResourcesPredicate(t *T, offer *mesos.Offer) bool {
|
||||
var (
|
||||
offeredCpus float64
|
||||
offeredMem float64
|
||||
)
|
||||
for _, resource := range offer.Resources {
|
||||
if resource.GetName() == "cpus" {
|
||||
offeredCpus = resource.GetScalar().GetValue()
|
||||
}
|
||||
|
||||
if resource.GetName() == "mem" {
|
||||
offeredMem = resource.GetScalar().GetValue()
|
||||
}
|
||||
}
|
||||
log.V(4).Infof("trying to match offer with pod %v/%v: cpus: %.2f mem: %.2f MB", t.Pod.Namespace, t.Pod.Name, minimalCpus, minimalMem)
|
||||
if (minimalCpus > offeredCpus) || (minimalMem > offeredMem) {
|
||||
log.V(3).Infof("not enough resources for pod %v/%v: cpus: %.2f mem: %.2f MB", t.Pod.Namespace, t.Pod.Name, minimalCpus, minimalMem)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func MinimalPodResourcesProcurement(t *T, details *mesos.Offer) error {
|
||||
log.V(3).Infof("Recording offer(s) %s/%s against pod %v: cpu: %.2f, mem: %.2f MB", details.Id, t.Pod.Namespace, t.Pod.Name, minimalCpus, minimalMem)
|
||||
t.Spec.CPU = minimalCpus
|
||||
t.Spec.Memory = minimalMem
|
||||
return nil
|
||||
}
|
@ -18,7 +18,6 @@ package podtask
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gogo/protobuf/proto"
|
||||
@ -28,7 +27,6 @@ import (
|
||||
"k8s.io/kubernetes/contrib/mesos/pkg/scheduler/metrics"
|
||||
mresource "k8s.io/kubernetes/contrib/mesos/pkg/scheduler/resource"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
|
||||
log "github.com/golang/glog"
|
||||
mesos "github.com/mesos/mesos-go/mesosproto"
|
||||
@ -150,59 +148,6 @@ func (t *T) BuildTaskInfo() *mesos.TaskInfo {
|
||||
return info
|
||||
}
|
||||
|
||||
// Fill the Spec in the T, should be called during k8s scheduling, before binding.
|
||||
func (t *T) FillFromDetails(details *mesos.Offer) error {
|
||||
if details == nil {
|
||||
//programming error
|
||||
panic("offer details are nil")
|
||||
}
|
||||
|
||||
// compute used resources
|
||||
cpu := mresource.PodCPULimit(&t.Pod)
|
||||
mem := mresource.PodMemLimit(&t.Pod)
|
||||
log.V(3).Infof("Recording offer(s) %s/%s against pod %v: cpu: %.2f, mem: %.2f MB", details.Id, t.Pod.Namespace, t.Pod.Name, cpu, mem)
|
||||
|
||||
t.Spec = Spec{
|
||||
SlaveID: details.GetSlaveId().GetValue(),
|
||||
AssignedSlave: details.GetHostname(),
|
||||
CPU: cpu,
|
||||
Memory: mem,
|
||||
}
|
||||
|
||||
// fill in port mapping
|
||||
if mapping, err := t.mapper.Generate(t, details); err != nil {
|
||||
t.Reset()
|
||||
return err
|
||||
} else {
|
||||
ports := []uint64{}
|
||||
for _, entry := range mapping {
|
||||
ports = append(ports, entry.OfferPort)
|
||||
}
|
||||
t.Spec.PortMap = mapping
|
||||
t.Spec.Ports = ports
|
||||
}
|
||||
|
||||
// hostname needs of the executor needs to match that of the offer, otherwise
|
||||
// the kubelet node status checker/updater is very unhappy
|
||||
const HOSTNAME_OVERRIDE_FLAG = "--hostname-override="
|
||||
hostname := details.GetHostname() // required field, non-empty
|
||||
hostnameOverride := HOSTNAME_OVERRIDE_FLAG + hostname
|
||||
|
||||
argv := t.executor.Command.Arguments
|
||||
overwrite := false
|
||||
for i, arg := range argv {
|
||||
if strings.HasPrefix(arg, HOSTNAME_OVERRIDE_FLAG) {
|
||||
overwrite = true
|
||||
argv[i] = hostnameOverride
|
||||
break
|
||||
}
|
||||
}
|
||||
if !overwrite {
|
||||
t.executor.Command.Arguments = append(argv, hostnameOverride)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Clear offer-related details from the task, should be called if/when an offer
|
||||
// has already been assigned to a task but for some reason is no longer valid.
|
||||
func (t *T) Reset() {
|
||||
@ -211,65 +156,6 @@ func (t *T) Reset() {
|
||||
t.Spec = Spec{}
|
||||
}
|
||||
|
||||
func (t *T) AcceptOffer(offer *mesos.Offer) bool {
|
||||
if offer == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// if the user has specified a target host, make sure this offer is for that host
|
||||
if t.Pod.Spec.NodeName != "" && offer.GetHostname() != t.Pod.Spec.NodeName {
|
||||
return false
|
||||
}
|
||||
|
||||
// check the NodeSelector
|
||||
if len(t.Pod.Spec.NodeSelector) > 0 {
|
||||
slaveLabels := map[string]string{}
|
||||
for _, a := range offer.Attributes {
|
||||
if a.GetType() == mesos.Value_TEXT {
|
||||
slaveLabels[a.GetName()] = a.GetText().GetValue()
|
||||
}
|
||||
}
|
||||
selector := labels.SelectorFromSet(t.Pod.Spec.NodeSelector)
|
||||
if !selector.Matches(labels.Set(slaveLabels)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// check ports
|
||||
if _, err := t.mapper.Generate(t, offer); err != nil {
|
||||
log.V(3).Info(err)
|
||||
return false
|
||||
}
|
||||
|
||||
// find offered cpu and mem
|
||||
var (
|
||||
offeredCpus mresource.CPUShares
|
||||
offeredMem mresource.MegaBytes
|
||||
)
|
||||
for _, resource := range offer.Resources {
|
||||
if resource.GetName() == "cpus" {
|
||||
offeredCpus = mresource.CPUShares(*resource.GetScalar().Value)
|
||||
}
|
||||
|
||||
if resource.GetName() == "mem" {
|
||||
offeredMem = mresource.MegaBytes(*resource.GetScalar().Value)
|
||||
}
|
||||
}
|
||||
|
||||
// calculate cpu and mem sum over all containers of the pod
|
||||
// TODO (@sttts): also support pod.spec.resources.limit.request
|
||||
// TODO (@sttts): take into account the executor resources
|
||||
cpu := mresource.PodCPULimit(&t.Pod)
|
||||
mem := mresource.PodMemLimit(&t.Pod)
|
||||
log.V(4).Infof("trying to match offer with pod %v/%v: cpus: %.2f mem: %.2f MB", t.Pod.Namespace, t.Pod.Name, cpu, mem)
|
||||
if (cpu > offeredCpus) || (mem > offeredMem) {
|
||||
log.V(3).Infof("not enough resources for pod %v/%v: cpus: %.2f mem: %.2f MB", t.Pod.Namespace, t.Pod.Name, cpu, mem)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (t *T) Set(f FlagType) {
|
||||
t.Flags[f] = struct{}{}
|
||||
if Launched == f {
|
||||
|
@ -146,10 +146,10 @@ func TestEmptyOffer(t *testing.T) {
|
||||
mresource.LimitPodCPU(&task.Pod, mresource.DefaultDefaultContainerCPULimit)
|
||||
mresource.LimitPodMem(&task.Pod, mresource.DefaultDefaultContainerMemLimit)
|
||||
|
||||
if ok := task.AcceptOffer(nil); ok {
|
||||
if ok := DefaultPredicate(task, nil); ok {
|
||||
t.Fatalf("accepted nil offer")
|
||||
}
|
||||
if ok := task.AcceptOffer(&mesos.Offer{}); ok {
|
||||
if ok := DefaultPredicate(task, &mesos.Offer{}); ok {
|
||||
t.Fatalf("accepted empty offer")
|
||||
}
|
||||
}
|
||||
@ -176,7 +176,7 @@ func TestNoPortsInPodOrOffer(t *testing.T) {
|
||||
mutil.NewScalarResource("mem", 0.001),
|
||||
},
|
||||
}
|
||||
if ok := task.AcceptOffer(offer); ok {
|
||||
if ok := DefaultPredicate(task, offer); ok {
|
||||
t.Fatalf("accepted offer %v:", offer)
|
||||
}
|
||||
|
||||
@ -186,7 +186,7 @@ func TestNoPortsInPodOrOffer(t *testing.T) {
|
||||
mutil.NewScalarResource("mem", t_min_mem),
|
||||
},
|
||||
}
|
||||
if ok := task.AcceptOffer(offer); !ok {
|
||||
if ok := DefaultPredicate(task, offer); !ok {
|
||||
t.Fatalf("did not accepted offer %v:", offer)
|
||||
}
|
||||
}
|
||||
@ -203,7 +203,7 @@ func TestAcceptOfferPorts(t *testing.T) {
|
||||
rangeResource("ports", []uint64{1, 1}),
|
||||
},
|
||||
}
|
||||
if ok := task.AcceptOffer(offer); !ok {
|
||||
if ok := DefaultPredicate(task, offer); !ok {
|
||||
t.Fatalf("did not accepted offer %v:", offer)
|
||||
}
|
||||
|
||||
@ -218,17 +218,17 @@ func TestAcceptOfferPorts(t *testing.T) {
|
||||
mresource.LimitPodCPU(&task.Pod, mresource.DefaultDefaultContainerCPULimit)
|
||||
mresource.LimitPodMem(&task.Pod, mresource.DefaultDefaultContainerMemLimit)
|
||||
|
||||
if ok := task.AcceptOffer(offer); ok {
|
||||
if ok := DefaultPredicate(task, offer); ok {
|
||||
t.Fatalf("accepted offer %v:", offer)
|
||||
}
|
||||
|
||||
pod.Spec.Containers[0].Ports[0].HostPort = 1
|
||||
if ok := task.AcceptOffer(offer); !ok {
|
||||
if ok := DefaultPredicate(task, offer); !ok {
|
||||
t.Fatalf("did not accepted offer %v:", offer)
|
||||
}
|
||||
|
||||
pod.Spec.Containers[0].Ports[0].HostPort = 0
|
||||
if ok := task.AcceptOffer(offer); !ok {
|
||||
if ok := DefaultPredicate(task, offer); !ok {
|
||||
t.Fatalf("did not accepted offer %v:", offer)
|
||||
}
|
||||
|
||||
@ -236,12 +236,12 @@ func TestAcceptOfferPorts(t *testing.T) {
|
||||
mutil.NewScalarResource("cpus", t_min_cpu),
|
||||
mutil.NewScalarResource("mem", t_min_mem),
|
||||
}
|
||||
if ok := task.AcceptOffer(offer); ok {
|
||||
if ok := DefaultPredicate(task, offer); ok {
|
||||
t.Fatalf("accepted offer %v:", offer)
|
||||
}
|
||||
|
||||
pod.Spec.Containers[0].Ports[0].HostPort = 1
|
||||
if ok := task.AcceptOffer(offer); ok {
|
||||
if ok := DefaultPredicate(task, offer); ok {
|
||||
t.Fatalf("accepted offer %v:", offer)
|
||||
}
|
||||
}
|
||||
@ -297,7 +297,7 @@ func TestNodeSelector(t *testing.T) {
|
||||
},
|
||||
Attributes: ts.attrs,
|
||||
}
|
||||
if got, want := task.AcceptOffer(offer), ts.ok; got != want {
|
||||
if got, want := DefaultPredicate(task, offer), ts.ok; got != want {
|
||||
t.Fatalf("expected acceptance of offer %v for selector %v to be %v, got %v:", want, got, ts.attrs, ts.selector)
|
||||
}
|
||||
}
|
||||
|
110
contrib/mesos/pkg/scheduler/podtask/predicate.go
Normal file
110
contrib/mesos/pkg/scheduler/podtask/predicate.go
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors 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.
|
||||
*/
|
||||
|
||||
package podtask
|
||||
|
||||
import (
|
||||
log "github.com/golang/glog"
|
||||
mesos "github.com/mesos/mesos-go/mesosproto"
|
||||
mresource "k8s.io/kubernetes/contrib/mesos/pkg/scheduler/resource"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
)
|
||||
|
||||
var DefaultPredicate = RequireAllPredicate([]FitPredicate{
|
||||
ValidationPredicate,
|
||||
NodeSelectorPredicate,
|
||||
PodFitsResourcesPredicate,
|
||||
PortsPredicate,
|
||||
}).Fit
|
||||
|
||||
// FitPredicate implementations determine if the given task "fits" into offered Mesos resources.
|
||||
// Neither the task or offer should be modified.
|
||||
type FitPredicate func(*T, *mesos.Offer) bool
|
||||
|
||||
type RequireAllPredicate []FitPredicate
|
||||
|
||||
func (f RequireAllPredicate) Fit(t *T, offer *mesos.Offer) bool {
|
||||
for _, p := range f {
|
||||
if !p(t, offer) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func ValidationPredicate(t *T, offer *mesos.Offer) bool {
|
||||
return t != nil && offer != nil
|
||||
}
|
||||
|
||||
func NodeSelectorPredicate(t *T, offer *mesos.Offer) bool {
|
||||
// if the user has specified a target host, make sure this offer is for that host
|
||||
if t.Pod.Spec.NodeName != "" && offer.GetHostname() != t.Pod.Spec.NodeName {
|
||||
return false
|
||||
}
|
||||
|
||||
// check the NodeSelector
|
||||
if len(t.Pod.Spec.NodeSelector) > 0 {
|
||||
slaveLabels := map[string]string{}
|
||||
for _, a := range offer.Attributes {
|
||||
if a.GetType() == mesos.Value_TEXT {
|
||||
slaveLabels[a.GetName()] = a.GetText().GetValue()
|
||||
}
|
||||
}
|
||||
selector := labels.SelectorFromSet(t.Pod.Spec.NodeSelector)
|
||||
if !selector.Matches(labels.Set(slaveLabels)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func PortsPredicate(t *T, offer *mesos.Offer) bool {
|
||||
// check ports
|
||||
if _, err := t.mapper.Generate(t, offer); err != nil {
|
||||
log.V(3).Info(err)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func PodFitsResourcesPredicate(t *T, offer *mesos.Offer) bool {
|
||||
// find offered cpu and mem
|
||||
var (
|
||||
offeredCpus mresource.CPUShares
|
||||
offeredMem mresource.MegaBytes
|
||||
)
|
||||
for _, resource := range offer.Resources {
|
||||
if resource.GetName() == "cpus" {
|
||||
offeredCpus = mresource.CPUShares(*resource.GetScalar().Value)
|
||||
}
|
||||
|
||||
if resource.GetName() == "mem" {
|
||||
offeredMem = mresource.MegaBytes(*resource.GetScalar().Value)
|
||||
}
|
||||
}
|
||||
|
||||
// calculate cpu and mem sum over all containers of the pod
|
||||
// TODO (@sttts): also support pod.spec.resources.limit.request
|
||||
// TODO (@sttts): take into account the executor resources
|
||||
cpu := mresource.PodCPULimit(&t.Pod)
|
||||
mem := mresource.PodMemLimit(&t.Pod)
|
||||
log.V(4).Infof("trying to match offer with pod %v/%v: cpus: %.2f mem: %.2f MB", t.Pod.Namespace, t.Pod.Name, cpu, mem)
|
||||
if (cpu > offeredCpus) || (mem > offeredMem) {
|
||||
log.V(3).Infof("not enough resources for pod %v/%v: cpus: %.2f mem: %.2f MB", t.Pod.Namespace, t.Pod.Name, cpu, mem)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
152
contrib/mesos/pkg/scheduler/podtask/procurement.go
Normal file
152
contrib/mesos/pkg/scheduler/podtask/procurement.go
Normal file
@ -0,0 +1,152 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors 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.
|
||||
*/
|
||||
|
||||
package podtask
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
log "github.com/golang/glog"
|
||||
mesos "github.com/mesos/mesos-go/mesosproto"
|
||||
mresource "k8s.io/kubernetes/contrib/mesos/pkg/scheduler/resource"
|
||||
)
|
||||
|
||||
// NewDefaultProcurement returns the default procurement strategy that combines validation
|
||||
// and responsible Mesos resource procurement. c and m are resource quantities written into
|
||||
// k8s api.Pod.Spec's that don't declare resources (all containers in k8s-mesos require cpu
|
||||
// and memory limits).
|
||||
func NewDefaultProcurement(c mresource.CPUShares, m mresource.MegaBytes) Procurement {
|
||||
requireSome := &RequireSomePodResources{
|
||||
defaultContainerCPULimit: c,
|
||||
defaultContainerMemLimit: m,
|
||||
}
|
||||
return AllOrNothingProcurement([]Procurement{
|
||||
ValidateProcurement,
|
||||
NodeProcurement,
|
||||
requireSome.Procure,
|
||||
PodResourcesProcurement,
|
||||
PortsProcurement,
|
||||
}).Procure
|
||||
}
|
||||
|
||||
// Procurement funcs allocate resources for a task from an offer.
|
||||
// Both the task and/or offer may be modified.
|
||||
type Procurement func(*T, *mesos.Offer) error
|
||||
|
||||
// AllOrNothingProcurement provides a convenient wrapper around multiple Procurement
|
||||
// objectives: the failure of any Procurement in the set results in Procure failing.
|
||||
// see AllOrNothingProcurement.Procure
|
||||
type AllOrNothingProcurement []Procurement
|
||||
|
||||
// Procure runs each Procurement in the receiver list. The first Procurement func that
|
||||
// fails triggers T.Reset() and the error is returned, otherwise returns nil.
|
||||
func (a AllOrNothingProcurement) Procure(t *T, offer *mesos.Offer) error {
|
||||
for _, p := range a {
|
||||
if err := p(t, offer); err != nil {
|
||||
t.Reset()
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateProcurement checks that the offered resources are kosher, and if not panics.
|
||||
// If things check out ok, t.Spec is cleared and nil is returned.
|
||||
func ValidateProcurement(t *T, offer *mesos.Offer) error {
|
||||
if offer == nil {
|
||||
//programming error
|
||||
panic("offer details are nil")
|
||||
}
|
||||
t.Spec = Spec{}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NodeProcurement updates t.Spec in preparation for the task to be launched on the
|
||||
// slave associated with the offer.
|
||||
func NodeProcurement(t *T, offer *mesos.Offer) error {
|
||||
t.Spec.SlaveID = offer.GetSlaveId().GetValue()
|
||||
t.Spec.AssignedSlave = offer.GetHostname()
|
||||
|
||||
// hostname needs of the executor needs to match that of the offer, otherwise
|
||||
// the kubelet node status checker/updater is very unhappy
|
||||
const HOSTNAME_OVERRIDE_FLAG = "--hostname-override="
|
||||
hostname := offer.GetHostname() // required field, non-empty
|
||||
hostnameOverride := HOSTNAME_OVERRIDE_FLAG + hostname
|
||||
|
||||
argv := t.executor.Command.Arguments
|
||||
overwrite := false
|
||||
for i, arg := range argv {
|
||||
if strings.HasPrefix(arg, HOSTNAME_OVERRIDE_FLAG) {
|
||||
overwrite = true
|
||||
argv[i] = hostnameOverride
|
||||
break
|
||||
}
|
||||
}
|
||||
if !overwrite {
|
||||
t.executor.Command.Arguments = append(argv, hostnameOverride)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type RequireSomePodResources struct {
|
||||
defaultContainerCPULimit mresource.CPUShares
|
||||
defaultContainerMemLimit mresource.MegaBytes
|
||||
}
|
||||
|
||||
func (r *RequireSomePodResources) Procure(t *T, offer *mesos.Offer) error {
|
||||
// write resource limits into the pod spec which is transferred to the executor. From here
|
||||
// on we can expect that the pod spec of a task has proper limits for CPU and memory.
|
||||
// TODO(sttts): For a later separation of the kubelet and the executor also patch the pod on the apiserver
|
||||
// TODO(jdef): changing the state of t.Pod here feels dirty, especially since we don't use a kosher
|
||||
// method to clone the api.Pod state in T.Clone(). This needs some love.
|
||||
if unlimitedCPU := mresource.LimitPodCPU(&t.Pod, r.defaultContainerCPULimit); unlimitedCPU {
|
||||
log.Warningf("Pod %s/%s without cpu limits is admitted %.2f cpu shares", t.Pod.Namespace, t.Pod.Name, mresource.PodCPULimit(&t.Pod))
|
||||
}
|
||||
if unlimitedMem := mresource.LimitPodMem(&t.Pod, r.defaultContainerMemLimit); unlimitedMem {
|
||||
log.Warningf("Pod %s/%s without memory limits is admitted %.2f MB", t.Pod.Namespace, t.Pod.Name, mresource.PodMemLimit(&t.Pod))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PodResourcesProcurement converts k8s pod cpu and memory resource requirements into
|
||||
// mesos resource allocations.
|
||||
func PodResourcesProcurement(t *T, offer *mesos.Offer) error {
|
||||
// compute used resources
|
||||
cpu := mresource.PodCPULimit(&t.Pod)
|
||||
mem := mresource.PodMemLimit(&t.Pod)
|
||||
|
||||
log.V(3).Infof("Recording offer(s) %s/%s against pod %v: cpu: %.2f, mem: %.2f MB", offer.Id, t.Pod.Namespace, t.Pod.Name, cpu, mem)
|
||||
|
||||
t.Spec.CPU = cpu
|
||||
t.Spec.Memory = mem
|
||||
return nil
|
||||
}
|
||||
|
||||
// PortsProcurement convert host port mappings into mesos port resource allocations.
|
||||
func PortsProcurement(t *T, offer *mesos.Offer) error {
|
||||
// fill in port mapping
|
||||
if mapping, err := t.mapper.Generate(t, offer); err != nil {
|
||||
return err
|
||||
} else {
|
||||
ports := []uint64{}
|
||||
for _, entry := range mapping {
|
||||
ports = append(ports, entry.OfferPort)
|
||||
}
|
||||
t.Spec.PortMap = mapping
|
||||
t.Spec.Ports = ports
|
||||
}
|
||||
return nil
|
||||
}
|
@ -39,7 +39,6 @@ import (
|
||||
"k8s.io/kubernetes/contrib/mesos/pkg/scheduler/meta"
|
||||
"k8s.io/kubernetes/contrib/mesos/pkg/scheduler/metrics"
|
||||
"k8s.io/kubernetes/contrib/mesos/pkg/scheduler/podtask"
|
||||
mresource "k8s.io/kubernetes/contrib/mesos/pkg/scheduler/resource"
|
||||
"k8s.io/kubernetes/contrib/mesos/pkg/scheduler/uid"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/errors"
|
||||
@ -118,19 +117,17 @@ type KubernetesScheduler struct {
|
||||
// and the invoking the pod registry interfaces.
|
||||
// In particular, changes to podtask.T objects are currently guarded by this lock.
|
||||
*sync.RWMutex
|
||||
PodScheduler
|
||||
|
||||
// Config related, write-once
|
||||
|
||||
schedcfg *schedcfg.Config
|
||||
executor *mesos.ExecutorInfo
|
||||
executorGroup uint64
|
||||
scheduleFunc PodScheduleFunc
|
||||
client *client.Client
|
||||
etcdClient tools.EtcdClient
|
||||
failoverTimeout float64 // in seconds
|
||||
reconcileInterval int64
|
||||
defaultContainerCPULimit mresource.CPUShares
|
||||
defaultContainerMemLimit mresource.MegaBytes
|
||||
schedcfg *schedcfg.Config
|
||||
executor *mesos.ExecutorInfo
|
||||
executorGroup uint64
|
||||
client *client.Client
|
||||
etcdClient tools.EtcdClient
|
||||
failoverTimeout float64 // in seconds
|
||||
reconcileInterval int64
|
||||
|
||||
// Mesos context.
|
||||
|
||||
@ -157,33 +154,29 @@ type KubernetesScheduler struct {
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Schedcfg schedcfg.Config
|
||||
Executor *mesos.ExecutorInfo
|
||||
ScheduleFunc PodScheduleFunc
|
||||
Client *client.Client
|
||||
EtcdClient tools.EtcdClient
|
||||
FailoverTimeout float64
|
||||
ReconcileInterval int64
|
||||
ReconcileCooldown time.Duration
|
||||
DefaultContainerCPULimit mresource.CPUShares
|
||||
DefaultContainerMemLimit mresource.MegaBytes
|
||||
Schedcfg schedcfg.Config
|
||||
Executor *mesos.ExecutorInfo
|
||||
Scheduler PodScheduler
|
||||
Client *client.Client
|
||||
EtcdClient tools.EtcdClient
|
||||
FailoverTimeout float64
|
||||
ReconcileInterval int64
|
||||
ReconcileCooldown time.Duration
|
||||
}
|
||||
|
||||
// New creates a new KubernetesScheduler
|
||||
func New(config Config) *KubernetesScheduler {
|
||||
var k *KubernetesScheduler
|
||||
k = &KubernetesScheduler{
|
||||
schedcfg: &config.Schedcfg,
|
||||
RWMutex: new(sync.RWMutex),
|
||||
executor: config.Executor,
|
||||
executorGroup: uid.Parse(config.Executor.ExecutorId.GetValue()).Group(),
|
||||
scheduleFunc: config.ScheduleFunc,
|
||||
client: config.Client,
|
||||
etcdClient: config.EtcdClient,
|
||||
failoverTimeout: config.FailoverTimeout,
|
||||
reconcileInterval: config.ReconcileInterval,
|
||||
defaultContainerCPULimit: config.DefaultContainerCPULimit,
|
||||
defaultContainerMemLimit: config.DefaultContainerMemLimit,
|
||||
schedcfg: &config.Schedcfg,
|
||||
RWMutex: new(sync.RWMutex),
|
||||
executor: config.Executor,
|
||||
executorGroup: uid.Parse(config.Executor.ExecutorId.GetValue()).Group(),
|
||||
PodScheduler: config.Scheduler,
|
||||
client: config.Client,
|
||||
etcdClient: config.EtcdClient,
|
||||
failoverTimeout: config.FailoverTimeout,
|
||||
reconcileInterval: config.ReconcileInterval,
|
||||
offers: offers.CreateRegistry(offers.RegistryConfig{
|
||||
Compat: func(o *mesos.Offer) bool {
|
||||
// filter the offers: the executor IDs must not identify a kubelet-
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user