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
|
.vagrant
|
||||||
network_closure.sh
|
network_closure.sh
|
||||||
|
|
||||||
|
# Local cluster env variables
|
||||||
|
/cluster/env.sh
|
||||||
|
|
||||||
# Compiled binaries in third_party
|
# Compiled binaries in third_party
|
||||||
/third_party/pkg
|
/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",
|
"ImportPath": "github.com/emicklei/go-restful",
|
||||||
"Comment": "v1.1.3-76-gbfd6ff2",
|
"Comment": "v1.1.3-98-g1f9a0ee",
|
||||||
"Rev": "bfd6ff29d2961031cec64346a92bae4cde96c868"
|
"Rev": "1f9a0ee00ff93717a275e15b30cf7df356255877"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/evanphx/json-patch",
|
"ImportPath": "github.com/evanphx/json-patch",
|
||||||
@ -224,7 +224,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/fsouza/go-dockerclient",
|
"ImportPath": "github.com/fsouza/go-dockerclient",
|
||||||
"Rev": "42d06e2b125654477366c320dcea99107a86e9c2"
|
"Rev": "76fd6c68cf24c48ee6a2b25def997182a29f940e"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/garyburd/redigo/internal",
|
"ImportPath": "github.com/garyburd/redigo/internal",
|
||||||
@ -512,7 +512,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/spf13/cobra",
|
"ImportPath": "github.com/spf13/cobra",
|
||||||
"Rev": "db0518444643a7b170abb78164bbeaf5a2bb816f"
|
"Rev": "68f5a81a722d56241bd70faf6860ceb05eb27d64"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/spf13/pflag",
|
"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
|
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
|
2015-03-20
|
||||||
- add configurable logging
|
- 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
|
- Filters for intercepting the request → response flow on Service or Route level
|
||||||
- Request-scoped variables using attributes
|
- Request-scoped variables using attributes
|
||||||
- Containers for WebServices on different HTTP endpoints
|
- 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 responses on OPTIONS (using a filter)
|
||||||
- Automatic CORS request handling (using a filter)
|
- Automatic CORS request handling (using a filter)
|
||||||
- API declaration for Swagger UI (see swagger package)
|
- 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
|
c.writer = httpWriter
|
||||||
var err error
|
var err error
|
||||||
if ENCODING_GZIP == encoding {
|
if ENCODING_GZIP == encoding {
|
||||||
c.compressor, err = gzip.NewWriterLevel(httpWriter, gzip.BestSpeed)
|
w := GzipWriterPool.Get().(*gzip.Writer)
|
||||||
if err != nil {
|
w.Reset(httpWriter)
|
||||||
return nil, err
|
c.compressor = w
|
||||||
}
|
|
||||||
} else if ENCODING_DEFLATE == encoding {
|
} else if ENCODING_DEFLATE == encoding {
|
||||||
c.compressor, err = zlib.NewWriterLevel(httpWriter, zlib.BestSpeed)
|
w := ZlibWriterPool.Get().(*zlib.Writer)
|
||||||
if err != nil {
|
w.Reset(httpWriter)
|
||||||
return nil, err
|
c.compressor = w
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return nil, errors.New("Unknown encoding:" + encoding)
|
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
|
package restful
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"compress/gzip"
|
||||||
|
"compress/zlib"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// go test -v -test.run TestGzip ...restful
|
||||||
func TestGzip(t *testing.T) {
|
func TestGzip(t *testing.T) {
|
||||||
EnableContentEncoding = true
|
EnableContentEncoding = true
|
||||||
httpRequest, _ := http.NewRequest("GET", "/test", nil)
|
httpRequest, _ := http.NewRequest("GET", "/test", nil)
|
||||||
@ -27,6 +33,17 @@ func TestGzip(t *testing.T) {
|
|||||||
if httpWriter.Header().Get("Content-Encoding") != "gzip" {
|
if httpWriter.Header().Get("Content-Encoding") != "gzip" {
|
||||||
t.Fatal("Missing gzip header")
|
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) {
|
func TestDeflate(t *testing.T) {
|
||||||
@ -50,4 +67,61 @@ func TestDeflate(t *testing.T) {
|
|||||||
if httpWriter.Header().Get("Content-Encoding") != "deflate" {
|
if httpWriter.Header().Get("Content-Encoding") != "deflate" {
|
||||||
t.Fatal("Missing deflate header")
|
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"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/emicklei/go-restful/log"
|
"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.
|
// 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
|
// The requests are further dispatched to routes of WebServices using a RouteSelector
|
||||||
type Container struct {
|
type Container struct {
|
||||||
|
webServicesLock sync.RWMutex
|
||||||
webServices []*WebService
|
webServices []*WebService
|
||||||
ServeMux *http.ServeMux
|
ServeMux *http.ServeMux
|
||||||
isRegisteredOnRoot bool
|
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.
|
// Add a WebService to the Container. It will detect duplicate root paths and panic in that case.
|
||||||
func (c *Container) Add(service *WebService) *Container {
|
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 registered on root then no additional specific mapping is needed
|
||||||
if !c.isRegisteredOnRoot {
|
if !c.isRegisteredOnRoot {
|
||||||
pattern := c.fixedPrefixPath(service.RootPath())
|
pattern := c.fixedPrefixPath(service.RootPath())
|
||||||
@ -122,6 +126,19 @@ func (c *Container) Add(service *WebService) *Container {
|
|||||||
return c
|
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
|
// logStackOnRecover is the default RecoverHandleFunction and is called
|
||||||
// when DoNotRecover is false and the recoverHandleFunc is not set for the container.
|
// 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.
|
// 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
|
// Find best match Route ; err is non nil if no match was found
|
||||||
webService, route, err := c.router.SelectRoute(
|
var webService *WebService
|
||||||
c.webServices,
|
var route *Route
|
||||||
httpRequest)
|
var err error
|
||||||
|
func() {
|
||||||
|
c.webServicesLock.RLock()
|
||||||
|
defer c.webServicesLock.RUnlock()
|
||||||
|
webService, route, err = c.router.SelectRoute(
|
||||||
|
c.webServices,
|
||||||
|
httpRequest)
|
||||||
|
}()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// a non-200 response has already been written
|
// a non-200 response has already been written
|
||||||
// run container filters anyway ; they should not touch the response...
|
// 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
|
// RegisteredWebServices returns the collections of added WebServices
|
||||||
func (c Container) RegisteredWebServices() []*WebService {
|
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
|
// 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
|
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 {
|
func (p *Parameter) DefaultValue(stringRepresentation string) *Parameter {
|
||||||
p.data.DefaultValue = stringRepresentation
|
p.data.DefaultValue = stringRepresentation
|
||||||
return p
|
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 (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"compress/gzip"
|
||||||
|
"compress/zlib"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"io"
|
"io"
|
||||||
@ -82,15 +84,17 @@ func (r *Request) HeaderParameter(name string) string {
|
|||||||
// ReadEntity checks the Accept header and reads the content into the entityPointer
|
// ReadEntity checks the Accept header and reads the content into the entityPointer
|
||||||
// May be called multiple times in the request-response flow
|
// May be called multiple times in the request-response flow
|
||||||
func (r *Request) ReadEntity(entityPointer interface{}) (err error) {
|
func (r *Request) ReadEntity(entityPointer interface{}) (err error) {
|
||||||
|
defer r.Request.Body.Close()
|
||||||
contentType := r.Request.Header.Get(HEADER_ContentType)
|
contentType := r.Request.Header.Get(HEADER_ContentType)
|
||||||
|
contentEncoding := r.Request.Header.Get(HEADER_ContentEncoding)
|
||||||
if doCacheReadEntityBytes {
|
if doCacheReadEntityBytes {
|
||||||
return r.cachingReadEntity(contentType, entityPointer)
|
return r.cachingReadEntity(contentType, contentEncoding, entityPointer)
|
||||||
}
|
}
|
||||||
// unmarshall directly from request Body
|
// 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
|
var buffer []byte
|
||||||
if r.bodyContent != nil {
|
if r.bodyContent != nil {
|
||||||
buffer = *r.bodyContent
|
buffer = *r.bodyContent
|
||||||
@ -101,22 +105,38 @@ func (r *Request) cachingReadEntity(contentType string, entityPointer interface{
|
|||||||
}
|
}
|
||||||
r.bodyContent = &buffer
|
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) {
|
func (r *Request) decodeEntity(reader io.Reader, contentType string, contentEncoding string, entityPointer interface{}) (err error) {
|
||||||
if strings.Contains(contentType, MIME_XML) {
|
entityReader := reader
|
||||||
return xml.NewDecoder(reader).Decode(entityPointer)
|
|
||||||
|
// 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 {
|
if strings.Contains(contentType, MIME_JSON) || MIME_JSON == defaultRequestContentType {
|
||||||
decoder := json.NewDecoder(reader)
|
decoder := json.NewDecoder(entityReader)
|
||||||
decoder.UseNumber()
|
decoder.UseNumber()
|
||||||
return decoder.Decode(entityPointer)
|
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.
|
// 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)
|
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
|
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.
|
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.
|
// Creates a new response based on a http ResponseWriter.
|
||||||
func NewResponse(httpWriter http.ResponseWriter) *Response {
|
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
|
// 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.
|
// WriteError write the http status and the error string on the response.
|
||||||
func (r *Response) WriteError(httpStatus int, err error) error {
|
func (r *Response) WriteError(httpStatus int, err error) error {
|
||||||
|
r.err = err
|
||||||
return r.WriteErrorString(httpStatus, err.Error())
|
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.
|
// WriteHeader is overridden to remember the Status Code that has been written.
|
||||||
// Note that using this method, the status value is only written when
|
// Note that using this method, the status value is only written when
|
||||||
// - calling WriteEntity,
|
// calling WriteEntity,
|
||||||
// - or directly calling WriteAsXml or WriteAsJson,
|
// or directly calling WriteAsXml or WriteAsJson,
|
||||||
// - or if the status is one for which no response is allowed (i.e.,
|
// or if the status is one for which no response is allowed:
|
||||||
// 204 (http.StatusNoContent) or 304 (http.StatusNotModified))
|
//
|
||||||
|
// 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) {
|
func (r *Response) WriteHeader(httpStatus int) {
|
||||||
r.statusCode = httpStatus
|
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 ||
|
if http.StatusNoContent == httpStatus ||
|
||||||
http.StatusNotModified == httpStatus ||
|
http.StatusNotModified == httpStatus ||
|
||||||
http.StatusPartialContent == httpStatus {
|
http.StatusPartialContent == httpStatus ||
|
||||||
|
http.StatusAccepted == httpStatus {
|
||||||
r.ResponseWriter.WriteHeader(httpStatus)
|
r.ResponseWriter.WriteHeader(httpStatus)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// StatusCode returns the code that has been written using WriteHeader.
|
// 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 {
|
func (r Response) StatusCode() int {
|
||||||
if 0 == r.statusCode {
|
if 0 == r.statusCode {
|
||||||
// no status code has been written yet; assume OK
|
// no status code has been written yet; assume OK
|
||||||
@ -245,3 +256,8 @@ func (r Response) ContentLength() int {
|
|||||||
func (r Response) CloseNotify() <-chan bool {
|
func (r Response) CloseNotify() <-chan bool {
|
||||||
return r.ResponseWriter.(http.CloseNotifier).CloseNotify()
|
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) {
|
func TestWriteHeader(t *testing.T) {
|
||||||
httpWriter := httptest.NewRecorder()
|
httpWriter := httptest.NewRecorder()
|
||||||
resp := Response{httpWriter, "*/*", []string{"*/*"}, 0, 0, true}
|
resp := Response{httpWriter, "*/*", []string{"*/*"}, 0, 0, true, nil}
|
||||||
resp.WriteHeader(123)
|
resp.WriteHeader(123)
|
||||||
if resp.StatusCode() != 123 {
|
if resp.StatusCode() != 123 {
|
||||||
t.Errorf("Unexpected status code:%d", resp.StatusCode())
|
t.Errorf("Unexpected status code:%d", resp.StatusCode())
|
||||||
@ -18,7 +18,7 @@ func TestWriteHeader(t *testing.T) {
|
|||||||
|
|
||||||
func TestNoWriteHeader(t *testing.T) {
|
func TestNoWriteHeader(t *testing.T) {
|
||||||
httpWriter := httptest.NewRecorder()
|
httpWriter := httptest.NewRecorder()
|
||||||
resp := Response{httpWriter, "*/*", []string{"*/*"}, 0, 0, true}
|
resp := Response{httpWriter, "*/*", []string{"*/*"}, 0, 0, true, nil}
|
||||||
if resp.StatusCode() != http.StatusOK {
|
if resp.StatusCode() != http.StatusOK {
|
||||||
t.Errorf("Unexpected status code:%d", resp.StatusCode())
|
t.Errorf("Unexpected status code:%d", resp.StatusCode())
|
||||||
}
|
}
|
||||||
@ -31,7 +31,7 @@ type food struct {
|
|||||||
// go test -v -test.run TestMeasureContentLengthXml ...restful
|
// go test -v -test.run TestMeasureContentLengthXml ...restful
|
||||||
func TestMeasureContentLengthXml(t *testing.T) {
|
func TestMeasureContentLengthXml(t *testing.T) {
|
||||||
httpWriter := httptest.NewRecorder()
|
httpWriter := httptest.NewRecorder()
|
||||||
resp := Response{httpWriter, "*/*", []string{"*/*"}, 0, 0, true}
|
resp := Response{httpWriter, "*/*", []string{"*/*"}, 0, 0, true, nil}
|
||||||
resp.WriteAsXml(food{"apple"})
|
resp.WriteAsXml(food{"apple"})
|
||||||
if resp.ContentLength() != 76 {
|
if resp.ContentLength() != 76 {
|
||||||
t.Errorf("Incorrect measured length:%d", resp.ContentLength())
|
t.Errorf("Incorrect measured length:%d", resp.ContentLength())
|
||||||
@ -41,7 +41,7 @@ func TestMeasureContentLengthXml(t *testing.T) {
|
|||||||
// go test -v -test.run TestMeasureContentLengthJson ...restful
|
// go test -v -test.run TestMeasureContentLengthJson ...restful
|
||||||
func TestMeasureContentLengthJson(t *testing.T) {
|
func TestMeasureContentLengthJson(t *testing.T) {
|
||||||
httpWriter := httptest.NewRecorder()
|
httpWriter := httptest.NewRecorder()
|
||||||
resp := Response{httpWriter, "*/*", []string{"*/*"}, 0, 0, true}
|
resp := Response{httpWriter, "*/*", []string{"*/*"}, 0, 0, true, nil}
|
||||||
resp.WriteAsJson(food{"apple"})
|
resp.WriteAsJson(food{"apple"})
|
||||||
if resp.ContentLength() != 22 {
|
if resp.ContentLength() != 22 {
|
||||||
t.Errorf("Incorrect measured length:%d", resp.ContentLength())
|
t.Errorf("Incorrect measured length:%d", resp.ContentLength())
|
||||||
@ -51,7 +51,7 @@ func TestMeasureContentLengthJson(t *testing.T) {
|
|||||||
// go test -v -test.run TestMeasureContentLengthJsonNotPretty ...restful
|
// go test -v -test.run TestMeasureContentLengthJsonNotPretty ...restful
|
||||||
func TestMeasureContentLengthJsonNotPretty(t *testing.T) {
|
func TestMeasureContentLengthJsonNotPretty(t *testing.T) {
|
||||||
httpWriter := httptest.NewRecorder()
|
httpWriter := httptest.NewRecorder()
|
||||||
resp := Response{httpWriter, "*/*", []string{"*/*"}, 0, 0, false}
|
resp := Response{httpWriter, "*/*", []string{"*/*"}, 0, 0, false, nil}
|
||||||
resp.WriteAsJson(food{"apple"})
|
resp.WriteAsJson(food{"apple"})
|
||||||
if resp.ContentLength() != 16 {
|
if resp.ContentLength() != 16 {
|
||||||
t.Errorf("Incorrect measured length:%d", resp.ContentLength())
|
t.Errorf("Incorrect measured length:%d", resp.ContentLength())
|
||||||
@ -61,7 +61,7 @@ func TestMeasureContentLengthJsonNotPretty(t *testing.T) {
|
|||||||
// go test -v -test.run TestMeasureContentLengthWriteErrorString ...restful
|
// go test -v -test.run TestMeasureContentLengthWriteErrorString ...restful
|
||||||
func TestMeasureContentLengthWriteErrorString(t *testing.T) {
|
func TestMeasureContentLengthWriteErrorString(t *testing.T) {
|
||||||
httpWriter := httptest.NewRecorder()
|
httpWriter := httptest.NewRecorder()
|
||||||
resp := Response{httpWriter, "*/*", []string{"*/*"}, 0, 0, true}
|
resp := Response{httpWriter, "*/*", []string{"*/*"}, 0, 0, true, nil}
|
||||||
resp.WriteErrorString(404, "Invalid")
|
resp.WriteErrorString(404, "Invalid")
|
||||||
if resp.ContentLength() != len("Invalid") {
|
if resp.ContentLength() != len("Invalid") {
|
||||||
t.Errorf("Incorrect measured length:%d", resp.ContentLength())
|
t.Errorf("Incorrect measured length:%d", resp.ContentLength())
|
||||||
@ -79,7 +79,7 @@ func TestStatusIsPassedToResponse(t *testing.T) {
|
|||||||
{write: 400, read: 200},
|
{write: 400, read: 200},
|
||||||
} {
|
} {
|
||||||
httpWriter := httptest.NewRecorder()
|
httpWriter := httptest.NewRecorder()
|
||||||
resp := Response{httpWriter, "*/*", []string{"*/*"}, 0, 0, true}
|
resp := Response{httpWriter, "*/*", []string{"*/*"}, 0, 0, true, nil}
|
||||||
resp.WriteHeader(each.write)
|
resp.WriteHeader(each.write)
|
||||||
if got, want := httpWriter.Code, each.read; got != want {
|
if got, want := httpWriter.Code, each.read; got != want {
|
||||||
t.Error("got %v want %v", 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
|
// go test -v -test.run TestStatusCreatedAndContentTypeJson_Issue54 ...restful
|
||||||
func TestStatusCreatedAndContentTypeJson_Issue54(t *testing.T) {
|
func TestStatusCreatedAndContentTypeJson_Issue54(t *testing.T) {
|
||||||
httpWriter := httptest.NewRecorder()
|
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.WriteHeader(201)
|
||||||
resp.WriteAsJson(food{"Juicy"})
|
resp.WriteAsJson(food{"Juicy"})
|
||||||
if httpWriter.HeaderMap.Get("Content-Type") != "application/json" {
|
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
|
// go test -v -test.run TestLastWriteErrorCaught ...restful
|
||||||
func TestLastWriteErrorCaught(t *testing.T) {
|
func TestLastWriteErrorCaught(t *testing.T) {
|
||||||
httpWriter := errorOnWriteRecorder{httptest.NewRecorder()}
|
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"})
|
err := resp.WriteAsJson(food{"Juicy"})
|
||||||
if err.Error() != "fail" {
|
if err.Error() != "fail" {
|
||||||
t.Errorf("Unexpected error message:%v", err)
|
t.Errorf("Unexpected error message:%v", err)
|
||||||
@ -123,7 +123,7 @@ func TestLastWriteErrorCaught(t *testing.T) {
|
|||||||
func TestAcceptStarStar_Issue83(t *testing.T) {
|
func TestAcceptStarStar_Issue83(t *testing.T) {
|
||||||
httpWriter := httptest.NewRecorder()
|
httpWriter := httptest.NewRecorder()
|
||||||
// Accept Produces
|
// 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"})
|
resp.WriteEntity(food{"Juicy"})
|
||||||
ct := httpWriter.Header().Get("Content-Type")
|
ct := httpWriter.Header().Get("Content-Type")
|
||||||
if "application/json" != ct {
|
if "application/json" != ct {
|
||||||
@ -135,7 +135,7 @@ func TestAcceptStarStar_Issue83(t *testing.T) {
|
|||||||
func TestAcceptSkipStarStar_Issue83(t *testing.T) {
|
func TestAcceptSkipStarStar_Issue83(t *testing.T) {
|
||||||
httpWriter := httptest.NewRecorder()
|
httpWriter := httptest.NewRecorder()
|
||||||
// Accept Produces
|
// 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"})
|
resp.WriteEntity(food{"Juicy"})
|
||||||
ct := httpWriter.Header().Get("Content-Type")
|
ct := httpWriter.Header().Get("Content-Type")
|
||||||
if "application/xml" != ct {
|
if "application/xml" != ct {
|
||||||
@ -147,7 +147,7 @@ func TestAcceptSkipStarStar_Issue83(t *testing.T) {
|
|||||||
func TestAcceptXmlBeforeStarStar_Issue83(t *testing.T) {
|
func TestAcceptXmlBeforeStarStar_Issue83(t *testing.T) {
|
||||||
httpWriter := httptest.NewRecorder()
|
httpWriter := httptest.NewRecorder()
|
||||||
// Accept Produces
|
// 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"})
|
resp.WriteEntity(food{"Juicy"})
|
||||||
ct := httpWriter.Header().Get("Content-Type")
|
ct := httpWriter.Header().Get("Content-Type")
|
||||||
if "application/json" != ct {
|
if "application/json" != ct {
|
||||||
@ -158,7 +158,7 @@ func TestAcceptXmlBeforeStarStar_Issue83(t *testing.T) {
|
|||||||
// go test -v -test.run TestWriteHeaderNoContent_Issue124 ...restful
|
// go test -v -test.run TestWriteHeaderNoContent_Issue124 ...restful
|
||||||
func TestWriteHeaderNoContent_Issue124(t *testing.T) {
|
func TestWriteHeaderNoContent_Issue124(t *testing.T) {
|
||||||
httpWriter := httptest.NewRecorder()
|
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)
|
resp.WriteHeader(http.StatusNoContent)
|
||||||
if httpWriter.Code != http.StatusNoContent {
|
if httpWriter.Code != http.StatusNoContent {
|
||||||
t.Errorf("got %d want %d", 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
|
// go test -v -test.run TestStatusCreatedAndContentTypeJson_Issue163 ...restful
|
||||||
func TestStatusCreatedAndContentTypeJson_Issue163(t *testing.T) {
|
func TestStatusCreatedAndContentTypeJson_Issue163(t *testing.T) {
|
||||||
httpWriter := httptest.NewRecorder()
|
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)
|
resp.WriteHeader(http.StatusNotModified)
|
||||||
if httpWriter.Code != http.StatusNotModified {
|
if httpWriter.Code != http.StatusNotModified {
|
||||||
t.Errorf("Got %d want %d", 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 {
|
} else {
|
||||||
host = hostvalues[0]
|
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)
|
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:
|
go:
|
||||||
- 1.3.1
|
- 1.3.1
|
||||||
- 1.4
|
- 1.4
|
||||||
|
- 1.5
|
||||||
- tip
|
- tip
|
||||||
env:
|
env:
|
||||||
- GOARCH=amd64
|
- 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>
|
Brian Palmer <brianp@instructure.com>
|
||||||
Burke Libbey <burke@libbey.me>
|
Burke Libbey <burke@libbey.me>
|
||||||
Carlos Diaz-Padron <cpadron@mozilla.com>
|
Carlos Diaz-Padron <cpadron@mozilla.com>
|
||||||
|
Cesar Wong <cewong@redhat.com>
|
||||||
Cezar Sa Espinola <cezar.sa@corp.globo.com>
|
Cezar Sa Espinola <cezar.sa@corp.globo.com>
|
||||||
Cheah Chu Yeow <chuyeow@gmail.com>
|
Cheah Chu Yeow <chuyeow@gmail.com>
|
||||||
cheneydeng <cheneydeng@qq.com>
|
cheneydeng <cheneydeng@qq.com>
|
||||||
@ -33,6 +34,7 @@ Fabio Rehm <fgrehm@gmail.com>
|
|||||||
Fatih Arslan <ftharsln@gmail.com>
|
Fatih Arslan <ftharsln@gmail.com>
|
||||||
Flavia Missi <flaviamissi@gmail.com>
|
Flavia Missi <flaviamissi@gmail.com>
|
||||||
Francisco Souza <f@souza.cc>
|
Francisco Souza <f@souza.cc>
|
||||||
|
Grégoire Delattre <gregoire.delattre@gmail.com>
|
||||||
Guillermo Álvarez Fernández <guillermo@cientifico.net>
|
Guillermo Álvarez Fernández <guillermo@cientifico.net>
|
||||||
He Simei <hesimei@zju.edu.cn>
|
He Simei <hesimei@zju.edu.cn>
|
||||||
Ivan Mikushin <i.mikushin@gmail.com>
|
Ivan Mikushin <i.mikushin@gmail.com>
|
||||||
@ -66,12 +68,14 @@ Paul Morie <pmorie@gmail.com>
|
|||||||
Paul Weil <pweil@redhat.com>
|
Paul Weil <pweil@redhat.com>
|
||||||
Peter Edge <peter.edge@gmail.com>
|
Peter Edge <peter.edge@gmail.com>
|
||||||
Peter Jihoon Kim <raingrove@gmail.com>
|
Peter Jihoon Kim <raingrove@gmail.com>
|
||||||
|
Phil Lu <lu@stackengine.com>
|
||||||
Philippe Lafoucrière <philippe.lafoucriere@tech-angels.com>
|
Philippe Lafoucrière <philippe.lafoucriere@tech-angels.com>
|
||||||
Rafe Colton <rafael.colton@gmail.com>
|
Rafe Colton <rafael.colton@gmail.com>
|
||||||
Rob Miller <rob@kalistra.com>
|
Rob Miller <rob@kalistra.com>
|
||||||
Robert Williamson <williamson.robert@gmail.com>
|
Robert Williamson <williamson.robert@gmail.com>
|
||||||
Salvador Gironès <salvadorgirones@gmail.com>
|
Salvador Gironès <salvadorgirones@gmail.com>
|
||||||
Sam Rijs <srijs@airpost.net>
|
Sam Rijs <srijs@airpost.net>
|
||||||
|
Samuel Karp <skarp@amazon.com>
|
||||||
Simon Eskildsen <sirup@sirupsen.com>
|
Simon Eskildsen <sirup@sirupsen.com>
|
||||||
Simon Menke <simon.menke@gmail.com>
|
Simon Menke <simon.menke@gmail.com>
|
||||||
Skolos <skolos@gopherlab.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"
|
"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
|
// AuthConfiguration represents authentication options to use in the PushImage
|
||||||
// method. It represents the authentication in the Docker index server.
|
// method. It represents the authentication in the Docker index server.
|
||||||
@ -33,6 +34,10 @@ type AuthConfigurations struct {
|
|||||||
Configs map[string]AuthConfiguration `json:"configs"`
|
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
|
// dockerConfig represents a registry authentation configuration from the
|
||||||
// .dockercfg file.
|
// .dockercfg file.
|
||||||
type dockerConfig struct {
|
type dockerConfig struct {
|
||||||
@ -103,7 +108,7 @@ func authConfigs(confs map[string]dockerConfig) (*AuthConfigurations, error) {
|
|||||||
}
|
}
|
||||||
userpass := strings.Split(string(data), ":")
|
userpass := strings.Split(string(data), ":")
|
||||||
if len(userpass) != 2 {
|
if len(userpass) != 2 {
|
||||||
return nil, AuthParseError
|
return nil, ErrCannotParseDockercfg
|
||||||
}
|
}
|
||||||
c.Configs[reg] = AuthConfiguration{
|
c.Configs[reg] = AuthConfiguration{
|
||||||
Email: conf.Email,
|
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.
|
// 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 {
|
func (c *Client) AuthCheck(conf *AuthConfiguration) error {
|
||||||
if conf == nil {
|
if conf == nil {
|
||||||
return fmt.Errorf("conf is 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"))
|
auth := base64.StdEncoding.EncodeToString([]byte("userpass"))
|
||||||
read := strings.NewReader(fmt.Sprintf(`{"docker.io":{"auth":"%s","email":"user@example.com"}}`, auth))
|
read := strings.NewReader(fmt.Sprintf(`{"docker.io":{"auth":"%s","email":"user@example.com"}}`, auth))
|
||||||
ac, err := NewAuthConfigurations(read)
|
ac, err := NewAuthConfigurations(read)
|
||||||
if err != AuthParseError {
|
if err != ErrCannotParseDockercfg {
|
||||||
t.Errorf("Incorrect error returned %v\n", err)
|
t.Errorf("Incorrect error returned %v\n", err)
|
||||||
}
|
}
|
||||||
if ac != nil {
|
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.
|
// 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 {
|
type Change struct {
|
||||||
Path string
|
Path string
|
||||||
Kind ChangeType
|
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.
|
// 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
|
package docker
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -45,6 +45,8 @@ var (
|
|||||||
ErrConnectionRefused = errors.New("cannot connect to Docker endpoint")
|
ErrConnectionRefused = errors.New("cannot connect to Docker endpoint")
|
||||||
|
|
||||||
apiVersion112, _ = NewAPIVersion("1.12")
|
apiVersion112, _ = NewAPIVersion("1.12")
|
||||||
|
|
||||||
|
apiVersion119, _ = NewAPIVersion("1.19")
|
||||||
)
|
)
|
||||||
|
|
||||||
// APIVersion is an internal representation of a version of the Remote API.
|
// 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
|
// 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 {
|
func (c *Client) Ping() error {
|
||||||
path := "/_ping"
|
path := "/_ping"
|
||||||
body, status, err := c.do("GET", path, doOptions{})
|
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
|
address := c.endpointURL.Path
|
||||||
if streamOptions.stdout == nil {
|
if streamOptions.stdout == nil {
|
||||||
streamOptions.stdout = ioutil.Discard
|
streamOptions.stdout = ioutil.Discard
|
||||||
|
} else if t, ok := streamOptions.stdout.(io.Closer); ok {
|
||||||
|
defer t.Close()
|
||||||
}
|
}
|
||||||
if streamOptions.stderr == nil {
|
if streamOptions.stderr == nil {
|
||||||
streamOptions.stderr = ioutil.Discard
|
streamOptions.stderr = ioutil.Discard
|
||||||
|
} else if t, ok := streamOptions.stderr.(io.Closer); ok {
|
||||||
|
defer t.Close()
|
||||||
}
|
}
|
||||||
if protocol == "unix" {
|
if protocol == "unix" {
|
||||||
dial, err := net.Dial(protocol, address)
|
dial, err := net.Dial(protocol, address)
|
||||||
@ -583,6 +589,8 @@ func (c *Client) hijack(method, path string, hijackOptions hijackOptions) error
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
req.Header.Set("Content-Type", "plain/text")
|
req.Header.Set("Content-Type", "plain/text")
|
||||||
|
req.Header.Set("Connection", "Upgrade")
|
||||||
|
req.Header.Set("Upgrade", "tcp")
|
||||||
protocol := c.endpointURL.Scheme
|
protocol := c.endpointURL.Scheme
|
||||||
address := c.endpointURL.Path
|
address := c.endpointURL.Path
|
||||||
if protocol != "unix" {
|
if protocol != "unix" {
|
||||||
@ -612,13 +620,16 @@ func (c *Client) hijack(method, path string, hijackOptions hijackOptions) error
|
|||||||
defer rwc.Close()
|
defer rwc.Close()
|
||||||
errChanOut := make(chan error, 1)
|
errChanOut := make(chan error, 1)
|
||||||
errChanIn := make(chan error, 1)
|
errChanIn := make(chan error, 1)
|
||||||
exit := make(chan bool)
|
|
||||||
go func() {
|
go func() {
|
||||||
defer close(exit)
|
defer func() {
|
||||||
defer close(errChanOut)
|
if hijackOptions.in != nil {
|
||||||
|
if closer, ok := hijackOptions.in.(io.Closer); ok {
|
||||||
|
closer.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
var err error
|
var err error
|
||||||
if hijackOptions.setRawTerminal {
|
if hijackOptions.setRawTerminal {
|
||||||
// When TTY is ON, use regular copy
|
|
||||||
_, err = io.Copy(hijackOptions.stdout, br)
|
_, err = io.Copy(hijackOptions.stdout, br)
|
||||||
} else {
|
} else {
|
||||||
_, err = stdcopy.StdCopy(hijackOptions.stdout, hijackOptions.stderr, br)
|
_, err = stdcopy.StdCopy(hijackOptions.stdout, hijackOptions.stderr, br)
|
||||||
@ -626,17 +637,15 @@ func (c *Client) hijack(method, path string, hijackOptions hijackOptions) error
|
|||||||
errChanOut <- err
|
errChanOut <- err
|
||||||
}()
|
}()
|
||||||
go func() {
|
go func() {
|
||||||
|
var err error
|
||||||
if hijackOptions.in != nil {
|
if hijackOptions.in != nil {
|
||||||
_, err := io.Copy(rwc, hijackOptions.in)
|
_, err = io.Copy(rwc, hijackOptions.in)
|
||||||
errChanIn <- err
|
|
||||||
} else {
|
|
||||||
errChanIn <- nil
|
|
||||||
}
|
}
|
||||||
|
errChanIn <- err
|
||||||
rwc.(interface {
|
rwc.(interface {
|
||||||
CloseWrite() error
|
CloseWrite() error
|
||||||
}).CloseWrite()
|
}).CloseWrite()
|
||||||
}()
|
}()
|
||||||
<-exit
|
|
||||||
errIn := <-errChanIn
|
errIn := <-errChanIn
|
||||||
errOut := <-errChanOut
|
errOut := <-errChanOut
|
||||||
if errIn != nil {
|
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.
|
// 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 {
|
type ListContainersOptions struct {
|
||||||
All bool
|
All bool
|
||||||
Size bool
|
Size bool
|
||||||
@ -41,24 +41,24 @@ type APIPort struct {
|
|||||||
IP string `json:"IP,omitempty" yaml:"IP,omitempty"`
|
IP string `json:"IP,omitempty" yaml:"IP,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// APIContainers represents a container.
|
// APIContainers represents each container in the list returned by
|
||||||
//
|
// ListContainers.
|
||||||
// See http://goo.gl/QeFH7U for more details.
|
|
||||||
type APIContainers struct {
|
type APIContainers struct {
|
||||||
ID string `json:"Id" yaml:"Id"`
|
ID string `json:"Id" yaml:"Id"`
|
||||||
Image string `json:"Image,omitempty" yaml:"Image,omitempty"`
|
Image string `json:"Image,omitempty" yaml:"Image,omitempty"`
|
||||||
Command string `json:"Command,omitempty" yaml:"Command,omitempty"`
|
Command string `json:"Command,omitempty" yaml:"Command,omitempty"`
|
||||||
Created int64 `json:"Created,omitempty" yaml:"Created,omitempty"`
|
Created int64 `json:"Created,omitempty" yaml:"Created,omitempty"`
|
||||||
Status string `json:"Status,omitempty" yaml:"Status,omitempty"`
|
Status string `json:"Status,omitempty" yaml:"Status,omitempty"`
|
||||||
Ports []APIPort `json:"Ports,omitempty" yaml:"Ports,omitempty"`
|
Ports []APIPort `json:"Ports,omitempty" yaml:"Ports,omitempty"`
|
||||||
SizeRw int64 `json:"SizeRw,omitempty" yaml:"SizeRw,omitempty"`
|
SizeRw int64 `json:"SizeRw,omitempty" yaml:"SizeRw,omitempty"`
|
||||||
SizeRootFs int64 `json:"SizeRootFs,omitempty" yaml:"SizeRootFs,omitempty"`
|
SizeRootFs int64 `json:"SizeRootFs,omitempty" yaml:"SizeRootFs,omitempty"`
|
||||||
Names []string `json:"Names,omitempty" yaml:"Names,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.
|
// 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) {
|
func (c *Client) ListContainers(opts ListContainersOptions) ([]APIContainers, error) {
|
||||||
path := "/containers/json?" + queryString(opts)
|
path := "/containers/json?" + queryString(opts)
|
||||||
body, _, err := c.do("GET", path, doOptions{})
|
body, _, err := c.do("GET", path, doOptions{})
|
||||||
@ -213,9 +213,21 @@ type Config struct {
|
|||||||
NetworkDisabled bool `json:"NetworkDisabled,omitempty" yaml:"NetworkDisabled,omitempty"`
|
NetworkDisabled bool `json:"NetworkDisabled,omitempty" yaml:"NetworkDisabled,omitempty"`
|
||||||
SecurityOpts []string `json:"SecurityOpts,omitempty" yaml:"SecurityOpts,omitempty"`
|
SecurityOpts []string `json:"SecurityOpts,omitempty" yaml:"SecurityOpts,omitempty"`
|
||||||
OnBuild []string `json:"OnBuild,omitempty" yaml:"OnBuild,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"`
|
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.
|
// LogConfig defines the log driver type and the configuration for it.
|
||||||
type LogConfig struct {
|
type LogConfig struct {
|
||||||
Type string `json:"Type,omitempty" yaml:"Type,omitempty"`
|
Type string `json:"Type,omitempty" yaml:"Type,omitempty"`
|
||||||
@ -279,7 +291,7 @@ type Container struct {
|
|||||||
|
|
||||||
// RenameContainerOptions specify parameters to the RenameContainer function.
|
// 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 {
|
type RenameContainerOptions struct {
|
||||||
// ID of container to rename
|
// ID of container to rename
|
||||||
ID string `qs:"-"`
|
ID string `qs:"-"`
|
||||||
@ -290,7 +302,7 @@ type RenameContainerOptions struct {
|
|||||||
|
|
||||||
// RenameContainer updates and existing containers name
|
// 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 {
|
func (c *Client) RenameContainer(opts RenameContainerOptions) error {
|
||||||
_, _, err := c.do("POST", fmt.Sprintf("/containers/"+opts.ID+"/rename?%s", queryString(opts)), doOptions{})
|
_, _, err := c.do("POST", fmt.Sprintf("/containers/"+opts.ID+"/rename?%s", queryString(opts)), doOptions{})
|
||||||
return err
|
return err
|
||||||
@ -298,7 +310,7 @@ func (c *Client) RenameContainer(opts RenameContainerOptions) error {
|
|||||||
|
|
||||||
// InspectContainer returns information about a container by its ID.
|
// 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) {
|
func (c *Client) InspectContainer(id string) (*Container, error) {
|
||||||
path := "/containers/" + id + "/json"
|
path := "/containers/" + id + "/json"
|
||||||
body, status, err := c.do("GET", path, doOptions{})
|
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.
|
// 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) {
|
func (c *Client) ContainerChanges(id string) ([]Change, error) {
|
||||||
path := "/containers/" + id + "/changes"
|
path := "/containers/" + id + "/changes"
|
||||||
body, status, err := c.do("GET", path, doOptions{})
|
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.
|
// 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 {
|
type CreateContainerOptions struct {
|
||||||
Name string
|
Name string
|
||||||
Config *Config `qs:"-"`
|
Config *Config `qs:"-"`
|
||||||
@ -348,7 +360,7 @@ type CreateContainerOptions struct {
|
|||||||
// CreateContainer creates a new container, returning the container instance,
|
// CreateContainer creates a new container, returning the container instance,
|
||||||
// or an error in case of failure.
|
// 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) {
|
func (c *Client) CreateContainer(opts CreateContainerOptions) (*Container, error) {
|
||||||
path := "/containers/create?" + queryString(opts)
|
path := "/containers/create?" + queryString(opts)
|
||||||
body, status, err := c.do(
|
body, status, err := c.do(
|
||||||
@ -434,41 +446,46 @@ type Device struct {
|
|||||||
// HostConfig contains the container options related to starting a container on
|
// HostConfig contains the container options related to starting a container on
|
||||||
// a given host
|
// a given host
|
||||||
type HostConfig struct {
|
type HostConfig struct {
|
||||||
Binds []string `json:"Binds,omitempty" yaml:"Binds,omitempty"`
|
Binds []string `json:"Binds,omitempty" yaml:"Binds,omitempty"`
|
||||||
CapAdd []string `json:"CapAdd,omitempty" yaml:"CapAdd,omitempty"`
|
CapAdd []string `json:"CapAdd,omitempty" yaml:"CapAdd,omitempty"`
|
||||||
CapDrop []string `json:"CapDrop,omitempty" yaml:"CapDrop,omitempty"`
|
CapDrop []string `json:"CapDrop,omitempty" yaml:"CapDrop,omitempty"`
|
||||||
ContainerIDFile string `json:"ContainerIDFile,omitempty" yaml:"ContainerIDFile,omitempty"`
|
ContainerIDFile string `json:"ContainerIDFile,omitempty" yaml:"ContainerIDFile,omitempty"`
|
||||||
LxcConf []KeyValuePair `json:"LxcConf,omitempty" yaml:"LxcConf,omitempty"`
|
LxcConf []KeyValuePair `json:"LxcConf,omitempty" yaml:"LxcConf,omitempty"`
|
||||||
Privileged bool `json:"Privileged,omitempty" yaml:"Privileged,omitempty"`
|
Privileged bool `json:"Privileged,omitempty" yaml:"Privileged,omitempty"`
|
||||||
PortBindings map[Port][]PortBinding `json:"PortBindings,omitempty" yaml:"PortBindings,omitempty"`
|
PortBindings map[Port][]PortBinding `json:"PortBindings,omitempty" yaml:"PortBindings,omitempty"`
|
||||||
Links []string `json:"Links,omitempty" yaml:"Links,omitempty"`
|
Links []string `json:"Links,omitempty" yaml:"Links,omitempty"`
|
||||||
PublishAllPorts bool `json:"PublishAllPorts,omitempty" yaml:"PublishAllPorts,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
|
DNS []string `json:"Dns,omitempty" yaml:"Dns,omitempty"` // For Docker API v1.10 and above only
|
||||||
DNSSearch []string `json:"DnsSearch,omitempty" yaml:"DnsSearch,omitempty"`
|
DNSSearch []string `json:"DnsSearch,omitempty" yaml:"DnsSearch,omitempty"`
|
||||||
ExtraHosts []string `json:"ExtraHosts,omitempty" yaml:"ExtraHosts,omitempty"`
|
ExtraHosts []string `json:"ExtraHosts,omitempty" yaml:"ExtraHosts,omitempty"`
|
||||||
VolumesFrom []string `json:"VolumesFrom,omitempty" yaml:"VolumesFrom,omitempty"`
|
VolumesFrom []string `json:"VolumesFrom,omitempty" yaml:"VolumesFrom,omitempty"`
|
||||||
NetworkMode string `json:"NetworkMode,omitempty" yaml:"NetworkMode,omitempty"`
|
NetworkMode string `json:"NetworkMode,omitempty" yaml:"NetworkMode,omitempty"`
|
||||||
IpcMode string `json:"IpcMode,omitempty" yaml:"IpcMode,omitempty"`
|
IpcMode string `json:"IpcMode,omitempty" yaml:"IpcMode,omitempty"`
|
||||||
PidMode string `json:"PidMode,omitempty" yaml:"PidMode,omitempty"`
|
PidMode string `json:"PidMode,omitempty" yaml:"PidMode,omitempty"`
|
||||||
UTSMode string `json:"UTSMode,omitempty" yaml:"UTSMode,omitempty"`
|
UTSMode string `json:"UTSMode,omitempty" yaml:"UTSMode,omitempty"`
|
||||||
RestartPolicy RestartPolicy `json:"RestartPolicy,omitempty" yaml:"RestartPolicy,omitempty"`
|
RestartPolicy RestartPolicy `json:"RestartPolicy,omitempty" yaml:"RestartPolicy,omitempty"`
|
||||||
Devices []Device `json:"Devices,omitempty" yaml:"Devices,omitempty"`
|
Devices []Device `json:"Devices,omitempty" yaml:"Devices,omitempty"`
|
||||||
LogConfig LogConfig `json:"LogConfig,omitempty" yaml:"LogConfig,omitempty"`
|
LogConfig LogConfig `json:"LogConfig,omitempty" yaml:"LogConfig,omitempty"`
|
||||||
ReadonlyRootfs bool `json:"ReadonlyRootfs,omitempty" yaml:"ReadonlyRootfs,omitempty"`
|
ReadonlyRootfs bool `json:"ReadonlyRootfs,omitempty" yaml:"ReadonlyRootfs,omitempty"`
|
||||||
SecurityOpt []string `json:"SecurityOpt,omitempty" yaml:"SecurityOpt,omitempty"`
|
SecurityOpt []string `json:"SecurityOpt,omitempty" yaml:"SecurityOpt,omitempty"`
|
||||||
CgroupParent string `json:"CgroupParent,omitempty" yaml:"CgroupParent,omitempty"`
|
CgroupParent string `json:"CgroupParent,omitempty" yaml:"CgroupParent,omitempty"`
|
||||||
Memory int64 `json:"Memory,omitempty" yaml:"Memory,omitempty"`
|
Memory int64 `json:"Memory,omitempty" yaml:"Memory,omitempty"`
|
||||||
MemorySwap int64 `json:"MemorySwap,omitempty" yaml:"MemorySwap,omitempty"`
|
MemorySwap int64 `json:"MemorySwap,omitempty" yaml:"MemorySwap,omitempty"`
|
||||||
CPUShares int64 `json:"CpuShares,omitempty" yaml:"CpuShares,omitempty"`
|
MemorySwappiness int64 `json:"MemorySwappiness,omitempty" yaml:"MemorySwappiness,omitempty"`
|
||||||
CPUSet string `json:"Cpuset,omitempty" yaml:"Cpuset,omitempty"`
|
OOMKillDisable bool `json:"OomKillDisable,omitempty" yaml:"OomKillDisable"`
|
||||||
CPUQuota int64 `json:"CpuQuota,omitempty" yaml:"CpuQuota,omitempty"`
|
CPUShares int64 `json:"CpuShares,omitempty" yaml:"CpuShares,omitempty"`
|
||||||
CPUPeriod int64 `json:"CpuPeriod,omitempty" yaml:"CpuPeriod,omitempty"`
|
CPUSet string `json:"Cpuset,omitempty" yaml:"Cpuset,omitempty"`
|
||||||
Ulimits []ULimit `json:"Ulimits,omitempty" yaml:"Ulimits,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.
|
// 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 {
|
func (c *Client) StartContainer(id string, hostConfig *HostConfig) error {
|
||||||
path := "/containers/" + id + "/start"
|
path := "/containers/" + id + "/start"
|
||||||
_, status, err := c.do("POST", path, doOptions{data: hostConfig, forceJSON: true})
|
_, 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
|
// StopContainer stops a container, killing it after the given timeout (in
|
||||||
// seconds).
|
// 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 {
|
func (c *Client) StopContainer(id string, timeout uint) error {
|
||||||
path := fmt.Sprintf("/containers/%s/stop?t=%d", id, timeout)
|
path := fmt.Sprintf("/containers/%s/stop?t=%d", id, timeout)
|
||||||
_, status, err := c.do("POST", path, doOptions{})
|
_, 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
|
// RestartContainer stops a container, killing it after the given timeout (in
|
||||||
// seconds), during the stop process.
|
// 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 {
|
func (c *Client) RestartContainer(id string, timeout uint) error {
|
||||||
path := fmt.Sprintf("/containers/%s/restart?t=%d", id, timeout)
|
path := fmt.Sprintf("/containers/%s/restart?t=%d", id, timeout)
|
||||||
_, status, err := c.do("POST", path, doOptions{})
|
_, 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.
|
// 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 {
|
func (c *Client) PauseContainer(id string) error {
|
||||||
path := fmt.Sprintf("/containers/%s/pause", id)
|
path := fmt.Sprintf("/containers/%s/pause", id)
|
||||||
_, status, err := c.do("POST", path, doOptions{})
|
_, status, err := c.do("POST", path, doOptions{})
|
||||||
@ -536,7 +553,7 @@ func (c *Client) PauseContainer(id string) error {
|
|||||||
|
|
||||||
// UnpauseContainer unpauses the given container.
|
// 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 {
|
func (c *Client) UnpauseContainer(id string) error {
|
||||||
path := fmt.Sprintf("/containers/%s/unpause", id)
|
path := fmt.Sprintf("/containers/%s/unpause", id)
|
||||||
_, status, err := c.do("POST", path, doOptions{})
|
_, 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
|
// TopResult represents the list of processes running in a container, as
|
||||||
// returned by /containers/<id>/top.
|
// returned by /containers/<id>/top.
|
||||||
//
|
//
|
||||||
// See http://goo.gl/qu4gse for more details.
|
// See https://goo.gl/Rb46aY for more details.
|
||||||
type TopResult struct {
|
type TopResult struct {
|
||||||
Titles []string
|
Titles []string
|
||||||
Processes [][]string
|
Processes [][]string
|
||||||
@ -560,7 +577,7 @@ type TopResult struct {
|
|||||||
|
|
||||||
// TopContainer returns processes running inside a container
|
// 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) {
|
func (c *Client) TopContainer(id string, psArgs string) (TopResult, error) {
|
||||||
var args string
|
var args string
|
||||||
var result TopResult
|
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.
|
// 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 {
|
type Stats struct {
|
||||||
Read time.Time `json:"read,omitempty" yaml:"read,omitempty"`
|
Read time.Time `json:"read,omitempty" yaml:"read,omitempty"`
|
||||||
Network struct {
|
Network struct {
|
||||||
@ -674,7 +691,7 @@ type BlkioStatsEntry struct {
|
|||||||
|
|
||||||
// StatsOptions specify parameters to the Stats function.
|
// 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 {
|
type StatsOptions struct {
|
||||||
ID string
|
ID string
|
||||||
Stats chan<- *Stats
|
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
|
// 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
|
// 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
|
// 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) {
|
func (c *Client) Stats(opts StatsOptions) (retErr error) {
|
||||||
errC := make(chan error, 1)
|
errC := make(chan error, 1)
|
||||||
readCloser, writeCloser := io.Pipe()
|
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
|
// KillContainerOptions represents the set of options that can be used in a
|
||||||
// call to KillContainer.
|
// call to KillContainer.
|
||||||
//
|
//
|
||||||
// See http://goo.gl/TFkECx for more details.
|
// See https://goo.gl/hkS9i8 for more details.
|
||||||
type KillContainerOptions struct {
|
type KillContainerOptions struct {
|
||||||
// The ID of the container.
|
// The ID of the container.
|
||||||
ID string `qs:"-"`
|
ID string `qs:"-"`
|
||||||
@ -773,9 +791,10 @@ type KillContainerOptions struct {
|
|||||||
Signal Signal
|
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 {
|
func (c *Client) KillContainer(opts KillContainerOptions) error {
|
||||||
path := "/containers/" + opts.ID + "/kill" + "?" + queryString(opts)
|
path := "/containers/" + opts.ID + "/kill" + "?" + queryString(opts)
|
||||||
_, status, err := c.do("POST", path, doOptions{})
|
_, 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.
|
// 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 {
|
type RemoveContainerOptions struct {
|
||||||
// The ID of the container.
|
// The ID of the container.
|
||||||
ID string `qs:"-"`
|
ID string `qs:"-"`
|
||||||
@ -806,7 +825,7 @@ type RemoveContainerOptions struct {
|
|||||||
|
|
||||||
// RemoveContainer removes a container, returning an error in case of failure.
|
// 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 {
|
func (c *Client) RemoveContainer(opts RemoveContainerOptions) error {
|
||||||
path := "/containers/" + opts.ID + "?" + queryString(opts)
|
path := "/containers/" + opts.ID + "?" + queryString(opts)
|
||||||
_, status, err := c.do("DELETE", path, doOptions{})
|
_, 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
|
// CopyFromContainerOptions is the set of options that can be used when copying
|
||||||
// files or folders from a container.
|
// 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 {
|
type CopyFromContainerOptions struct {
|
||||||
OutputStream io.Writer `json:"-"`
|
OutputStream io.Writer `json:"-"`
|
||||||
Container string `json:"-"`
|
Container string `json:"-"`
|
||||||
@ -832,7 +851,7 @@ type CopyFromContainerOptions struct {
|
|||||||
// CopyFromContainer copy files or folders from a container, using a given
|
// CopyFromContainer copy files or folders from a container, using a given
|
||||||
// resource.
|
// resource.
|
||||||
//
|
//
|
||||||
// See http://goo.gl/rINMlw for more details.
|
// See https://goo.gl/4L7b07 for more details.
|
||||||
func (c *Client) CopyFromContainer(opts CopyFromContainerOptions) error {
|
func (c *Client) CopyFromContainer(opts CopyFromContainerOptions) error {
|
||||||
if opts.Container == "" {
|
if opts.Container == "" {
|
||||||
return &NoSuchContainer{ID: 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
|
// WaitContainer blocks until the given container stops, return the exit code
|
||||||
// of the container status.
|
// 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) {
|
func (c *Client) WaitContainer(id string) (int, error) {
|
||||||
body, status, err := c.do("POST", "/containers/"+id+"/wait", doOptions{})
|
body, status, err := c.do("POST", "/containers/"+id+"/wait", doOptions{})
|
||||||
if status == http.StatusNotFound {
|
if status == http.StatusNotFound {
|
||||||
@ -871,7 +890,7 @@ func (c *Client) WaitContainer(id string) (int, error) {
|
|||||||
|
|
||||||
// CommitContainerOptions aggregates parameters to the CommitContainer method.
|
// 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 {
|
type CommitContainerOptions struct {
|
||||||
Container string
|
Container string
|
||||||
Repository string `qs:"repo"`
|
Repository string `qs:"repo"`
|
||||||
@ -883,7 +902,7 @@ type CommitContainerOptions struct {
|
|||||||
|
|
||||||
// CommitContainer creates a new image from a container's changes.
|
// 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) {
|
func (c *Client) CommitContainer(opts CommitContainerOptions) (*Image, error) {
|
||||||
path := "/commit?" + queryString(opts)
|
path := "/commit?" + queryString(opts)
|
||||||
body, status, err := c.do("POST", path, doOptions{data: opts.Run})
|
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
|
// AttachToContainerOptions is the set of options that can be used when
|
||||||
// attaching to a container.
|
// attaching to a container.
|
||||||
//
|
//
|
||||||
// See http://goo.gl/RRAhws for more details.
|
// See https://goo.gl/NKpkFk for more details.
|
||||||
type AttachToContainerOptions struct {
|
type AttachToContainerOptions struct {
|
||||||
Container string `qs:"-"`
|
Container string `qs:"-"`
|
||||||
InputStream io.Reader `qs:"-"`
|
InputStream io.Reader `qs:"-"`
|
||||||
@ -939,7 +958,7 @@ type AttachToContainerOptions struct {
|
|||||||
|
|
||||||
// AttachToContainer attaches to a container, using the given options.
|
// 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 {
|
func (c *Client) AttachToContainer(opts AttachToContainerOptions) error {
|
||||||
if opts.Container == "" {
|
if opts.Container == "" {
|
||||||
return &NoSuchContainer{ID: 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
|
// LogsOptions represents the set of options used when getting logs from a
|
||||||
// container.
|
// container.
|
||||||
//
|
//
|
||||||
// See http://goo.gl/rLhKSU for more details.
|
// See https://goo.gl/yl8PGm for more details.
|
||||||
type LogsOptions struct {
|
type LogsOptions struct {
|
||||||
Container string `qs:"-"`
|
Container string `qs:"-"`
|
||||||
OutputStream io.Writer `qs:"-"`
|
OutputStream io.Writer `qs:"-"`
|
||||||
@ -975,7 +994,7 @@ type LogsOptions struct {
|
|||||||
|
|
||||||
// Logs gets stdout and stderr logs from the specified container.
|
// 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 {
|
func (c *Client) Logs(opts LogsOptions) error {
|
||||||
if opts.Container == "" {
|
if opts.Container == "" {
|
||||||
return &NoSuchContainer{ID: 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.
|
// 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 {
|
func (c *Client) ResizeContainerTTY(id string, height, width int) error {
|
||||||
params := make(url.Values)
|
params := make(url.Values)
|
||||||
params.Set("h", strconv.Itoa(height))
|
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
|
// ExportContainerOptions is the set of parameters to the ExportContainer
|
||||||
// method.
|
// method.
|
||||||
//
|
//
|
||||||
// See http://goo.gl/hnzE62 for more details.
|
// See https://goo.gl/dOkTyk for more details.
|
||||||
type ExportContainerOptions struct {
|
type ExportContainerOptions struct {
|
||||||
ID string
|
ID string
|
||||||
OutputStream io.Writer
|
OutputStream io.Writer
|
||||||
@ -1012,7 +1033,7 @@ type ExportContainerOptions struct {
|
|||||||
// ExportContainer export the contents of container id as tar archive
|
// ExportContainer export the contents of container id as tar archive
|
||||||
// and prints the exported contents to stdout.
|
// 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 {
|
func (c *Client) ExportContainer(opts ExportContainerOptions) error {
|
||||||
if opts.ID == "" {
|
if opts.ID == "" {
|
||||||
return &NoSuchContainer{ID: 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,
|
Stream: true,
|
||||||
RawTerminal: false,
|
RawTerminal: false,
|
||||||
}
|
}
|
||||||
err := client.AttachToContainer(opts)
|
client.AttachToContainer(opts)
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
expected := map[string][]string{
|
expected := map[string][]string{
|
||||||
"stdin": {"1"},
|
"stdin": {"1"},
|
||||||
"stdout": {"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
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// 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
|
package docker
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -15,9 +13,15 @@ import (
|
|||||||
"strconv"
|
"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.
|
// 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 {
|
type CreateExecOptions struct {
|
||||||
AttachStdin bool `json:"AttachStdin,omitempty" yaml:"AttachStdin,omitempty"`
|
AttachStdin bool `json:"AttachStdin,omitempty" yaml:"AttachStdin,omitempty"`
|
||||||
AttachStdout bool `json:"AttachStdout,omitempty" yaml:"AttachStdout,omitempty"`
|
AttachStdout bool `json:"AttachStdout,omitempty" yaml:"AttachStdout,omitempty"`
|
||||||
@ -28,9 +32,31 @@ type CreateExecOptions struct {
|
|||||||
User string `json:"User,omitempty" yaml:"User,omitempty"`
|
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.
|
// 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 {
|
type StartExecOptions struct {
|
||||||
Detach bool `json:"Detach,omitempty" yaml:"Detach,omitempty"`
|
Detach bool `json:"Detach,omitempty" yaml:"Detach,omitempty"`
|
||||||
|
|
||||||
@ -51,67 +77,11 @@ type StartExecOptions struct {
|
|||||||
Success chan struct{} `json:"-"`
|
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
|
// 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
|
// true, it returns after starting the exec command. Otherwise, it sets up an
|
||||||
// interactive session with the exec command.
|
// 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 {
|
func (c *Client) StartExec(id string, opts StartExecOptions) error {
|
||||||
if id == "" {
|
if id == "" {
|
||||||
return &NoSuchExec{ID: 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
|
// is valid only if Tty was specified as part of creating and starting the exec
|
||||||
// command.
|
// 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 {
|
func (c *Client) ResizeExecTTY(id string, height, width int) error {
|
||||||
params := make(url.Values)
|
params := make(url.Values)
|
||||||
params.Set("h", strconv.Itoa(height))
|
params.Set("h", strconv.Itoa(height))
|
||||||
@ -155,9 +125,35 @@ func (c *Client) ResizeExecTTY(id string, height, width int) error {
|
|||||||
return err
|
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.
|
// 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) {
|
func (c *Client) InspectExec(id string) (*ExecInspect, error) {
|
||||||
path := fmt.Sprintf("/exec/%s/json", id)
|
path := fmt.Sprintf("/exec/%s/json", id)
|
||||||
body, status, err := c.do("GET", path, doOptions{})
|
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"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
@ -46,16 +45,6 @@ type Image struct {
|
|||||||
VirtualSize int64 `json:"VirtualSize,omitempty" yaml:"VirtualSize,omitempty"`
|
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
|
// 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)
|
// earlier versions of the Docker API (pre-012 to be specific)
|
||||||
type ImagePre012 struct {
|
type ImagePre012 struct {
|
||||||
@ -72,15 +61,6 @@ type ImagePre012 struct {
|
|||||||
Size int64 `json:"size,omitempty"`
|
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 (
|
var (
|
||||||
// ErrNoSuchImage is the error returned when the image does not exist.
|
// ErrNoSuchImage is the error returned when the image does not exist.
|
||||||
ErrNoSuchImage = errors.New("no such image")
|
ErrNoSuchImage = errors.New("no such image")
|
||||||
@ -102,9 +82,18 @@ var (
|
|||||||
ErrMustSpecifyNames = errors.New("must specify at least one name to export")
|
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.
|
// 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) {
|
func (c *Client) ListImages(opts ListImagesOptions) ([]APIImages, error) {
|
||||||
path := "/images/json?" + queryString(opts)
|
path := "/images/json?" + queryString(opts)
|
||||||
body, _, err := c.do("GET", path, doOptions{})
|
body, _, err := c.do("GET", path, doOptions{})
|
||||||
@ -119,9 +108,19 @@ func (c *Client) ListImages(opts ListImagesOptions) ([]APIImages, error) {
|
|||||||
return images, nil
|
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.
|
// 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) {
|
func (c *Client) ImageHistory(name string) ([]ImageHistory, error) {
|
||||||
body, status, err := c.do("GET", "/images/"+name+"/history", doOptions{})
|
body, status, err := c.do("GET", "/images/"+name+"/history", doOptions{})
|
||||||
if status == http.StatusNotFound {
|
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.
|
// 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 {
|
func (c *Client) RemoveImage(name string) error {
|
||||||
_, status, err := c.do("DELETE", "/images/"+name, doOptions{})
|
_, status, err := c.do("DELETE", "/images/"+name, doOptions{})
|
||||||
if status == http.StatusNotFound {
|
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
|
// RemoveImageOptions present the set of options available for removing an image
|
||||||
// from a registry.
|
// from a registry.
|
||||||
//
|
//
|
||||||
// See http://goo.gl/6V48bF for more details.
|
// See https://goo.gl/V3ZWnK for more details.
|
||||||
type RemoveImageOptions struct {
|
type RemoveImageOptions struct {
|
||||||
Force bool `qs:"force"`
|
Force bool `qs:"force"`
|
||||||
NoPrune bool `qs:"noprune"`
|
NoPrune bool `qs:"noprune"`
|
||||||
@ -161,7 +160,7 @@ type RemoveImageOptions struct {
|
|||||||
// RemoveImageExtended removes an image by its name or ID.
|
// RemoveImageExtended removes an image by its name or ID.
|
||||||
// Extra params can be passed, see RemoveImageOptions
|
// 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 {
|
func (c *Client) RemoveImageExtended(name string, opts RemoveImageOptions) error {
|
||||||
uri := fmt.Sprintf("/images/%s?%s", name, queryString(&opts))
|
uri := fmt.Sprintf("/images/%s?%s", name, queryString(&opts))
|
||||||
_, status, err := c.do("DELETE", uri, doOptions{})
|
_, 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.
|
// 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) {
|
func (c *Client) InspectImage(name string) (*Image, error) {
|
||||||
body, status, err := c.do("GET", "/images/"+name+"/json", doOptions{})
|
body, status, err := c.do("GET", "/images/"+name+"/json", doOptions{})
|
||||||
if status == http.StatusNotFound {
|
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.
|
// 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 {
|
type PushImageOptions struct {
|
||||||
// Name of the image
|
// Name of the image
|
||||||
Name string
|
Name string
|
||||||
@ -236,7 +235,7 @@ type PushImageOptions struct {
|
|||||||
// An empty instance of AuthConfiguration may be used for unauthenticated
|
// An empty instance of AuthConfiguration may be used for unauthenticated
|
||||||
// pushes.
|
// 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 {
|
func (c *Client) PushImage(opts PushImageOptions, auth AuthConfiguration) error {
|
||||||
if opts.Name == "" {
|
if opts.Name == "" {
|
||||||
return ErrNoSuchImage
|
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
|
// PullImageOptions present the set of options available for pulling an image
|
||||||
// from a registry.
|
// from a registry.
|
||||||
//
|
//
|
||||||
// See http://goo.gl/ACyYNS for more details.
|
// See https://goo.gl/iJkZjD for more details.
|
||||||
type PullImageOptions struct {
|
type PullImageOptions struct {
|
||||||
Repository string `qs:"fromImage"`
|
Repository string `qs:"fromImage"`
|
||||||
Registry string
|
Registry string
|
||||||
@ -268,9 +267,10 @@ type PullImageOptions struct {
|
|||||||
RawJSONStream bool `qs:"-"`
|
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 {
|
func (c *Client) PullImage(opts PullImageOptions, auth AuthConfiguration) error {
|
||||||
if opts.Repository == "" {
|
if opts.Repository == "" {
|
||||||
return ErrNoSuchImage
|
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
|
// 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 {
|
type LoadImageOptions struct {
|
||||||
InputStream io.Reader
|
InputStream io.Reader
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadImage imports a tarball docker image
|
// 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 {
|
func (c *Client) LoadImage(opts LoadImageOptions) error {
|
||||||
return c.stream("POST", "/images/load", streamOptions{
|
return c.stream("POST", "/images/load", streamOptions{
|
||||||
setRawTerminal: true,
|
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 {
|
type ExportImageOptions struct {
|
||||||
Name string
|
Name string
|
||||||
OutputStream io.Writer
|
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 {
|
func (c *Client) ExportImage(opts ExportImageOptions) error {
|
||||||
return c.stream("GET", fmt.Sprintf("/images/%s/get", opts.Name), streamOptions{
|
return c.stream("GET", fmt.Sprintf("/images/%s/get", opts.Name), streamOptions{
|
||||||
setRawTerminal: true,
|
setRawTerminal: true,
|
||||||
@ -331,7 +331,7 @@ func (c *Client) ExportImage(opts ExportImageOptions) error {
|
|||||||
|
|
||||||
// ExportImagesOptions represent the options for ExportImages Docker API call
|
// 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 {
|
type ExportImagesOptions struct {
|
||||||
Names []string
|
Names []string
|
||||||
OutputStream io.Writer `qs:"-"`
|
OutputStream io.Writer `qs:"-"`
|
||||||
@ -339,7 +339,7 @@ type ExportImagesOptions struct {
|
|||||||
|
|
||||||
// ExportImages exports one or more images (as a tar file) into the stream
|
// 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 {
|
func (c *Client) ExportImages(opts ExportImagesOptions) error {
|
||||||
if opts.Names == nil || len(opts.Names) == 0 {
|
if opts.Names == nil || len(opts.Names) == 0 {
|
||||||
return ErrMustSpecifyNames
|
return ErrMustSpecifyNames
|
||||||
@ -353,7 +353,7 @@ func (c *Client) ExportImages(opts ExportImagesOptions) error {
|
|||||||
// ImportImageOptions present the set of informations available for importing
|
// ImportImageOptions present the set of informations available for importing
|
||||||
// an image from a source file or the stdin.
|
// 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 {
|
type ImportImageOptions struct {
|
||||||
Repository string `qs:"repo"`
|
Repository string `qs:"repo"`
|
||||||
Source string `qs:"fromSrc"`
|
Source string `qs:"fromSrc"`
|
||||||
@ -366,7 +366,7 @@ type ImportImageOptions struct {
|
|||||||
|
|
||||||
// ImportImage imports an image from a url, a file or stdin
|
// 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 {
|
func (c *Client) ImportImage(opts ImportImageOptions) error {
|
||||||
if opts.Repository == "" {
|
if opts.Repository == "" {
|
||||||
return ErrNoSuchImage
|
return ErrNoSuchImage
|
||||||
@ -379,8 +379,7 @@ func (c *Client) ImportImage(opts ImportImageOptions) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
b, err := ioutil.ReadAll(f)
|
opts.InputStream = f
|
||||||
opts.InputStream = bytes.NewBuffer(b)
|
|
||||||
opts.Source = "-"
|
opts.Source = "-"
|
||||||
}
|
}
|
||||||
return c.createImage(queryString(&opts), nil, opts.InputStream, opts.OutputStream, opts.RawJSONStream)
|
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
|
// BuildImage builds an image from a tarball's url or a Dockerfile in the input
|
||||||
// stream.
|
// stream.
|
||||||
//
|
//
|
||||||
// See http://goo.gl/7nuGXa for more details.
|
// See https://goo.gl/xySxCe for more details.
|
||||||
func (c *Client) BuildImage(opts BuildImageOptions) error {
|
func (c *Client) BuildImage(opts BuildImageOptions) error {
|
||||||
if opts.OutputStream == nil {
|
if opts.OutputStream == nil {
|
||||||
return ErrMissingOutputStream
|
return ErrMissingOutputStream
|
||||||
}
|
}
|
||||||
headers, err := headersWithAuth(opts.Auth, opts.AuthConfigs)
|
headers, err := headersWithAuth(opts.Auth, c.versionedAuthConfigs(opts.AuthConfigs))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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.
|
// 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 {
|
type TagImageOptions struct {
|
||||||
Repo string
|
Repo string
|
||||||
Tag string
|
Tag string
|
||||||
@ -463,7 +472,7 @@ type TagImageOptions struct {
|
|||||||
|
|
||||||
// TagImage adds a tag to the image identified by the given name.
|
// 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 {
|
func (c *Client) TagImage(name string, opts TagImageOptions) error {
|
||||||
if name == "" {
|
if name == "" {
|
||||||
return ErrNoSuchImage
|
return ErrNoSuchImage
|
||||||
@ -497,7 +506,7 @@ func headersWithAuth(auths ...interface{}) (map[string]string, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
headers["X-Registry-Auth"] = base64.URLEncoding.EncodeToString(buf.Bytes())
|
headers["X-Registry-Auth"] = base64.URLEncoding.EncodeToString(buf.Bytes())
|
||||||
case AuthConfigurations:
|
case AuthConfigurations, AuthConfigurations119:
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
if err := json.NewEncoder(&buf).Encode(auth); err != nil {
|
if err := json.NewEncoder(&buf).Encode(auth); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -509,9 +518,9 @@ func headersWithAuth(auths ...interface{}) (map[string]string, error) {
|
|||||||
return headers, nil
|
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 {
|
type APIImageSearch struct {
|
||||||
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||||
IsOfficial bool `json:"is_official,omitempty" yaml:"is_official,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.
|
// 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) {
|
func (c *Client) SearchImages(term string) ([]APIImageSearch, error) {
|
||||||
body, _, err := c.do("GET", "/images/search?term="+term, doOptions{})
|
body, _, err := c.do("GET", "/images/search?term="+term, doOptions{})
|
||||||
if err != nil {
|
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 {
|
func newTestClient(rt *FakeRoundTripper) Client {
|
||||||
endpoint := "http://localhost:4243"
|
endpoint := "http://localhost:4243"
|
||||||
u, _ := parseEndpoint("http://localhost:4243", false)
|
u, _ := parseEndpoint("http://localhost:4243", false)
|
||||||
|
testAPIVersion, _ := NewAPIVersion("1.17")
|
||||||
client := Client{
|
client := Client{
|
||||||
HTTPClient: &http.Client{Transport: rt},
|
HTTPClient: &http.Client{Transport: rt},
|
||||||
endpoint: endpoint,
|
endpoint: endpoint,
|
||||||
endpointURL: u,
|
endpointURL: u,
|
||||||
SkipServerVersionCheck: true,
|
SkipServerVersionCheck: true,
|
||||||
|
serverAPIVersion: testAPIVersion,
|
||||||
}
|
}
|
||||||
return client
|
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.
|
// 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) {
|
func (c *Client) Version() (*Env, error) {
|
||||||
body, _, err := c.do("GET", "/version", doOptions{})
|
body, _, err := c.do("GET", "/version", doOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -26,7 +26,7 @@ func (c *Client) Version() (*Env, error) {
|
|||||||
|
|
||||||
// Info returns system-wide information about the Docker server.
|
// 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) {
|
func (c *Client) Info() (*Env, error) {
|
||||||
body, _, err := c.do("GET", "/info", doOptions{})
|
body, _, err := c.do("GET", "/info", doOptions{})
|
||||||
if err != nil {
|
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"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
mathrand "math/rand"
|
mathrand "math/rand"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -532,7 +533,7 @@ func (s *DockerServer) startContainer(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
container.HostConfig = &hostConfig
|
container.HostConfig = &hostConfig
|
||||||
if container.State.Running {
|
if container.State.Running {
|
||||||
http.Error(w, "Container already running", http.StatusBadRequest)
|
http.Error(w, "", http.StatusNotModified)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
container.State.Running = true
|
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)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
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)
|
outStream := stdcopy.NewStdWriter(conn, stdcopy.Stdout)
|
||||||
if container.State.Running {
|
if container.State.Running {
|
||||||
fmt.Fprintf(outStream, "Container %q is running\n", container.ID)
|
fmt.Fprintf(outStream, "Container is running\n")
|
||||||
} else {
|
} 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, "What happened?")
|
||||||
fmt.Fprintln(outStream, "Something 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()
|
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
|
package testing
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -624,8 +626,8 @@ func TestStartContainerAlreadyRunning(t *testing.T) {
|
|||||||
path := fmt.Sprintf("/containers/%s/start", server.containers[0].ID)
|
path := fmt.Sprintf("/containers/%s/start", server.containers[0].ID)
|
||||||
request, _ := http.NewRequest("POST", path, bytes.NewBuffer([]byte("null")))
|
request, _ := http.NewRequest("POST", path, bytes.NewBuffer([]byte("null")))
|
||||||
server.ServeHTTP(recorder, request)
|
server.ServeHTTP(recorder, request)
|
||||||
if recorder.Code != http.StatusBadRequest {
|
if recorder.Code != http.StatusNotModified {
|
||||||
t.Errorf("StartContainer: wrong status code. Want %d. Got %d.", http.StatusBadRequest, recorder.Code)
|
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) {
|
func TestAttachContainer(t *testing.T) {
|
||||||
server := DockerServer{}
|
server := DockerServer{}
|
||||||
addContainers(&server, 1)
|
addContainers(&server, 1)
|
||||||
server.containers[0].State.Running = true
|
server.containers[0].State.Running = true
|
||||||
server.buildMuxer()
|
server.buildMuxer()
|
||||||
recorder := httptest.NewRecorder()
|
recorder := &HijackableResponseRecorder{}
|
||||||
path := fmt.Sprintf("/containers/%s/attach?logs=1", server.containers[0].ID)
|
path := fmt.Sprintf("/containers/%s/attach?logs=1", server.containers[0].ID)
|
||||||
request, _ := http.NewRequest("POST", path, nil)
|
request, _ := http.NewRequest("POST", path, nil)
|
||||||
server.ServeHTTP(recorder, request)
|
server.ServeHTTP(recorder, request)
|
||||||
lines := []string{
|
lines := []string{
|
||||||
fmt.Sprintf("\x01\x00\x00\x00\x03\x00\x00\x00Container %q is running", server.containers[0].ID),
|
"\x01\x00\x00\x00\x00\x00\x00\x15Container is running",
|
||||||
"What happened?",
|
"\x01\x00\x00\x00\x00\x00\x00\x0fWhat happened?",
|
||||||
"Something happened",
|
"\x01\x00\x00\x00\x00\x00\x00\x13Something happened",
|
||||||
}
|
}
|
||||||
expected := strings.Join(lines, "\n") + "\n"
|
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)
|
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) {
|
func TestAttachContainerNotFound(t *testing.T) {
|
||||||
server := DockerServer{}
|
server := DockerServer{}
|
||||||
server.buildMuxer()
|
server.buildMuxer()
|
||||||
recorder := httptest.NewRecorder()
|
recorder := &HijackableResponseRecorder{}
|
||||||
path := "/containers/abc123/attach?logs=1"
|
path := "/containers/abc123/attach?logs=1"
|
||||||
request, _ := http.NewRequest("POST", path, nil)
|
request, _ := http.NewRequest("POST", path, nil)
|
||||||
server.ServeHTTP(recorder, request)
|
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) {
|
func TestRemoveContainer(t *testing.T) {
|
||||||
server := DockerServer{}
|
server := DockerServer{}
|
||||||
addContainers(&server, 1)
|
addContainers(&server, 1)
|
||||||
@ -1690,7 +1749,7 @@ func addNetworks(server *DockerServer, n int) {
|
|||||||
ID: fmt.Sprintf("%x", rand.Int()%10000),
|
ID: fmt.Sprintf("%x", rand.Int()%10000),
|
||||||
Type: "bridge",
|
Type: "bridge",
|
||||||
Endpoints: []*docker.Endpoint{
|
Endpoints: []*docker.Endpoint{
|
||||||
&docker.Endpoint{
|
{
|
||||||
Name: "blah",
|
Name: "blah",
|
||||||
ID: fmt.Sprintf("%x", rand.Int()%10000),
|
ID: fmt.Sprintf("%x", rand.Int()%10000),
|
||||||
Network: netid,
|
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)
|
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
|
## 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)
|
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) {
|
func preamble(out *bytes.Buffer) {
|
||||||
fmt.Fprintf(out, `#!/bin/bash
|
fmt.Fprintf(out, `#!/bin/bash
|
||||||
|
|
||||||
|
|
||||||
__debug()
|
__debug()
|
||||||
{
|
{
|
||||||
if [[ -n ${BASH_COMP_DEBUG_FILE} ]]; then
|
if [[ -n ${BASH_COMP_DEBUG_FILE} ]]; then
|
||||||
@ -27,6 +26,14 @@ __debug()
|
|||||||
fi
|
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()
|
__index_of_word()
|
||||||
{
|
{
|
||||||
local w word=$1
|
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, "__start_%s()\n", name)
|
||||||
fmt.Fprintf(out, `{
|
fmt.Fprintf(out, `{
|
||||||
local cur prev words cword
|
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 c=0
|
||||||
local flags=()
|
local flags=()
|
||||||
@ -212,7 +223,7 @@ func postscript(out *bytes.Buffer, name string) {
|
|||||||
func writeCommands(cmd *Command, out *bytes.Buffer) {
|
func writeCommands(cmd *Command, out *bytes.Buffer) {
|
||||||
fmt.Fprintf(out, " commands=()\n")
|
fmt.Fprintf(out, " commands=()\n")
|
||||||
for _, c := range cmd.Commands() {
|
for _, c := range cmd.Commands() {
|
||||||
if len(c.Deprecated) > 0 {
|
if len(c.Deprecated) > 0 || c == cmd.helpCommand {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
fmt.Fprintf(out, " commands+=(%q)\n", c.Name())
|
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")
|
fmt.Fprintf(out, " must_have_one_flag=()\n")
|
||||||
flags := cmd.NonInheritedFlags()
|
flags := cmd.NonInheritedFlags()
|
||||||
flags.VisitAll(func(flag *pflag.Flag) {
|
flags.VisitAll(func(flag *pflag.Flag) {
|
||||||
for key, _ := range flag.Annotations {
|
for key := range flag.Annotations {
|
||||||
switch key {
|
switch key {
|
||||||
case BashCompOneRequiredFlag:
|
case BashCompOneRequiredFlag:
|
||||||
format := " must_have_one_flag+=(\"--%s"
|
format := " must_have_one_flag+=(\"--%s"
|
||||||
@ -321,7 +332,7 @@ func writeRequiredNoun(cmd *Command, out *bytes.Buffer) {
|
|||||||
|
|
||||||
func gen(cmd *Command, out *bytes.Buffer) {
|
func gen(cmd *Command, out *bytes.Buffer) {
|
||||||
for _, c := range cmd.Commands() {
|
for _, c := range cmd.Commands() {
|
||||||
if len(c.Deprecated) > 0 {
|
if len(c.Deprecated) > 0 || c == cmd.helpCommand {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
gen(c, out)
|
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"
|
"text/template"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var templateFuncs template.FuncMap = template.FuncMap{
|
||||||
|
"trim": strings.TrimSpace,
|
||||||
|
"rpad": rpad,
|
||||||
|
"gt": Gt,
|
||||||
|
"eq": Eq,
|
||||||
|
}
|
||||||
|
|
||||||
var initializers []func()
|
var initializers []func()
|
||||||
|
|
||||||
// automatic prefix matching can be a dangerous thing to automatically enable in CLI tools.
|
// 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.
|
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().
|
//OnInitialize takes a series of func() arguments and appends them to a slice of func().
|
||||||
func OnInitialize(y ...func()) {
|
func OnInitialize(y ...func()) {
|
||||||
for _, x := range y {
|
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.
|
// tmpl executes the given template text on data, writing the result to w.
|
||||||
func tmpl(w io.Writer, text string, data interface{}) error {
|
func tmpl(w io.Writer, text string, data interface{}) error {
|
||||||
t := template.New("top")
|
t := template.New("top")
|
||||||
t.Funcs(template.FuncMap{
|
t.Funcs(templateFuncs)
|
||||||
"trim": strings.TrimSpace,
|
|
||||||
"rpad": rpad,
|
|
||||||
"gt": Gt,
|
|
||||||
"eq": Eq,
|
|
||||||
})
|
|
||||||
template.Must(t.Parse(text))
|
template.Must(t.Parse(text))
|
||||||
return t.Execute(w, data)
|
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"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
)
|
)
|
||||||
@ -971,3 +972,20 @@ func TestFlagOnPflagCommandLine(t *testing.T) {
|
|||||||
|
|
||||||
checkResultContains(t, r, flagName)
|
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
|
// All functions get the same args, the arguments after the command name
|
||||||
// PersistentPreRun: children of this command will inherit and execute
|
// PersistentPreRun: children of this command will inherit and execute
|
||||||
PersistentPreRun func(cmd *Command, args []string)
|
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: children of this command will not inherit.
|
||||||
PreRun func(cmd *Command, args []string)
|
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: Typically the actual work function. Most commands will only implement this
|
||||||
Run func(cmd *Command, args []string)
|
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: run after the Run command.
|
||||||
PostRun func(cmd *Command, args []string)
|
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: children of this command will inherit and execute after PostRun
|
||||||
PersistentPostRun func(cmd *Command, args []string)
|
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 is the list of commands supported by this program.
|
||||||
commands []*Command
|
commands []*Command
|
||||||
// Parent Command for this command
|
// Parent Command for this command
|
||||||
@ -92,7 +102,6 @@ type Command struct {
|
|||||||
helpTemplate string // Can be defined by Application
|
helpTemplate string // Can be defined by Application
|
||||||
helpFunc func(*Command, []string) // Help can be defined by application
|
helpFunc func(*Command, []string) // Help can be defined by application
|
||||||
helpCommand *Command // The help command
|
helpCommand *Command // The help command
|
||||||
helpFlagVal bool
|
|
||||||
// The global normalization function that we can use on every pFlag set and children commands
|
// The global normalization function that we can use on every pFlag set and children commands
|
||||||
globNormFunc func(f *flag.FlagSet, name string) flag.NormalizedName
|
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) {
|
func (c *Command) HelpFunc() func(*Command, []string) {
|
||||||
if c.helpFunc != nil {
|
cmd := c
|
||||||
return c.helpFunc
|
for cmd != nil {
|
||||||
|
if cmd.helpFunc != nil {
|
||||||
|
return cmd.helpFunc
|
||||||
|
}
|
||||||
|
cmd = cmd.parent
|
||||||
}
|
}
|
||||||
|
return func(*Command, []string) {
|
||||||
if c.HasParent() {
|
err := c.Help()
|
||||||
return c.parent.HelpFunc()
|
if err != nil {
|
||||||
} else {
|
c.Println(err)
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -270,7 +268,7 @@ Global Flags:
|
|||||||
{{.InheritedFlags.FlagUsages}}{{end}}{{if .HasHelpSubCommands}}
|
{{.InheritedFlags.FlagUsages}}{{end}}{{if .HasHelpSubCommands}}
|
||||||
|
|
||||||
Additional help topics: {{range .Commands}}{{if .IsHelpCommand}}
|
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.
|
Use "{{.CommandPath}} [command] --help" for more information about a command.
|
||||||
{{end}}`
|
{{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)
|
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)
|
err = c.ParseFlags(a)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// If help is called, regardless of other flags, return we want help
|
// If help is called, regardless of other flags, return we want help
|
||||||
// Also say we need help if c.Run is nil.
|
// Also say we need help if the command isn't runnable.
|
||||||
if c.helpFlagVal || !c.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
|
return flag.ErrHelp
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -464,22 +473,45 @@ func (c *Command) execute(a []string) (err error) {
|
|||||||
argWoFlags := c.Flags().Args()
|
argWoFlags := c.Flags().Args()
|
||||||
|
|
||||||
for p := c; p != nil; p = p.Parent() {
|
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)
|
p.PersistentPreRun(c, argWoFlags)
|
||||||
break
|
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.PreRun(c, argWoFlags)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Run(c, argWoFlags)
|
if c.RunE != nil {
|
||||||
|
if err := c.RunE(c, argWoFlags); err != nil {
|
||||||
if c.PostRun != 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)
|
c.PostRun(c, argWoFlags)
|
||||||
}
|
}
|
||||||
for p := c; p != nil; p = p.Parent() {
|
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)
|
p.PersistentPostRun(c, argWoFlags)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -526,7 +558,7 @@ func (c *Command) Execute() (err error) {
|
|||||||
|
|
||||||
// initialize help as the last point possible to allow for user
|
// initialize help as the last point possible to allow for user
|
||||||
// overriding
|
// overriding
|
||||||
c.initHelp()
|
c.initHelpCmd()
|
||||||
|
|
||||||
var args []string
|
var args []string
|
||||||
|
|
||||||
@ -550,7 +582,7 @@ func (c *Command) Execute() (err error) {
|
|||||||
err = cmd.execute(flags)
|
err = cmd.execute(flags)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == flag.ErrHelp {
|
if err == flag.ErrHelp {
|
||||||
cmd.Help()
|
cmd.HelpFunc()(cmd, args)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
c.Println(cmd.UsageString())
|
c.Println(cmd.UsageString())
|
||||||
@ -560,7 +592,13 @@ func (c *Command) Execute() (err error) {
|
|||||||
return
|
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.helpCommand == nil {
|
||||||
if !c.HasSubCommands() {
|
if !c.HasSubCommands() {
|
||||||
return
|
return
|
||||||
@ -571,9 +609,19 @@ func (c *Command) initHelp() {
|
|||||||
Short: "Help about any command",
|
Short: "Help about any command",
|
||||||
Long: `Help provides help for any command in the application.
|
Long: `Help provides help for any command in the application.
|
||||||
Simply type ` + c.Name() + ` help [path to command] for full details.`,
|
Simply type ` + c.Name() + ` help [path to command] for full details.`,
|
||||||
Run: c.HelpFunc(),
|
|
||||||
PersistentPreRun: func(cmd *Command, args []string) {},
|
PersistentPreRun: func(cmd *Command, args []string) {},
|
||||||
PersistentPostRun: 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)
|
c.AddCommand(c.helpCommand)
|
||||||
@ -794,7 +842,7 @@ func (c *Command) HasExample() bool {
|
|||||||
|
|
||||||
// Determine if the command is itself runnable
|
// Determine if the command is itself runnable
|
||||||
func (c *Command) Runnable() bool {
|
func (c *Command) Runnable() bool {
|
||||||
return c.Run != nil
|
return c.Run != nil || c.RunE != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine if the command has children commands
|
// Determine if the command has children commands
|
||||||
@ -859,7 +907,6 @@ func (c *Command) Flags() *flag.FlagSet {
|
|||||||
c.flagErrorBuf = new(bytes.Buffer)
|
c.flagErrorBuf = new(bytes.Buffer)
|
||||||
}
|
}
|
||||||
c.flags.SetOutput(c.flagErrorBuf)
|
c.flags.SetOutput(c.flagErrorBuf)
|
||||||
c.PersistentFlags().BoolVarP(&c.helpFlagVal, "help", "h", false, "help for "+c.Name())
|
|
||||||
}
|
}
|
||||||
return c.flags
|
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 (s byName) Less(i, j int) bool { return s[i].Name() < s[j].Name() }
|
||||||
|
|
||||||
func GenMarkdown(cmd *Command, out *bytes.Buffer) {
|
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) {
|
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()
|
name := cmd.CommandPath()
|
||||||
|
|
||||||
short := cmd.Short
|
short := cmd.Short
|
||||||
@ -75,7 +83,7 @@ func GenMarkdownCustom(cmd *Command, out *bytes.Buffer, linkHandler func(string)
|
|||||||
|
|
||||||
printOptions(out, cmd, name)
|
printOptions(out, cmd, name)
|
||||||
|
|
||||||
if len(cmd.Commands()) > 0 || cmd.HasParent() {
|
if cmd.hasSeeAlso() {
|
||||||
fmt.Fprintf(out, "### SEE ALSO\n")
|
fmt.Fprintf(out, "### SEE ALSO\n")
|
||||||
if cmd.HasParent() {
|
if cmd.HasParent() {
|
||||||
parent := cmd.Parent()
|
parent := cmd.Parent()
|
||||||
@ -89,7 +97,7 @@ func GenMarkdownCustom(cmd *Command, out *bytes.Buffer, linkHandler func(string)
|
|||||||
sort.Sort(byName(children))
|
sort.Sort(byName(children))
|
||||||
|
|
||||||
for _, child := range children {
|
for _, child := range children {
|
||||||
if len(child.Deprecated) > 0 {
|
if len(child.Deprecated) > 0 || child == cmd.helpCommand {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
cname := name + " " + child.Name()
|
cname := name + " " + child.Name()
|
||||||
@ -104,18 +112,29 @@ func GenMarkdownCustom(cmd *Command, out *bytes.Buffer, linkHandler func(string)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func GenMarkdownTree(cmd *Command, dir string) {
|
func GenMarkdownTree(cmd *Command, dir string) {
|
||||||
|
cmd.GenMarkdownTree(dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cmd *Command) GenMarkdownTree(dir string) {
|
||||||
identity := func(s string) string { return s }
|
identity := func(s string) string { return s }
|
||||||
emptyStr := func(s string) string { return "" }
|
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) {
|
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() {
|
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)
|
out := new(bytes.Buffer)
|
||||||
|
|
||||||
GenMarkdownCustom(cmd, out, linkHandler)
|
cmd.GenMarkdownCustom(out, linkHandler)
|
||||||
|
|
||||||
filename := cmd.CommandPath()
|
filename := cmd.CommandPath()
|
||||||
filename = dir + strings.Replace(filename, " ", "_", -1) + ".md"
|
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:
|
For Q&A, our threads are at:
|
||||||
|
|
||||||
* [Stack Overflow](http://stackoverflow.com/questions/tagged/kubernetes)
|
* [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?
|
#### Want to do more than just 'discuss' Kubernetes?
|
||||||
|
|
||||||
|
@ -11879,6 +11879,9 @@
|
|||||||
"v1.PersistentVolumeClaimList": {
|
"v1.PersistentVolumeClaimList": {
|
||||||
"id": "v1.PersistentVolumeClaimList",
|
"id": "v1.PersistentVolumeClaimList",
|
||||||
"description": "PersistentVolumeClaimList is a list of PersistentVolumeClaim items.",
|
"description": "PersistentVolumeClaimList is a list of PersistentVolumeClaim items.",
|
||||||
|
"required": [
|
||||||
|
"items"
|
||||||
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"kind": {
|
"kind": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@ -11990,6 +11993,9 @@
|
|||||||
"v1.PersistentVolumeList": {
|
"v1.PersistentVolumeList": {
|
||||||
"id": "v1.PersistentVolumeList",
|
"id": "v1.PersistentVolumeList",
|
||||||
"description": "PersistentVolumeList is a list of PersistentVolume items.",
|
"description": "PersistentVolumeList is a list of PersistentVolume items.",
|
||||||
|
"required": [
|
||||||
|
"items"
|
||||||
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"kind": {
|
"kind": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
@ -1,22 +1,22 @@
|
|||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: ReplicationController
|
kind: ReplicationController
|
||||||
metadata:
|
metadata:
|
||||||
name: kube-dns-v8
|
name: kube-dns-v9
|
||||||
namespace: kube-system
|
namespace: kube-system
|
||||||
labels:
|
labels:
|
||||||
k8s-app: kube-dns
|
k8s-app: kube-dns
|
||||||
version: v8
|
version: v9
|
||||||
kubernetes.io/cluster-service: "true"
|
kubernetes.io/cluster-service: "true"
|
||||||
spec:
|
spec:
|
||||||
replicas: {{ pillar['dns_replicas'] }}
|
replicas: {{ pillar['dns_replicas'] }}
|
||||||
selector:
|
selector:
|
||||||
k8s-app: kube-dns
|
k8s-app: kube-dns
|
||||||
version: v8
|
version: v9
|
||||||
template:
|
template:
|
||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
k8s-app: kube-dns
|
k8s-app: kube-dns
|
||||||
version: v8
|
version: v9
|
||||||
kubernetes.io/cluster-service: "true"
|
kubernetes.io/cluster-service: "true"
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
@ -73,6 +73,13 @@ spec:
|
|||||||
scheme: HTTP
|
scheme: HTTP
|
||||||
initialDelaySeconds: 30
|
initialDelaySeconds: 30
|
||||||
timeoutSeconds: 5
|
timeoutSeconds: 5
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /healthz
|
||||||
|
port: 8080
|
||||||
|
scheme: HTTP
|
||||||
|
initialDelaySeconds: 1
|
||||||
|
timeoutSeconds: 5
|
||||||
- name: healthz
|
- name: healthz
|
||||||
image: gcr.io/google_containers/exechealthz:1.0
|
image: gcr.io/google_containers/exechealthz:1.0
|
||||||
resources:
|
resources:
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
This directory contains the source files needed to make a Docker image
|
This directory contains the source files needed to make a Docker image
|
||||||
that collects Docker container log files using [Fluentd](http://www.fluentd.org/)
|
that collects Docker container log files using [Fluentd](http://www.fluentd.org/)
|
||||||
and sends them to an instance of [Elasticsearch](http://www.elasticsearch.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
|
cluster bring up process. The image resides at DockerHub under the name
|
||||||
[kubernetes/fluentd-eslasticsearch](https://registry.hub.docker.com/u/kubernetes/fluentd-elasticsearch/).
|
[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
|
This directory contains the source files needed to make a Docker image
|
||||||
that collects Docker container log files using [Fluentd](http://www.fluentd.org/)
|
that collects Docker container log files using [Fluentd](http://www.fluentd.org/)
|
||||||
and sends them to GCP.
|
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
|
cluster bring up process. The image resides at DockerHub under the name
|
||||||
[kubernetes/fluentd-gcp](https://registry.hub.docker.com/u/kubernetes/fluentd-gcp/).
|
[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}"
|
ENABLE_CLUSTER_UI="${KUBE_ENABLE_CLUSTER_UI:-true}"
|
||||||
|
|
||||||
# Admission Controllers to invoke prior to persisting objects in cluster
|
# 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.
|
# 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!
|
# 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}"
|
ENABLE_CLUSTER_UI="${KUBE_ENABLE_CLUSTER_UI:-true}"
|
||||||
|
|
||||||
# Admission Controllers to invoke prior to persisting objects in cluster
|
# 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.
|
# 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!
|
# 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}"
|
ENABLE_CLUSTER_UI="${KUBE_ENABLE_CLUSTER_UI:-true}"
|
||||||
|
|
||||||
# Admission Controllers to invoke prior to persisting objects in cluster
|
# 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}"
|
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.
|
# Gets username, password for the current-context in kubeconfig, if they exist.
|
||||||
# Assumed vars:
|
# Assumed vars:
|
||||||
# KUBECONFIG # if unset, defaults to global
|
# KUBECONFIG # if unset, defaults to global
|
||||||
@ -278,3 +287,4 @@ function tars_from_version() {
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,7 +96,7 @@ if [[ "${ENABLE_NODE_AUTOSCALER}" == "true" ]]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Admission Controllers to invoke prior to persisting objects in cluster
|
# 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.
|
# 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}
|
KUBE_UP_AUTOMATIC_CLEANUP=${KUBE_UP_AUTOMATIC_CLEANUP:-false}
|
||||||
|
@ -18,9 +18,9 @@
|
|||||||
# gcloud multiplexing for shared GCE/GKE tests.
|
# gcloud multiplexing for shared GCE/GKE tests.
|
||||||
GCLOUD=gcloud
|
GCLOUD=gcloud
|
||||||
ZONE=${KUBE_GCE_ZONE:-us-central1-b}
|
ZONE=${KUBE_GCE_ZONE:-us-central1-b}
|
||||||
MASTER_SIZE=${MASTER_SIZE:-n1-standard-1}
|
MASTER_SIZE=${MASTER_SIZE:-n1-standard-2}
|
||||||
MINION_SIZE=${MINION_SIZE:-n1-standard-1}
|
MINION_SIZE=${MINION_SIZE:-n1-standard-2}
|
||||||
NUM_MINIONS=${NUM_MINIONS:-2}
|
NUM_MINIONS=${NUM_MINIONS:-3}
|
||||||
MASTER_DISK_TYPE=pd-ssd
|
MASTER_DISK_TYPE=pd-ssd
|
||||||
MASTER_DISK_SIZE=${MASTER_DISK_SIZE:-20GB}
|
MASTER_DISK_SIZE=${MASTER_DISK_SIZE:-20GB}
|
||||||
MINION_DISK_TYPE=pd-standard
|
MINION_DISK_TYPE=pd-standard
|
||||||
@ -100,7 +100,7 @@ if [[ "${ENABLE_NODE_AUTOSCALER}" == "true" ]]; then
|
|||||||
TARGET_NODE_UTILIZATION="${KUBE_TARGET_NODE_UTILIZATION:-0.7}"
|
TARGET_NODE_UTILIZATION="${KUBE_TARGET_NODE_UTILIZATION:-0.7}"
|
||||||
fi
|
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.
|
# 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}
|
KUBE_UP_AUTOMATIC_CLEANUP=${KUBE_UP_AUTOMATIC_CLEANUP:-false}
|
||||||
|
@ -540,10 +540,11 @@ grains:
|
|||||||
- kubernetes-master
|
- kubernetes-master
|
||||||
cloud: gce
|
cloud: gce
|
||||||
EOF
|
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
|
cat <<EOF >/etc/gce.conf
|
||||||
[global]
|
[global]
|
||||||
token-url = ${TOKEN_URL}
|
token-url = ${TOKEN_URL}
|
||||||
|
token-body = ${TOKEN_BODY}
|
||||||
project-id = ${PROJECT_ID}
|
project-id = ${PROJECT_ID}
|
||||||
network-name = ${NODE_NETWORK}
|
network-name = ${NODE_NETWORK}
|
||||||
EOF
|
EOF
|
||||||
|
@ -21,6 +21,11 @@ set -o nounset
|
|||||||
set -o pipefail
|
set -o pipefail
|
||||||
|
|
||||||
KUBE_ROOT=$(dirname "${BASH_SOURCE}")/..
|
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-env.sh"
|
||||||
source "${KUBE_ROOT}/cluster/kube-util.sh"
|
source "${KUBE_ROOT}/cluster/kube-util.sh"
|
||||||
|
|
||||||
|
@ -24,6 +24,11 @@ set -o nounset
|
|||||||
set -o pipefail
|
set -o pipefail
|
||||||
|
|
||||||
KUBE_ROOT=$(dirname "${BASH_SOURCE}")/..
|
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-env.sh"
|
||||||
source "${KUBE_ROOT}/cluster/kube-util.sh"
|
source "${KUBE_ROOT}/cluster/kube-util.sh"
|
||||||
|
|
||||||
|
@ -25,6 +25,11 @@ set -o nounset
|
|||||||
set -o pipefail
|
set -o pipefail
|
||||||
|
|
||||||
KUBE_ROOT=$(dirname "${BASH_SOURCE}")/..
|
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-env.sh"
|
||||||
source "${KUBE_ROOT}/cluster/kube-util.sh"
|
source "${KUBE_ROOT}/cluster/kube-util.sh"
|
||||||
|
|
||||||
|
@ -195,7 +195,7 @@ function wait-cluster-readiness {
|
|||||||
|
|
||||||
local timeout=120
|
local timeout=120
|
||||||
while [[ $timeout -ne 0 ]]; do
|
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"
|
echo "Nb ready minions: $nb_ready_minions / $NUM_MINIONS"
|
||||||
if [[ "$nb_ready_minions" -eq "$NUM_MINIONS" ]]; then
|
if [[ "$nb_ready_minions" -eq "$NUM_MINIONS" ]]; then
|
||||||
return 0
|
return 0
|
||||||
@ -294,7 +294,7 @@ function upload-server-tars {
|
|||||||
tar -x -C "$POOL_PATH/kubernetes" -f "$SERVER_BINARY_TAR" kubernetes
|
tar -x -C "$POOL_PATH/kubernetes" -f "$SERVER_BINARY_TAR" kubernetes
|
||||||
rm -rf "$POOL_PATH/kubernetes/bin"
|
rm -rf "$POOL_PATH/kubernetes/bin"
|
||||||
mv "$POOL_PATH/kubernetes/kubernetes/server/bin" "$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
|
# Update a kubernetes cluster with latest source
|
||||||
|
@ -89,7 +89,7 @@ apiserver:
|
|||||||
--external-hostname=apiserver
|
--external-hostname=apiserver
|
||||||
--etcd-servers=http://etcd:4001
|
--etcd-servers=http://etcd:4001
|
||||||
--port=8888
|
--port=8888
|
||||||
--admission-control=NamespaceLifecycle,NamespaceExists,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota
|
--admission-control=NamespaceLifecycle,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota
|
||||||
--authorization-mode=AlwaysAllow
|
--authorization-mode=AlwaysAllow
|
||||||
--token-auth-file=/var/run/kubernetes/auth/token-users
|
--token-auth-file=/var/run/kubernetes/auth/token-users
|
||||||
--basic-auth-file=/var/run/kubernetes/auth/basic-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)
|
# returns a list of <namespace>/<name> pairs (nsnames)
|
||||||
function get-addon-nsnames-from-server() {
|
function get-addon-nsnames-from-server() {
|
||||||
local -r obj_type=$1
|
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)
|
# returns the characters after the last separator (including)
|
||||||
|
@ -174,7 +174,7 @@ start_addon /etc/kubernetes/addons/namespace.yaml 100 10 "" &
|
|||||||
token_found=""
|
token_found=""
|
||||||
while [ -z "${token_found}" ]; do
|
while [ -z "${token_found}" ]; do
|
||||||
sleep .5
|
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
|
done
|
||||||
|
|
||||||
echo "== default service account in the ${SYSTEM_NAMESPACE} namespace has token ${token_found} =="
|
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'] %}
|
{% set pod_cidr = "--pod-cidr=" + grains['cbr-cidr'] %}
|
||||||
{% endif %}
|
{% 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 = "" -%}
|
{% set test_args = "" -%}
|
||||||
{% if pillar['kubelet_test_args'] is defined -%}
|
{% if pillar['kubelet_test_args'] is defined -%}
|
||||||
{% set test_args=pillar['kubelet_test_args'] %}
|
{% set test_args=pillar['kubelet_test_args'] %}
|
||||||
{% endif -%}
|
{% endif -%}
|
||||||
|
|
||||||
# test_args has to be kept at the end, so they'll overwrite any prior configuration
|
# 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
|
tar xzf etcd.tar.gz
|
||||||
fi
|
fi
|
||||||
cp $ETCD/etcd $ETCD/etcdctl binaries/master
|
cp $ETCD/etcd $ETCD/etcdctl binaries/master
|
||||||
cp $ETCD/etcd $ETCD/etcdctl binaries/minion
|
|
||||||
|
|
||||||
# k8s
|
# k8s
|
||||||
echo "Download kubernetes release ..."
|
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}
|
export FLANNEL_NET=${FLANNEL_NET:-172.16.0.0/16}
|
||||||
|
|
||||||
# Admission Controllers to invoke prior to persisting objects in cluster
|
# 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"}
|
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
|
respawn
|
||||||
|
|
||||||
# start in conjunction with etcd
|
start on (net-device-up
|
||||||
start on started etcd
|
and local-filesystems
|
||||||
stop on stopping etcd
|
and runlevel [2345])
|
||||||
|
|
||||||
pre-start script
|
pre-start script
|
||||||
FLANNEL=/opt/bin/$UPSTART_JOB
|
FLANNEL=/opt/bin/$UPSTART_JOB
|
||||||
if [ -f /etc/default/$UPSTART_JOB ]; then
|
if [ -f /etc/default/$UPSTART_JOB ]; then
|
||||||
|
@ -3,9 +3,9 @@ author "@jainvipin"
|
|||||||
|
|
||||||
respawn
|
respawn
|
||||||
|
|
||||||
# start in conjunction with etcd
|
# start in conjunction with flanneld
|
||||||
start on started etcd
|
start on started flanneld
|
||||||
stop on stopping etcd
|
stop on stopping flanneld
|
||||||
|
|
||||||
limit nofile 65536 65536
|
limit nofile 65536 65536
|
||||||
|
|
||||||
|
@ -3,9 +3,9 @@ author "@jainvipin"
|
|||||||
|
|
||||||
respawn
|
respawn
|
||||||
|
|
||||||
# start in conjunction with etcd
|
# start in conjunction with flanneld
|
||||||
start on started etcd
|
start on started flanneld
|
||||||
stop on stopping etcd
|
stop on stopping flanneld
|
||||||
|
|
||||||
pre-start script
|
pre-start script
|
||||||
# see also https://github.com/jainvipin/kubernetes-ubuntu-start
|
# 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_PIDFILE=/var/run/$BASE.pid
|
||||||
KUBELET_LOGFILE=/var/log/$BASE.log
|
KUBELET_LOGFILE=/var/log/$BASE.log
|
||||||
KUBELET_OPTS=""
|
KUBELET_OPTS=""
|
||||||
KUBELET_DESC="Kube-Apiserver"
|
KUBELET_DESC="Kubelet"
|
||||||
|
|
||||||
# Get lsb functions
|
# Get lsb functions
|
||||||
. /lib/lsb/init-functions
|
. /lib/lsb/init-functions
|
||||||
|
@ -21,33 +21,48 @@ if [ "$(id -u)" != "0" ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
source ~/kube/config-default.sh
|
|
||||||
|
|
||||||
attempt=0
|
function config_etcd {
|
||||||
while true; do
|
|
||||||
/opt/bin/etcdctl get /coreos.com/network/config
|
source ~/kube/config-default.sh
|
||||||
if [[ "$?" == 0 ]]; then
|
|
||||||
break
|
attempt=0
|
||||||
else
|
while true; do
|
||||||
# enough timeout??
|
/opt/bin/etcdctl get /coreos.com/network/config
|
||||||
if (( attempt > 600 )); then
|
if [[ "$?" == 0 ]]; then
|
||||||
echo "timeout for waiting network config" > ~/kube/err.log
|
break
|
||||||
exit 2
|
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
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
/opt/bin/etcdctl mk /coreos.com/network/config "{\"Network\":\"${FLANNEL_NET}\"}"
|
function restart_docker {
|
||||||
attempt=$((attempt+1))
|
#wait some secs for /run/flannel/subnet.env ready
|
||||||
sleep 3
|
sleep 15
|
||||||
fi
|
sudo ip link set dev docker0 down
|
||||||
done
|
sudo brctl delbr docker0
|
||||||
|
|
||||||
#wait some secs for /run/flannel/subnet.env ready
|
source /run/flannel/subnet.env
|
||||||
sleep 15
|
|
||||||
sudo ip link set dev docker0 down
|
|
||||||
sudo brctl delbr docker0
|
|
||||||
|
|
||||||
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 \
|
if [[ $1 == "i" ]]; then
|
||||||
--bip=${FLANNEL_SUBNET} --mtu=${FLANNEL_MTU}\" > /etc/default/docker
|
restart_docker
|
||||||
sudo service docker restart
|
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
|
# use an array to record name and ip
|
||||||
declare -A mm
|
declare -A mm
|
||||||
CLUSTER=""
|
|
||||||
MASTER=""
|
MASTER=""
|
||||||
MASTER_IP=""
|
MASTER_IP=""
|
||||||
MINION_IPS=""
|
MINION_IPS=""
|
||||||
@ -44,28 +43,18 @@ function setClusterInfo() {
|
|||||||
MINION_IPS=""
|
MINION_IPS=""
|
||||||
|
|
||||||
ii=0
|
ii=0
|
||||||
for i in $nodes
|
for i in $nodes; do
|
||||||
do
|
|
||||||
name="infra"$ii
|
|
||||||
nodeIP=${i#*@}
|
nodeIP=${i#*@}
|
||||||
|
|
||||||
item="$name=http://$nodeIP:2380"
|
if [[ "${roles[${ii}]}" == "ai" ]]; then
|
||||||
if [ "$ii" == 0 ]; then
|
|
||||||
CLUSTER=$item
|
|
||||||
else
|
|
||||||
CLUSTER="$CLUSTER,$item"
|
|
||||||
fi
|
|
||||||
mm[$nodeIP]=$name
|
|
||||||
|
|
||||||
if [ "${roles[${ii}]}" == "ai" ]; then
|
|
||||||
MASTER_IP=$nodeIP
|
MASTER_IP=$nodeIP
|
||||||
MASTER=$i
|
MASTER=$i
|
||||||
MINION_IPS="$nodeIP"
|
MINION_IPS="$nodeIP"
|
||||||
elif [ "${roles[${ii}]}" == "a" ]; then
|
elif [[ "${roles[${ii}]}" == "a" ]]; then
|
||||||
MASTER_IP=$nodeIP
|
MASTER_IP=$nodeIP
|
||||||
MASTER=$i
|
MASTER=$i
|
||||||
elif [ "${roles[${ii}]}" == "i" ]; then
|
elif [[ "${roles[${ii}]}" == "i" ]]; then
|
||||||
if [ -z "${MINION_IPS}" ];then
|
if [[ -z "${MINION_IPS}" ]];then
|
||||||
MINION_IPS="$nodeIP"
|
MINION_IPS="$nodeIP"
|
||||||
else
|
else
|
||||||
MINION_IPS="$MINION_IPS,$nodeIP"
|
MINION_IPS="$MINION_IPS,$nodeIP"
|
||||||
@ -191,12 +180,9 @@ function verify-minion(){
|
|||||||
|
|
||||||
function create-etcd-opts(){
|
function create-etcd-opts(){
|
||||||
cat <<EOF > ~/kube/default/etcd
|
cat <<EOF > ~/kube/default/etcd
|
||||||
ETCD_OPTS="-name $1 \
|
ETCD_OPTS="-name infra
|
||||||
-initial-advertise-peer-urls http://$2:2380 \
|
-listen-client-urls http://0.0.0.0:4001 \
|
||||||
-listen-peer-urls http://$2:2380 \
|
-advertise-client-urls http://127.0.0.1:4001"
|
||||||
-initial-cluster-token etcd-cluster-1 \
|
|
||||||
-initial-cluster $3 \
|
|
||||||
-initial-cluster-state new"
|
|
||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -256,7 +242,7 @@ EOF
|
|||||||
|
|
||||||
function create-flanneld-opts(){
|
function create-flanneld-opts(){
|
||||||
cat <<EOF > ~/kube/default/flanneld
|
cat <<EOF > ~/kube/default/flanneld
|
||||||
FLANNEL_OPTS=""
|
FLANNEL_OPTS="--etcd-endpoints=http://${1}:4001"
|
||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -324,10 +310,10 @@ function kube-up() {
|
|||||||
{
|
{
|
||||||
if [ "${roles[${ii}]}" == "a" ]; then
|
if [ "${roles[${ii}]}" == "a" ]; then
|
||||||
provision-master
|
provision-master
|
||||||
elif [ "${roles[${ii}]}" == "i" ]; then
|
|
||||||
provision-minion $i
|
|
||||||
elif [ "${roles[${ii}]}" == "ai" ]; then
|
elif [ "${roles[${ii}]}" == "ai" ]; then
|
||||||
provision-masterandminion
|
provision-masterandminion
|
||||||
|
elif [ "${roles[${ii}]}" == "i" ]; then
|
||||||
|
provision-minion $i
|
||||||
else
|
else
|
||||||
echo "unsupported role for ${i}. please check"
|
echo "unsupported role for ${i}. please check"
|
||||||
exit 1
|
exit 1
|
||||||
@ -356,21 +342,22 @@ function provision-master() {
|
|||||||
echo "Deploying master on machine ${MASTER_IP}"
|
echo "Deploying master on machine ${MASTER_IP}"
|
||||||
echo
|
echo
|
||||||
ssh $SSH_OPTS $MASTER "mkdir -p ~/kube/default"
|
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
|
# remote login to MASTER and use sudo to configue k8s master
|
||||||
ssh $SSH_OPTS -t $MASTER "source ~/kube/util.sh; \
|
ssh $SSH_OPTS -t $MASTER "source ~/kube/util.sh; \
|
||||||
setClusterInfo; \
|
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-apiserver-opts "${SERVICE_CLUSTER_IP_RANGE}" "${ADMISSION_CONTROL}" "${SERVICE_NODE_PORT_RANGE}"; \
|
||||||
create-kube-controller-manager-opts "${MINION_IPS}"; \
|
create-kube-controller-manager-opts "${MINION_IPS}"; \
|
||||||
create-kube-scheduler-opts; \
|
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 -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 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 ~/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 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() {
|
function provision-minion() {
|
||||||
@ -383,14 +370,13 @@ function provision-minion() {
|
|||||||
# remote login to MASTER and use sudo to configue k8s master
|
# remote login to MASTER and use sudo to configue k8s master
|
||||||
ssh $SSH_OPTS -t $1 "source ~/kube/util.sh; \
|
ssh $SSH_OPTS -t $1 "source ~/kube/util.sh; \
|
||||||
setClusterInfo; \
|
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-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 -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 mkdir -p /opt/bin/ && sudo cp ~/kube/minion/* /opt/bin; \
|
||||||
sudo service etcd start; \
|
sudo service flanneld start; \
|
||||||
sudo FLANNEL_NET=${FLANNEL_NET} -b ~/kube/reconfDocker.sh"
|
sudo -b ~/kube/reconfDocker.sh "i";"
|
||||||
}
|
}
|
||||||
|
|
||||||
function provision-masterandminion() {
|
function provision-masterandminion() {
|
||||||
@ -398,24 +384,25 @@ function provision-masterandminion() {
|
|||||||
echo "Deploying master and minion on machine ${MASTER_IP}"
|
echo "Deploying master and minion on machine ${MASTER_IP}"
|
||||||
echo
|
echo
|
||||||
ssh $SSH_OPTS $MASTER "mkdir -p ~/kube/default"
|
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
|
# remote login to the node and use sudo to configue k8s
|
||||||
ssh $SSH_OPTS -t $MASTER "source ~/kube/util.sh; \
|
ssh $SSH_OPTS -t $MASTER "source ~/kube/util.sh; \
|
||||||
setClusterInfo; \
|
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-apiserver-opts "${SERVICE_CLUSTER_IP_RANGE}" "${ADMISSION_CONTROL}" "${SERVICE_NODE_PORT_RANGE}"; \
|
||||||
create-kube-controller-manager-opts "${MINION_IPS}"; \
|
create-kube-controller-manager-opts "${MINION_IPS}"; \
|
||||||
create-kube-scheduler-opts; \
|
create-kube-scheduler-opts; \
|
||||||
create-kubelet-opts "${MASTER_IP}" "${MASTER_IP}" "${DNS_SERVER_IP}" "${DNS_DOMAIN}";
|
create-kubelet-opts "${MASTER_IP}" "${MASTER_IP}" "${DNS_SERVER_IP}" "${DNS_DOMAIN}";
|
||||||
create-kube-proxy-opts "${MASTER_IP}";\
|
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 -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 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 ~/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 mkdir -p /opt/bin/ && sudo cp ~/kube/master/* /opt/bin/ && sudo cp ~/kube/minion/* /opt/bin/; \
|
||||||
sudo service etcd start; \
|
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
|
# Delete a kubernetes cluster
|
||||||
@ -423,20 +410,116 @@ function kube-down {
|
|||||||
KUBE_ROOT=$(dirname "${BASH_SOURCE}")/../..
|
KUBE_ROOT=$(dirname "${BASH_SOURCE}")/../..
|
||||||
source "${KUBE_ROOT}/cluster/ubuntu/${KUBE_CONFIG_FILE-"config-default.sh"}"
|
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
|
for i in ${nodes}; do
|
||||||
{
|
{
|
||||||
echo "Cleaning on node ${i#*@}"
|
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*'
|
if [[ "${roles[${ii}]}" == "ai" || "${roles[${ii}]}" == "a" ]]; then
|
||||||
# Delete the files in order to generate a clean environment, so you can change each node's role at next deployment.
|
ssh -t $i 'pgrep etcd && sudo -p "[sudo] password for cleaning etcd data: " service etcd stop && sudo rm -rf /infra*;
|
||||||
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'
|
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
|
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
|
# Update a kubernetes cluster with latest source
|
||||||
function kube-push {
|
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
|
# Perform preparations required to run e2e tests
|
||||||
|
@ -49,7 +49,7 @@ declare -a resources=(
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Find all the namespaces.
|
# 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:-}" ]
|
if [ -z "${namespaces:-}" ]
|
||||||
then
|
then
|
||||||
echo "Unexpected: No namespace found. Nothing to do."
|
echo "Unexpected: No namespace found. Nothing to do."
|
||||||
@ -59,7 +59,7 @@ for resource in "${resources[@]}"
|
|||||||
do
|
do
|
||||||
for namespace in "${namespaces[@]}"
|
for namespace in "${namespaces[@]}"
|
||||||
do
|
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.
|
# Nothing to do if there is no instance of that resource.
|
||||||
if [[ -z "${instances:-}" ]]
|
if [[ -z "${instances:-}" ]]
|
||||||
then
|
then
|
||||||
@ -84,7 +84,7 @@ do
|
|||||||
echo "Looks like ${instance} got deleted. Ignoring it"
|
echo "Looks like ${instance} got deleted. Ignoring it"
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
output=$("${KUBECTL}" update -f "${filename}" --namespace="${namespace}") || true
|
output=$("${KUBECTL}" replace -f "${filename}" --namespace="${namespace}") || true
|
||||||
rm "${filename}"
|
rm "${filename}"
|
||||||
if [ -n "${output:-}" ]
|
if [ -n "${output:-}" ]
|
||||||
then
|
then
|
||||||
|
@ -53,7 +53,7 @@ MASTER_USER=vagrant
|
|||||||
MASTER_PASSWD=vagrant
|
MASTER_PASSWD=vagrant
|
||||||
|
|
||||||
# Admission Controllers to invoke prior to persisting objects in cluster
|
# 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.
|
# Optional: Enable node logging.
|
||||||
ENABLE_NODE_LOGGING=false
|
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 --selinux-enabled --insecure-registry 10.0.0.0/8"
|
||||||
EXTRA_DOCKER_OPTS="-b=cbr0 --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.
|
# Optional: Install cluster DNS.
|
||||||
ENABLE_CLUSTER_DNS="${KUBE_ENABLE_CLUSTER_DNS:-true}"
|
ENABLE_CLUSTER_DNS="${KUBE_ENABLE_CLUSTER_DNS:-true}"
|
||||||
DNS_SERVER_IP="10.247.0.10"
|
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")'
|
dns_domain: '$(echo "$DNS_DOMAIN" | sed -e "s/'/''/g")'
|
||||||
instance_prefix: '$(echo "$INSTANCE_PREFIX" | sed -e "s/'/''/g")'
|
instance_prefix: '$(echo "$INSTANCE_PREFIX" | sed -e "s/'/''/g")'
|
||||||
admission_control: '$(echo "$ADMISSION_CONTROL" | 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
|
EOF
|
||||||
|
|
||||||
# Configure the salt-master
|
# Configure the salt-master
|
||||||
|
@ -155,6 +155,10 @@ grains:
|
|||||||
docker_opts: '$(echo "$DOCKER_OPTS" | sed -e "s/'/''/g")'
|
docker_opts: '$(echo "$DOCKER_OPTS" | sed -e "s/'/''/g")'
|
||||||
EOF
|
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
|
# 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
|
if ! which salt-minion >/dev/null 2>&1; then
|
||||||
# Install Salt
|
# Install Salt
|
||||||
|
@ -153,6 +153,7 @@ function create-provision-scripts {
|
|||||||
echo "KUBELET_TOKEN='${KUBELET_TOKEN:-}'"
|
echo "KUBELET_TOKEN='${KUBELET_TOKEN:-}'"
|
||||||
echo "KUBE_PROXY_TOKEN='${KUBE_PROXY_TOKEN:-}'"
|
echo "KUBE_PROXY_TOKEN='${KUBE_PROXY_TOKEN:-}'"
|
||||||
echo "MASTER_EXTRA_SANS='${MASTER_EXTRA_SANS:-}'"
|
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-network.sh"
|
||||||
awk '!/^#/' "${KUBE_ROOT}/cluster/vagrant/provision-master.sh"
|
awk '!/^#/' "${KUBE_ROOT}/cluster/vagrant/provision-master.sh"
|
||||||
) > "${KUBE_TEMP}/master-start.sh"
|
) > "${KUBE_TEMP}/master-start.sh"
|
||||||
@ -198,6 +199,9 @@ function verify-cluster {
|
|||||||
local machine="master"
|
local machine="master"
|
||||||
local -a required_daemon=("salt-master" "salt-minion" "kubelet")
|
local -a required_daemon=("salt-master" "salt-minion" "kubelet")
|
||||||
local validated="1"
|
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
|
until [[ "$validated" == "0" ]]; do
|
||||||
validated="0"
|
validated="0"
|
||||||
local daemon
|
local daemon
|
||||||
@ -237,7 +241,7 @@ function verify-cluster {
|
|||||||
local count="0"
|
local count="0"
|
||||||
until [[ "$count" == "1" ]]; do
|
until [[ "$count" == "1" ]]; do
|
||||||
local minions
|
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]}") || {
|
count=$(echo $minions | grep -c "${MINION_IPS[i]}") || {
|
||||||
printf "."
|
printf "."
|
||||||
sleep 2
|
sleep 2
|
||||||
|
@ -38,6 +38,7 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
apierrors "k8s.io/kubernetes/pkg/api/errors"
|
apierrors "k8s.io/kubernetes/pkg/api/errors"
|
||||||
"k8s.io/kubernetes/pkg/api/latest"
|
"k8s.io/kubernetes/pkg/api/latest"
|
||||||
|
"k8s.io/kubernetes/pkg/api/testapi"
|
||||||
"k8s.io/kubernetes/pkg/apiserver"
|
"k8s.io/kubernetes/pkg/apiserver"
|
||||||
client "k8s.io/kubernetes/pkg/client/unversioned"
|
client "k8s.io/kubernetes/pkg/client/unversioned"
|
||||||
"k8s.io/kubernetes/pkg/client/unversioned/record"
|
"k8s.io/kubernetes/pkg/client/unversioned/record"
|
||||||
@ -69,8 +70,6 @@ import (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
fakeDocker1, fakeDocker2 dockertools.FakeDockerClient
|
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.
|
// Limit the number of concurrent tests.
|
||||||
maxConcurrency int
|
maxConcurrency int
|
||||||
)
|
)
|
||||||
@ -93,7 +92,7 @@ func (h *delegateHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||||||
w.WriteHeader(http.StatusNotFound)
|
w.WriteHeader(http.StatusNotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
func startComponents(firstManifestURL, secondManifestURL, apiVersion string) (string, string) {
|
func startComponents(firstManifestURL, secondManifestURL string) (string, string) {
|
||||||
// Setup
|
// Setup
|
||||||
servers := []string{}
|
servers := []string{}
|
||||||
glog.Infof("Creating etcd client pointing to %v", servers)
|
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")
|
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 {
|
if err != nil {
|
||||||
glog.Fatalf("Unable to get etcd storage: %v", err)
|
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 {
|
if err != nil {
|
||||||
glog.Fatalf("Unable to get etcd storage for experimental: %v", err)
|
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)
|
type testFunc func(*client.Client)
|
||||||
|
|
||||||
func addFlags(fs *pflag.FlagSet) {
|
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(
|
fs.IntVar(
|
||||||
&maxConcurrency, "max-concurrency", -1, "Maximum number of tests to be run simultaneously. Unlimited if set to negative.")
|
&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.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)
|
firstManifestURL := ServeCachedManifestFile(testPodSpecFile)
|
||||||
secondManifestURL := ServeCachedManifestFile(testPodSpecFile)
|
secondManifestURL := ServeCachedManifestFile(testPodSpecFile)
|
||||||
apiServerURL, _ := startComponents(firstManifestURL, secondManifestURL, apiVersion)
|
apiServerURL, _ := startComponents(firstManifestURL, secondManifestURL)
|
||||||
|
|
||||||
// Ok. we're good to go.
|
// Ok. we're good to go.
|
||||||
glog.Infof("API Server started on %s", apiServerURL)
|
glog.Infof("API Server started on %s", apiServerURL)
|
||||||
// Wait for the synchronization threads to come up.
|
// Wait for the synchronization threads to come up.
|
||||||
time.Sleep(time.Second * 10)
|
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
|
// Run tests in parallel
|
||||||
testFuncs := []testFunc{
|
testFuncs := []testFunc{
|
||||||
|
@ -68,8 +68,6 @@ type APIServer struct {
|
|||||||
AdvertiseAddress net.IP
|
AdvertiseAddress net.IP
|
||||||
SecurePort int
|
SecurePort int
|
||||||
ExternalHost string
|
ExternalHost string
|
||||||
APIRate float32
|
|
||||||
APIBurst int
|
|
||||||
TLSCertFile string
|
TLSCertFile string
|
||||||
TLSPrivateKeyFile string
|
TLSPrivateKeyFile string
|
||||||
CertDirectory string
|
CertDirectory string
|
||||||
@ -107,6 +105,7 @@ type APIServer struct {
|
|||||||
KubeletConfig client.KubeletConfig
|
KubeletConfig client.KubeletConfig
|
||||||
ClusterName string
|
ClusterName string
|
||||||
EnableProfiling bool
|
EnableProfiling bool
|
||||||
|
EnableWatchCache bool
|
||||||
MaxRequestsInFlight int
|
MaxRequestsInFlight int
|
||||||
MinRequestTimeout int
|
MinRequestTimeout int
|
||||||
LongRunningRequestRE string
|
LongRunningRequestRE string
|
||||||
@ -122,8 +121,6 @@ func NewAPIServer() *APIServer {
|
|||||||
InsecureBindAddress: net.ParseIP("127.0.0.1"),
|
InsecureBindAddress: net.ParseIP("127.0.0.1"),
|
||||||
BindAddress: net.ParseIP("0.0.0.0"),
|
BindAddress: net.ParseIP("0.0.0.0"),
|
||||||
SecurePort: 6443,
|
SecurePort: 6443,
|
||||||
APIRate: 10.0,
|
|
||||||
APIBurst: 200,
|
|
||||||
APIPrefix: "/api",
|
APIPrefix: "/api",
|
||||||
ExpAPIPrefix: "/experimental",
|
ExpAPIPrefix: "/experimental",
|
||||||
EventTTL: 1 * time.Hour,
|
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). "+
|
"The IP address on which to serve the --insecure-port (set to 0.0.0.0 for all interfaces). "+
|
||||||
"Defaults to localhost.")
|
"Defaults to localhost.")
|
||||||
fs.IPVar(&s.InsecureBindAddress, "address", s.InsecureBindAddress, "DEPRECATED: see --insecure-bind-address instead")
|
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, ""+
|
fs.IPVar(&s.BindAddress, "bind-address", s.BindAddress, ""+
|
||||||
"The IP address on which to serve the --read-only-port and --secure-port ports. The "+
|
"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 "+
|
"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, ""+
|
fs.IntVar(&s.SecurePort, "secure-port", s.SecurePort, ""+
|
||||||
"The port on which to serve HTTPS with authentication and authorization. If 0, "+
|
"The port on which to serve HTTPS with authentication and authorization. If 0, "+
|
||||||
"don't serve HTTPS at all.")
|
"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, ""+
|
fs.StringVar(&s.TLSCertFile, "tls-cert-file", s.TLSCertFile, ""+
|
||||||
"File containing x509 Certificate for HTTPS. (CA cert, if any, concatenated after server cert). "+
|
"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, "+
|
"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.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.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.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.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.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.")
|
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.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.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/")
|
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.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.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.")
|
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.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.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.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.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.")
|
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)
|
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 {
|
if err != nil {
|
||||||
glog.Fatalf("Invalid Authorization Config: %v", err)
|
glog.Fatalf("Invalid Authorization Config: %v", err)
|
||||||
}
|
}
|
||||||
@ -429,6 +427,7 @@ func (s *APIServer) Run(_ []string) error {
|
|||||||
EnableUISupport: true,
|
EnableUISupport: true,
|
||||||
EnableSwaggerSupport: true,
|
EnableSwaggerSupport: true,
|
||||||
EnableProfiling: s.EnableProfiling,
|
EnableProfiling: s.EnableProfiling,
|
||||||
|
EnableWatchCache: s.EnableWatchCache,
|
||||||
EnableIndex: true,
|
EnableIndex: true,
|
||||||
APIPrefix: s.APIPrefix,
|
APIPrefix: s.APIPrefix,
|
||||||
ExpAPIPrefix: s.ExpAPIPrefix,
|
ExpAPIPrefix: s.ExpAPIPrefix,
|
||||||
|
@ -36,6 +36,7 @@ import (
|
|||||||
clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
|
clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
|
||||||
"k8s.io/kubernetes/pkg/cloudprovider"
|
"k8s.io/kubernetes/pkg/cloudprovider"
|
||||||
"k8s.io/kubernetes/pkg/controller/autoscaler"
|
"k8s.io/kubernetes/pkg/controller/autoscaler"
|
||||||
|
"k8s.io/kubernetes/pkg/controller/autoscaler/metrics"
|
||||||
"k8s.io/kubernetes/pkg/controller/endpoint"
|
"k8s.io/kubernetes/pkg/controller/endpoint"
|
||||||
"k8s.io/kubernetes/pkg/controller/namespace"
|
"k8s.io/kubernetes/pkg/controller/namespace"
|
||||||
"k8s.io/kubernetes/pkg/controller/node"
|
"k8s.io/kubernetes/pkg/controller/node"
|
||||||
@ -291,7 +292,8 @@ func (s *CMServer) Run(_ []string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Fatalf("Invalid API configuration: %v", err)
|
glog.Fatalf("Invalid API configuration: %v", err)
|
||||||
}
|
}
|
||||||
horizontalPodAutoscalerController := autoscalercontroller.New(kubeClient, expClient)
|
horizontalPodAutoscalerController := autoscalercontroller.New(kubeClient, expClient,
|
||||||
|
metrics.NewHeapsterMetricsClient(kubeClient))
|
||||||
horizontalPodAutoscalerController.Run(s.HorizontalPodAutoscalerSyncPeriod)
|
horizontalPodAutoscalerController.Run(s.HorizontalPodAutoscalerSyncPeriod)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,9 +159,6 @@ func (s *ProxyServer) Run(_ []string) error {
|
|||||||
Namespace: "",
|
Namespace: "",
|
||||||
}
|
}
|
||||||
|
|
||||||
// Birth Cry
|
|
||||||
s.birthCry()
|
|
||||||
|
|
||||||
serviceConfig := config.NewServiceConfig()
|
serviceConfig := config.NewServiceConfig()
|
||||||
endpointsConfig := config.NewEndpointsConfig()
|
endpointsConfig := config.NewEndpointsConfig()
|
||||||
|
|
||||||
@ -200,7 +197,7 @@ func (s *ProxyServer) Run(_ []string) error {
|
|||||||
ipt := utiliptables.New(execer, protocol)
|
ipt := utiliptables.New(execer, protocol)
|
||||||
proxierUserspace, err := userspace.NewProxier(loadBalancer, s.BindAddress, ipt, s.PortRange, s.SyncPeriod)
|
proxierUserspace, err := userspace.NewProxier(loadBalancer, s.BindAddress, ipt, s.PortRange, s.SyncPeriod)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Fatalf("Unable to create proxer: %v", err)
|
glog.Fatalf("Unable to create proxier: %v", err)
|
||||||
}
|
}
|
||||||
proxier = proxierUserspace
|
proxier = proxierUserspace
|
||||||
// Remove artifacts from the pure-iptables Proxier.
|
// Remove artifacts from the pure-iptables Proxier.
|
||||||
@ -208,6 +205,9 @@ func (s *ProxyServer) Run(_ []string) error {
|
|||||||
iptables.CleanupLeftovers(ipt)
|
iptables.CleanupLeftovers(ipt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Birth Cry after the birth is successful
|
||||||
|
s.birthCry()
|
||||||
|
|
||||||
// Wire proxier to handle changes to services
|
// Wire proxier to handle changes to services
|
||||||
serviceConfig.RegisterHandler(proxier)
|
serviceConfig.RegisterHandler(proxier)
|
||||||
// And wire endpointsHandler to handle changes to endpoints to services
|
// And wire endpointsHandler to handle changes to endpoints to services
|
||||||
|
@ -124,7 +124,7 @@ type KubeletServer struct {
|
|||||||
MaxPods int
|
MaxPods int
|
||||||
DockerExecHandlerName string
|
DockerExecHandlerName string
|
||||||
ResolverConfig string
|
ResolverConfig string
|
||||||
|
CPUCFSQuota bool
|
||||||
// Flags intended for testing
|
// Flags intended for testing
|
||||||
|
|
||||||
// Crash immediately, rather than eating panics.
|
// Crash immediately, rather than eating panics.
|
||||||
@ -189,6 +189,7 @@ func NewKubeletServer() *KubeletServer {
|
|||||||
SystemContainer: "",
|
SystemContainer: "",
|
||||||
ConfigureCBR0: false,
|
ConfigureCBR0: false,
|
||||||
DockerExecHandlerName: "native",
|
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.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.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.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.
|
// 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.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]")
|
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,
|
MaxPods: s.MaxPods,
|
||||||
DockerExecHandler: dockerExecHandler,
|
DockerExecHandler: dockerExecHandler,
|
||||||
ResolverConfig: s.ResolverConfig,
|
ResolverConfig: s.ResolverConfig,
|
||||||
|
CPUCFSQuota: s.CPUCFSQuota,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -604,6 +607,7 @@ func SimpleKubelet(client *client.Client,
|
|||||||
MaxPods: 32,
|
MaxPods: 32,
|
||||||
DockerExecHandler: &dockertools.NativeExecHandler{},
|
DockerExecHandler: &dockertools.NativeExecHandler{},
|
||||||
ResolverConfig: kubelet.ResolvConfDefault,
|
ResolverConfig: kubelet.ResolvConfDefault,
|
||||||
|
CPUCFSQuota: false,
|
||||||
}
|
}
|
||||||
return &kcfg
|
return &kcfg
|
||||||
}
|
}
|
||||||
@ -774,6 +778,7 @@ type KubeletConfig struct {
|
|||||||
MaxPods int
|
MaxPods int
|
||||||
DockerExecHandler dockertools.ExecHandler
|
DockerExecHandler dockertools.ExecHandler
|
||||||
ResolverConfig string
|
ResolverConfig string
|
||||||
|
CPUCFSQuota bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func createAndInitKubelet(kc *KubeletConfig) (k KubeletBootstrap, pc *config.PodConfig, err error) {
|
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.PodCIDR,
|
||||||
kc.MaxPods,
|
kc.MaxPods,
|
||||||
kc.DockerExecHandler,
|
kc.DockerExecHandler,
|
||||||
kc.ResolverConfig)
|
kc.ResolverConfig,
|
||||||
|
kc.CPUCFSQuota)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
|
|
||||||
__debug()
|
__debug()
|
||||||
{
|
{
|
||||||
if [[ -n ${BASH_COMP_DEBUG_FILE} ]]; then
|
if [[ -n ${BASH_COMP_DEBUG_FILE} ]]; then
|
||||||
@ -8,6 +7,14 @@ __debug()
|
|||||||
fi
|
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()
|
__index_of_word()
|
||||||
{
|
{
|
||||||
local w word=$1
|
local w word=$1
|
||||||
@ -256,12 +263,10 @@ _kubectl_get()
|
|||||||
flags+=("--all-namespaces")
|
flags+=("--all-namespaces")
|
||||||
flags+=("--filename=")
|
flags+=("--filename=")
|
||||||
flags_with_completion+=("--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")
|
two_word_flags+=("-f")
|
||||||
flags_with_completion+=("-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+=("--help")
|
|
||||||
flags+=("-h")
|
|
||||||
flags+=("--label-columns=")
|
flags+=("--label-columns=")
|
||||||
two_word_flags+=("-L")
|
two_word_flags+=("-L")
|
||||||
flags+=("--no-headers")
|
flags+=("--no-headers")
|
||||||
@ -285,6 +290,7 @@ _kubectl_get()
|
|||||||
must_have_one_noun+=("deployment")
|
must_have_one_noun+=("deployment")
|
||||||
must_have_one_noun+=("endpoints")
|
must_have_one_noun+=("endpoints")
|
||||||
must_have_one_noun+=("event")
|
must_have_one_noun+=("event")
|
||||||
|
must_have_one_noun+=("horizontalpodautoscaler")
|
||||||
must_have_one_noun+=("limitrange")
|
must_have_one_noun+=("limitrange")
|
||||||
must_have_one_noun+=("namespace")
|
must_have_one_noun+=("namespace")
|
||||||
must_have_one_noun+=("node")
|
must_have_one_noun+=("node")
|
||||||
@ -312,12 +318,10 @@ _kubectl_describe()
|
|||||||
|
|
||||||
flags+=("--filename=")
|
flags+=("--filename=")
|
||||||
flags_with_completion+=("--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")
|
two_word_flags+=("-f")
|
||||||
flags_with_completion+=("-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+=("--help")
|
|
||||||
flags+=("-h")
|
|
||||||
flags+=("--selector=")
|
flags+=("--selector=")
|
||||||
two_word_flags+=("-l")
|
two_word_flags+=("-l")
|
||||||
|
|
||||||
@ -349,12 +353,10 @@ _kubectl_create()
|
|||||||
|
|
||||||
flags+=("--filename=")
|
flags+=("--filename=")
|
||||||
flags_with_completion+=("--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")
|
two_word_flags+=("-f")
|
||||||
flags_with_completion+=("-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+=("--help")
|
|
||||||
flags+=("-h")
|
|
||||||
flags+=("--output=")
|
flags+=("--output=")
|
||||||
two_word_flags+=("-o")
|
two_word_flags+=("-o")
|
||||||
flags+=("--validate")
|
flags+=("--validate")
|
||||||
@ -378,14 +380,12 @@ _kubectl_replace()
|
|||||||
flags+=("--cascade")
|
flags+=("--cascade")
|
||||||
flags+=("--filename=")
|
flags+=("--filename=")
|
||||||
flags_with_completion+=("--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")
|
two_word_flags+=("-f")
|
||||||
flags_with_completion+=("-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+=("--force")
|
||||||
flags+=("--grace-period=")
|
flags+=("--grace-period=")
|
||||||
flags+=("--help")
|
|
||||||
flags+=("-h")
|
|
||||||
flags+=("--output=")
|
flags+=("--output=")
|
||||||
two_word_flags+=("-o")
|
two_word_flags+=("-o")
|
||||||
flags+=("--timeout=")
|
flags+=("--timeout=")
|
||||||
@ -409,12 +409,10 @@ _kubectl_patch()
|
|||||||
|
|
||||||
flags+=("--filename=")
|
flags+=("--filename=")
|
||||||
flags_with_completion+=("--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")
|
two_word_flags+=("-f")
|
||||||
flags_with_completion+=("-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+=("--help")
|
|
||||||
flags+=("-h")
|
|
||||||
flags+=("--output=")
|
flags+=("--output=")
|
||||||
two_word_flags+=("-o")
|
two_word_flags+=("-o")
|
||||||
flags+=("--patch=")
|
flags+=("--patch=")
|
||||||
@ -440,13 +438,11 @@ _kubectl_delete()
|
|||||||
flags+=("--cascade")
|
flags+=("--cascade")
|
||||||
flags+=("--filename=")
|
flags+=("--filename=")
|
||||||
flags_with_completion+=("--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")
|
two_word_flags+=("-f")
|
||||||
flags_with_completion+=("-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+=("--grace-period=")
|
||||||
flags+=("--help")
|
|
||||||
flags+=("-h")
|
|
||||||
flags+=("--ignore-not-found")
|
flags+=("--ignore-not-found")
|
||||||
flags+=("--output=")
|
flags+=("--output=")
|
||||||
two_word_flags+=("-o")
|
two_word_flags+=("-o")
|
||||||
@ -460,6 +456,7 @@ _kubectl_delete()
|
|||||||
must_have_one_noun+=("deployment")
|
must_have_one_noun+=("deployment")
|
||||||
must_have_one_noun+=("endpoints")
|
must_have_one_noun+=("endpoints")
|
||||||
must_have_one_noun+=("event")
|
must_have_one_noun+=("event")
|
||||||
|
must_have_one_noun+=("horizontalpodautoscaler")
|
||||||
must_have_one_noun+=("limitrange")
|
must_have_one_noun+=("limitrange")
|
||||||
must_have_one_noun+=("namespace")
|
must_have_one_noun+=("namespace")
|
||||||
must_have_one_noun+=("node")
|
must_have_one_noun+=("node")
|
||||||
@ -485,8 +482,6 @@ _kubectl_namespace()
|
|||||||
flags_with_completion=()
|
flags_with_completion=()
|
||||||
flags_completion=()
|
flags_completion=()
|
||||||
|
|
||||||
flags+=("--help")
|
|
||||||
flags+=("-h")
|
|
||||||
|
|
||||||
must_have_one_flag=()
|
must_have_one_flag=()
|
||||||
must_have_one_noun=()
|
must_have_one_noun=()
|
||||||
@ -506,8 +501,6 @@ _kubectl_logs()
|
|||||||
two_word_flags+=("-c")
|
two_word_flags+=("-c")
|
||||||
flags+=("--follow")
|
flags+=("--follow")
|
||||||
flags+=("-f")
|
flags+=("-f")
|
||||||
flags+=("--help")
|
|
||||||
flags+=("-h")
|
|
||||||
flags+=("--interactive")
|
flags+=("--interactive")
|
||||||
flags+=("--previous")
|
flags+=("--previous")
|
||||||
flags+=("-p")
|
flags+=("-p")
|
||||||
@ -530,12 +523,10 @@ _kubectl_rolling-update()
|
|||||||
flags+=("--dry-run")
|
flags+=("--dry-run")
|
||||||
flags+=("--filename=")
|
flags+=("--filename=")
|
||||||
flags_with_completion+=("--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")
|
two_word_flags+=("-f")
|
||||||
flags_with_completion+=("-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+=("--help")
|
|
||||||
flags+=("-h")
|
|
||||||
flags+=("--image=")
|
flags+=("--image=")
|
||||||
flags+=("--no-headers")
|
flags+=("--no-headers")
|
||||||
flags+=("--output=")
|
flags+=("--output=")
|
||||||
@ -572,12 +563,10 @@ _kubectl_scale()
|
|||||||
flags+=("--current-replicas=")
|
flags+=("--current-replicas=")
|
||||||
flags+=("--filename=")
|
flags+=("--filename=")
|
||||||
flags_with_completion+=("--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")
|
two_word_flags+=("-f")
|
||||||
flags_with_completion+=("-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+=("--help")
|
|
||||||
flags+=("-h")
|
|
||||||
flags+=("--output=")
|
flags+=("--output=")
|
||||||
two_word_flags+=("-o")
|
two_word_flags+=("-o")
|
||||||
flags+=("--replicas=")
|
flags+=("--replicas=")
|
||||||
@ -601,8 +590,6 @@ _kubectl_attach()
|
|||||||
|
|
||||||
flags+=("--container=")
|
flags+=("--container=")
|
||||||
two_word_flags+=("-c")
|
two_word_flags+=("-c")
|
||||||
flags+=("--help")
|
|
||||||
flags+=("-h")
|
|
||||||
flags+=("--stdin")
|
flags+=("--stdin")
|
||||||
flags+=("-i")
|
flags+=("-i")
|
||||||
flags+=("--tty")
|
flags+=("--tty")
|
||||||
@ -624,8 +611,6 @@ _kubectl_exec()
|
|||||||
|
|
||||||
flags+=("--container=")
|
flags+=("--container=")
|
||||||
two_word_flags+=("-c")
|
two_word_flags+=("-c")
|
||||||
flags+=("--help")
|
|
||||||
flags+=("-h")
|
|
||||||
flags+=("--pod=")
|
flags+=("--pod=")
|
||||||
two_word_flags+=("-p")
|
two_word_flags+=("-p")
|
||||||
flags+=("--stdin")
|
flags+=("--stdin")
|
||||||
@ -647,8 +632,6 @@ _kubectl_port-forward()
|
|||||||
flags_with_completion=()
|
flags_with_completion=()
|
||||||
flags_completion=()
|
flags_completion=()
|
||||||
|
|
||||||
flags+=("--help")
|
|
||||||
flags+=("-h")
|
|
||||||
flags+=("--pod=")
|
flags+=("--pod=")
|
||||||
two_word_flags+=("-p")
|
two_word_flags+=("-p")
|
||||||
|
|
||||||
@ -670,8 +653,6 @@ _kubectl_proxy()
|
|||||||
flags+=("--accept-paths=")
|
flags+=("--accept-paths=")
|
||||||
flags+=("--api-prefix=")
|
flags+=("--api-prefix=")
|
||||||
flags+=("--disable-filter")
|
flags+=("--disable-filter")
|
||||||
flags+=("--help")
|
|
||||||
flags+=("-h")
|
|
||||||
flags+=("--port=")
|
flags+=("--port=")
|
||||||
two_word_flags+=("-p")
|
two_word_flags+=("-p")
|
||||||
flags+=("--reject-methods=")
|
flags+=("--reject-methods=")
|
||||||
@ -700,9 +681,8 @@ _kubectl_run()
|
|||||||
flags+=("--attach")
|
flags+=("--attach")
|
||||||
flags+=("--command")
|
flags+=("--command")
|
||||||
flags+=("--dry-run")
|
flags+=("--dry-run")
|
||||||
|
flags+=("--env=")
|
||||||
flags+=("--generator=")
|
flags+=("--generator=")
|
||||||
flags+=("--help")
|
|
||||||
flags+=("-h")
|
|
||||||
flags+=("--hostport=")
|
flags+=("--hostport=")
|
||||||
flags+=("--image=")
|
flags+=("--image=")
|
||||||
flags+=("--labels=")
|
flags+=("--labels=")
|
||||||
@ -743,13 +723,11 @@ _kubectl_stop()
|
|||||||
flags+=("--all")
|
flags+=("--all")
|
||||||
flags+=("--filename=")
|
flags+=("--filename=")
|
||||||
flags_with_completion+=("--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")
|
two_word_flags+=("-f")
|
||||||
flags_with_completion+=("-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+=("--grace-period=")
|
||||||
flags+=("--help")
|
|
||||||
flags+=("-h")
|
|
||||||
flags+=("--ignore-not-found")
|
flags+=("--ignore-not-found")
|
||||||
flags+=("--output=")
|
flags+=("--output=")
|
||||||
two_word_flags+=("-o")
|
two_word_flags+=("-o")
|
||||||
@ -777,13 +755,11 @@ _kubectl_expose()
|
|||||||
flags+=("--external-ip=")
|
flags+=("--external-ip=")
|
||||||
flags+=("--filename=")
|
flags+=("--filename=")
|
||||||
flags_with_completion+=("--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")
|
two_word_flags+=("-f")
|
||||||
flags_with_completion+=("-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+=("--generator=")
|
||||||
flags+=("--help")
|
|
||||||
flags+=("-h")
|
|
||||||
flags+=("--labels=")
|
flags+=("--labels=")
|
||||||
two_word_flags+=("-l")
|
two_word_flags+=("-l")
|
||||||
flags+=("--name=")
|
flags+=("--name=")
|
||||||
@ -820,14 +796,13 @@ _kubectl_label()
|
|||||||
flags_completion=()
|
flags_completion=()
|
||||||
|
|
||||||
flags+=("--all")
|
flags+=("--all")
|
||||||
|
flags+=("--dry-run")
|
||||||
flags+=("--filename=")
|
flags+=("--filename=")
|
||||||
flags_with_completion+=("--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")
|
two_word_flags+=("-f")
|
||||||
flags_with_completion+=("-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+=("--help")
|
|
||||||
flags+=("-h")
|
|
||||||
flags+=("--no-headers")
|
flags+=("--no-headers")
|
||||||
flags+=("--output=")
|
flags+=("--output=")
|
||||||
two_word_flags+=("-o")
|
two_word_flags+=("-o")
|
||||||
@ -848,6 +823,7 @@ _kubectl_label()
|
|||||||
must_have_one_noun+=("deployment")
|
must_have_one_noun+=("deployment")
|
||||||
must_have_one_noun+=("endpoints")
|
must_have_one_noun+=("endpoints")
|
||||||
must_have_one_noun+=("event")
|
must_have_one_noun+=("event")
|
||||||
|
must_have_one_noun+=("horizontalpodautoscaler")
|
||||||
must_have_one_noun+=("limitrange")
|
must_have_one_noun+=("limitrange")
|
||||||
must_have_one_noun+=("namespace")
|
must_have_one_noun+=("namespace")
|
||||||
must_have_one_noun+=("node")
|
must_have_one_noun+=("node")
|
||||||
@ -876,12 +852,10 @@ _kubectl_annotate()
|
|||||||
flags+=("--all")
|
flags+=("--all")
|
||||||
flags+=("--filename=")
|
flags+=("--filename=")
|
||||||
flags_with_completion+=("--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")
|
two_word_flags+=("-f")
|
||||||
flags_with_completion+=("-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+=("--help")
|
|
||||||
flags+=("-h")
|
|
||||||
flags+=("--overwrite")
|
flags+=("--overwrite")
|
||||||
flags+=("--resource-version=")
|
flags+=("--resource-version=")
|
||||||
|
|
||||||
@ -900,8 +874,6 @@ _kubectl_config_view()
|
|||||||
flags_completion=()
|
flags_completion=()
|
||||||
|
|
||||||
flags+=("--flatten")
|
flags+=("--flatten")
|
||||||
flags+=("--help")
|
|
||||||
flags+=("-h")
|
|
||||||
flags+=("--merge")
|
flags+=("--merge")
|
||||||
flags+=("--minify")
|
flags+=("--minify")
|
||||||
flags+=("--no-headers")
|
flags+=("--no-headers")
|
||||||
@ -932,8 +904,6 @@ _kubectl_config_set-cluster()
|
|||||||
flags+=("--api-version=")
|
flags+=("--api-version=")
|
||||||
flags+=("--certificate-authority=")
|
flags+=("--certificate-authority=")
|
||||||
flags+=("--embed-certs")
|
flags+=("--embed-certs")
|
||||||
flags+=("--help")
|
|
||||||
flags+=("-h")
|
|
||||||
flags+=("--insecure-skip-tls-verify")
|
flags+=("--insecure-skip-tls-verify")
|
||||||
flags+=("--server=")
|
flags+=("--server=")
|
||||||
|
|
||||||
@ -954,8 +924,6 @@ _kubectl_config_set-credentials()
|
|||||||
flags+=("--client-certificate=")
|
flags+=("--client-certificate=")
|
||||||
flags+=("--client-key=")
|
flags+=("--client-key=")
|
||||||
flags+=("--embed-certs")
|
flags+=("--embed-certs")
|
||||||
flags+=("--help")
|
|
||||||
flags+=("-h")
|
|
||||||
flags+=("--password=")
|
flags+=("--password=")
|
||||||
flags+=("--token=")
|
flags+=("--token=")
|
||||||
flags+=("--username=")
|
flags+=("--username=")
|
||||||
@ -975,8 +943,6 @@ _kubectl_config_set-context()
|
|||||||
flags_completion=()
|
flags_completion=()
|
||||||
|
|
||||||
flags+=("--cluster=")
|
flags+=("--cluster=")
|
||||||
flags+=("--help")
|
|
||||||
flags+=("-h")
|
|
||||||
flags+=("--namespace=")
|
flags+=("--namespace=")
|
||||||
flags+=("--user=")
|
flags+=("--user=")
|
||||||
|
|
||||||
@ -994,8 +960,6 @@ _kubectl_config_set()
|
|||||||
flags_with_completion=()
|
flags_with_completion=()
|
||||||
flags_completion=()
|
flags_completion=()
|
||||||
|
|
||||||
flags+=("--help")
|
|
||||||
flags+=("-h")
|
|
||||||
|
|
||||||
must_have_one_flag=()
|
must_have_one_flag=()
|
||||||
must_have_one_noun=()
|
must_have_one_noun=()
|
||||||
@ -1011,8 +975,6 @@ _kubectl_config_unset()
|
|||||||
flags_with_completion=()
|
flags_with_completion=()
|
||||||
flags_completion=()
|
flags_completion=()
|
||||||
|
|
||||||
flags+=("--help")
|
|
||||||
flags+=("-h")
|
|
||||||
|
|
||||||
must_have_one_flag=()
|
must_have_one_flag=()
|
||||||
must_have_one_noun=()
|
must_have_one_noun=()
|
||||||
@ -1028,8 +990,6 @@ _kubectl_config_use-context()
|
|||||||
flags_with_completion=()
|
flags_with_completion=()
|
||||||
flags_completion=()
|
flags_completion=()
|
||||||
|
|
||||||
flags+=("--help")
|
|
||||||
flags+=("-h")
|
|
||||||
|
|
||||||
must_have_one_flag=()
|
must_have_one_flag=()
|
||||||
must_have_one_noun=()
|
must_have_one_noun=()
|
||||||
@ -1052,8 +1012,6 @@ _kubectl_config()
|
|||||||
flags_with_completion=()
|
flags_with_completion=()
|
||||||
flags_completion=()
|
flags_completion=()
|
||||||
|
|
||||||
flags+=("--help")
|
|
||||||
flags+=("-h")
|
|
||||||
flags+=("--kubeconfig=")
|
flags+=("--kubeconfig=")
|
||||||
|
|
||||||
must_have_one_flag=()
|
must_have_one_flag=()
|
||||||
@ -1070,8 +1028,6 @@ _kubectl_cluster-info()
|
|||||||
flags_with_completion=()
|
flags_with_completion=()
|
||||||
flags_completion=()
|
flags_completion=()
|
||||||
|
|
||||||
flags+=("--help")
|
|
||||||
flags+=("-h")
|
|
||||||
|
|
||||||
must_have_one_flag=()
|
must_have_one_flag=()
|
||||||
must_have_one_noun=()
|
must_have_one_noun=()
|
||||||
@ -1087,8 +1043,6 @@ _kubectl_api-versions()
|
|||||||
flags_with_completion=()
|
flags_with_completion=()
|
||||||
flags_completion=()
|
flags_completion=()
|
||||||
|
|
||||||
flags+=("--help")
|
|
||||||
flags+=("-h")
|
|
||||||
|
|
||||||
must_have_one_flag=()
|
must_have_one_flag=()
|
||||||
must_have_one_noun=()
|
must_have_one_noun=()
|
||||||
@ -1106,8 +1060,6 @@ _kubectl_version()
|
|||||||
|
|
||||||
flags+=("--client")
|
flags+=("--client")
|
||||||
flags+=("-c")
|
flags+=("-c")
|
||||||
flags+=("--help")
|
|
||||||
flags+=("-h")
|
|
||||||
|
|
||||||
must_have_one_flag=()
|
must_have_one_flag=()
|
||||||
must_have_one_noun=()
|
must_have_one_noun=()
|
||||||
@ -1153,8 +1105,6 @@ _kubectl()
|
|||||||
flags+=("--client-key=")
|
flags+=("--client-key=")
|
||||||
flags+=("--cluster=")
|
flags+=("--cluster=")
|
||||||
flags+=("--context=")
|
flags+=("--context=")
|
||||||
flags+=("--help")
|
|
||||||
flags+=("-h")
|
|
||||||
flags+=("--insecure-skip-tls-verify")
|
flags+=("--insecure-skip-tls-verify")
|
||||||
flags+=("--kubeconfig=")
|
flags+=("--kubeconfig=")
|
||||||
flags+=("--log-backtrace-at=")
|
flags+=("--log-backtrace-at=")
|
||||||
@ -1180,7 +1130,11 @@ _kubectl()
|
|||||||
__start_kubectl()
|
__start_kubectl()
|
||||||
{
|
{
|
||||||
local cur prev words cword
|
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 c=0
|
||||||
local flags=()
|
local flags=()
|
||||||
|
@ -318,7 +318,7 @@ func TestExecutorLaunchAndKillTask(t *testing.T) {
|
|||||||
Updates: updates,
|
Updates: updates,
|
||||||
APIClient: client.NewOrDie(&client.Config{
|
APIClient: client.NewOrDie(&client.Config{
|
||||||
Host: testApiServer.server.URL,
|
Host: testApiServer.server.URL,
|
||||||
Version: testapi.Version(),
|
Version: testapi.Default.Version(),
|
||||||
}),
|
}),
|
||||||
Kubelet: &fakeKubelet{
|
Kubelet: &fakeKubelet{
|
||||||
Kubelet: &kubelet.Kubelet{},
|
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")
|
assert.Equal(t, nil, err, "must be able to create a task from a pod")
|
||||||
|
|
||||||
taskInfo := podTask.BuildTaskInfo()
|
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")
|
assert.Equal(t, nil, err, "must be able to encode a pod's spec data")
|
||||||
taskInfo.Data = data
|
taskInfo.Data = data
|
||||||
var statusUpdateCalls sync.WaitGroup
|
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
|
Updates: make(chan interface{}, 1), // allow kube-executor source to proceed past init
|
||||||
APIClient: client.NewOrDie(&client.Config{
|
APIClient: client.NewOrDie(&client.Config{
|
||||||
Host: testApiServer.server.URL,
|
Host: testApiServer.server.URL,
|
||||||
Version: testapi.Version(),
|
Version: testapi.Default.Version(),
|
||||||
}),
|
}),
|
||||||
Kubelet: &kubelet.Kubelet{},
|
Kubelet: &kubelet.Kubelet{},
|
||||||
PodStatusFunc: func(kl KubeletInterface, pod *api.Pod) (*api.PodStatus, error) {
|
PodStatusFunc: func(kl KubeletInterface, pod *api.Pod) (*api.PodStatus, error) {
|
||||||
@ -565,7 +565,7 @@ func TestExecutorFrameworkMessage(t *testing.T) {
|
|||||||
Updates: make(chan interface{}, 1024),
|
Updates: make(chan interface{}, 1024),
|
||||||
APIClient: client.NewOrDie(&client.Config{
|
APIClient: client.NewOrDie(&client.Config{
|
||||||
Host: testApiServer.server.URL,
|
Host: testApiServer.server.URL,
|
||||||
Version: testapi.Version(),
|
Version: testapi.Default.Version(),
|
||||||
}),
|
}),
|
||||||
Kubelet: &fakeKubelet{
|
Kubelet: &fakeKubelet{
|
||||||
Kubelet: &kubelet.Kubelet{},
|
Kubelet: &kubelet.Kubelet{},
|
||||||
@ -602,7 +602,7 @@ func TestExecutorFrameworkMessage(t *testing.T) {
|
|||||||
*pod, &mesosproto.ExecutorInfo{})
|
*pod, &mesosproto.ExecutorInfo{})
|
||||||
|
|
||||||
taskInfo := podTask.BuildTaskInfo()
|
taskInfo := podTask.BuildTaskInfo()
|
||||||
data, _ := testapi.Codec().Encode(pod)
|
data, _ := testapi.Default.Codec().Encode(pod)
|
||||||
taskInfo.Data = data
|
taskInfo.Data = data
|
||||||
|
|
||||||
mockDriver.On(
|
mockDriver.On(
|
||||||
@ -660,11 +660,11 @@ func TestExecutorFrameworkMessage(t *testing.T) {
|
|||||||
func NewTestPod(i int) *api.Pod {
|
func NewTestPod(i int) *api.Pod {
|
||||||
name := fmt.Sprintf("pod%d", i)
|
name := fmt.Sprintf("pod%d", i)
|
||||||
return &api.Pod{
|
return &api.Pod{
|
||||||
TypeMeta: api.TypeMeta{APIVersion: testapi.Version()},
|
TypeMeta: api.TypeMeta{APIVersion: testapi.Default.Version()},
|
||||||
ObjectMeta: api.ObjectMeta{
|
ObjectMeta: api.ObjectMeta{
|
||||||
Name: name,
|
Name: name,
|
||||||
Namespace: api.NamespaceDefault,
|
Namespace: api.NamespaceDefault,
|
||||||
SelfLink: testapi.SelfLink("pods", string(i)),
|
SelfLink: testapi.Default.SelfLink("pods", string(i)),
|
||||||
},
|
},
|
||||||
Spec: api.PodSpec{
|
Spec: api.PodSpec{
|
||||||
Containers: []api.Container{
|
Containers: []api.Container{
|
||||||
@ -710,7 +710,7 @@ func NewTestServer(t *testing.T, namespace string, pods *api.PodList) *TestServe
|
|||||||
}
|
}
|
||||||
mux := http.NewServeMux()
|
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)
|
w.WriteHeader(http.StatusOK)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -228,6 +228,7 @@ func (s *KubeletExecutorServer) Run(hks hyperkube.Interface, _ []string) error {
|
|||||||
MaxPods: s.MaxPods,
|
MaxPods: s.MaxPods,
|
||||||
DockerExecHandler: dockerExecHandler,
|
DockerExecHandler: dockerExecHandler,
|
||||||
ResolverConfig: s.ResolverConfig,
|
ResolverConfig: s.ResolverConfig,
|
||||||
|
CPUCFSQuota: s.CPUCFSQuota,
|
||||||
}
|
}
|
||||||
|
|
||||||
kcfg.NodeName = kcfg.Hostname
|
kcfg.NodeName = kcfg.Hostname
|
||||||
@ -330,6 +331,7 @@ func (ks *KubeletExecutorServer) createAndInitKubelet(
|
|||||||
kc.MaxPods,
|
kc.MaxPods,
|
||||||
kc.DockerExecHandler,
|
kc.DockerExecHandler,
|
||||||
kc.ResolverConfig,
|
kc.ResolverConfig,
|
||||||
|
kc.CPUCFSQuota,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -21,15 +21,15 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/signal"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"syscall"
|
||||||
|
|
||||||
exservice "k8s.io/kubernetes/contrib/mesos/pkg/executor/service"
|
exservice "k8s.io/kubernetes/contrib/mesos/pkg/executor/service"
|
||||||
"k8s.io/kubernetes/contrib/mesos/pkg/hyperkube"
|
"k8s.io/kubernetes/contrib/mesos/pkg/hyperkube"
|
||||||
"k8s.io/kubernetes/contrib/mesos/pkg/minion/config"
|
"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"
|
"k8s.io/kubernetes/pkg/api/resource"
|
||||||
client "k8s.io/kubernetes/pkg/client/unversioned"
|
client "k8s.io/kubernetes/pkg/client/unversioned"
|
||||||
|
|
||||||
@ -39,6 +39,11 @@ import (
|
|||||||
"gopkg.in/natefinch/lumberjack.v2"
|
"gopkg.in/natefinch/lumberjack.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
proxyLogFilename = "proxy.log"
|
||||||
|
executorLogFilename = "executor.log"
|
||||||
|
)
|
||||||
|
|
||||||
type MinionServer struct {
|
type MinionServer struct {
|
||||||
// embed the executor server to be able to use its flags
|
// 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
|
// 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
|
hks hyperkube.Interface
|
||||||
clientConfig *client.Config
|
clientConfig *client.Config
|
||||||
kmBinary string
|
kmBinary string
|
||||||
done chan struct{} // closed when shutting down
|
tasks []*tasks.Task
|
||||||
exit chan error // to signal fatal errors
|
|
||||||
|
|
||||||
pathOverride string // the PATH environment for the sub-processes
|
pathOverride string // the PATH environment for the sub-processes
|
||||||
cgroupPrefix string // e.g. mesos
|
cgroupPrefix string // e.g. mesos
|
||||||
cgroupRoot string // e.g. /mesos/{container-id}, determined at runtime
|
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
|
logMaxSize resource.Quantity
|
||||||
logMaxBackups int
|
logMaxBackups int
|
||||||
logMaxAgeInDays int
|
logMaxAgeInDays int
|
||||||
|
logVerbosity int32 // see glog.Level
|
||||||
|
|
||||||
runProxy bool
|
runProxy bool
|
||||||
proxyLogV int
|
proxyLogV int
|
||||||
@ -69,15 +76,12 @@ func NewMinionServer() *MinionServer {
|
|||||||
s := &MinionServer{
|
s := &MinionServer{
|
||||||
KubeletExecutorServer: exservice.NewKubeletExecutorServer(),
|
KubeletExecutorServer: exservice.NewKubeletExecutorServer(),
|
||||||
privateMountNS: false, // disabled until Docker supports customization of the parent mount namespace
|
privateMountNS: false, // disabled until Docker supports customization of the parent mount namespace
|
||||||
done: make(chan struct{}),
|
cgroupPrefix: config.DefaultCgroupPrefix,
|
||||||
exit: make(chan error),
|
containPodResources: true,
|
||||||
|
logMaxSize: config.DefaultLogMaxSize(),
|
||||||
cgroupPrefix: config.DefaultCgroupPrefix,
|
logMaxBackups: config.DefaultLogMaxBackups,
|
||||||
logMaxSize: config.DefaultLogMaxSize(),
|
logMaxAgeInDays: config.DefaultLogMaxAgeInDays,
|
||||||
logMaxBackups: config.DefaultLogMaxBackups,
|
runProxy: true,
|
||||||
logMaxAgeInDays: config.DefaultLogMaxAgeInDays,
|
|
||||||
|
|
||||||
runProxy: true,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// cache this for later use
|
// cache this for later use
|
||||||
@ -131,7 +135,7 @@ func (ms *MinionServer) launchProxyServer() {
|
|||||||
fmt.Sprintf("--bind-address=%s", bindAddress),
|
fmt.Sprintf("--bind-address=%s", bindAddress),
|
||||||
fmt.Sprintf("--v=%d", ms.proxyLogV),
|
fmt.Sprintf("--v=%d", ms.proxyLogV),
|
||||||
"--logtostderr=true",
|
"--logtostderr=true",
|
||||||
"--resource-container=" + path.Join("/", ms.cgroupRoot, "kube-proxy"),
|
"--resource-container=" + path.Join("/", ms.mesosCgroup, "kube-proxy"),
|
||||||
}
|
}
|
||||||
|
|
||||||
if ms.clientConfig.Host != "" {
|
if ms.clientConfig.Host != "" {
|
||||||
@ -141,10 +145,13 @@ func (ms *MinionServer) launchProxyServer() {
|
|||||||
args = append(args, fmt.Sprintf("--hostname-override=%s", ms.KubeletExecutorServer.HostnameOverride))
|
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:]
|
allArgs := os.Args[1:]
|
||||||
|
|
||||||
// filter out minion flags, leaving those for the executor
|
// filter out minion flags, leaving those for the executor
|
||||||
@ -153,117 +160,71 @@ func (ms *MinionServer) launchExecutorServer() {
|
|||||||
ms.AddExecutorFlags(executorFlags)
|
ms.AddExecutorFlags(executorFlags)
|
||||||
executorArgs, _ := filterArgsByFlagSet(allArgs, 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 != "" {
|
if ms.cgroupRoot != "" {
|
||||||
executorArgs = append(executorArgs, "--cgroup-root="+ms.cgroupRoot)
|
executorArgs = append(executorArgs, "--cgroup-root="+ms.cgroupRoot)
|
||||||
}
|
}
|
||||||
|
|
||||||
// run executor and quit minion server when this exits cleanly
|
// run executor and quit minion server when this exits cleanly
|
||||||
err := ms.launchHyperkubeServer(hyperkube.CommandExecutor, &executorArgs, "executor.log")
|
execDied := make(chan struct{})
|
||||||
if err != nil {
|
decorator := func(t *tasks.Task) *tasks.Task {
|
||||||
// just return, executor will be restarted on error
|
t.Finished = func(_ bool) bool {
|
||||||
log.Error(err)
|
// this func implements the task.finished spec, so when the executor exits
|
||||||
return
|
// 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
|
||||||
}
|
}
|
||||||
|
ms.launchHyperkubeServer(hyperkube.CommandExecutor, executorArgs, executorLogFilename, decorator)
|
||||||
log.Info("Executor exited cleanly, stopping the minion")
|
return execDied
|
||||||
ms.exit <- nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
log.V(2).Infof("Spawning hyperkube %v with args '%+v'", server, args)
|
||||||
|
|
||||||
// prepare parameters
|
kmArgs := append([]string{server}, args...)
|
||||||
kmArgs := []string{server}
|
maxSize := ms.logMaxSize.Value()
|
||||||
for _, arg := range *args {
|
if maxSize > 0 {
|
||||||
kmArgs = append(kmArgs, arg)
|
// convert to MB
|
||||||
}
|
maxSize = maxSize / 1024 / 1024
|
||||||
|
if maxSize == 0 {
|
||||||
// create command
|
log.Warning("maximal log file size is rounded to 1 MB")
|
||||||
cmd := exec.Command(ms.kmBinary, kmArgs...)
|
maxSize = 1
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
writer := &lumberjack.Logger{
|
}
|
||||||
|
|
||||||
|
writerFunc := func() io.WriteCloser {
|
||||||
|
return &lumberjack.Logger{
|
||||||
Filename: logFileName,
|
Filename: logFileName,
|
||||||
MaxSize: int(maxSize),
|
MaxSize: int(maxSize),
|
||||||
MaxBackups: ms.logMaxBackups,
|
MaxBackups: ms.logMaxBackups,
|
||||||
MaxAge: ms.logMaxAgeInDays,
|
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
|
// use given environment, but add /usr/sbin to the path for the iptables binary used in kube-proxy
|
||||||
|
var kmEnv []string
|
||||||
if ms.pathOverride != "" {
|
if ms.pathOverride != "" {
|
||||||
env := os.Environ()
|
env := os.Environ()
|
||||||
cmd.Env = make([]string, 0, len(env))
|
kmEnv = make([]string, 0, len(env))
|
||||||
for _, e := range env {
|
for _, e := range env {
|
||||||
if !strings.HasPrefix(e, "PATH=") {
|
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
|
t := tasks.New(server, ms.kmBinary, kmArgs, kmEnv, writerFunc)
|
||||||
// wait for the proxy process to end (and release resources after).
|
if decorator != nil {
|
||||||
if err := cmd.Start(); err != nil {
|
t = decorator(t)
|
||||||
// fatal error => terminate minion
|
|
||||||
err = fmt.Errorf("error starting %v: %v", server, err)
|
|
||||||
ms.exit <- err
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
close(ch)
|
go t.Start()
|
||||||
if err := cmd.Wait(); err != nil {
|
ms.tasks = append(ms.tasks, t)
|
||||||
log.Errorf("%v exited with error: %v", server, err)
|
|
||||||
err = fmt.Errorf("%v exited with error: %v", server, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// runs the main kubelet loop, closing the kubeletFinished chan when the loop exits.
|
// 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
|
ms.clientConfig = clientConfig
|
||||||
|
|
||||||
// derive the executor cgroup and use it as:
|
// 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 kubelet container
|
||||||
// - parent of kube-proxy 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
|
cgroupLogger := log.Infof
|
||||||
if ms.cgroupRoot == "" {
|
if ms.cgroupRoot == "" {
|
||||||
cgroupLogger = log.Warningf
|
cgroupLogger = log.Warningf
|
||||||
}
|
}
|
||||||
|
|
||||||
cgroupLogger("using cgroup-root %q", ms.cgroupRoot)
|
cgroupLogger("using cgroup-root %q", ms.cgroupRoot)
|
||||||
|
|
||||||
// run subprocesses until ms.done is closed on return of this function
|
// run subprocesses until ms.done is closed on return of this function
|
||||||
defer close(ms.done)
|
|
||||||
if ms.runProxy {
|
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
|
// abort closes when the kubelet-executor dies
|
||||||
// don't close ms.exit here to avoid panics of go routines writing an error to it
|
abort := ms.launchExecutorServer()
|
||||||
return <-ms.exit
|
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) {
|
func (ms *MinionServer) AddExecutorFlags(fs *pflag.FlagSet) {
|
||||||
ms.KubeletExecutorServer.AddFlags(fs)
|
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) {
|
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.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.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.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
|
// log file flags
|
||||||
fs.Var(resource.NewQuantityFlagValue(&ms.logMaxSize), "max-log-size", "Maximum log file size for the executor and proxy before rotation")
|
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"
|
"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
|
// 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)
|
podName := fmt.Sprintf("%s/%s", task.Pod.Namespace, task.Pod.Name)
|
||||||
var acceptedOffer offers.Perishable
|
var acceptedOffer offers.Perishable
|
||||||
err := r.Walk(func(p offers.Perishable) (bool, error) {
|
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 {
|
if offer == nil {
|
||||||
return false, fmt.Errorf("nil offer while scheduling task %v", task.ID)
|
return false, fmt.Errorf("nil offer while scheduling task %v", task.ID)
|
||||||
}
|
}
|
||||||
if task.AcceptOffer(offer) {
|
if fps.FitPredicate()(task, offer) {
|
||||||
if p.Acquire() {
|
if p.Acquire() {
|
||||||
acceptedOffer = p
|
acceptedOffer = p
|
||||||
log.V(3).Infof("Pod %s accepted offer %v", podName, offer.Id.GetValue())
|
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)
|
ok = args.Bool(1)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
func (m *MockScheduler) algorithm() (f PodScheduleFunc) {
|
func (m *MockScheduler) algorithm() (f PodScheduler) {
|
||||||
args := m.Called()
|
args := m.Called()
|
||||||
x := args.Get(0)
|
x := args.Get(0)
|
||||||
if x != nil {
|
if x != nil {
|
||||||
f = x.(PodScheduleFunc)
|
f = x.(PodScheduler)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,6 @@ import (
|
|||||||
"k8s.io/kubernetes/contrib/mesos/pkg/runtime"
|
"k8s.io/kubernetes/contrib/mesos/pkg/runtime"
|
||||||
annotation "k8s.io/kubernetes/contrib/mesos/pkg/scheduler/meta"
|
annotation "k8s.io/kubernetes/contrib/mesos/pkg/scheduler/meta"
|
||||||
"k8s.io/kubernetes/contrib/mesos/pkg/scheduler/podtask"
|
"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"
|
||||||
"k8s.io/kubernetes/pkg/api/errors"
|
"k8s.io/kubernetes/pkg/api/errors"
|
||||||
client "k8s.io/kubernetes/pkg/client/unversioned"
|
client "k8s.io/kubernetes/pkg/client/unversioned"
|
||||||
@ -56,8 +55,9 @@ const (
|
|||||||
// scheduler abstraction to allow for easier unit testing
|
// scheduler abstraction to allow for easier unit testing
|
||||||
type schedulerInterface interface {
|
type schedulerInterface interface {
|
||||||
sync.Locker // synchronize scheduler plugin operations
|
sync.Locker // synchronize scheduler plugin operations
|
||||||
|
|
||||||
SlaveIndex
|
SlaveIndex
|
||||||
algorithm() PodScheduleFunc // see types.go
|
algorithm() PodScheduler
|
||||||
offers() offers.Registry
|
offers() offers.Registry
|
||||||
tasks() podtask.Registry
|
tasks() podtask.Registry
|
||||||
|
|
||||||
@ -76,8 +76,8 @@ type k8smScheduler struct {
|
|||||||
internal *KubernetesScheduler
|
internal *KubernetesScheduler
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *k8smScheduler) algorithm() PodScheduleFunc {
|
func (k *k8smScheduler) algorithm() PodScheduler {
|
||||||
return k.internal.scheduleFunc
|
return k.internal
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *k8smScheduler) offers() offers.Registry {
|
func (k *k8smScheduler) offers() offers.Registry {
|
||||||
@ -231,10 +231,8 @@ func (b *binder) prepareTaskForLaunch(ctx api.Context, machine string, task *pod
|
|||||||
}
|
}
|
||||||
|
|
||||||
type kubeScheduler struct {
|
type kubeScheduler struct {
|
||||||
api schedulerInterface
|
api schedulerInterface
|
||||||
podUpdates queue.FIFO
|
podUpdates queue.FIFO
|
||||||
defaultContainerCPULimit mresource.CPUShares
|
|
||||||
defaultContainerMemLimit mresource.MegaBytes
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// recoverAssignedSlave recovers the assigned Mesos slave from a pod by searching
|
// 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 {
|
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 {
|
if err != nil {
|
||||||
return "", err
|
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)
|
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.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 {
|
if err := k.api.tasks().Update(task); err != nil {
|
||||||
offer.Release()
|
offer.Release()
|
||||||
@ -556,7 +544,7 @@ func (k *errorHandler) handleSchedulingError(pod *api.Pod, schedulingErr error)
|
|||||||
defer k.api.Unlock()
|
defer k.api.Unlock()
|
||||||
switch task, state := k.api.tasks().Get(task.ID); state {
|
switch task, state := k.api.tasks().Get(task.ID); state {
|
||||||
case podtask.StatePending:
|
case podtask.StatePending:
|
||||||
return !task.Has(podtask.Launched) && task.AcceptOffer(offer)
|
return !task.Has(podtask.Launched) && k.api.algorithm().FitPredicate()(task, offer)
|
||||||
default:
|
default:
|
||||||
// no point in continuing to check for matching offers
|
// no point in continuing to check for matching offers
|
||||||
return true
|
return true
|
||||||
@ -698,10 +686,8 @@ func (k *KubernetesScheduler) NewPluginConfig(terminate <-chan struct{}, mux *ht
|
|||||||
Config: &plugin.Config{
|
Config: &plugin.Config{
|
||||||
MinionLister: nil,
|
MinionLister: nil,
|
||||||
Algorithm: &kubeScheduler{
|
Algorithm: &kubeScheduler{
|
||||||
api: kapi,
|
api: kapi,
|
||||||
podUpdates: podUpdates,
|
podUpdates: podUpdates,
|
||||||
defaultContainerCPULimit: k.defaultContainerCPULimit,
|
|
||||||
defaultContainerMemLimit: k.defaultContainerMemLimit,
|
|
||||||
},
|
},
|
||||||
Binder: &binder{api: kapi},
|
Binder: &binder{api: kapi},
|
||||||
NextPod: q.yield,
|
NextPod: q.yield,
|
||||||
|
@ -61,13 +61,13 @@ func NewTestServer(t *testing.T, namespace string, mockPodListWatch *MockPodsLis
|
|||||||
}
|
}
|
||||||
mux := http.NewServeMux()
|
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)
|
w.WriteHeader(http.StatusOK)
|
||||||
pods := mockPodListWatch.Pods()
|
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) {
|
mux.HandleFunc(podsPrefix, func(w http.ResponseWriter, r *http.Request) {
|
||||||
name := r.URL.Path[len(podsPrefix):]
|
name := r.URL.Path[len(podsPrefix):]
|
||||||
|
|
||||||
@ -79,13 +79,13 @@ func NewTestServer(t *testing.T, namespace string, mockPodListWatch *MockPodsLis
|
|||||||
p := mockPodListWatch.GetPod(name)
|
p := mockPodListWatch.GetPod(name)
|
||||||
if p != nil {
|
if p != nil {
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
w.Write([]byte(runtime.EncodeOrDie(testapi.Codec(), p)))
|
w.Write([]byte(runtime.EncodeOrDie(testapi.Default.Codec(), p)))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.WriteHeader(http.StatusNotFound)
|
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)
|
w.WriteHeader(http.StatusOK)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -196,7 +196,7 @@ func NewTestPod() (*api.Pod, int) {
|
|||||||
currentPodNum = currentPodNum + 1
|
currentPodNum = currentPodNum + 1
|
||||||
name := fmt.Sprintf("pod%d", currentPodNum)
|
name := fmt.Sprintf("pod%d", currentPodNum)
|
||||||
return &api.Pod{
|
return &api.Pod{
|
||||||
TypeMeta: api.TypeMeta{APIVersion: testapi.Version()},
|
TypeMeta: api.TypeMeta{APIVersion: testapi.Default.Version()},
|
||||||
ObjectMeta: api.ObjectMeta{
|
ObjectMeta: api.ObjectMeta{
|
||||||
Name: name,
|
Name: name,
|
||||||
Namespace: api.NamespaceDefault,
|
Namespace: api.NamespaceDefault,
|
||||||
@ -393,13 +393,14 @@ func TestPlugin_LifeCycle(t *testing.T) {
|
|||||||
executor.Data = []byte{0, 1, 2}
|
executor.Data = []byte{0, 1, 2}
|
||||||
|
|
||||||
// create scheduler
|
// create scheduler
|
||||||
|
as := NewAllocationStrategy(
|
||||||
|
podtask.DefaultPredicate,
|
||||||
|
podtask.NewDefaultProcurement(mresource.DefaultDefaultContainerCPULimit, mresource.DefaultDefaultContainerMemLimit))
|
||||||
testScheduler := New(Config{
|
testScheduler := New(Config{
|
||||||
Executor: executor,
|
Executor: executor,
|
||||||
Client: client.NewOrDie(&client.Config{Host: testApiServer.server.URL, Version: testapi.Version()}),
|
Client: client.NewOrDie(&client.Config{Host: testApiServer.server.URL, Version: testapi.Default.Version()}),
|
||||||
ScheduleFunc: FCFSScheduleFunc,
|
Scheduler: NewFCFSPodScheduler(as),
|
||||||
Schedcfg: *schedcfg.CreateDefaultConfig(),
|
Schedcfg: *schedcfg.CreateDefaultConfig(),
|
||||||
DefaultContainerCPULimit: mresource.DefaultDefaultContainerCPULimit,
|
|
||||||
DefaultContainerMemLimit: mresource.DefaultDefaultContainerMemLimit,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
assert.NotNil(testScheduler.client, "client is nil")
|
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 (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gogo/protobuf/proto"
|
"github.com/gogo/protobuf/proto"
|
||||||
@ -28,7 +27,6 @@ import (
|
|||||||
"k8s.io/kubernetes/contrib/mesos/pkg/scheduler/metrics"
|
"k8s.io/kubernetes/contrib/mesos/pkg/scheduler/metrics"
|
||||||
mresource "k8s.io/kubernetes/contrib/mesos/pkg/scheduler/resource"
|
mresource "k8s.io/kubernetes/contrib/mesos/pkg/scheduler/resource"
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/labels"
|
|
||||||
|
|
||||||
log "github.com/golang/glog"
|
log "github.com/golang/glog"
|
||||||
mesos "github.com/mesos/mesos-go/mesosproto"
|
mesos "github.com/mesos/mesos-go/mesosproto"
|
||||||
@ -150,59 +148,6 @@ func (t *T) BuildTaskInfo() *mesos.TaskInfo {
|
|||||||
return info
|
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
|
// 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.
|
// has already been assigned to a task but for some reason is no longer valid.
|
||||||
func (t *T) Reset() {
|
func (t *T) Reset() {
|
||||||
@ -211,65 +156,6 @@ func (t *T) Reset() {
|
|||||||
t.Spec = Spec{}
|
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) {
|
func (t *T) Set(f FlagType) {
|
||||||
t.Flags[f] = struct{}{}
|
t.Flags[f] = struct{}{}
|
||||||
if Launched == f {
|
if Launched == f {
|
||||||
|
@ -146,10 +146,10 @@ func TestEmptyOffer(t *testing.T) {
|
|||||||
mresource.LimitPodCPU(&task.Pod, mresource.DefaultDefaultContainerCPULimit)
|
mresource.LimitPodCPU(&task.Pod, mresource.DefaultDefaultContainerCPULimit)
|
||||||
mresource.LimitPodMem(&task.Pod, mresource.DefaultDefaultContainerMemLimit)
|
mresource.LimitPodMem(&task.Pod, mresource.DefaultDefaultContainerMemLimit)
|
||||||
|
|
||||||
if ok := task.AcceptOffer(nil); ok {
|
if ok := DefaultPredicate(task, nil); ok {
|
||||||
t.Fatalf("accepted nil offer")
|
t.Fatalf("accepted nil offer")
|
||||||
}
|
}
|
||||||
if ok := task.AcceptOffer(&mesos.Offer{}); ok {
|
if ok := DefaultPredicate(task, &mesos.Offer{}); ok {
|
||||||
t.Fatalf("accepted empty offer")
|
t.Fatalf("accepted empty offer")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -176,7 +176,7 @@ func TestNoPortsInPodOrOffer(t *testing.T) {
|
|||||||
mutil.NewScalarResource("mem", 0.001),
|
mutil.NewScalarResource("mem", 0.001),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if ok := task.AcceptOffer(offer); ok {
|
if ok := DefaultPredicate(task, offer); ok {
|
||||||
t.Fatalf("accepted offer %v:", offer)
|
t.Fatalf("accepted offer %v:", offer)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,7 +186,7 @@ func TestNoPortsInPodOrOffer(t *testing.T) {
|
|||||||
mutil.NewScalarResource("mem", t_min_mem),
|
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)
|
t.Fatalf("did not accepted offer %v:", offer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -203,7 +203,7 @@ func TestAcceptOfferPorts(t *testing.T) {
|
|||||||
rangeResource("ports", []uint64{1, 1}),
|
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)
|
t.Fatalf("did not accepted offer %v:", offer)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,17 +218,17 @@ func TestAcceptOfferPorts(t *testing.T) {
|
|||||||
mresource.LimitPodCPU(&task.Pod, mresource.DefaultDefaultContainerCPULimit)
|
mresource.LimitPodCPU(&task.Pod, mresource.DefaultDefaultContainerCPULimit)
|
||||||
mresource.LimitPodMem(&task.Pod, mresource.DefaultDefaultContainerMemLimit)
|
mresource.LimitPodMem(&task.Pod, mresource.DefaultDefaultContainerMemLimit)
|
||||||
|
|
||||||
if ok := task.AcceptOffer(offer); ok {
|
if ok := DefaultPredicate(task, offer); ok {
|
||||||
t.Fatalf("accepted offer %v:", offer)
|
t.Fatalf("accepted offer %v:", offer)
|
||||||
}
|
}
|
||||||
|
|
||||||
pod.Spec.Containers[0].Ports[0].HostPort = 1
|
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)
|
t.Fatalf("did not accepted offer %v:", offer)
|
||||||
}
|
}
|
||||||
|
|
||||||
pod.Spec.Containers[0].Ports[0].HostPort = 0
|
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)
|
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("cpus", t_min_cpu),
|
||||||
mutil.NewScalarResource("mem", t_min_mem),
|
mutil.NewScalarResource("mem", t_min_mem),
|
||||||
}
|
}
|
||||||
if ok := task.AcceptOffer(offer); ok {
|
if ok := DefaultPredicate(task, offer); ok {
|
||||||
t.Fatalf("accepted offer %v:", offer)
|
t.Fatalf("accepted offer %v:", offer)
|
||||||
}
|
}
|
||||||
|
|
||||||
pod.Spec.Containers[0].Ports[0].HostPort = 1
|
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)
|
t.Fatalf("accepted offer %v:", offer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -297,7 +297,7 @@ func TestNodeSelector(t *testing.T) {
|
|||||||
},
|
},
|
||||||
Attributes: ts.attrs,
|
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)
|
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/meta"
|
||||||
"k8s.io/kubernetes/contrib/mesos/pkg/scheduler/metrics"
|
"k8s.io/kubernetes/contrib/mesos/pkg/scheduler/metrics"
|
||||||
"k8s.io/kubernetes/contrib/mesos/pkg/scheduler/podtask"
|
"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/contrib/mesos/pkg/scheduler/uid"
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/api/errors"
|
"k8s.io/kubernetes/pkg/api/errors"
|
||||||
@ -118,19 +117,17 @@ type KubernetesScheduler struct {
|
|||||||
// and the invoking the pod registry interfaces.
|
// and the invoking the pod registry interfaces.
|
||||||
// In particular, changes to podtask.T objects are currently guarded by this lock.
|
// In particular, changes to podtask.T objects are currently guarded by this lock.
|
||||||
*sync.RWMutex
|
*sync.RWMutex
|
||||||
|
PodScheduler
|
||||||
|
|
||||||
// Config related, write-once
|
// Config related, write-once
|
||||||
|
|
||||||
schedcfg *schedcfg.Config
|
schedcfg *schedcfg.Config
|
||||||
executor *mesos.ExecutorInfo
|
executor *mesos.ExecutorInfo
|
||||||
executorGroup uint64
|
executorGroup uint64
|
||||||
scheduleFunc PodScheduleFunc
|
client *client.Client
|
||||||
client *client.Client
|
etcdClient tools.EtcdClient
|
||||||
etcdClient tools.EtcdClient
|
failoverTimeout float64 // in seconds
|
||||||
failoverTimeout float64 // in seconds
|
reconcileInterval int64
|
||||||
reconcileInterval int64
|
|
||||||
defaultContainerCPULimit mresource.CPUShares
|
|
||||||
defaultContainerMemLimit mresource.MegaBytes
|
|
||||||
|
|
||||||
// Mesos context.
|
// Mesos context.
|
||||||
|
|
||||||
@ -157,33 +154,29 @@ type KubernetesScheduler struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Schedcfg schedcfg.Config
|
Schedcfg schedcfg.Config
|
||||||
Executor *mesos.ExecutorInfo
|
Executor *mesos.ExecutorInfo
|
||||||
ScheduleFunc PodScheduleFunc
|
Scheduler PodScheduler
|
||||||
Client *client.Client
|
Client *client.Client
|
||||||
EtcdClient tools.EtcdClient
|
EtcdClient tools.EtcdClient
|
||||||
FailoverTimeout float64
|
FailoverTimeout float64
|
||||||
ReconcileInterval int64
|
ReconcileInterval int64
|
||||||
ReconcileCooldown time.Duration
|
ReconcileCooldown time.Duration
|
||||||
DefaultContainerCPULimit mresource.CPUShares
|
|
||||||
DefaultContainerMemLimit mresource.MegaBytes
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new KubernetesScheduler
|
// New creates a new KubernetesScheduler
|
||||||
func New(config Config) *KubernetesScheduler {
|
func New(config Config) *KubernetesScheduler {
|
||||||
var k *KubernetesScheduler
|
var k *KubernetesScheduler
|
||||||
k = &KubernetesScheduler{
|
k = &KubernetesScheduler{
|
||||||
schedcfg: &config.Schedcfg,
|
schedcfg: &config.Schedcfg,
|
||||||
RWMutex: new(sync.RWMutex),
|
RWMutex: new(sync.RWMutex),
|
||||||
executor: config.Executor,
|
executor: config.Executor,
|
||||||
executorGroup: uid.Parse(config.Executor.ExecutorId.GetValue()).Group(),
|
executorGroup: uid.Parse(config.Executor.ExecutorId.GetValue()).Group(),
|
||||||
scheduleFunc: config.ScheduleFunc,
|
PodScheduler: config.Scheduler,
|
||||||
client: config.Client,
|
client: config.Client,
|
||||||
etcdClient: config.EtcdClient,
|
etcdClient: config.EtcdClient,
|
||||||
failoverTimeout: config.FailoverTimeout,
|
failoverTimeout: config.FailoverTimeout,
|
||||||
reconcileInterval: config.ReconcileInterval,
|
reconcileInterval: config.ReconcileInterval,
|
||||||
defaultContainerCPULimit: config.DefaultContainerCPULimit,
|
|
||||||
defaultContainerMemLimit: config.DefaultContainerMemLimit,
|
|
||||||
offers: offers.CreateRegistry(offers.RegistryConfig{
|
offers: offers.CreateRegistry(offers.RegistryConfig{
|
||||||
Compat: func(o *mesos.Offer) bool {
|
Compat: func(o *mesos.Offer) bool {
|
||||||
// filter the offers: the executor IDs must not identify a kubelet-
|
// 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