Merge pull request #70663 from wenjiaswe/gonet110

Upgrade golang.org/x/net image to release-branch.go1.10

Kubernetes-commit: 471aff6673ebe4ac9c9219a7579d23831e1146be
This commit is contained in:
Kubernetes Publisher 2018-11-07 11:36:22 -08:00
commit f0dafbb5a8
107 changed files with 6651 additions and 5878 deletions

498
Godeps/Godeps.json generated

File diff suppressed because it is too large Load Diff

View File

@ -5,6 +5,8 @@
// Package context defines the Context type, which carries deadlines, // Package context defines the Context type, which carries deadlines,
// cancelation signals, and other request-scoped values across API boundaries // cancelation signals, and other request-scoped values across API boundaries
// and between processes. // and between processes.
// As of Go 1.7 this package is available in the standard library under the
// name context. https://golang.org/pkg/context.
// //
// Incoming requests to a server should create a Context, and outgoing calls to // Incoming requests to a server should create a Context, and outgoing calls to
// servers should accept a Context. The chain of function calls between must // servers should accept a Context. The chain of function calls between must

View File

@ -5,7 +5,7 @@
package http2 package http2
// A list of the possible cipher suite ids. Taken from // A list of the possible cipher suite ids. Taken from
// http://www.iana.org/assignments/tls-parameters/tls-parameters.txt // https://www.iana.org/assignments/tls-parameters/tls-parameters.txt
const ( const (
cipher_TLS_NULL_WITH_NULL_NULL uint16 = 0x0000 cipher_TLS_NULL_WITH_NULL_NULL uint16 = 0x0000

View File

@ -73,7 +73,7 @@ type noDialH2RoundTripper struct{ t *Transport }
func (rt noDialH2RoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { func (rt noDialH2RoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
res, err := rt.t.RoundTrip(req) res, err := rt.t.RoundTrip(req)
if err == ErrNoCachedConn { if isNoCachedConnError(err) {
return nil, http.ErrSkipAltProtocol return nil, http.ErrSkipAltProtocol
} }
return res, err return res, err

View File

@ -220,12 +220,15 @@ func ConfigureServer(s *http.Server, conf *Server) error {
} else if s.TLSConfig.CipherSuites != nil { } else if s.TLSConfig.CipherSuites != nil {
// If they already provided a CipherSuite list, return // If they already provided a CipherSuite list, return
// an error if it has a bad order or is missing // an error if it has a bad order or is missing
// ECDHE_RSA_WITH_AES_128_GCM_SHA256. // ECDHE_RSA_WITH_AES_128_GCM_SHA256 or ECDHE_ECDSA_WITH_AES_128_GCM_SHA256.
const requiredCipher = tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
haveRequired := false haveRequired := false
sawBad := false sawBad := false
for i, cs := range s.TLSConfig.CipherSuites { for i, cs := range s.TLSConfig.CipherSuites {
if cs == requiredCipher { switch cs {
case tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
// Alternative MTI cipher to not discourage ECDSA-only servers.
// See http://golang.org/cl/30721 for further information.
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
haveRequired = true haveRequired = true
} }
if isBadCipher(cs) { if isBadCipher(cs) {
@ -235,7 +238,7 @@ func ConfigureServer(s *http.Server, conf *Server) error {
} }
} }
if !haveRequired { if !haveRequired {
return fmt.Errorf("http2: TLSConfig.CipherSuites is missing HTTP/2-required TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256") return fmt.Errorf("http2: TLSConfig.CipherSuites is missing an HTTP/2-required AES_128_GCM_SHA256 cipher.")
} }
} }
@ -649,7 +652,7 @@ func (sc *serverConn) condlogf(err error, format string, args ...interface{}) {
if err == nil { if err == nil {
return return
} }
if err == io.EOF || err == io.ErrUnexpectedEOF || isClosedConnError(err) { if err == io.EOF || err == io.ErrUnexpectedEOF || isClosedConnError(err) || err == errPrefaceTimeout {
// Boring, expected errors. // Boring, expected errors.
sc.vlogf(format, args...) sc.vlogf(format, args...)
} else { } else {
@ -853,8 +856,13 @@ func (sc *serverConn) serve() {
} }
} }
if sc.inGoAway && sc.curOpenStreams() == 0 && !sc.needToSendGoAway && !sc.writingFrame { // Start the shutdown timer after sending a GOAWAY. When sending GOAWAY
return // with no error code (graceful shutdown), don't start the timer until
// all open streams have been completed.
sentGoAway := sc.inGoAway && !sc.needToSendGoAway && !sc.writingFrame
gracefulShutdownComplete := sc.goAwayCode == ErrCodeNo && sc.curOpenStreams() == 0
if sentGoAway && sc.shutdownTimer == nil && (sc.goAwayCode != ErrCodeNo || gracefulShutdownComplete) {
sc.shutDownIn(goAwayTimeout)
} }
} }
} }
@ -889,8 +897,11 @@ func (sc *serverConn) sendServeMsg(msg interface{}) {
} }
} }
// readPreface reads the ClientPreface greeting from the peer var errPrefaceTimeout = errors.New("timeout waiting for client preface")
// or returns an error on timeout or an invalid greeting.
// readPreface reads the ClientPreface greeting from the peer or
// returns errPrefaceTimeout on timeout, or an error if the greeting
// is invalid.
func (sc *serverConn) readPreface() error { func (sc *serverConn) readPreface() error {
errc := make(chan error, 1) errc := make(chan error, 1)
go func() { go func() {
@ -908,7 +919,7 @@ func (sc *serverConn) readPreface() error {
defer timer.Stop() defer timer.Stop()
select { select {
case <-timer.C: case <-timer.C:
return errors.New("timeout waiting for client preface") return errPrefaceTimeout
case err := <-errc: case err := <-errc:
if err == nil { if err == nil {
if VerboseLogs { if VerboseLogs {
@ -1218,30 +1229,31 @@ func (sc *serverConn) startGracefulShutdown() {
sc.shutdownOnce.Do(func() { sc.sendServeMsg(gracefulShutdownMsg) }) sc.shutdownOnce.Do(func() { sc.sendServeMsg(gracefulShutdownMsg) })
} }
// After sending GOAWAY, the connection will close after goAwayTimeout.
// If we close the connection immediately after sending GOAWAY, there may
// be unsent data in our kernel receive buffer, which will cause the kernel
// to send a TCP RST on close() instead of a FIN. This RST will abort the
// connection immediately, whether or not the client had received the GOAWAY.
//
// Ideally we should delay for at least 1 RTT + epsilon so the client has
// a chance to read the GOAWAY and stop sending messages. Measuring RTT
// is hard, so we approximate with 1 second. See golang.org/issue/18701.
//
// This is a var so it can be shorter in tests, where all requests uses the
// loopback interface making the expected RTT very small.
//
// TODO: configurable?
var goAwayTimeout = 1 * time.Second
func (sc *serverConn) startGracefulShutdownInternal() { func (sc *serverConn) startGracefulShutdownInternal() {
sc.goAwayIn(ErrCodeNo, 0) sc.goAway(ErrCodeNo)
} }
func (sc *serverConn) goAway(code ErrCode) { func (sc *serverConn) goAway(code ErrCode) {
sc.serveG.check()
var forceCloseIn time.Duration
if code != ErrCodeNo {
forceCloseIn = 250 * time.Millisecond
} else {
// TODO: configurable
forceCloseIn = 1 * time.Second
}
sc.goAwayIn(code, forceCloseIn)
}
func (sc *serverConn) goAwayIn(code ErrCode, forceCloseIn time.Duration) {
sc.serveG.check() sc.serveG.check()
if sc.inGoAway { if sc.inGoAway {
return return
} }
if forceCloseIn != 0 {
sc.shutDownIn(forceCloseIn)
}
sc.inGoAway = true sc.inGoAway = true
sc.needToSendGoAway = true sc.needToSendGoAway = true
sc.goAwayCode = code sc.goAwayCode = code
@ -2310,7 +2322,7 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) {
clen = strconv.Itoa(len(p)) clen = strconv.Itoa(len(p))
} }
_, hasContentType := rws.snapHeader["Content-Type"] _, hasContentType := rws.snapHeader["Content-Type"]
if !hasContentType && bodyAllowedForStatus(rws.status) { if !hasContentType && bodyAllowedForStatus(rws.status) && len(p) > 0 {
ctype = http.DetectContentType(p) ctype = http.DetectContentType(p)
} }
var date string var date string
@ -2478,6 +2490,24 @@ func (w *responseWriter) Header() http.Header {
return rws.handlerHeader return rws.handlerHeader
} }
// checkWriteHeaderCode is a copy of net/http's checkWriteHeaderCode.
func checkWriteHeaderCode(code int) {
// Issue 22880: require valid WriteHeader status codes.
// For now we only enforce that it's three digits.
// In the future we might block things over 599 (600 and above aren't defined
// at http://httpwg.org/specs/rfc7231.html#status.codes)
// and we might block under 200 (once we have more mature 1xx support).
// But for now any three digits.
//
// We used to send "HTTP/1.1 000 0" on the wire in responses but there's
// no equivalent bogus thing we can realistically send in HTTP/2,
// so we'll consistently panic instead and help people find their bugs
// early. (We can't return an error from WriteHeader even if we wanted to.)
if code < 100 || code > 999 {
panic(fmt.Sprintf("invalid WriteHeader code %v", code))
}
}
func (w *responseWriter) WriteHeader(code int) { func (w *responseWriter) WriteHeader(code int) {
rws := w.rws rws := w.rws
if rws == nil { if rws == nil {
@ -2488,6 +2518,7 @@ func (w *responseWriter) WriteHeader(code int) {
func (rws *responseWriterState) writeHeader(code int) { func (rws *responseWriterState) writeHeader(code int) {
if !rws.wroteHeader { if !rws.wroteHeader {
checkWriteHeaderCode(code)
rws.wroteHeader = true rws.wroteHeader = true
rws.status = code rws.status = code
if len(rws.handlerHeader) > 0 { if len(rws.handlerHeader) > 0 {

View File

@ -87,7 +87,7 @@ type Transport struct {
// MaxHeaderListSize is the http2 SETTINGS_MAX_HEADER_LIST_SIZE to // MaxHeaderListSize is the http2 SETTINGS_MAX_HEADER_LIST_SIZE to
// send in the initial settings frame. It is how many bytes // send in the initial settings frame. It is how many bytes
// of response headers are allow. Unlike the http2 spec, zero here // of response headers are allowed. Unlike the http2 spec, zero here
// means to use a default limit (currently 10MB). If you actually // means to use a default limit (currently 10MB). If you actually
// want to advertise an ulimited value to the peer, Transport // want to advertise an ulimited value to the peer, Transport
// interprets the highest possible value here (0xffffffff or 1<<32-1) // interprets the highest possible value here (0xffffffff or 1<<32-1)
@ -172,9 +172,10 @@ type ClientConn struct {
fr *Framer fr *Framer
lastActive time.Time lastActive time.Time
// Settings from peer: (also guarded by mu) // Settings from peer: (also guarded by mu)
maxFrameSize uint32 maxFrameSize uint32
maxConcurrentStreams uint32 maxConcurrentStreams uint32
initialWindowSize uint32 peerMaxHeaderListSize uint64
initialWindowSize uint32
hbuf bytes.Buffer // HPACK encoder writes into this hbuf bytes.Buffer // HPACK encoder writes into this
henc *hpack.Encoder henc *hpack.Encoder
@ -273,6 +274,13 @@ func (cs *clientStream) checkResetOrDone() error {
} }
} }
func (cs *clientStream) getStartedWrite() bool {
cc := cs.cc
cc.mu.Lock()
defer cc.mu.Unlock()
return cs.startedWrite
}
func (cs *clientStream) abortRequestBodyWrite(err error) { func (cs *clientStream) abortRequestBodyWrite(err error) {
if err == nil { if err == nil {
panic("nil error") panic("nil error")
@ -298,7 +306,26 @@ func (sew stickyErrWriter) Write(p []byte) (n int, err error) {
return return
} }
var ErrNoCachedConn = errors.New("http2: no cached connection was available") // noCachedConnError is the concrete type of ErrNoCachedConn, which
// needs to be detected by net/http regardless of whether it's its
// bundled version (in h2_bundle.go with a rewritten type name) or
// from a user's x/net/http2. As such, as it has a unique method name
// (IsHTTP2NoCachedConnError) that net/http sniffs for via func
// isNoCachedConnError.
type noCachedConnError struct{}
func (noCachedConnError) IsHTTP2NoCachedConnError() {}
func (noCachedConnError) Error() string { return "http2: no cached connection was available" }
// isNoCachedConnError reports whether err is of type noCachedConnError
// or its equivalent renamed type in net/http2's h2_bundle.go. Both types
// may coexist in the same running program.
func isNoCachedConnError(err error) bool {
_, ok := err.(interface{ IsHTTP2NoCachedConnError() })
return ok
}
var ErrNoCachedConn error = noCachedConnError{}
// RoundTripOpt are options for the Transport.RoundTripOpt method. // RoundTripOpt are options for the Transport.RoundTripOpt method.
type RoundTripOpt struct { type RoundTripOpt struct {
@ -348,14 +375,9 @@ func (t *Transport) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Res
return nil, err return nil, err
} }
traceGotConn(req, cc) traceGotConn(req, cc)
res, err := cc.RoundTrip(req) res, gotErrAfterReqBodyWrite, err := cc.roundTrip(req)
if err != nil && retry <= 6 { if err != nil && retry <= 6 {
afterBodyWrite := false if req, err = shouldRetryRequest(req, err, gotErrAfterReqBodyWrite); err == nil {
if e, ok := err.(afterReqBodyWriteError); ok {
err = e
afterBodyWrite = true
}
if req, err = shouldRetryRequest(req, err, afterBodyWrite); err == nil {
// After the first retry, do exponential backoff with 10% jitter. // After the first retry, do exponential backoff with 10% jitter.
if retry == 0 { if retry == 0 {
continue continue
@ -393,16 +415,6 @@ var (
errClientConnGotGoAway = errors.New("http2: Transport received Server's graceful shutdown GOAWAY") errClientConnGotGoAway = errors.New("http2: Transport received Server's graceful shutdown GOAWAY")
) )
// afterReqBodyWriteError is a wrapper around errors returned by ClientConn.RoundTrip.
// It is used to signal that err happened after part of Request.Body was sent to the server.
type afterReqBodyWriteError struct {
err error
}
func (e afterReqBodyWriteError) Error() string {
return e.err.Error() + "; some request body already written"
}
// shouldRetryRequest is called by RoundTrip when a request fails to get // shouldRetryRequest is called by RoundTrip when a request fails to get
// response headers. It is always called with a non-nil error. // response headers. It is always called with a non-nil error.
// It returns either a request to retry (either the same request, or a // It returns either a request to retry (either the same request, or a
@ -519,17 +531,18 @@ func (t *Transport) NewClientConn(c net.Conn) (*ClientConn, error) {
func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, error) { func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, error) {
cc := &ClientConn{ cc := &ClientConn{
t: t, t: t,
tconn: c, tconn: c,
readerDone: make(chan struct{}), readerDone: make(chan struct{}),
nextStreamID: 1, nextStreamID: 1,
maxFrameSize: 16 << 10, // spec default maxFrameSize: 16 << 10, // spec default
initialWindowSize: 65535, // spec default initialWindowSize: 65535, // spec default
maxConcurrentStreams: 1000, // "infinite", per spec. 1000 seems good enough. maxConcurrentStreams: 1000, // "infinite", per spec. 1000 seems good enough.
streams: make(map[uint32]*clientStream), peerMaxHeaderListSize: 0xffffffffffffffff, // "infinite", per spec. Use 2^64-1 instead.
singleUse: singleUse, streams: make(map[uint32]*clientStream),
wantSettingsAck: true, singleUse: singleUse,
pings: make(map[[8]byte]chan struct{}), wantSettingsAck: true,
pings: make(map[[8]byte]chan struct{}),
} }
if d := t.idleConnTimeout(); d != 0 { if d := t.idleConnTimeout(); d != 0 {
cc.idleTimeout = d cc.idleTimeout = d
@ -750,8 +763,13 @@ func actualContentLength(req *http.Request) int64 {
} }
func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) { func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
resp, _, err := cc.roundTrip(req)
return resp, err
}
func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAfterReqBodyWrite bool, err error) {
if err := checkConnHeaders(req); err != nil { if err := checkConnHeaders(req); err != nil {
return nil, err return nil, false, err
} }
if cc.idleTimer != nil { if cc.idleTimer != nil {
cc.idleTimer.Stop() cc.idleTimer.Stop()
@ -759,14 +777,14 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
trailers, err := commaSeparatedTrailers(req) trailers, err := commaSeparatedTrailers(req)
if err != nil { if err != nil {
return nil, err return nil, false, err
} }
hasTrailers := trailers != "" hasTrailers := trailers != ""
cc.mu.Lock() cc.mu.Lock()
if err := cc.awaitOpenSlotForRequest(req); err != nil { if err := cc.awaitOpenSlotForRequest(req); err != nil {
cc.mu.Unlock() cc.mu.Unlock()
return nil, err return nil, false, err
} }
body := req.Body body := req.Body
@ -800,7 +818,7 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
hdrs, err := cc.encodeHeaders(req, requestedGzip, trailers, contentLen) hdrs, err := cc.encodeHeaders(req, requestedGzip, trailers, contentLen)
if err != nil { if err != nil {
cc.mu.Unlock() cc.mu.Unlock()
return nil, err return nil, false, err
} }
cs := cc.newStream() cs := cc.newStream()
@ -812,7 +830,7 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
cc.wmu.Lock() cc.wmu.Lock()
endStream := !hasBody && !hasTrailers endStream := !hasBody && !hasTrailers
werr := cc.writeHeaders(cs.ID, endStream, hdrs) werr := cc.writeHeaders(cs.ID, endStream, int(cc.maxFrameSize), hdrs)
cc.wmu.Unlock() cc.wmu.Unlock()
traceWroteHeaders(cs.trace) traceWroteHeaders(cs.trace)
cc.mu.Unlock() cc.mu.Unlock()
@ -826,7 +844,7 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
// Don't bother sending a RST_STREAM (our write already failed; // Don't bother sending a RST_STREAM (our write already failed;
// no need to keep writing) // no need to keep writing)
traceWroteRequest(cs.trace, werr) traceWroteRequest(cs.trace, werr)
return nil, werr return nil, false, werr
} }
var respHeaderTimer <-chan time.Time var respHeaderTimer <-chan time.Time
@ -845,7 +863,7 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
bodyWritten := false bodyWritten := false
ctx := reqContext(req) ctx := reqContext(req)
handleReadLoopResponse := func(re resAndError) (*http.Response, error) { handleReadLoopResponse := func(re resAndError) (*http.Response, bool, error) {
res := re.res res := re.res
if re.err != nil || res.StatusCode > 299 { if re.err != nil || res.StatusCode > 299 {
// On error or status code 3xx, 4xx, 5xx, etc abort any // On error or status code 3xx, 4xx, 5xx, etc abort any
@ -861,18 +879,12 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
cs.abortRequestBodyWrite(errStopReqBodyWrite) cs.abortRequestBodyWrite(errStopReqBodyWrite)
} }
if re.err != nil { if re.err != nil {
cc.mu.Lock()
afterBodyWrite := cs.startedWrite
cc.mu.Unlock()
cc.forgetStreamID(cs.ID) cc.forgetStreamID(cs.ID)
if afterBodyWrite { return nil, cs.getStartedWrite(), re.err
return nil, afterReqBodyWriteError{re.err}
}
return nil, re.err
} }
res.Request = req res.Request = req
res.TLS = cc.tlsState res.TLS = cc.tlsState
return res, nil return res, false, nil
} }
for { for {
@ -887,7 +899,7 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
cs.abortRequestBodyWrite(errStopReqBodyWriteAndCancel) cs.abortRequestBodyWrite(errStopReqBodyWriteAndCancel)
} }
cc.forgetStreamID(cs.ID) cc.forgetStreamID(cs.ID)
return nil, errTimeout return nil, cs.getStartedWrite(), errTimeout
case <-ctx.Done(): case <-ctx.Done():
if !hasBody || bodyWritten { if !hasBody || bodyWritten {
cc.writeStreamReset(cs.ID, ErrCodeCancel, nil) cc.writeStreamReset(cs.ID, ErrCodeCancel, nil)
@ -896,7 +908,7 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
cs.abortRequestBodyWrite(errStopReqBodyWriteAndCancel) cs.abortRequestBodyWrite(errStopReqBodyWriteAndCancel)
} }
cc.forgetStreamID(cs.ID) cc.forgetStreamID(cs.ID)
return nil, ctx.Err() return nil, cs.getStartedWrite(), ctx.Err()
case <-req.Cancel: case <-req.Cancel:
if !hasBody || bodyWritten { if !hasBody || bodyWritten {
cc.writeStreamReset(cs.ID, ErrCodeCancel, nil) cc.writeStreamReset(cs.ID, ErrCodeCancel, nil)
@ -905,12 +917,12 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
cs.abortRequestBodyWrite(errStopReqBodyWriteAndCancel) cs.abortRequestBodyWrite(errStopReqBodyWriteAndCancel)
} }
cc.forgetStreamID(cs.ID) cc.forgetStreamID(cs.ID)
return nil, errRequestCanceled return nil, cs.getStartedWrite(), errRequestCanceled
case <-cs.peerReset: case <-cs.peerReset:
// processResetStream already removed the // processResetStream already removed the
// stream from the streams map; no need for // stream from the streams map; no need for
// forgetStreamID. // forgetStreamID.
return nil, cs.resetErr return nil, cs.getStartedWrite(), cs.resetErr
case err := <-bodyWriter.resc: case err := <-bodyWriter.resc:
// Prefer the read loop's response, if available. Issue 16102. // Prefer the read loop's response, if available. Issue 16102.
select { select {
@ -919,7 +931,7 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
default: default:
} }
if err != nil { if err != nil {
return nil, err return nil, cs.getStartedWrite(), err
} }
bodyWritten = true bodyWritten = true
if d := cc.responseHeaderTimeout(); d != 0 { if d := cc.responseHeaderTimeout(); d != 0 {
@ -971,13 +983,12 @@ func (cc *ClientConn) awaitOpenSlotForRequest(req *http.Request) error {
} }
// requires cc.wmu be held // requires cc.wmu be held
func (cc *ClientConn) writeHeaders(streamID uint32, endStream bool, hdrs []byte) error { func (cc *ClientConn) writeHeaders(streamID uint32, endStream bool, maxFrameSize int, hdrs []byte) error {
first := true // first frame written (HEADERS is first, then CONTINUATION) first := true // first frame written (HEADERS is first, then CONTINUATION)
frameSize := int(cc.maxFrameSize)
for len(hdrs) > 0 && cc.werr == nil { for len(hdrs) > 0 && cc.werr == nil {
chunk := hdrs chunk := hdrs
if len(chunk) > frameSize { if len(chunk) > maxFrameSize {
chunk = chunk[:frameSize] chunk = chunk[:maxFrameSize]
} }
hdrs = hdrs[len(chunk):] hdrs = hdrs[len(chunk):]
endHeaders := len(hdrs) == 0 endHeaders := len(hdrs) == 0
@ -1085,17 +1096,26 @@ func (cs *clientStream) writeRequestBody(body io.Reader, bodyCloser io.Closer) (
var trls []byte var trls []byte
if hasTrailers { if hasTrailers {
cc.mu.Lock() cc.mu.Lock()
defer cc.mu.Unlock() trls, err = cc.encodeTrailers(req)
trls = cc.encodeTrailers(req) cc.mu.Unlock()
if err != nil {
cc.writeStreamReset(cs.ID, ErrCodeInternal, err)
cc.forgetStreamID(cs.ID)
return err
}
} }
cc.mu.Lock()
maxFrameSize := int(cc.maxFrameSize)
cc.mu.Unlock()
cc.wmu.Lock() cc.wmu.Lock()
defer cc.wmu.Unlock() defer cc.wmu.Unlock()
// Two ways to send END_STREAM: either with trailers, or // Two ways to send END_STREAM: either with trailers, or
// with an empty DATA frame. // with an empty DATA frame.
if len(trls) > 0 { if len(trls) > 0 {
err = cc.writeHeaders(cs.ID, true, trls) err = cc.writeHeaders(cs.ID, true, maxFrameSize, trls)
} else { } else {
err = cc.fr.WriteData(cs.ID, true, nil) err = cc.fr.WriteData(cs.ID, true, nil)
} }
@ -1189,62 +1209,86 @@ func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trail
} }
} }
// 8.1.2.3 Request Pseudo-Header Fields enumerateHeaders := func(f func(name, value string)) {
// The :path pseudo-header field includes the path and query parts of the // 8.1.2.3 Request Pseudo-Header Fields
// target URI (the path-absolute production and optionally a '?' character // The :path pseudo-header field includes the path and query parts of the
// followed by the query production (see Sections 3.3 and 3.4 of // target URI (the path-absolute production and optionally a '?' character
// [RFC3986]). // followed by the query production (see Sections 3.3 and 3.4 of
cc.writeHeader(":authority", host) // [RFC3986]).
cc.writeHeader(":method", req.Method) f(":authority", host)
if req.Method != "CONNECT" { f(":method", req.Method)
cc.writeHeader(":path", path) if req.Method != "CONNECT" {
cc.writeHeader(":scheme", req.URL.Scheme) f(":path", path)
} f(":scheme", req.URL.Scheme)
if trailers != "" { }
cc.writeHeader("trailer", trailers) if trailers != "" {
f("trailer", trailers)
}
var didUA bool
for k, vv := range req.Header {
if strings.EqualFold(k, "host") || strings.EqualFold(k, "content-length") {
// Host is :authority, already sent.
// Content-Length is automatic, set below.
continue
} else if strings.EqualFold(k, "connection") || strings.EqualFold(k, "proxy-connection") ||
strings.EqualFold(k, "transfer-encoding") || strings.EqualFold(k, "upgrade") ||
strings.EqualFold(k, "keep-alive") {
// Per 8.1.2.2 Connection-Specific Header
// Fields, don't send connection-specific
// fields. We have already checked if any
// are error-worthy so just ignore the rest.
continue
} else if strings.EqualFold(k, "user-agent") {
// Match Go's http1 behavior: at most one
// User-Agent. If set to nil or empty string,
// then omit it. Otherwise if not mentioned,
// include the default (below).
didUA = true
if len(vv) < 1 {
continue
}
vv = vv[:1]
if vv[0] == "" {
continue
}
}
for _, v := range vv {
f(k, v)
}
}
if shouldSendReqContentLength(req.Method, contentLength) {
f("content-length", strconv.FormatInt(contentLength, 10))
}
if addGzipHeader {
f("accept-encoding", "gzip")
}
if !didUA {
f("user-agent", defaultUserAgent)
}
} }
var didUA bool // Do a first pass over the headers counting bytes to ensure
for k, vv := range req.Header { // we don't exceed cc.peerMaxHeaderListSize. This is done as a
lowKey := strings.ToLower(k) // separate pass before encoding the headers to prevent
switch lowKey { // modifying the hpack state.
case "host", "content-length": hlSize := uint64(0)
// Host is :authority, already sent. enumerateHeaders(func(name, value string) {
// Content-Length is automatic, set below. hf := hpack.HeaderField{Name: name, Value: value}
continue hlSize += uint64(hf.Size())
case "connection", "proxy-connection", "transfer-encoding", "upgrade", "keep-alive": })
// Per 8.1.2.2 Connection-Specific Header
// Fields, don't send connection-specific if hlSize > cc.peerMaxHeaderListSize {
// fields. We have already checked if any return nil, errRequestHeaderListSize
// are error-worthy so just ignore the rest.
continue
case "user-agent":
// Match Go's http1 behavior: at most one
// User-Agent. If set to nil or empty string,
// then omit it. Otherwise if not mentioned,
// include the default (below).
didUA = true
if len(vv) < 1 {
continue
}
vv = vv[:1]
if vv[0] == "" {
continue
}
}
for _, v := range vv {
cc.writeHeader(lowKey, v)
}
}
if shouldSendReqContentLength(req.Method, contentLength) {
cc.writeHeader("content-length", strconv.FormatInt(contentLength, 10))
}
if addGzipHeader {
cc.writeHeader("accept-encoding", "gzip")
}
if !didUA {
cc.writeHeader("user-agent", defaultUserAgent)
} }
// Header list size is ok. Write the headers.
enumerateHeaders(func(name, value string) {
cc.writeHeader(strings.ToLower(name), value)
})
return cc.hbuf.Bytes(), nil return cc.hbuf.Bytes(), nil
} }
@ -1271,17 +1315,29 @@ func shouldSendReqContentLength(method string, contentLength int64) bool {
} }
// requires cc.mu be held. // requires cc.mu be held.
func (cc *ClientConn) encodeTrailers(req *http.Request) []byte { func (cc *ClientConn) encodeTrailers(req *http.Request) ([]byte, error) {
cc.hbuf.Reset() cc.hbuf.Reset()
hlSize := uint64(0)
for k, vv := range req.Trailer { for k, vv := range req.Trailer {
// Transfer-Encoding, etc.. have already been filter at the for _, v := range vv {
hf := hpack.HeaderField{Name: k, Value: v}
hlSize += uint64(hf.Size())
}
}
if hlSize > cc.peerMaxHeaderListSize {
return nil, errRequestHeaderListSize
}
for k, vv := range req.Trailer {
// Transfer-Encoding, etc.. have already been filtered at the
// start of RoundTrip // start of RoundTrip
lowKey := strings.ToLower(k) lowKey := strings.ToLower(k)
for _, v := range vv { for _, v := range vv {
cc.writeHeader(lowKey, v) cc.writeHeader(lowKey, v)
} }
} }
return cc.hbuf.Bytes() return cc.hbuf.Bytes(), nil
} }
func (cc *ClientConn) writeHeader(name, value string) { func (cc *ClientConn) writeHeader(name, value string) {
@ -1339,17 +1395,12 @@ func (cc *ClientConn) streamByID(id uint32, andRemove bool) *clientStream {
// clientConnReadLoop is the state owned by the clientConn's frame-reading readLoop. // clientConnReadLoop is the state owned by the clientConn's frame-reading readLoop.
type clientConnReadLoop struct { type clientConnReadLoop struct {
cc *ClientConn cc *ClientConn
activeRes map[uint32]*clientStream // keyed by streamID
closeWhenIdle bool closeWhenIdle bool
} }
// readLoop runs in its own goroutine and reads and dispatches frames. // readLoop runs in its own goroutine and reads and dispatches frames.
func (cc *ClientConn) readLoop() { func (cc *ClientConn) readLoop() {
rl := &clientConnReadLoop{ rl := &clientConnReadLoop{cc: cc}
cc: cc,
activeRes: make(map[uint32]*clientStream),
}
defer rl.cleanup() defer rl.cleanup()
cc.readerErr = rl.run() cc.readerErr = rl.run()
if ce, ok := cc.readerErr.(ConnectionError); ok { if ce, ok := cc.readerErr.(ConnectionError); ok {
@ -1404,10 +1455,8 @@ func (rl *clientConnReadLoop) cleanup() {
} else if err == io.EOF { } else if err == io.EOF {
err = io.ErrUnexpectedEOF err = io.ErrUnexpectedEOF
} }
for _, cs := range rl.activeRes {
cs.bufPipe.CloseWithError(err)
}
for _, cs := range cc.streams { for _, cs := range cc.streams {
cs.bufPipe.CloseWithError(err) // no-op if already closed
select { select {
case cs.resc <- resAndError{err: err}: case cs.resc <- resAndError{err: err}:
default: default:
@ -1485,7 +1534,7 @@ func (rl *clientConnReadLoop) run() error {
} }
return err return err
} }
if rl.closeWhenIdle && gotReply && maybeIdle && len(rl.activeRes) == 0 { if rl.closeWhenIdle && gotReply && maybeIdle {
cc.closeIfIdle() cc.closeIfIdle()
} }
} }
@ -1493,13 +1542,31 @@ func (rl *clientConnReadLoop) run() error {
func (rl *clientConnReadLoop) processHeaders(f *MetaHeadersFrame) error { func (rl *clientConnReadLoop) processHeaders(f *MetaHeadersFrame) error {
cc := rl.cc cc := rl.cc
cs := cc.streamByID(f.StreamID, f.StreamEnded()) cs := cc.streamByID(f.StreamID, false)
if cs == nil { if cs == nil {
// We'd get here if we canceled a request while the // We'd get here if we canceled a request while the
// server had its response still in flight. So if this // server had its response still in flight. So if this
// was just something we canceled, ignore it. // was just something we canceled, ignore it.
return nil return nil
} }
if f.StreamEnded() {
// Issue 20521: If the stream has ended, streamByID() causes
// clientStream.done to be closed, which causes the request's bodyWriter
// to be closed with an errStreamClosed, which may be received by
// clientConn.RoundTrip before the result of processing these headers.
// Deferring stream closure allows the header processing to occur first.
// clientConn.RoundTrip may still receive the bodyWriter error first, but
// the fix for issue 16102 prioritises any response.
//
// Issue 22413: If there is no request body, we should close the
// stream before writing to cs.resc so that the stream is closed
// immediately once RoundTrip returns.
if cs.req.Body != nil {
defer cc.forgetStreamID(f.StreamID)
} else {
cc.forgetStreamID(f.StreamID)
}
}
if !cs.firstByte { if !cs.firstByte {
if cs.trace != nil { if cs.trace != nil {
// TODO(bradfitz): move first response byte earlier, // TODO(bradfitz): move first response byte earlier,
@ -1523,6 +1590,7 @@ func (rl *clientConnReadLoop) processHeaders(f *MetaHeadersFrame) error {
} }
// Any other error type is a stream error. // Any other error type is a stream error.
cs.cc.writeStreamReset(f.StreamID, ErrCodeProtocol, err) cs.cc.writeStreamReset(f.StreamID, ErrCodeProtocol, err)
cc.forgetStreamID(cs.ID)
cs.resc <- resAndError{err: err} cs.resc <- resAndError{err: err}
return nil // return nil from process* funcs to keep conn alive return nil // return nil from process* funcs to keep conn alive
} }
@ -1530,9 +1598,6 @@ func (rl *clientConnReadLoop) processHeaders(f *MetaHeadersFrame) error {
// (nil, nil) special case. See handleResponse docs. // (nil, nil) special case. See handleResponse docs.
return nil return nil
} }
if res.Body != noBody {
rl.activeRes[cs.ID] = cs
}
cs.resTrailer = &res.Trailer cs.resTrailer = &res.Trailer
cs.resc <- resAndError{res: res} cs.resc <- resAndError{res: res}
return nil return nil
@ -1552,11 +1617,11 @@ func (rl *clientConnReadLoop) handleResponse(cs *clientStream, f *MetaHeadersFra
status := f.PseudoValue("status") status := f.PseudoValue("status")
if status == "" { if status == "" {
return nil, errors.New("missing status pseudo header") return nil, errors.New("malformed response from server: missing status pseudo header")
} }
statusCode, err := strconv.Atoi(status) statusCode, err := strconv.Atoi(status)
if err != nil { if err != nil {
return nil, errors.New("malformed non-numeric status pseudo header") return nil, errors.New("malformed response from server: malformed non-numeric status pseudo header")
} }
if statusCode == 100 { if statusCode == 100 {
@ -1789,7 +1854,23 @@ func (rl *clientConnReadLoop) processData(f *DataFrame) error {
} }
return nil return nil
} }
if !cs.firstByte {
cc.logf("protocol error: received DATA before a HEADERS frame")
rl.endStreamError(cs, StreamError{
StreamID: f.StreamID,
Code: ErrCodeProtocol,
})
return nil
}
if f.Length > 0 { if f.Length > 0 {
if cs.req.Method == "HEAD" && len(data) > 0 {
cc.logf("protocol error: received DATA on a HEAD request")
rl.endStreamError(cs, StreamError{
StreamID: f.StreamID,
Code: ErrCodeProtocol,
})
return nil
}
// Check connection-level flow control. // Check connection-level flow control.
cc.mu.Lock() cc.mu.Lock()
if cs.inflow.available() >= int32(f.Length) { if cs.inflow.available() >= int32(f.Length) {
@ -1851,11 +1932,10 @@ func (rl *clientConnReadLoop) endStreamError(cs *clientStream, err error) {
err = io.EOF err = io.EOF
code = cs.copyTrailers code = cs.copyTrailers
} }
cs.bufPipe.closeWithErrorAndCode(err, code)
delete(rl.activeRes, cs.ID)
if isConnectionCloseRequest(cs.req) { if isConnectionCloseRequest(cs.req) {
rl.closeWhenIdle = true rl.closeWhenIdle = true
} }
cs.bufPipe.closeWithErrorAndCode(err, code)
select { select {
case cs.resc <- resAndError{err: err}: case cs.resc <- resAndError{err: err}:
@ -1903,6 +1983,8 @@ func (rl *clientConnReadLoop) processSettings(f *SettingsFrame) error {
cc.maxFrameSize = s.Val cc.maxFrameSize = s.Val
case SettingMaxConcurrentStreams: case SettingMaxConcurrentStreams:
cc.maxConcurrentStreams = s.Val cc.maxConcurrentStreams = s.Val
case SettingMaxHeaderListSize:
cc.peerMaxHeaderListSize = uint64(s.Val)
case SettingInitialWindowSize: case SettingInitialWindowSize:
// Values above the maximum flow-control // Values above the maximum flow-control
// window size of 2^31-1 MUST be treated as a // window size of 2^31-1 MUST be treated as a
@ -1980,7 +2062,6 @@ func (rl *clientConnReadLoop) processResetStream(f *RSTStreamFrame) error {
cs.bufPipe.CloseWithError(err) cs.bufPipe.CloseWithError(err)
cs.cc.cond.Broadcast() // wake up checkResetOrDone via clientStream.awaitFlowControl cs.cc.cond.Broadcast() // wake up checkResetOrDone via clientStream.awaitFlowControl
} }
delete(rl.activeRes, cs.ID)
return nil return nil
} }
@ -2069,6 +2150,7 @@ func (cc *ClientConn) writeStreamReset(streamID uint32, code ErrCode, err error)
var ( var (
errResponseHeaderListSize = errors.New("http2: response header list larger than advertised limit") errResponseHeaderListSize = errors.New("http2: response header list larger than advertised limit")
errRequestHeaderListSize = errors.New("http2: request header list larger than peer's advertised limit")
errPseudoTrailers = errors.New("http2: invalid pseudo header in trailers") errPseudoTrailers = errors.New("http2: invalid pseudo header in trailers")
) )

View File

@ -10,7 +10,6 @@ import (
"log" "log"
"net/http" "net/http"
"net/url" "net/url"
"time"
"golang.org/x/net/http2/hpack" "golang.org/x/net/http2/hpack"
"golang.org/x/net/lex/httplex" "golang.org/x/net/lex/httplex"
@ -90,11 +89,7 @@ type writeGoAway struct {
func (p *writeGoAway) writeFrame(ctx writeContext) error { func (p *writeGoAway) writeFrame(ctx writeContext) error {
err := ctx.Framer().WriteGoAway(p.maxStreamID, p.code, nil) err := ctx.Framer().WriteGoAway(p.maxStreamID, p.code, nil)
if p.code != 0 { ctx.Flush() // ignore error: we're hanging up on them anyway
ctx.Flush() // ignore error: we're hanging up on them anyway
time.Sleep(50 * time.Millisecond)
ctx.CloseConn()
}
return err return err
} }

126
vendor/golang.org/x/net/idna/idna.go generated vendored
View File

@ -21,6 +21,7 @@ import (
"unicode/utf8" "unicode/utf8"
"golang.org/x/text/secure/bidirule" "golang.org/x/text/secure/bidirule"
"golang.org/x/text/unicode/bidi"
"golang.org/x/text/unicode/norm" "golang.org/x/text/unicode/norm"
) )
@ -68,7 +69,7 @@ func VerifyDNSLength(verify bool) Option {
} }
// RemoveLeadingDots removes leading label separators. Leading runes that map to // RemoveLeadingDots removes leading label separators. Leading runes that map to
// dots, such as U+3002, are removed as well. // dots, such as U+3002 IDEOGRAPHIC FULL STOP, are removed as well.
// //
// This is the behavior suggested by the UTS #46 and is adopted by some // This is the behavior suggested by the UTS #46 and is adopted by some
// browsers. // browsers.
@ -92,7 +93,7 @@ func ValidateLabels(enable bool) Option {
} }
} }
// StrictDomainName limits the set of permissable ASCII characters to those // StrictDomainName limits the set of permissible ASCII characters to those
// allowed in domain names as defined in RFC 1034 (A-Z, a-z, 0-9 and the // allowed in domain names as defined in RFC 1034 (A-Z, a-z, 0-9 and the
// hyphen). This is set by default for MapForLookup and ValidateForRegistration. // hyphen). This is set by default for MapForLookup and ValidateForRegistration.
// //
@ -142,7 +143,6 @@ func MapForLookup() Option {
o.mapping = validateAndMap o.mapping = validateAndMap
StrictDomainName(true)(o) StrictDomainName(true)(o)
ValidateLabels(true)(o) ValidateLabels(true)(o)
RemoveLeadingDots(true)(o)
} }
} }
@ -160,14 +160,14 @@ type options struct {
// mapping implements a validation and mapping step as defined in RFC 5895 // mapping implements a validation and mapping step as defined in RFC 5895
// or UTS 46, tailored to, for example, domain registration or lookup. // or UTS 46, tailored to, for example, domain registration or lookup.
mapping func(p *Profile, s string) (string, error) mapping func(p *Profile, s string) (mapped string, isBidi bool, err error)
// bidirule, if specified, checks whether s conforms to the Bidi Rule // bidirule, if specified, checks whether s conforms to the Bidi Rule
// defined in RFC 5893. // defined in RFC 5893.
bidirule func(s string) bool bidirule func(s string) bool
} }
// A Profile defines the configuration of a IDNA mapper. // A Profile defines the configuration of an IDNA mapper.
type Profile struct { type Profile struct {
options options
} }
@ -251,23 +251,21 @@ var (
punycode = &Profile{} punycode = &Profile{}
lookup = &Profile{options{ lookup = &Profile{options{
transitional: true, transitional: true,
useSTD3Rules: true, useSTD3Rules: true,
validateLabels: true, validateLabels: true,
removeLeadingDots: true, trie: trie,
trie: trie, fromPuny: validateFromPunycode,
fromPuny: validateFromPunycode, mapping: validateAndMap,
mapping: validateAndMap, bidirule: bidirule.ValidString,
bidirule: bidirule.ValidString,
}} }}
display = &Profile{options{ display = &Profile{options{
useSTD3Rules: true, useSTD3Rules: true,
validateLabels: true, validateLabels: true,
removeLeadingDots: true, trie: trie,
trie: trie, fromPuny: validateFromPunycode,
fromPuny: validateFromPunycode, mapping: validateAndMap,
mapping: validateAndMap, bidirule: bidirule.ValidString,
bidirule: bidirule.ValidString,
}} }}
registration = &Profile{options{ registration = &Profile{options{
useSTD3Rules: true, useSTD3Rules: true,
@ -302,14 +300,16 @@ func (e runeError) Error() string {
// see http://www.unicode.org/reports/tr46. // see http://www.unicode.org/reports/tr46.
func (p *Profile) process(s string, toASCII bool) (string, error) { func (p *Profile) process(s string, toASCII bool) (string, error) {
var err error var err error
var isBidi bool
if p.mapping != nil { if p.mapping != nil {
s, err = p.mapping(p, s) s, isBidi, err = p.mapping(p, s)
} }
// Remove leading empty labels. // Remove leading empty labels.
if p.removeLeadingDots { if p.removeLeadingDots {
for ; len(s) > 0 && s[0] == '.'; s = s[1:] { for ; len(s) > 0 && s[0] == '.'; s = s[1:] {
} }
} }
// TODO: allow for a quick check of the tables data.
// It seems like we should only create this error on ToASCII, but the // It seems like we should only create this error on ToASCII, but the
// UTS 46 conformance tests suggests we should always check this. // UTS 46 conformance tests suggests we should always check this.
if err == nil && p.verifyDNSLength && s == "" { if err == nil && p.verifyDNSLength && s == "" {
@ -335,6 +335,7 @@ func (p *Profile) process(s string, toASCII bool) (string, error) {
// Spec says keep the old label. // Spec says keep the old label.
continue continue
} }
isBidi = isBidi || bidirule.DirectionString(u) != bidi.LeftToRight
labels.set(u) labels.set(u)
if err == nil && p.validateLabels { if err == nil && p.validateLabels {
err = p.fromPuny(p, u) err = p.fromPuny(p, u)
@ -349,6 +350,14 @@ func (p *Profile) process(s string, toASCII bool) (string, error) {
err = p.validateLabel(label) err = p.validateLabel(label)
} }
} }
if isBidi && p.bidirule != nil && err == nil {
for labels.reset(); !labels.done(); labels.next() {
if !p.bidirule(labels.label()) {
err = &labelError{s, "B"}
break
}
}
}
if toASCII { if toASCII {
for labels.reset(); !labels.done(); labels.next() { for labels.reset(); !labels.done(); labels.next() {
label := labels.label() label := labels.label()
@ -380,16 +389,26 @@ func (p *Profile) process(s string, toASCII bool) (string, error) {
return s, err return s, err
} }
func normalize(p *Profile, s string) (string, error) { func normalize(p *Profile, s string) (mapped string, isBidi bool, err error) {
return norm.NFC.String(s), nil // TODO: consider first doing a quick check to see if any of these checks
// need to be done. This will make it slower in the general case, but
// faster in the common case.
mapped = norm.NFC.String(s)
isBidi = bidirule.DirectionString(mapped) == bidi.RightToLeft
return mapped, isBidi, nil
} }
func validateRegistration(p *Profile, s string) (string, error) { func validateRegistration(p *Profile, s string) (idem string, bidi bool, err error) {
// TODO: filter need for normalization in loop below.
if !norm.NFC.IsNormalString(s) { if !norm.NFC.IsNormalString(s) {
return s, &labelError{s, "V1"} return s, false, &labelError{s, "V1"}
} }
for i := 0; i < len(s); { for i := 0; i < len(s); {
v, sz := trie.lookupString(s[i:]) v, sz := trie.lookupString(s[i:])
if sz == 0 {
return s, bidi, runeError(utf8.RuneError)
}
bidi = bidi || info(v).isBidi(s[i:])
// Copy bytes not copied so far. // Copy bytes not copied so far.
switch p.simplify(info(v).category()) { switch p.simplify(info(v).category()) {
// TODO: handle the NV8 defined in the Unicode idna data set to allow // TODO: handle the NV8 defined in the Unicode idna data set to allow
@ -397,21 +416,50 @@ func validateRegistration(p *Profile, s string) (string, error) {
case valid, deviation: case valid, deviation:
case disallowed, mapped, unknown, ignored: case disallowed, mapped, unknown, ignored:
r, _ := utf8.DecodeRuneInString(s[i:]) r, _ := utf8.DecodeRuneInString(s[i:])
return s, runeError(r) return s, bidi, runeError(r)
} }
i += sz i += sz
} }
return s, nil return s, bidi, nil
} }
func validateAndMap(p *Profile, s string) (string, error) { func (c info) isBidi(s string) bool {
if !c.isMapped() {
return c&attributesMask == rtl
}
// TODO: also store bidi info for mapped data. This is possible, but a bit
// cumbersome and not for the common case.
p, _ := bidi.LookupString(s)
switch p.Class() {
case bidi.R, bidi.AL, bidi.AN:
return true
}
return false
}
func validateAndMap(p *Profile, s string) (vm string, bidi bool, err error) {
var ( var (
err error b []byte
b []byte k int
k int
) )
// combinedInfoBits contains the or-ed bits of all runes. We use this
// to derive the mayNeedNorm bit later. This may trigger normalization
// overeagerly, but it will not do so in the common case. The end result
// is another 10% saving on BenchmarkProfile for the common case.
var combinedInfoBits info
for i := 0; i < len(s); { for i := 0; i < len(s); {
v, sz := trie.lookupString(s[i:]) v, sz := trie.lookupString(s[i:])
if sz == 0 {
b = append(b, s[k:i]...)
b = append(b, "\ufffd"...)
k = len(s)
if err == nil {
err = runeError(utf8.RuneError)
}
break
}
combinedInfoBits |= info(v)
bidi = bidi || info(v).isBidi(s[i:])
start := i start := i
i += sz i += sz
// Copy bytes not copied so far. // Copy bytes not copied so far.
@ -438,7 +486,9 @@ func validateAndMap(p *Profile, s string) (string, error) {
} }
if k == 0 { if k == 0 {
// No changes so far. // No changes so far.
s = norm.NFC.String(s) if combinedInfoBits&mayNeedNorm != 0 {
s = norm.NFC.String(s)
}
} else { } else {
b = append(b, s[k:]...) b = append(b, s[k:]...)
if norm.NFC.QuickSpan(b) != len(b) { if norm.NFC.QuickSpan(b) != len(b) {
@ -447,7 +497,7 @@ func validateAndMap(p *Profile, s string) (string, error) {
// TODO: the punycode converters require strings as input. // TODO: the punycode converters require strings as input.
s = string(b) s = string(b)
} }
return s, err return s, bidi, err
} }
// A labelIter allows iterating over domain name labels. // A labelIter allows iterating over domain name labels.
@ -542,8 +592,13 @@ func validateFromPunycode(p *Profile, s string) error {
if !norm.NFC.IsNormalString(s) { if !norm.NFC.IsNormalString(s) {
return &labelError{s, "V1"} return &labelError{s, "V1"}
} }
// TODO: detect whether string may have to be normalized in the following
// loop.
for i := 0; i < len(s); { for i := 0; i < len(s); {
v, sz := trie.lookupString(s[i:]) v, sz := trie.lookupString(s[i:])
if sz == 0 {
return runeError(utf8.RuneError)
}
if c := p.simplify(info(v).category()); c != valid && c != deviation { if c := p.simplify(info(v).category()); c != valid && c != deviation {
return &labelError{s, "V6"} return &labelError{s, "V6"}
} }
@ -616,16 +671,13 @@ var joinStates = [][numJoinTypes]joinState{
// validateLabel validates the criteria from Section 4.1. Item 1, 4, and 6 are // validateLabel validates the criteria from Section 4.1. Item 1, 4, and 6 are
// already implicitly satisfied by the overall implementation. // already implicitly satisfied by the overall implementation.
func (p *Profile) validateLabel(s string) error { func (p *Profile) validateLabel(s string) (err error) {
if s == "" { if s == "" {
if p.verifyDNSLength { if p.verifyDNSLength {
return &labelError{s, "A4"} return &labelError{s, "A4"}
} }
return nil return nil
} }
if p.bidirule != nil && !p.bidirule(s) {
return &labelError{s, "B"}
}
if !p.validateLabels { if !p.validateLabels {
return nil return nil
} }

4396
vendor/golang.org/x/net/idna/tables.go generated vendored

File diff suppressed because it is too large Load Diff

View File

@ -26,9 +26,9 @@ package idna
// 15..3 index into xor or mapping table // 15..3 index into xor or mapping table
// } // }
// } else { // } else {
// 15..13 unused // 15..14 unused
// 12 modifier (including virama) // 13 mayNeedNorm
// 11 virama modifier // 12..11 attributes
// 10..8 joining type // 10..8 joining type
// 7..3 category type // 7..3 category type
// } // }
@ -49,15 +49,20 @@ const (
joinShift = 8 joinShift = 8
joinMask = 0x07 joinMask = 0x07
viramaModifier = 0x0800 // Attributes
attributesMask = 0x1800
viramaModifier = 0x1800
modifier = 0x1000 modifier = 0x1000
rtl = 0x0800
mayNeedNorm = 0x2000
) )
// A category corresponds to a category defined in the IDNA mapping table. // A category corresponds to a category defined in the IDNA mapping table.
type category uint16 type category uint16
const ( const (
unknown category = 0 // not defined currently in unicode. unknown category = 0 // not currently defined in unicode.
mapped category = 1 mapped category = 1
disallowedSTD3Mapped category = 2 disallowedSTD3Mapped category = 2
deviation category = 3 deviation category = 3
@ -110,5 +115,5 @@ func (c info) isModifier() bool {
} }
func (c info) isViramaModifier() bool { func (c info) isViramaModifier() bool {
return c&(viramaModifier|catSmallMask) == viramaModifier return c&(attributesMask|catSmallMask) == viramaModifier
} }

6
vendor/k8s.io/api/apps/v1/types.go generated vendored
View File

@ -32,6 +32,8 @@ const (
) )
// +genclient // +genclient
// +genclient:method=GetScale,verb=get,subresource=scale,result=k8s.io/api/autoscaling/v1.Scale
// +genclient:method=UpdateScale,verb=update,subresource=scale,input=k8s.io/api/autoscaling/v1.Scale,result=k8s.io/api/autoscaling/v1.Scale
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// StatefulSet represents a set of pods with consistent identities. // StatefulSet represents a set of pods with consistent identities.
@ -244,6 +246,8 @@ type StatefulSetList struct {
} }
// +genclient // +genclient
// +genclient:method=GetScale,verb=get,subresource=scale,result=k8s.io/api/autoscaling/v1.Scale
// +genclient:method=UpdateScale,verb=update,subresource=scale,input=k8s.io/api/autoscaling/v1.Scale,result=k8s.io/api/autoscaling/v1.Scale
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// Deployment enables declarative updates for Pods and ReplicaSets. // Deployment enables declarative updates for Pods and ReplicaSets.
@ -654,6 +658,8 @@ type DaemonSetList struct {
} }
// +genclient // +genclient
// +genclient:method=GetScale,verb=get,subresource=scale,result=k8s.io/api/autoscaling/v1.Scale
// +genclient:method=UpdateScale,verb=update,subresource=scale,input=k8s.io/api/autoscaling/v1.Scale,result=k8s.io/api/autoscaling/v1.Scale
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// ReplicaSet ensures that a specified number of pod replicas are running at any given time. // ReplicaSet ensures that a specified number of pod replicas are running at any given time.

View File

@ -55,8 +55,6 @@ type ScaleStatus struct {
TargetSelector string `json:"targetSelector,omitempty" protobuf:"bytes,3,opt,name=targetSelector"` TargetSelector string `json:"targetSelector,omitempty" protobuf:"bytes,3,opt,name=targetSelector"`
} }
// +genclient
// +genclient:noVerbs
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// Scale represents a scaling request for a resource. // Scale represents a scaling request for a resource.

View File

@ -57,8 +57,6 @@ type ScaleStatus struct {
TargetSelector string `json:"targetSelector,omitempty" protobuf:"bytes,3,opt,name=targetSelector"` TargetSelector string `json:"targetSelector,omitempty" protobuf:"bytes,3,opt,name=targetSelector"`
} }
// +genclient
// +genclient:noVerbs
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// Scale represents a scaling request for a resource. // Scale represents a scaling request for a resource.

4
vendor/k8s.io/api/core/v1/types.go generated vendored
View File

@ -3285,8 +3285,8 @@ type ReplicationControllerCondition struct {
} }
// +genclient // +genclient
// +genclient:method=GetScale,verb=get,subresource=scale,result=k8s.io/api/extensions/v1beta1.Scale // +genclient:method=GetScale,verb=get,subresource=scale,result=k8s.io/api/autoscaling/v1.Scale
// +genclient:method=UpdateScale,verb=update,subresource=scale,input=k8s.io/api/extensions/v1beta1.Scale,result=k8s.io/api/extensions/v1beta1.Scale // +genclient:method=UpdateScale,verb=update,subresource=scale,input=k8s.io/api/autoscaling/v1.Scale,result=k8s.io/api/autoscaling/v1.Scale
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// ReplicationController represents the configuration of a replication controller. // ReplicationController represents the configuration of a replication controller.

View File

@ -49,8 +49,6 @@ type ScaleStatus struct {
TargetSelector string `json:"targetSelector,omitempty" protobuf:"bytes,3,opt,name=targetSelector"` TargetSelector string `json:"targetSelector,omitempty" protobuf:"bytes,3,opt,name=targetSelector"`
} }
// +genclient
// +genclient:noVerbs
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// represents a scaling request for a resource. // represents a scaling request for a resource.

View File

@ -20,6 +20,7 @@ package v1
import ( import (
v1 "k8s.io/api/apps/v1" v1 "k8s.io/api/apps/v1"
autoscalingv1 "k8s.io/api/autoscaling/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
types "k8s.io/apimachinery/pkg/types" types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch" watch "k8s.io/apimachinery/pkg/watch"
@ -44,6 +45,9 @@ type DeploymentInterface interface {
List(opts metav1.ListOptions) (*v1.DeploymentList, error) List(opts metav1.ListOptions) (*v1.DeploymentList, error)
Watch(opts metav1.ListOptions) (watch.Interface, error) Watch(opts metav1.ListOptions) (watch.Interface, error)
Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.Deployment, err error) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.Deployment, err error)
GetScale(deploymentName string, options metav1.GetOptions) (*autoscalingv1.Scale, error)
UpdateScale(deploymentName string, scale *autoscalingv1.Scale) (*autoscalingv1.Scale, error)
DeploymentExpansion DeploymentExpansion
} }
@ -172,3 +176,31 @@ func (c *deployments) Patch(name string, pt types.PatchType, data []byte, subres
Into(result) Into(result)
return return
} }
// GetScale takes name of the deployment, and returns the corresponding autoscalingv1.Scale object, and an error if there is any.
func (c *deployments) GetScale(deploymentName string, options metav1.GetOptions) (result *autoscalingv1.Scale, err error) {
result = &autoscalingv1.Scale{}
err = c.client.Get().
Namespace(c.ns).
Resource("deployments").
Name(deploymentName).
SubResource("scale").
VersionedParams(&options, scheme.ParameterCodec).
Do().
Into(result)
return
}
// UpdateScale takes the top resource name and the representation of a scale and updates it. Returns the server's representation of the scale, and an error, if there is any.
func (c *deployments) UpdateScale(deploymentName string, scale *autoscalingv1.Scale) (result *autoscalingv1.Scale, err error) {
result = &autoscalingv1.Scale{}
err = c.client.Put().
Namespace(c.ns).
Resource("deployments").
Name(deploymentName).
SubResource("scale").
Body(scale).
Do().
Into(result)
return
}

View File

@ -20,6 +20,7 @@ package fake
import ( import (
appsv1 "k8s.io/api/apps/v1" appsv1 "k8s.io/api/apps/v1"
autoscalingv1 "k8s.io/api/autoscaling/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
labels "k8s.io/apimachinery/pkg/labels" labels "k8s.io/apimachinery/pkg/labels"
schema "k8s.io/apimachinery/pkg/runtime/schema" schema "k8s.io/apimachinery/pkg/runtime/schema"
@ -138,3 +139,25 @@ func (c *FakeDeployments) Patch(name string, pt types.PatchType, data []byte, su
} }
return obj.(*appsv1.Deployment), err return obj.(*appsv1.Deployment), err
} }
// GetScale takes name of the deployment, and returns the corresponding scale object, and an error if there is any.
func (c *FakeDeployments) GetScale(deploymentName string, options v1.GetOptions) (result *autoscalingv1.Scale, err error) {
obj, err := c.Fake.
Invokes(testing.NewGetSubresourceAction(deploymentsResource, c.ns, "scale", deploymentName), &autoscalingv1.Scale{})
if obj == nil {
return nil, err
}
return obj.(*autoscalingv1.Scale), err
}
// UpdateScale takes the representation of a scale and updates it. Returns the server's representation of the scale, and an error, if there is any.
func (c *FakeDeployments) UpdateScale(deploymentName string, scale *autoscalingv1.Scale) (result *autoscalingv1.Scale, err error) {
obj, err := c.Fake.
Invokes(testing.NewUpdateSubresourceAction(deploymentsResource, "scale", c.ns, scale), &autoscalingv1.Scale{})
if obj == nil {
return nil, err
}
return obj.(*autoscalingv1.Scale), err
}

View File

@ -20,6 +20,7 @@ package fake
import ( import (
appsv1 "k8s.io/api/apps/v1" appsv1 "k8s.io/api/apps/v1"
autoscalingv1 "k8s.io/api/autoscaling/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
labels "k8s.io/apimachinery/pkg/labels" labels "k8s.io/apimachinery/pkg/labels"
schema "k8s.io/apimachinery/pkg/runtime/schema" schema "k8s.io/apimachinery/pkg/runtime/schema"
@ -138,3 +139,25 @@ func (c *FakeReplicaSets) Patch(name string, pt types.PatchType, data []byte, su
} }
return obj.(*appsv1.ReplicaSet), err return obj.(*appsv1.ReplicaSet), err
} }
// GetScale takes name of the replicaSet, and returns the corresponding scale object, and an error if there is any.
func (c *FakeReplicaSets) GetScale(replicaSetName string, options v1.GetOptions) (result *autoscalingv1.Scale, err error) {
obj, err := c.Fake.
Invokes(testing.NewGetSubresourceAction(replicasetsResource, c.ns, "scale", replicaSetName), &autoscalingv1.Scale{})
if obj == nil {
return nil, err
}
return obj.(*autoscalingv1.Scale), err
}
// UpdateScale takes the representation of a scale and updates it. Returns the server's representation of the scale, and an error, if there is any.
func (c *FakeReplicaSets) UpdateScale(replicaSetName string, scale *autoscalingv1.Scale) (result *autoscalingv1.Scale, err error) {
obj, err := c.Fake.
Invokes(testing.NewUpdateSubresourceAction(replicasetsResource, "scale", c.ns, scale), &autoscalingv1.Scale{})
if obj == nil {
return nil, err
}
return obj.(*autoscalingv1.Scale), err
}

View File

@ -20,6 +20,7 @@ package fake
import ( import (
appsv1 "k8s.io/api/apps/v1" appsv1 "k8s.io/api/apps/v1"
autoscalingv1 "k8s.io/api/autoscaling/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
labels "k8s.io/apimachinery/pkg/labels" labels "k8s.io/apimachinery/pkg/labels"
schema "k8s.io/apimachinery/pkg/runtime/schema" schema "k8s.io/apimachinery/pkg/runtime/schema"
@ -138,3 +139,25 @@ func (c *FakeStatefulSets) Patch(name string, pt types.PatchType, data []byte, s
} }
return obj.(*appsv1.StatefulSet), err return obj.(*appsv1.StatefulSet), err
} }
// GetScale takes name of the statefulSet, and returns the corresponding scale object, and an error if there is any.
func (c *FakeStatefulSets) GetScale(statefulSetName string, options v1.GetOptions) (result *autoscalingv1.Scale, err error) {
obj, err := c.Fake.
Invokes(testing.NewGetSubresourceAction(statefulsetsResource, c.ns, "scale", statefulSetName), &autoscalingv1.Scale{})
if obj == nil {
return nil, err
}
return obj.(*autoscalingv1.Scale), err
}
// UpdateScale takes the representation of a scale and updates it. Returns the server's representation of the scale, and an error, if there is any.
func (c *FakeStatefulSets) UpdateScale(statefulSetName string, scale *autoscalingv1.Scale) (result *autoscalingv1.Scale, err error) {
obj, err := c.Fake.
Invokes(testing.NewUpdateSubresourceAction(statefulsetsResource, "scale", c.ns, scale), &autoscalingv1.Scale{})
if obj == nil {
return nil, err
}
return obj.(*autoscalingv1.Scale), err
}

View File

@ -20,6 +20,7 @@ package v1
import ( import (
v1 "k8s.io/api/apps/v1" v1 "k8s.io/api/apps/v1"
autoscalingv1 "k8s.io/api/autoscaling/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
types "k8s.io/apimachinery/pkg/types" types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch" watch "k8s.io/apimachinery/pkg/watch"
@ -44,6 +45,9 @@ type ReplicaSetInterface interface {
List(opts metav1.ListOptions) (*v1.ReplicaSetList, error) List(opts metav1.ListOptions) (*v1.ReplicaSetList, error)
Watch(opts metav1.ListOptions) (watch.Interface, error) Watch(opts metav1.ListOptions) (watch.Interface, error)
Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.ReplicaSet, err error) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.ReplicaSet, err error)
GetScale(replicaSetName string, options metav1.GetOptions) (*autoscalingv1.Scale, error)
UpdateScale(replicaSetName string, scale *autoscalingv1.Scale) (*autoscalingv1.Scale, error)
ReplicaSetExpansion ReplicaSetExpansion
} }
@ -172,3 +176,31 @@ func (c *replicaSets) Patch(name string, pt types.PatchType, data []byte, subres
Into(result) Into(result)
return return
} }
// GetScale takes name of the replicaSet, and returns the corresponding autoscalingv1.Scale object, and an error if there is any.
func (c *replicaSets) GetScale(replicaSetName string, options metav1.GetOptions) (result *autoscalingv1.Scale, err error) {
result = &autoscalingv1.Scale{}
err = c.client.Get().
Namespace(c.ns).
Resource("replicasets").
Name(replicaSetName).
SubResource("scale").
VersionedParams(&options, scheme.ParameterCodec).
Do().
Into(result)
return
}
// UpdateScale takes the top resource name and the representation of a scale and updates it. Returns the server's representation of the scale, and an error, if there is any.
func (c *replicaSets) UpdateScale(replicaSetName string, scale *autoscalingv1.Scale) (result *autoscalingv1.Scale, err error) {
result = &autoscalingv1.Scale{}
err = c.client.Put().
Namespace(c.ns).
Resource("replicasets").
Name(replicaSetName).
SubResource("scale").
Body(scale).
Do().
Into(result)
return
}

View File

@ -20,6 +20,7 @@ package v1
import ( import (
v1 "k8s.io/api/apps/v1" v1 "k8s.io/api/apps/v1"
autoscalingv1 "k8s.io/api/autoscaling/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
types "k8s.io/apimachinery/pkg/types" types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch" watch "k8s.io/apimachinery/pkg/watch"
@ -44,6 +45,9 @@ type StatefulSetInterface interface {
List(opts metav1.ListOptions) (*v1.StatefulSetList, error) List(opts metav1.ListOptions) (*v1.StatefulSetList, error)
Watch(opts metav1.ListOptions) (watch.Interface, error) Watch(opts metav1.ListOptions) (watch.Interface, error)
Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.StatefulSet, err error) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.StatefulSet, err error)
GetScale(statefulSetName string, options metav1.GetOptions) (*autoscalingv1.Scale, error)
UpdateScale(statefulSetName string, scale *autoscalingv1.Scale) (*autoscalingv1.Scale, error)
StatefulSetExpansion StatefulSetExpansion
} }
@ -172,3 +176,31 @@ func (c *statefulSets) Patch(name string, pt types.PatchType, data []byte, subre
Into(result) Into(result)
return return
} }
// GetScale takes name of the statefulSet, and returns the corresponding autoscalingv1.Scale object, and an error if there is any.
func (c *statefulSets) GetScale(statefulSetName string, options metav1.GetOptions) (result *autoscalingv1.Scale, err error) {
result = &autoscalingv1.Scale{}
err = c.client.Get().
Namespace(c.ns).
Resource("statefulsets").
Name(statefulSetName).
SubResource("scale").
VersionedParams(&options, scheme.ParameterCodec).
Do().
Into(result)
return
}
// UpdateScale takes the top resource name and the representation of a scale and updates it. Returns the server's representation of the scale, and an error, if there is any.
func (c *statefulSets) UpdateScale(statefulSetName string, scale *autoscalingv1.Scale) (result *autoscalingv1.Scale, err error) {
result = &autoscalingv1.Scale{}
err = c.client.Put().
Namespace(c.ns).
Resource("statefulsets").
Name(statefulSetName).
SubResource("scale").
Body(scale).
Do().
Into(result)
return
}

View File

@ -29,7 +29,6 @@ type AppsV1beta1Interface interface {
RESTClient() rest.Interface RESTClient() rest.Interface
ControllerRevisionsGetter ControllerRevisionsGetter
DeploymentsGetter DeploymentsGetter
ScalesGetter
StatefulSetsGetter StatefulSetsGetter
} }
@ -46,10 +45,6 @@ func (c *AppsV1beta1Client) Deployments(namespace string) DeploymentInterface {
return newDeployments(c, namespace) return newDeployments(c, namespace)
} }
func (c *AppsV1beta1Client) Scales(namespace string) ScaleInterface {
return newScales(c, namespace)
}
func (c *AppsV1beta1Client) StatefulSets(namespace string) StatefulSetInterface { func (c *AppsV1beta1Client) StatefulSets(namespace string) StatefulSetInterface {
return newStatefulSets(c, namespace) return newStatefulSets(c, namespace)
} }

View File

@ -36,10 +36,6 @@ func (c *FakeAppsV1beta1) Deployments(namespace string) v1beta1.DeploymentInterf
return &FakeDeployments{c, namespace} return &FakeDeployments{c, namespace}
} }
func (c *FakeAppsV1beta1) Scales(namespace string) v1beta1.ScaleInterface {
return &FakeScales{c, namespace}
}
func (c *FakeAppsV1beta1) StatefulSets(namespace string) v1beta1.StatefulSetInterface { func (c *FakeAppsV1beta1) StatefulSets(namespace string) v1beta1.StatefulSetInterface {
return &FakeStatefulSets{c, namespace} return &FakeStatefulSets{c, namespace}
} }

View File

@ -1,25 +0,0 @@
/*
Copyright The Kubernetes Authors.
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.
*/
// Code generated by client-gen. DO NOT EDIT.
package fake
// FakeScales implements ScaleInterface
type FakeScales struct {
Fake *FakeAppsV1beta1
ns string
}

View File

@ -22,6 +22,4 @@ type ControllerRevisionExpansion interface{}
type DeploymentExpansion interface{} type DeploymentExpansion interface{}
type ScaleExpansion interface{}
type StatefulSetExpansion interface{} type StatefulSetExpansion interface{}

View File

@ -1,48 +0,0 @@
/*
Copyright The Kubernetes Authors.
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.
*/
// Code generated by client-gen. DO NOT EDIT.
package v1beta1
import (
rest "k8s.io/client-go/rest"
)
// ScalesGetter has a method to return a ScaleInterface.
// A group's client should implement this interface.
type ScalesGetter interface {
Scales(namespace string) ScaleInterface
}
// ScaleInterface has methods to work with Scale resources.
type ScaleInterface interface {
ScaleExpansion
}
// scales implements ScaleInterface
type scales struct {
client rest.Interface
ns string
}
// newScales returns a Scales
func newScales(c *AppsV1beta1Client, namespace string) *scales {
return &scales{
client: c.RESTClient(),
ns: namespace,
}
}

View File

@ -31,7 +31,6 @@ type AppsV1beta2Interface interface {
DaemonSetsGetter DaemonSetsGetter
DeploymentsGetter DeploymentsGetter
ReplicaSetsGetter ReplicaSetsGetter
ScalesGetter
StatefulSetsGetter StatefulSetsGetter
} }
@ -56,10 +55,6 @@ func (c *AppsV1beta2Client) ReplicaSets(namespace string) ReplicaSetInterface {
return newReplicaSets(c, namespace) return newReplicaSets(c, namespace)
} }
func (c *AppsV1beta2Client) Scales(namespace string) ScaleInterface {
return newScales(c, namespace)
}
func (c *AppsV1beta2Client) StatefulSets(namespace string) StatefulSetInterface { func (c *AppsV1beta2Client) StatefulSets(namespace string) StatefulSetInterface {
return newStatefulSets(c, namespace) return newStatefulSets(c, namespace)
} }

View File

@ -44,10 +44,6 @@ func (c *FakeAppsV1beta2) ReplicaSets(namespace string) v1beta2.ReplicaSetInterf
return &FakeReplicaSets{c, namespace} return &FakeReplicaSets{c, namespace}
} }
func (c *FakeAppsV1beta2) Scales(namespace string) v1beta2.ScaleInterface {
return &FakeScales{c, namespace}
}
func (c *FakeAppsV1beta2) StatefulSets(namespace string) v1beta2.StatefulSetInterface { func (c *FakeAppsV1beta2) StatefulSets(namespace string) v1beta2.StatefulSetInterface {
return &FakeStatefulSets{c, namespace} return &FakeStatefulSets{c, namespace}
} }

View File

@ -1,25 +0,0 @@
/*
Copyright The Kubernetes Authors.
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.
*/
// Code generated by client-gen. DO NOT EDIT.
package fake
// FakeScales implements ScaleInterface
type FakeScales struct {
Fake *FakeAppsV1beta2
ns string
}

View File

@ -26,6 +26,4 @@ type DeploymentExpansion interface{}
type ReplicaSetExpansion interface{} type ReplicaSetExpansion interface{}
type ScaleExpansion interface{}
type StatefulSetExpansion interface{} type StatefulSetExpansion interface{}

View File

@ -1,48 +0,0 @@
/*
Copyright The Kubernetes Authors.
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.
*/
// Code generated by client-gen. DO NOT EDIT.
package v1beta2
import (
rest "k8s.io/client-go/rest"
)
// ScalesGetter has a method to return a ScaleInterface.
// A group's client should implement this interface.
type ScalesGetter interface {
Scales(namespace string) ScaleInterface
}
// ScaleInterface has methods to work with Scale resources.
type ScaleInterface interface {
ScaleExpansion
}
// scales implements ScaleInterface
type scales struct {
client rest.Interface
ns string
}
// newScales returns a Scales
func newScales(c *AppsV1beta2Client, namespace string) *scales {
return &scales{
client: c.RESTClient(),
ns: namespace,
}
}

View File

@ -19,8 +19,8 @@ limitations under the License.
package fake package fake
import ( import (
autoscalingv1 "k8s.io/api/autoscaling/v1"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
v1beta1 "k8s.io/api/extensions/v1beta1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
labels "k8s.io/apimachinery/pkg/labels" labels "k8s.io/apimachinery/pkg/labels"
schema "k8s.io/apimachinery/pkg/runtime/schema" schema "k8s.io/apimachinery/pkg/runtime/schema"
@ -141,23 +141,23 @@ func (c *FakeReplicationControllers) Patch(name string, pt types.PatchType, data
} }
// GetScale takes name of the replicationController, and returns the corresponding scale object, and an error if there is any. // GetScale takes name of the replicationController, and returns the corresponding scale object, and an error if there is any.
func (c *FakeReplicationControllers) GetScale(replicationControllerName string, options v1.GetOptions) (result *v1beta1.Scale, err error) { func (c *FakeReplicationControllers) GetScale(replicationControllerName string, options v1.GetOptions) (result *autoscalingv1.Scale, err error) {
obj, err := c.Fake. obj, err := c.Fake.
Invokes(testing.NewGetSubresourceAction(replicationcontrollersResource, c.ns, "scale", replicationControllerName), &v1beta1.Scale{}) Invokes(testing.NewGetSubresourceAction(replicationcontrollersResource, c.ns, "scale", replicationControllerName), &autoscalingv1.Scale{})
if obj == nil { if obj == nil {
return nil, err return nil, err
} }
return obj.(*v1beta1.Scale), err return obj.(*autoscalingv1.Scale), err
} }
// UpdateScale takes the representation of a scale and updates it. Returns the server's representation of the scale, and an error, if there is any. // UpdateScale takes the representation of a scale and updates it. Returns the server's representation of the scale, and an error, if there is any.
func (c *FakeReplicationControllers) UpdateScale(replicationControllerName string, scale *v1beta1.Scale) (result *v1beta1.Scale, err error) { func (c *FakeReplicationControllers) UpdateScale(replicationControllerName string, scale *autoscalingv1.Scale) (result *autoscalingv1.Scale, err error) {
obj, err := c.Fake. obj, err := c.Fake.
Invokes(testing.NewUpdateSubresourceAction(replicationcontrollersResource, "scale", c.ns, scale), &v1beta1.Scale{}) Invokes(testing.NewUpdateSubresourceAction(replicationcontrollersResource, "scale", c.ns, scale), &autoscalingv1.Scale{})
if obj == nil { if obj == nil {
return nil, err return nil, err
} }
return obj.(*v1beta1.Scale), err return obj.(*autoscalingv1.Scale), err
} }

View File

@ -19,8 +19,8 @@ limitations under the License.
package v1 package v1
import ( import (
autoscalingv1 "k8s.io/api/autoscaling/v1"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
v1beta1 "k8s.io/api/extensions/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
types "k8s.io/apimachinery/pkg/types" types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch" watch "k8s.io/apimachinery/pkg/watch"
@ -45,8 +45,8 @@ type ReplicationControllerInterface interface {
List(opts metav1.ListOptions) (*v1.ReplicationControllerList, error) List(opts metav1.ListOptions) (*v1.ReplicationControllerList, error)
Watch(opts metav1.ListOptions) (watch.Interface, error) Watch(opts metav1.ListOptions) (watch.Interface, error)
Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.ReplicationController, err error) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.ReplicationController, err error)
GetScale(replicationControllerName string, options metav1.GetOptions) (*v1beta1.Scale, error) GetScale(replicationControllerName string, options metav1.GetOptions) (*autoscalingv1.Scale, error)
UpdateScale(replicationControllerName string, scale *v1beta1.Scale) (*v1beta1.Scale, error) UpdateScale(replicationControllerName string, scale *autoscalingv1.Scale) (*autoscalingv1.Scale, error)
ReplicationControllerExpansion ReplicationControllerExpansion
} }
@ -177,9 +177,9 @@ func (c *replicationControllers) Patch(name string, pt types.PatchType, data []b
return return
} }
// GetScale takes name of the replicationController, and returns the corresponding v1beta1.Scale object, and an error if there is any. // GetScale takes name of the replicationController, and returns the corresponding autoscalingv1.Scale object, and an error if there is any.
func (c *replicationControllers) GetScale(replicationControllerName string, options metav1.GetOptions) (result *v1beta1.Scale, err error) { func (c *replicationControllers) GetScale(replicationControllerName string, options metav1.GetOptions) (result *autoscalingv1.Scale, err error) {
result = &v1beta1.Scale{} result = &autoscalingv1.Scale{}
err = c.client.Get(). err = c.client.Get().
Namespace(c.ns). Namespace(c.ns).
Resource("replicationcontrollers"). Resource("replicationcontrollers").
@ -192,8 +192,8 @@ func (c *replicationControllers) GetScale(replicationControllerName string, opti
} }
// UpdateScale takes the top resource name and the representation of a scale and updates it. Returns the server's representation of the scale, and an error, if there is any. // UpdateScale takes the top resource name and the representation of a scale and updates it. Returns the server's representation of the scale, and an error, if there is any.
func (c *replicationControllers) UpdateScale(replicationControllerName string, scale *v1beta1.Scale) (result *v1beta1.Scale, err error) { func (c *replicationControllers) UpdateScale(replicationControllerName string, scale *autoscalingv1.Scale) (result *autoscalingv1.Scale, err error) {
result = &v1beta1.Scale{} result = &autoscalingv1.Scale{}
err = c.client.Put(). err = c.client.Put().
Namespace(c.ns). Namespace(c.ns).
Resource("replicationcontrollers"). Resource("replicationcontrollers").

View File

@ -32,7 +32,6 @@ type ExtensionsV1beta1Interface interface {
IngressesGetter IngressesGetter
PodSecurityPoliciesGetter PodSecurityPoliciesGetter
ReplicaSetsGetter ReplicaSetsGetter
ScalesGetter
} }
// ExtensionsV1beta1Client is used to interact with features provided by the extensions group. // ExtensionsV1beta1Client is used to interact with features provided by the extensions group.
@ -60,10 +59,6 @@ func (c *ExtensionsV1beta1Client) ReplicaSets(namespace string) ReplicaSetInterf
return newReplicaSets(c, namespace) return newReplicaSets(c, namespace)
} }
func (c *ExtensionsV1beta1Client) Scales(namespace string) ScaleInterface {
return newScales(c, namespace)
}
// NewForConfig creates a new ExtensionsV1beta1Client for the given config. // NewForConfig creates a new ExtensionsV1beta1Client for the given config.
func NewForConfig(c *rest.Config) (*ExtensionsV1beta1Client, error) { func NewForConfig(c *rest.Config) (*ExtensionsV1beta1Client, error) {
config := *c config := *c

View File

@ -48,10 +48,6 @@ func (c *FakeExtensionsV1beta1) ReplicaSets(namespace string) v1beta1.ReplicaSet
return &FakeReplicaSets{c, namespace} return &FakeReplicaSets{c, namespace}
} }
func (c *FakeExtensionsV1beta1) Scales(namespace string) v1beta1.ScaleInterface {
return &FakeScales{c, namespace}
}
// RESTClient returns a RESTClient that is used to communicate // RESTClient returns a RESTClient that is used to communicate
// with API server by this client implementation. // with API server by this client implementation.
func (c *FakeExtensionsV1beta1) RESTClient() rest.Interface { func (c *FakeExtensionsV1beta1) RESTClient() rest.Interface {

View File

@ -1,25 +0,0 @@
/*
Copyright The Kubernetes Authors.
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.
*/
// Code generated by client-gen. DO NOT EDIT.
package fake
// FakeScales implements ScaleInterface
type FakeScales struct {
Fake *FakeExtensionsV1beta1
ns string
}

View File

@ -1,47 +0,0 @@
/*
Copyright 2015 The Kubernetes Authors.
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 fake
import (
"k8s.io/api/extensions/v1beta1"
"k8s.io/apimachinery/pkg/runtime/schema"
core "k8s.io/client-go/testing"
)
func (c *FakeScales) Get(kind string, name string) (result *v1beta1.Scale, err error) {
action := core.GetActionImpl{}
action.Verb = "get"
action.Namespace = c.ns
action.Resource = schema.GroupVersionResource{Resource: kind}
action.Subresource = "scale"
action.Name = name
obj, err := c.Fake.Invokes(action, &v1beta1.Scale{})
result = obj.(*v1beta1.Scale)
return
}
func (c *FakeScales) Update(kind string, scale *v1beta1.Scale) (result *v1beta1.Scale, err error) {
action := core.UpdateActionImpl{}
action.Verb = "update"
action.Namespace = c.ns
action.Resource = schema.GroupVersionResource{Resource: kind}
action.Subresource = "scale"
action.Object = scale
obj, err := c.Fake.Invokes(action, scale)
result = obj.(*v1beta1.Scale)
return
}

View File

@ -1,48 +0,0 @@
/*
Copyright The Kubernetes Authors.
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.
*/
// Code generated by client-gen. DO NOT EDIT.
package v1beta1
import (
rest "k8s.io/client-go/rest"
)
// ScalesGetter has a method to return a ScaleInterface.
// A group's client should implement this interface.
type ScalesGetter interface {
Scales(namespace string) ScaleInterface
}
// ScaleInterface has methods to work with Scale resources.
type ScaleInterface interface {
ScaleExpansion
}
// scales implements ScaleInterface
type scales struct {
client rest.Interface
ns string
}
// newScales returns a Scales
func newScales(c *ExtensionsV1beta1Client, namespace string) *scales {
return &scales{
client: c.RESTClient(),
ns: namespace,
}
}

View File

@ -1,65 +0,0 @@
/*
Copyright 2016 The Kubernetes Authors.
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 v1beta1
import (
"k8s.io/api/extensions/v1beta1"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime/schema"
)
// The ScaleExpansion interface allows manually adding extra methods to the ScaleInterface.
type ScaleExpansion interface {
Get(kind string, name string) (*v1beta1.Scale, error)
Update(kind string, scale *v1beta1.Scale) (*v1beta1.Scale, error)
}
// Get takes the reference to scale subresource and returns the subresource or error, if one occurs.
func (c *scales) Get(kind string, name string) (result *v1beta1.Scale, err error) {
result = &v1beta1.Scale{}
// TODO this method needs to take a proper unambiguous kind
fullyQualifiedKind := schema.GroupVersionKind{Kind: kind}
resource, _ := meta.UnsafeGuessKindToResource(fullyQualifiedKind)
err = c.client.Get().
Namespace(c.ns).
Resource(resource.Resource).
Name(name).
SubResource("scale").
Do().
Into(result)
return
}
func (c *scales) Update(kind string, scale *v1beta1.Scale) (result *v1beta1.Scale, err error) {
result = &v1beta1.Scale{}
// TODO this method needs to take a proper unambiguous kind
fullyQualifiedKind := schema.GroupVersionKind{Kind: kind}
resource, _ := meta.UnsafeGuessKindToResource(fullyQualifiedKind)
err = c.client.Put().
Namespace(scale.Namespace).
Resource(resource.Resource).
Name(scale.Name).
SubResource("scale").
Body(scale).
Do().
Into(result)
return
}

View File

@ -33,11 +33,3 @@ type DeploymentListerExpansion interface{}
// DeploymentNamespaceListerExpansion allows custom methods to be added to // DeploymentNamespaceListerExpansion allows custom methods to be added to
// DeploymentNamespaceLister. // DeploymentNamespaceLister.
type DeploymentNamespaceListerExpansion interface{} type DeploymentNamespaceListerExpansion interface{}
// ScaleListerExpansion allows custom methods to be added to
// ScaleLister.
type ScaleListerExpansion interface{}
// ScaleNamespaceListerExpansion allows custom methods to be added to
// ScaleNamespaceLister.
type ScaleNamespaceListerExpansion interface{}

View File

@ -1,94 +0,0 @@
/*
Copyright The Kubernetes Authors.
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.
*/
// Code generated by lister-gen. DO NOT EDIT.
package v1beta1
import (
v1beta1 "k8s.io/api/apps/v1beta1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/tools/cache"
)
// ScaleLister helps list Scales.
type ScaleLister interface {
// List lists all Scales in the indexer.
List(selector labels.Selector) (ret []*v1beta1.Scale, err error)
// Scales returns an object that can list and get Scales.
Scales(namespace string) ScaleNamespaceLister
ScaleListerExpansion
}
// scaleLister implements the ScaleLister interface.
type scaleLister struct {
indexer cache.Indexer
}
// NewScaleLister returns a new ScaleLister.
func NewScaleLister(indexer cache.Indexer) ScaleLister {
return &scaleLister{indexer: indexer}
}
// List lists all Scales in the indexer.
func (s *scaleLister) List(selector labels.Selector) (ret []*v1beta1.Scale, err error) {
err = cache.ListAll(s.indexer, selector, func(m interface{}) {
ret = append(ret, m.(*v1beta1.Scale))
})
return ret, err
}
// Scales returns an object that can list and get Scales.
func (s *scaleLister) Scales(namespace string) ScaleNamespaceLister {
return scaleNamespaceLister{indexer: s.indexer, namespace: namespace}
}
// ScaleNamespaceLister helps list and get Scales.
type ScaleNamespaceLister interface {
// List lists all Scales in the indexer for a given namespace.
List(selector labels.Selector) (ret []*v1beta1.Scale, err error)
// Get retrieves the Scale from the indexer for a given namespace and name.
Get(name string) (*v1beta1.Scale, error)
ScaleNamespaceListerExpansion
}
// scaleNamespaceLister implements the ScaleNamespaceLister
// interface.
type scaleNamespaceLister struct {
indexer cache.Indexer
namespace string
}
// List lists all Scales in the indexer for a given namespace.
func (s scaleNamespaceLister) List(selector labels.Selector) (ret []*v1beta1.Scale, err error) {
err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) {
ret = append(ret, m.(*v1beta1.Scale))
})
return ret, err
}
// Get retrieves the Scale from the indexer for a given namespace and name.
func (s scaleNamespaceLister) Get(name string) (*v1beta1.Scale, error) {
obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name)
if err != nil {
return nil, err
}
if !exists {
return nil, errors.NewNotFound(v1beta1.Resource("scale"), name)
}
return obj.(*v1beta1.Scale), nil
}

View File

@ -25,11 +25,3 @@ type ControllerRevisionListerExpansion interface{}
// ControllerRevisionNamespaceListerExpansion allows custom methods to be added to // ControllerRevisionNamespaceListerExpansion allows custom methods to be added to
// ControllerRevisionNamespaceLister. // ControllerRevisionNamespaceLister.
type ControllerRevisionNamespaceListerExpansion interface{} type ControllerRevisionNamespaceListerExpansion interface{}
// ScaleListerExpansion allows custom methods to be added to
// ScaleLister.
type ScaleListerExpansion interface{}
// ScaleNamespaceListerExpansion allows custom methods to be added to
// ScaleNamespaceLister.
type ScaleNamespaceListerExpansion interface{}

View File

@ -1,94 +0,0 @@
/*
Copyright The Kubernetes Authors.
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.
*/
// Code generated by lister-gen. DO NOT EDIT.
package v1beta2
import (
v1beta2 "k8s.io/api/apps/v1beta2"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/tools/cache"
)
// ScaleLister helps list Scales.
type ScaleLister interface {
// List lists all Scales in the indexer.
List(selector labels.Selector) (ret []*v1beta2.Scale, err error)
// Scales returns an object that can list and get Scales.
Scales(namespace string) ScaleNamespaceLister
ScaleListerExpansion
}
// scaleLister implements the ScaleLister interface.
type scaleLister struct {
indexer cache.Indexer
}
// NewScaleLister returns a new ScaleLister.
func NewScaleLister(indexer cache.Indexer) ScaleLister {
return &scaleLister{indexer: indexer}
}
// List lists all Scales in the indexer.
func (s *scaleLister) List(selector labels.Selector) (ret []*v1beta2.Scale, err error) {
err = cache.ListAll(s.indexer, selector, func(m interface{}) {
ret = append(ret, m.(*v1beta2.Scale))
})
return ret, err
}
// Scales returns an object that can list and get Scales.
func (s *scaleLister) Scales(namespace string) ScaleNamespaceLister {
return scaleNamespaceLister{indexer: s.indexer, namespace: namespace}
}
// ScaleNamespaceLister helps list and get Scales.
type ScaleNamespaceLister interface {
// List lists all Scales in the indexer for a given namespace.
List(selector labels.Selector) (ret []*v1beta2.Scale, err error)
// Get retrieves the Scale from the indexer for a given namespace and name.
Get(name string) (*v1beta2.Scale, error)
ScaleNamespaceListerExpansion
}
// scaleNamespaceLister implements the ScaleNamespaceLister
// interface.
type scaleNamespaceLister struct {
indexer cache.Indexer
namespace string
}
// List lists all Scales in the indexer for a given namespace.
func (s scaleNamespaceLister) List(selector labels.Selector) (ret []*v1beta2.Scale, err error) {
err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) {
ret = append(ret, m.(*v1beta2.Scale))
})
return ret, err
}
// Get retrieves the Scale from the indexer for a given namespace and name.
func (s scaleNamespaceLister) Get(name string) (*v1beta2.Scale, error) {
obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name)
if err != nil {
return nil, err
}
if !exists {
return nil, errors.NewNotFound(v1beta2.Resource("scale"), name)
}
return obj.(*v1beta2.Scale), nil
}

View File

@ -29,11 +29,3 @@ type IngressNamespaceListerExpansion interface{}
// PodSecurityPolicyListerExpansion allows custom methods to be added to // PodSecurityPolicyListerExpansion allows custom methods to be added to
// PodSecurityPolicyLister. // PodSecurityPolicyLister.
type PodSecurityPolicyListerExpansion interface{} type PodSecurityPolicyListerExpansion interface{}
// ScaleListerExpansion allows custom methods to be added to
// ScaleLister.
type ScaleListerExpansion interface{}
// ScaleNamespaceListerExpansion allows custom methods to be added to
// ScaleNamespaceLister.
type ScaleNamespaceListerExpansion interface{}

View File

@ -1,94 +0,0 @@
/*
Copyright The Kubernetes Authors.
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.
*/
// Code generated by lister-gen. DO NOT EDIT.
package v1beta1
import (
v1beta1 "k8s.io/api/extensions/v1beta1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/tools/cache"
)
// ScaleLister helps list Scales.
type ScaleLister interface {
// List lists all Scales in the indexer.
List(selector labels.Selector) (ret []*v1beta1.Scale, err error)
// Scales returns an object that can list and get Scales.
Scales(namespace string) ScaleNamespaceLister
ScaleListerExpansion
}
// scaleLister implements the ScaleLister interface.
type scaleLister struct {
indexer cache.Indexer
}
// NewScaleLister returns a new ScaleLister.
func NewScaleLister(indexer cache.Indexer) ScaleLister {
return &scaleLister{indexer: indexer}
}
// List lists all Scales in the indexer.
func (s *scaleLister) List(selector labels.Selector) (ret []*v1beta1.Scale, err error) {
err = cache.ListAll(s.indexer, selector, func(m interface{}) {
ret = append(ret, m.(*v1beta1.Scale))
})
return ret, err
}
// Scales returns an object that can list and get Scales.
func (s *scaleLister) Scales(namespace string) ScaleNamespaceLister {
return scaleNamespaceLister{indexer: s.indexer, namespace: namespace}
}
// ScaleNamespaceLister helps list and get Scales.
type ScaleNamespaceLister interface {
// List lists all Scales in the indexer for a given namespace.
List(selector labels.Selector) (ret []*v1beta1.Scale, err error)
// Get retrieves the Scale from the indexer for a given namespace and name.
Get(name string) (*v1beta1.Scale, error)
ScaleNamespaceListerExpansion
}
// scaleNamespaceLister implements the ScaleNamespaceLister
// interface.
type scaleNamespaceLister struct {
indexer cache.Indexer
namespace string
}
// List lists all Scales in the indexer for a given namespace.
func (s scaleNamespaceLister) List(selector labels.Selector) (ret []*v1beta1.Scale, err error) {
err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) {
ret = append(ret, m.(*v1beta1.Scale))
})
return ret, err
}
// Get retrieves the Scale from the indexer for a given namespace and name.
func (s scaleNamespaceLister) Get(name string) (*v1beta1.Scale, error) {
obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name)
if err != nil {
return nil, err
}
if !exists {
return nil, errors.NewNotFound(v1beta1.Resource("scale"), name)
}
return obj.(*v1beta1.Scale), nil
}

View File

@ -0,0 +1,7 @@
# approval on api packages bubbles to api-approvers
reviewers:
- sig-auth-authenticators-approvers
- sig-auth-authenticators-reviewers
labels:
- sig/auth

View File

@ -322,7 +322,7 @@ func InClusterConfig() (*Config, error) {
return nil, ErrNotInCluster return nil, ErrNotInCluster
} }
ts := newCachedPathTokenSource(tokenFile) ts := NewCachedFileTokenSource(tokenFile)
if _, err := ts.Token(); err != nil { if _, err := ts.Token(); err != nil {
return nil, err return nil, err

View File

@ -42,7 +42,9 @@ func TokenSourceWrapTransport(ts oauth2.TokenSource) func(http.RoundTripper) htt
} }
} }
func newCachedPathTokenSource(path string) oauth2.TokenSource { // NewCachedFileTokenSource returns a oauth2.TokenSource reads a token from a
// file at a specified path and periodically reloads it.
func NewCachedFileTokenSource(path string) oauth2.TokenSource {
return &cachingTokenSource{ return &cachingTokenSource{
now: time.Now, now: time.Now,
leeway: 1 * time.Minute, leeway: 1 * time.Minute,

7
vendor/k8s.io/client-go/tools/auth/OWNERS generated vendored Normal file
View File

@ -0,0 +1,7 @@
approvers:
- sig-auth-authenticators-approvers
reviewers:
- sig-auth-authenticators-reviewers
labels:
- sig/auth

View File

@ -229,11 +229,11 @@ func (config *DirectClientConfig) getUserIdentificationPartialConfig(configAuthI
if len(configAuthInfo.Token) > 0 { if len(configAuthInfo.Token) > 0 {
mergedConfig.BearerToken = configAuthInfo.Token mergedConfig.BearerToken = configAuthInfo.Token
} else if len(configAuthInfo.TokenFile) > 0 { } else if len(configAuthInfo.TokenFile) > 0 {
tokenBytes, err := ioutil.ReadFile(configAuthInfo.TokenFile) ts := restclient.NewCachedFileTokenSource(configAuthInfo.TokenFile)
if err != nil { if _, err := ts.Token(); err != nil {
return nil, err return nil, err
} }
mergedConfig.BearerToken = string(tokenBytes) mergedConfig.WrapTransport = restclient.TokenSourceWrapTransport(ts)
} }
if len(configAuthInfo.Impersonate) > 0 { if len(configAuthInfo.Impersonate) > 0 {
mergedConfig.Impersonate = restclient.ImpersonationConfig{ mergedConfig.Impersonate = restclient.ImpersonationConfig{

7
vendor/k8s.io/client-go/util/cert/OWNERS generated vendored Normal file
View File

@ -0,0 +1,7 @@
approvers:
- sig-auth-certificates-approvers
reviewers:
- sig-auth-certificates-reviewers
labels:
- sig/auth

View File

@ -24,19 +24,19 @@
}, },
{ {
"ImportPath": "github.com/go-openapi/jsonpointer", "ImportPath": "github.com/go-openapi/jsonpointer",
"Rev": "46af16f9f7b149af66e5d1bd010e3574dc06de98" "Rev": "ef5f0afec364d3b9396b7b77b43dbe26bf1f8004"
}, },
{ {
"ImportPath": "github.com/go-openapi/jsonreference", "ImportPath": "github.com/go-openapi/jsonreference",
"Rev": "13c6e3589ad90f49bd3e3bbe2c2cb3d7a4142272" "Rev": "8483a886a90412cd6858df4ea3483dce9c8e35a3"
}, },
{ {
"ImportPath": "github.com/go-openapi/spec", "ImportPath": "github.com/go-openapi/spec",
"Rev": "1de3e0542de65ad8d75452a595886fdd0befb363" "Rev": "5bae59e25b21498baea7f9d46e9c147ec106a42e"
}, },
{ {
"ImportPath": "github.com/go-openapi/swag", "ImportPath": "github.com/go-openapi/swag",
"Rev": "f3f9494671f93fcff853e3c6e9e948b3eb71e590" "Rev": "5899d5c5e619fda5fa86e14795a835f473ca284c"
}, },
{ {
"ImportPath": "github.com/gogo/protobuf/gogoproto", "ImportPath": "github.com/gogo/protobuf/gogoproto",
@ -160,7 +160,7 @@
}, },
{ {
"ImportPath": "golang.org/x/net/idna", "ImportPath": "golang.org/x/net/idna",
"Rev": "1c05540f6879653db88113bc4a2b70aec4bd491f" "Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
}, },
{ {
"ImportPath": "golang.org/x/text/cases", "ImportPath": "golang.org/x/text/cases",

View File

@ -1 +0,0 @@
eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.pDqezepze0YqRx4u6M8GFaWtnVR-utTWZic-GX-RvMATAoYpG4H2sc9tlnGNCxa44dbRY0vY10qfBU7Sno8vkp21fsK42ofGLfen_suum_0ilm0sFS0X-kAwk7TIq5L5lPPKiChPMUiGp5oJW-g5MqMFX1jNiI-4fP-vSM3B3-eyZtJD_O517TgfIRLnblCzqwIkyRmAfPNopi-Fe8Y31TmO2Vd0nFc1Aqro_VaJSACzEVxOHTNpjETcMjlYzwgMXLeiAfLV-5hM0f6DXgHMlLSuMkB_Ndnw25dkB7hreGk4x0tHQ3X9mUfTgLq1hIDoyeeKDIM83Tqw4LBRph20BQ.qd_pNuyi23B0PlWz.JtpO7kqOm0SWOGzWDalkWheHuNd-eDpVbqI9WPAEFDOIBvz7TbsYMBlIYVWEGWbat4mkx_ejxnMn1L1l996NJnyP7eY-QE82cfPJbjx94d0Ob70KZ4DCm_UxcY2t-OKFiPJqxW7MA5jKyDuGD16bdxpjLEoe_cMSEr8FNu-MVG6wcchPcyYyRkqTQSl4mb09KikkAzHjwjo-DcO0f8ps4Uzsoc0aqAAWdE-ocG0YqierLoemjusYMiLH-eLF6MvaLRvHSte-cLzPuYCeZURnBDgxu3i3UApgddnX7g1c7tdGGBGvgCl-tEEDW58Vxgdjksim2S7y3lfoJ8FFzSWeRH2y7Kq04hgew3b2J_RiDB9ejzIopzG8ZGjJa3EO1-i9ORTl12nXK1RdlLGqu604ENaeVOPCIHL-0C8e6_wHdUGHydLZImSxKYSrNvy8resP1D_9t4B-3q2mkS9mhnMONrXbPDVw5QY5mvXlWs0Db99ARwzsl-Qlu0A_tsZwMjWT2I1QMvWPyTRScmMm0FJSv9zStjzxWa_q2GL7Naz1fI4Dd6ZgNJWYYq-mHN5chEeBdIcwb_zMPHczMQXXNL5nmfRGM1aPffkToFWCDpIlI8IXec83ZC6_POxZegS6n9Drrvc.6Nz8EXxs1lWX3ASaCeNElA

View File

@ -1,32 +0,0 @@
clone:
path: github.com/go-openapi/jsonpointer
matrix:
GO_VERSION:
- "1.6"
build:
integration:
image: golang:$$GO_VERSION
pull: true
commands:
- go get -u github.com/stretchr/testify/assert
- go get -u github.com/go-openapi/swag
- go test -race
- go test -v -cover -coverprofile=coverage.out -covermode=count ./...
notify:
slack:
channel: bots
webhook_url: $$SLACK_URL
username: drone
publish:
coverage:
server: https://coverage.vmware.run
token: $$GITHUB_TOKEN
# threshold: 70
# must_increase: true
when:
matrix:
GO_VERSION: "1.6"

View File

@ -0,0 +1,26 @@
# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
# Set default charset
[*.{js,py,go,scala,rb,java,html,css,less,sass,md}]
charset = utf-8
# Tab indentation (no size specified)
[*.go]
indent_style = tab
[*.md]
trim_trailing_whitespace = false
# Matches the exact files either package.json or .travis.yml
[{package.json,.travis.yml}]
indent_style = space
indent_size = 2

View File

@ -1,13 +0,0 @@
approve_by_comment: true
approve_regex: '^(:shipit:|:\+1:|\+1|LGTM|lgtm|Approved)'
reject_regex: ^[Rr]ejected
reset_on_push: false
reviewers:
members:
- casualjim
- chancez
- frapposelli
- vburenin
- pytlesk4
name: pullapprove
required: 1

View File

@ -0,0 +1,15 @@
after_success:
- bash <(curl -s https://codecov.io/bash)
go:
- '1.9'
- 1.10.x
- 1.11.x
install:
- go get -u github.com/stretchr/testify/assert
- go get -u github.com/go-openapi/swag
language: go
notifications:
slack:
secure: a5VgoiwB1G/AZqzmephPZIhEB9avMlsWSlVnM1dSAtYAwdrQHGTQxAmpOxYIoSPDhWNN5bfZmjd29++UlTwLcHSR+e0kJhH6IfDlsHj/HplNCJ9tyI0zYc7XchtdKgeMxMzBKCzgwFXGSbQGydXTliDNBo0HOzmY3cou/daMFTP60K+offcjS+3LRAYb1EroSRXZqrk1nuF/xDL3792DZUdPMiFR/L/Df6y74D6/QP4sTkTDFQitz4Wy/7jbsfj8dG6qK2zivgV6/l+w4OVjFkxVpPXogDWY10vVXNVynqxfJ7to2d1I9lNCHE2ilBCkWMIPdyJF7hjF8pKW+82yP4EzRh0vu8Xn0HT5MZpQxdRY/YMxNrWaG7SxsoEaO4q5uhgdzAqLYY3TRa7MjIK+7Ur+aqOeTXn6OKwVi0CjvZ6mIU3WUKSwiwkFZMbjRAkSb5CYwMEfGFO/z964xz83qGt6WAtBXNotqCQpTIiKtDHQeLOMfksHImCg6JLhQcWBVxamVgu0G3Pdh8Y6DyPnxraXY95+QDavbjqv7TeYT9T/FNnrkXaTTK0s4iWE5H4ACU0Qvz0wUYgfQrZv0/Hp7V17+rabUwnzYySHCy9SWX/7OV9Cfh31iMp9ZIffr76xmmThtOEqs8TrTtU6BWI3rWwvA9cXQipZTVtL0oswrGw=
script:
- go test -v -race -cover -coverprofile=coverage.txt -covermode=atomic ./...

View File

@ -1,4 +1,4 @@
# gojsonpointer [![Build Status](https://ci.vmware.run/api/badges/go-openapi/jsonpointer/status.svg)](https://ci.vmware.run/go-openapi/jsonpointer) [![Coverage](https://coverage.vmware.run/badges/go-openapi/jsonpointer/coverage.svg)](https://coverage.vmware.run/go-openapi/jsonpointer) [![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io) # gojsonpointer [![Build Status](https://travis-ci.org/go-openapi/jsonpointer.svg?branch=master)](https://travis-ci.org/go-openapi/jsonpointer) [![codecov](https://codecov.io/gh/go-openapi/jsonpointer/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/jsonpointer) [![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io)
[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/jsonpointer/master/LICENSE) [![GoDoc](https://godoc.org/github.com/go-openapi/jsonpointer?status.svg)](http://godoc.org/github.com/go-openapi/jsonpointer) [![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/jsonpointer/master/LICENSE) [![GoDoc](https://godoc.org/github.com/go-openapi/jsonpointer?status.svg)](http://godoc.org/github.com/go-openapi/jsonpointer)
An implementation of JSON Pointer - Go language An implementation of JSON Pointer - Go language

View File

@ -0,0 +1,10 @@
module github.com/go-openapi/jsonpointer
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-openapi/swag v0.17.0
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/testify v1.2.2
gopkg.in/yaml.v2 v2.2.1 // indirect
)

View File

@ -0,0 +1,11 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-openapi/swag v0.17.0 h1:7wu+dZ5k83kvUWeAb+WUkFiUhDzwGqzTR/NhWzeo1JU=
github.com/go-openapi/swag v0.17.0/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 h1:2gxZ0XQIU/5z3Z3bUBu+FXuk2pFbkN6tcwi/pjyaDic=
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@ -43,6 +43,7 @@ const (
) )
var jsonPointableType = reflect.TypeOf(new(JSONPointable)).Elem() var jsonPointableType = reflect.TypeOf(new(JSONPointable)).Elem()
var jsonSetableType = reflect.TypeOf(new(JSONSetable)).Elem()
// JSONPointable is an interface for structs to implement when they need to customize the // JSONPointable is an interface for structs to implement when they need to customize the
// json pointer process // json pointer process
@ -50,16 +51,10 @@ type JSONPointable interface {
JSONLookup(string) (interface{}, error) JSONLookup(string) (interface{}, error)
} }
type implStruct struct { // JSONSetable is an interface for structs to implement when they need to customize the
mode string // "SET" or "GET" // json pointer process
type JSONSetable interface {
inDocument interface{} JSONSet(string, interface{}) error
setInValue interface{}
getOutNode interface{}
getOutKind reflect.Kind
outError error
} }
// New creates a new json pointer for the given string // New creates a new json pointer for the given string
@ -100,15 +95,25 @@ func (p *Pointer) Get(document interface{}) (interface{}, reflect.Kind, error) {
return p.get(document, swag.DefaultJSONNameProvider) return p.get(document, swag.DefaultJSONNameProvider)
} }
// Set uses the pointer to set a value from a JSON document
func (p *Pointer) Set(document interface{}, value interface{}) (interface{}, error) {
return document, p.set(document, value, swag.DefaultJSONNameProvider)
}
// GetForToken gets a value for a json pointer token 1 level deep // GetForToken gets a value for a json pointer token 1 level deep
func GetForToken(document interface{}, decodedToken string) (interface{}, reflect.Kind, error) { func GetForToken(document interface{}, decodedToken string) (interface{}, reflect.Kind, error) {
return getSingleImpl(document, decodedToken, swag.DefaultJSONNameProvider) return getSingleImpl(document, decodedToken, swag.DefaultJSONNameProvider)
} }
// SetForToken gets a value for a json pointer token 1 level deep
func SetForToken(document interface{}, decodedToken string, value interface{}) (interface{}, error) {
return document, setSingleImpl(document, value, decodedToken, swag.DefaultJSONNameProvider)
}
func getSingleImpl(node interface{}, decodedToken string, nameProvider *swag.NameProvider) (interface{}, reflect.Kind, error) { func getSingleImpl(node interface{}, decodedToken string, nameProvider *swag.NameProvider) (interface{}, reflect.Kind, error) {
kind := reflect.Invalid
rValue := reflect.Indirect(reflect.ValueOf(node)) rValue := reflect.Indirect(reflect.ValueOf(node))
kind = rValue.Kind() kind := rValue.Kind()
switch kind { switch kind {
case reflect.Struct: case reflect.Struct:
@ -129,6 +134,7 @@ func getSingleImpl(node interface{}, decodedToken string, nameProvider *swag.Nam
case reflect.Map: case reflect.Map:
kv := reflect.ValueOf(decodedToken) kv := reflect.ValueOf(decodedToken)
mv := rValue.MapIndex(kv) mv := rValue.MapIndex(kv)
if mv.IsValid() && !swag.IsZero(mv) { if mv.IsValid() && !swag.IsZero(mv) {
return mv.Interface(), kind, nil return mv.Interface(), kind, nil
} }
@ -141,7 +147,7 @@ func getSingleImpl(node interface{}, decodedToken string, nameProvider *swag.Nam
} }
sLength := rValue.Len() sLength := rValue.Len()
if tokenIndex < 0 || tokenIndex >= sLength { if tokenIndex < 0 || tokenIndex >= sLength {
return nil, kind, fmt.Errorf("index out of bounds array[0,%d] index '%d'", sLength, tokenIndex) return nil, kind, fmt.Errorf("index out of bounds array[0,%d] index '%d'", sLength-1, tokenIndex)
} }
elem := rValue.Index(tokenIndex) elem := rValue.Index(tokenIndex)
@ -153,6 +159,57 @@ func getSingleImpl(node interface{}, decodedToken string, nameProvider *swag.Nam
} }
func setSingleImpl(node, data interface{}, decodedToken string, nameProvider *swag.NameProvider) error {
rValue := reflect.Indirect(reflect.ValueOf(node))
switch rValue.Kind() {
case reflect.Struct:
if ns, ok := node.(JSONSetable); ok { // pointer impl
return ns.JSONSet(decodedToken, data)
}
if rValue.Type().Implements(jsonSetableType) {
return node.(JSONSetable).JSONSet(decodedToken, data)
}
nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken)
if !ok {
return fmt.Errorf("object has no field %q", decodedToken)
}
fld := rValue.FieldByName(nm)
if fld.IsValid() {
fld.Set(reflect.ValueOf(data))
}
return nil
case reflect.Map:
kv := reflect.ValueOf(decodedToken)
rValue.SetMapIndex(kv, reflect.ValueOf(data))
return nil
case reflect.Slice:
tokenIndex, err := strconv.Atoi(decodedToken)
if err != nil {
return err
}
sLength := rValue.Len()
if tokenIndex < 0 || tokenIndex >= sLength {
return fmt.Errorf("index out of bounds array[0,%d] index '%d'", sLength, tokenIndex)
}
elem := rValue.Index(tokenIndex)
if !elem.CanSet() {
return fmt.Errorf("can't set slice index %s to %v", decodedToken, data)
}
elem.Set(reflect.ValueOf(data))
return nil
default:
return fmt.Errorf("invalid token reference %q", decodedToken)
}
}
func (p *Pointer) get(node interface{}, nameProvider *swag.NameProvider) (interface{}, reflect.Kind, error) { func (p *Pointer) get(node interface{}, nameProvider *swag.NameProvider) (interface{}, reflect.Kind, error) {
if nameProvider == nil { if nameProvider == nil {
@ -184,6 +241,101 @@ func (p *Pointer) get(node interface{}, nameProvider *swag.NameProvider) (interf
return node, kind, nil return node, kind, nil
} }
func (p *Pointer) set(node, data interface{}, nameProvider *swag.NameProvider) error {
knd := reflect.ValueOf(node).Kind()
if knd != reflect.Ptr && knd != reflect.Struct && knd != reflect.Map && knd != reflect.Slice && knd != reflect.Array {
return fmt.Errorf("only structs, pointers, maps and slices are supported for setting values")
}
if nameProvider == nil {
nameProvider = swag.DefaultJSONNameProvider
}
// Full document when empty
if len(p.referenceTokens) == 0 {
return nil
}
lastI := len(p.referenceTokens) - 1
for i, token := range p.referenceTokens {
isLastToken := i == lastI
decodedToken := Unescape(token)
if isLastToken {
return setSingleImpl(node, data, decodedToken, nameProvider)
}
rValue := reflect.Indirect(reflect.ValueOf(node))
kind := rValue.Kind()
switch kind {
case reflect.Struct:
if rValue.Type().Implements(jsonPointableType) {
r, err := node.(JSONPointable).JSONLookup(decodedToken)
if err != nil {
return err
}
fld := reflect.ValueOf(r)
if fld.CanAddr() && fld.Kind() != reflect.Interface && fld.Kind() != reflect.Map && fld.Kind() != reflect.Slice && fld.Kind() != reflect.Ptr {
node = fld.Addr().Interface()
continue
}
node = r
continue
}
nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken)
if !ok {
return fmt.Errorf("object has no field %q", decodedToken)
}
fld := rValue.FieldByName(nm)
if fld.CanAddr() && fld.Kind() != reflect.Interface && fld.Kind() != reflect.Map && fld.Kind() != reflect.Slice && fld.Kind() != reflect.Ptr {
node = fld.Addr().Interface()
continue
}
node = fld.Interface()
case reflect.Map:
kv := reflect.ValueOf(decodedToken)
mv := rValue.MapIndex(kv)
if !mv.IsValid() {
return fmt.Errorf("object has no key %q", decodedToken)
}
if mv.CanAddr() && mv.Kind() != reflect.Interface && mv.Kind() != reflect.Map && mv.Kind() != reflect.Slice && mv.Kind() != reflect.Ptr {
node = mv.Addr().Interface()
continue
}
node = mv.Interface()
case reflect.Slice:
tokenIndex, err := strconv.Atoi(decodedToken)
if err != nil {
return err
}
sLength := rValue.Len()
if tokenIndex < 0 || tokenIndex >= sLength {
return fmt.Errorf("index out of bounds array[0,%d] index '%d'", sLength, tokenIndex)
}
elem := rValue.Index(tokenIndex)
if elem.CanAddr() && elem.Kind() != reflect.Interface && elem.Kind() != reflect.Map && elem.Kind() != reflect.Slice && elem.Kind() != reflect.Ptr {
node = elem.Addr().Interface()
continue
}
node = elem.Interface()
default:
return fmt.Errorf("invalid token reference %q", decodedToken)
}
}
return nil
}
// DecodedTokens returns the decoded tokens // DecodedTokens returns the decoded tokens
func (p *Pointer) DecodedTokens() []string { func (p *Pointer) DecodedTokens() []string {
result := make([]string, 0, len(p.referenceTokens)) result := make([]string, 0, len(p.referenceTokens))

View File

@ -1 +0,0 @@
eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.Xe40Wx6g5Y-iN0JVMhKyFfubtOId3zAVE564szw_yYGzFNhc_cGZO9F3BtAcJ55CfHG9C_ozn9dpnUDl_zYZoy_6cPCq13Ekb95z8NAC3ekDtbAATsc9HZwRNwI7UfkhstdwxljEouGB01qoLcUn6lFutrou-Ho21COHeDb2caemnPSA-rEAnXkOiBFu0RQ1MIwMygzvHXIHHYNpNwAtXqmiggM10miSjqBM3JmRPxCi7VK6_Rxij5p6LlhmK1BDi8Y6oBh-9BX3--5GAJeWZ6Vof5TnP-Enioia18j8c8KFtfY4q0y6Ednjb-AarLZ12gj695ppkBNJUdTJQmwGwA.fVcz_RiLrUB5fgMS.rjWllDYC6m_NB-ket_LizNEy9mlJ27odBTZQcMKaUqqXZBtWUCmPrOoMXGq-_cc-c7chg7D-WMh9SPQ23pV0P-DY-jsDpbOqHG2STOMEfW9ZREoaOLJXQaWcuBldLjRyWFcq0HGj97LgE6szD1Zlou3bmdHS_Q-U9Up9YQ_8_YnDcESD_cj1w5FZom7HjchKJFeGjQjfDQpoCKCQNMJaavUqy9jHQEeQ_uVocSrETg3GpewDcUF2tuv8uGq7ZZWu7Vl8zmnY1MFTynaGBWzTCSRmCkAXjcsaUheDP_NT5D7k-xUS6LwtqEUiXAXV07SNFraorFj5lnBQZRDlZMYcA3NWR6zHiOxekR9LBYPofst6w1rIqUchj_5m1tDpVTBMPir1eAaFcnJtPgo4ch17OF-kmcmQGLhJI3U7n8wv4sTrmP1dewtRRKrvlJe5r3_6eDiK4xZ8K0rnK1D4g6zuQqU1gA8KaU7pmZkKpFx3Bew4v-6DH32YwQBvAI7Lbb8afou9WsCNB_iswz5XGimP4bifiJRwpWBEz9VGhZFdiw-hZpYWgbxzVb5gtqfTDLIvpbLDmFz1vge16uUQHHVFpo1pSozyr7A60X8qsh9pmmO3RcJ-ZGZBWqiRC-Kl5ejz7WQ.LFoK4Ibi11B2lWQ5WcPSag

View File

@ -1,33 +0,0 @@
clone:
path: github.com/go-openapi/jsonreference
matrix:
GO_VERSION:
- "1.6"
build:
integration:
image: golang:$$GO_VERSION
pull: true
commands:
- go get -u github.com/stretchr/testify/assert
- go get -u github.com/PuerkitoBio/purell
- go get -u github.com/go-openapi/jsonpointer
- go test -race
- go test -v -cover -coverprofile=coverage.out -covermode=count ./...
notify:
slack:
channel: bots
webhook_url: $$SLACK_URL
username: drone
publish:
coverage:
server: https://coverage.vmware.run
token: $$GITHUB_TOKEN
# threshold: 70
# must_increase: true
when:
matrix:
GO_VERSION: "1.6"

View File

@ -1,13 +0,0 @@
approve_by_comment: true
approve_regex: '^(:shipit:|:\+1:|\+1|LGTM|lgtm|Approved)'
reject_regex: ^[Rr]ejected
reset_on_push: false
reviewers:
members:
- casualjim
- chancez
- frapposelli
- vburenin
- pytlesk4
name: pullapprove
required: 1

View File

@ -0,0 +1,16 @@
after_success:
- bash <(curl -s https://codecov.io/bash)
go:
- '1.9'
- 1.10.x
- 1.11.x
install:
- go get -u github.com/stretchr/testify/assert
- go get -u github.com/PuerkitoBio/purell
- go get -u github.com/go-openapi/jsonpointer
language: go
notifications:
slack:
secure: OpQG/36F7DSF00HLm9WZMhyqFCYYyYTsVDObW226cWiR8PWYiNfLZiSEvIzT1Gx4dDjhigKTIqcLhG34CkL5iNXDjm9Yyo2RYhQPlK8NErNqUEXuBqn4RqYHW48VGhEhOyDd4Ei0E2FN5ZbgpvHgtpkdZ6XDi64r3Ac89isP9aPHXQTuv2Jog6b4/OKKiUTftLcTIst0p4Cp3gqOJWf1wnoj+IadWiECNVQT6zb47IYjtyw6+uV8iUjTzdKcRB6Zc6b4Dq7JAg1Zd7Jfxkql3hlKp4PNlRf9Cy7y5iA3G7MLyg3FcPX5z2kmcyPt2jOTRMBWUJ5zIQpOxizAcN8WsT3WWBL5KbuYK6k0PzujrIDLqdxGpNmjkkMfDBT9cKmZpm2FdW+oZgPFJP+oKmAo4u4KJz/vjiPTXgQlN5bmrLuRMCp+AwC5wkIohTqWZVPE2TK6ZSnMYcg/W39s+RP/9mJoyryAvPSpBOLTI+biCgaUCTOAZxNTWpMFc3tPYntc41WWkdKcooZ9JA5DwfcaVFyTGQ3YXz+HvX6G1z/gW0Q/A4dBi9mj2iE1xm7tRTT+4VQ2AXFvSEI1HJpfPgYnwAtwOD1v3Qm2EUHk9sCdtEDR4wVGEPIVn44GnwFMnGKx9JWppMPYwFu3SVDdHt+E+LOlhZUply11Aa+IVrT2KUQ=
script:
- go test -v -race -cover -coverprofile=coverage.txt -covermode=atomic ./...

View File

@ -1,4 +1,4 @@
# gojsonreference [![Build Status](https://ci.vmware.run/api/badges/go-openapi/jsonreference/status.svg)](https://ci.vmware.run/go-openapi/jsonreference) [![Coverage](https://coverage.vmware.run/badges/go-openapi/jsonreference/coverage.svg)](https://coverage.vmware.run/go-openapi/jsonreference) [![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io) # gojsonreference [![Build Status](https://travis-ci.org/go-openapi/jsonreference.svg?branch=master)](https://travis-ci.org/go-openapi/jsonreference) [![codecov](https://codecov.io/gh/go-openapi/jsonreference/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/jsonreference) [![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io)
[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/jsonreference/master/LICENSE) [![GoDoc](https://godoc.org/github.com/go-openapi/jsonreference?status.svg)](http://godoc.org/github.com/go-openapi/jsonreference) [![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/jsonreference/master/LICENSE) [![GoDoc](https://godoc.org/github.com/go-openapi/jsonreference?status.svg)](http://godoc.org/github.com/go-openapi/jsonreference)
An implementation of JSON Reference - Go language An implementation of JSON Reference - Go language
@ -7,7 +7,7 @@ An implementation of JSON Reference - Go language
Work in progress ( 90% done ) Work in progress ( 90% done )
## Dependencies ## Dependencies
https://github.com/xeipuuv/gojsonpointer https://github.com/go-openapi/jsonpointer
## References ## References
http://tools.ietf.org/html/draft-ietf-appsawg-json-pointer-07 http://tools.ietf.org/html/draft-ietf-appsawg-json-pointer-07

View File

@ -0,0 +1,15 @@
module github.com/go-openapi/jsonreference
require (
github.com/PuerkitoBio/purell v1.1.0
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-openapi/jsonpointer v0.17.0
github.com/go-openapi/swag v0.17.0 // indirect
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/testify v1.2.2
golang.org/x/net v0.0.0-20181005035420-146acd28ed58 // indirect
golang.org/x/text v0.3.0 // indirect
gopkg.in/yaml.v2 v2.2.1 // indirect
)

View File

@ -0,0 +1,20 @@
github.com/PuerkitoBio/purell v1.1.0 h1:rmGxhojJlM0tuKtfdvliR84CFHljx9ag64t2xmVkjK4=
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-openapi/jsonpointer v0.17.0 h1:Bpl2DtZ6k7wKqfFs7e+4P08+M9I3FQgn09a1UsRUQbk=
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
github.com/go-openapi/swag v0.17.0 h1:7wu+dZ5k83kvUWeAb+WUkFiUhDzwGqzTR/NhWzeo1JU=
github.com/go-openapi/swag v0.17.0/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 h1:2gxZ0XQIU/5z3Z3bUBu+FXuk2pFbkN6tcwi/pjyaDic=
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
golang.org/x/net v0.0.0-20181005035420-146acd28ed58 h1:otZG8yDCO4LVps5+9bxOeNiCvgmOyt96J3roHTYs7oE=
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@ -0,0 +1,21 @@
linters-settings:
govet:
check-shadowing: true
golint:
min-confidence: 0
gocyclo:
min-complexity: 25
maligned:
suggest-new: true
dupl:
threshold: 100
goconst:
min-len: 2
min-occurrences: 2
linters:
enable-all: true
disable:
- maligned
- unparam
- lll

View File

@ -1,16 +1,18 @@
language: go after_success:
- bash <(curl -s https://codecov.io/bash)
go: go:
- 1.7 - '1.9'
- 1.10.x
- 1.11.x
install: install:
- go get -u github.com/stretchr/testify - go get -u github.com/stretchr/testify
- go get -u github.com/go-openapi/swag - go get -u github.com/go-openapi/swag
- go get -u gopkg.in/yaml.v2 - go get -u gopkg.in/yaml.v2
- go get -u github.com/go-openapi/jsonpointer - go get -u github.com/go-openapi/jsonpointer
- go get -u github.com/go-openapi/jsonreference - go get -u github.com/go-openapi/jsonreference
script: language: go
- go test -v -race -cover -coverprofile=coverage.txt -covermode=atomic ./...
after_success:
- bash <(curl -s https://codecov.io/bash)
notifications: notifications:
slack: slack:
secure: QUWvCkBBK09GF7YtEvHHVt70JOkdlNBG0nIKu/5qc4/nW5HP8I2w0SEf/XR2je0eED1Qe3L/AfMCWwrEj+IUZc3l4v+ju8X8R3Lomhme0Eb0jd1MTMCuPcBT47YCj0M7RON7vXtbFfm1hFJ/jLe5+9FXz0hpXsR24PJc5ZIi/ogNwkaPqG4BmndzecpSh0vc2FJPZUD9LT0I09REY/vXR0oQAalLkW0asGD5taHZTUZq/kBpsNxaAFrLM23i4mUcf33M5fjLpvx5LRICrX/57XpBrDh2TooBU6Qj3CgoY0uPRYUmSNxbVx1czNzl2JtEpb5yjoxfVPQeg0BvQM00G8LJINISR+ohrjhkZmAqchDupAX+yFrxTtORa78CtnIL6z/aTNlgwwVD8kvL/1pFA/JWYmKDmz93mV/+6wubGzNSQCstzjkFA4/iZEKewKUoRIAi/fxyscP6L/rCpmY/4llZZvrnyTqVbt6URWpopUpH4rwYqreXAtJxJsfBJIeSmUIiDIOMGkCTvyTEW3fWGmGoqWtSHLoaWDyAIGb7azb+KvfpWtEcoPFWfSWU+LGee0A/YsUhBl7ADB9A0CJEuR8q4BPpKpfLwPKSiKSAXL7zDkyjExyhtgqbSl2jS+rKIHOZNL8JkCcTP2MKMVd563C5rC5FMKqu3S9m2b6380E= secure: QUWvCkBBK09GF7YtEvHHVt70JOkdlNBG0nIKu/5qc4/nW5HP8I2w0SEf/XR2je0eED1Qe3L/AfMCWwrEj+IUZc3l4v+ju8X8R3Lomhme0Eb0jd1MTMCuPcBT47YCj0M7RON7vXtbFfm1hFJ/jLe5+9FXz0hpXsR24PJc5ZIi/ogNwkaPqG4BmndzecpSh0vc2FJPZUD9LT0I09REY/vXR0oQAalLkW0asGD5taHZTUZq/kBpsNxaAFrLM23i4mUcf33M5fjLpvx5LRICrX/57XpBrDh2TooBU6Qj3CgoY0uPRYUmSNxbVx1czNzl2JtEpb5yjoxfVPQeg0BvQM00G8LJINISR+ohrjhkZmAqchDupAX+yFrxTtORa78CtnIL6z/aTNlgwwVD8kvL/1pFA/JWYmKDmz93mV/+6wubGzNSQCstzjkFA4/iZEKewKUoRIAi/fxyscP6L/rCpmY/4llZZvrnyTqVbt6URWpopUpH4rwYqreXAtJxJsfBJIeSmUIiDIOMGkCTvyTEW3fWGmGoqWtSHLoaWDyAIGb7azb+KvfpWtEcoPFWfSWU+LGee0A/YsUhBl7ADB9A0CJEuR8q4BPpKpfLwPKSiKSAXL7zDkyjExyhtgqbSl2jS+rKIHOZNL8JkCcTP2MKMVd563C5rC5FMKqu3S9m2b6380E=
script:
- go test -v -race -cover -coverprofile=coverage.txt -covermode=atomic ./...

View File

@ -1,5 +1,10 @@
# OAI object model [![Build Status](https://travis-ci.org/go-openapi/spec.svg?branch=master)](https://travis-ci.org/go-openapi/spec) [![codecov](https://codecov.io/gh/go-openapi/spec/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/spec) [![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io) # OAI object model [![Build Status](https://travis-ci.org/go-openapi/spec.svg?branch=master)](https://travis-ci.org/go-openapi/spec) [![codecov](https://codecov.io/gh/go-openapi/spec/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/spec) [![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io)
[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/spec/master/LICENSE) [![GoDoc](https://godoc.org/github.com/go-openapi/spec?status.svg)](http://godoc.org/github.com/go-openapi/spec) [![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/spec/master/LICENSE)
[![GoDoc](https://godoc.org/github.com/go-openapi/spec?status.svg)](http://godoc.org/github.com/go-openapi/spec)
[![GolangCI](https://golangci.com/badges/github.com/go-openapi/spec.svg)](https://golangci.com)
[![Go Report Card](https://goreportcard.com/badge/github.com/go-openapi/spec)](https://goreportcard.com/report/github.com/go-openapi/spec)
The object model for OpenAPI specification documents The object model for OpenAPI specification documents.
Currently supports Swagger 2.0.

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,47 @@
// Copyright 2015 go-swagger maintainers
//
// 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 spec
import (
"fmt"
"log"
"os"
"path/filepath"
"runtime"
)
var (
// Debug is true when the SWAGGER_DEBUG env var is not empty.
// It enables a more verbose logging of validators.
Debug = os.Getenv("SWAGGER_DEBUG") != ""
// validateLogger is a debug logger for this package
specLogger *log.Logger
)
func init() {
debugOptions()
}
func debugOptions() {
specLogger = log.New(os.Stdout, "spec:", log.LstdFlags)
}
func debugLog(msg string, args ...interface{}) {
// A private, trivial trace logger, based on go-openapi/spec/expander.go:debugLog()
if Debug {
_, file1, pos1, _ := runtime.Caller(1)
specLogger.Printf("%s:%d: %s", filepath.Base(file1), pos1, fmt.Sprintf(msg, args...))
}
}

View File

@ -30,16 +30,12 @@ import (
"github.com/go-openapi/swag" "github.com/go-openapi/swag"
) )
var (
// Debug enables logging when SWAGGER_DEBUG env var is not empty
Debug = os.Getenv("SWAGGER_DEBUG") != ""
)
// ExpandOptions provides options for expand. // ExpandOptions provides options for expand.
type ExpandOptions struct { type ExpandOptions struct {
RelativeBase string RelativeBase string
SkipSchemas bool SkipSchemas bool
ContinueOnError bool ContinueOnError bool
AbsoluteCircularRef bool
} }
// ResolutionCache a cache for resolving urls // ResolutionCache a cache for resolving urls
@ -49,7 +45,7 @@ type ResolutionCache interface {
} }
type simpleCache struct { type simpleCache struct {
lock sync.Mutex lock sync.RWMutex
store map[string]interface{} store map[string]interface{}
} }
@ -59,6 +55,7 @@ func init() {
resCache = initResolutionCache() resCache = initResolutionCache()
} }
// initResolutionCache initializes the URI resolution cache
func initResolutionCache() ResolutionCache { func initResolutionCache() ResolutionCache {
return &simpleCache{store: map[string]interface{}{ return &simpleCache{store: map[string]interface{}{
"http://swagger.io/v2/schema.json": MustLoadSwagger20Schema(), "http://swagger.io/v2/schema.json": MustLoadSwagger20Schema(),
@ -66,16 +63,37 @@ func initResolutionCache() ResolutionCache {
}} }}
} }
// resolverContext allows to share a context during spec processing.
// At the moment, it just holds the index of circular references found.
type resolverContext struct {
// circulars holds all visited circular references, which allows shortcuts.
// NOTE: this is not just a performance improvement: it is required to figure out
// circular references which participate several cycles.
// This structure is privately instantiated and needs not be locked against
// concurrent access, unless we chose to implement a parallel spec walking.
circulars map[string]bool
basePath string
}
func newResolverContext(originalBasePath string) *resolverContext {
return &resolverContext{
circulars: make(map[string]bool),
basePath: originalBasePath, // keep the root base path in context
}
}
// Get retrieves a cached URI
func (s *simpleCache) Get(uri string) (interface{}, bool) { func (s *simpleCache) Get(uri string) (interface{}, bool) {
debugLog("getting %q from resolution cache", uri) debugLog("getting %q from resolution cache", uri)
s.lock.Lock() s.lock.RLock()
v, ok := s.store[uri] v, ok := s.store[uri]
debugLog("got %q from resolution cache: %t", uri, ok) debugLog("got %q from resolution cache: %t", uri, ok)
s.lock.Unlock() s.lock.RUnlock()
return v, ok return v, ok
} }
// Set caches a URI
func (s *simpleCache) Set(uri string, data interface{}) { func (s *simpleCache) Set(uri string, data interface{}) {
s.lock.Lock() s.lock.Lock()
s.store[uri] = data s.store[uri] = data
@ -84,7 +102,7 @@ func (s *simpleCache) Set(uri string, data interface{}) {
// ResolveRefWithBase resolves a reference against a context root with preservation of base path // ResolveRefWithBase resolves a reference against a context root with preservation of base path
func ResolveRefWithBase(root interface{}, ref *Ref, opts *ExpandOptions) (*Schema, error) { func ResolveRefWithBase(root interface{}, ref *Ref, opts *ExpandOptions) (*Schema, error) {
resolver, err := defaultSchemaLoader(root, opts, nil) resolver, err := defaultSchemaLoader(root, opts, nil, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -116,21 +134,21 @@ func ResolveRef(root interface{}, ref *Ref) (*Schema, error) {
case map[string]interface{}: case map[string]interface{}:
b, _ := json.Marshal(sch) b, _ := json.Marshal(sch)
newSch := new(Schema) newSch := new(Schema)
json.Unmarshal(b, newSch) _ = json.Unmarshal(b, newSch)
return newSch, nil return newSch, nil
default: default:
return nil, fmt.Errorf("unknown type for the resolved reference") return nil, fmt.Errorf("unknown type for the resolved reference")
} }
} }
// ResolveParameter resolves a paramter reference against a context root // ResolveParameter resolves a parameter reference against a context root
func ResolveParameter(root interface{}, ref Ref) (*Parameter, error) { func ResolveParameter(root interface{}, ref Ref) (*Parameter, error) {
return ResolveParameterWithBase(root, ref, nil) return ResolveParameterWithBase(root, ref, nil)
} }
// ResolveParameterWithBase resolves a paramter reference against a context root and base path // ResolveParameterWithBase resolves a parameter reference against a context root and base path
func ResolveParameterWithBase(root interface{}, ref Ref, opts *ExpandOptions) (*Parameter, error) { func ResolveParameterWithBase(root interface{}, ref Ref, opts *ExpandOptions) (*Parameter, error) {
resolver, err := defaultSchemaLoader(root, opts, nil) resolver, err := defaultSchemaLoader(root, opts, nil, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -149,7 +167,7 @@ func ResolveResponse(root interface{}, ref Ref) (*Response, error) {
// ResolveResponseWithBase resolves response a reference against a context root and base path // ResolveResponseWithBase resolves response a reference against a context root and base path
func ResolveResponseWithBase(root interface{}, ref Ref, opts *ExpandOptions) (*Response, error) { func ResolveResponseWithBase(root interface{}, ref Ref, opts *ExpandOptions) (*Response, error) {
resolver, err := defaultSchemaLoader(root, opts, nil) resolver, err := defaultSchemaLoader(root, opts, nil, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -163,7 +181,7 @@ func ResolveResponseWithBase(root interface{}, ref Ref, opts *ExpandOptions) (*R
// ResolveItems resolves header and parameter items reference against a context root and base path // ResolveItems resolves header and parameter items reference against a context root and base path
func ResolveItems(root interface{}, ref Ref, opts *ExpandOptions) (*Items, error) { func ResolveItems(root interface{}, ref Ref, opts *ExpandOptions) (*Items, error) {
resolver, err := defaultSchemaLoader(root, opts, nil) resolver, err := defaultSchemaLoader(root, opts, nil, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -180,7 +198,7 @@ func ResolveItems(root interface{}, ref Ref, opts *ExpandOptions) (*Items, error
// ResolvePathItem resolves response a path item against a context root and base path // ResolvePathItem resolves response a path item against a context root and base path
func ResolvePathItem(root interface{}, ref Ref, opts *ExpandOptions) (*PathItem, error) { func ResolvePathItem(root interface{}, ref Ref, opts *ExpandOptions) (*PathItem, error) {
resolver, err := defaultSchemaLoader(root, opts, nil) resolver, err := defaultSchemaLoader(root, opts, nil, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -199,6 +217,7 @@ type schemaLoader struct {
root interface{} root interface{}
options *ExpandOptions options *ExpandOptions
cache ResolutionCache cache ResolutionCache
context *resolverContext
loadDoc func(string) (json.RawMessage, error) loadDoc func(string) (json.RawMessage, error)
} }
@ -221,7 +240,8 @@ func init() {
func defaultSchemaLoader( func defaultSchemaLoader(
root interface{}, root interface{},
expandOptions *ExpandOptions, expandOptions *ExpandOptions,
cache ResolutionCache) (*schemaLoader, error) { cache ResolutionCache,
context *resolverContext) (*schemaLoader, error) {
if cache == nil { if cache == nil {
cache = resCache cache = resCache
@ -229,11 +249,15 @@ func defaultSchemaLoader(
if expandOptions == nil { if expandOptions == nil {
expandOptions = &ExpandOptions{} expandOptions = &ExpandOptions{}
} }
absBase, _ := absPath(expandOptions.RelativeBase)
if context == nil {
context = newResolverContext(absBase)
}
return &schemaLoader{ return &schemaLoader{
root: root, root: root,
options: expandOptions, options: expandOptions,
cache: cache, cache: cache,
context: context,
loadDoc: func(path string) (json.RawMessage, error) { loadDoc: func(path string) (json.RawMessage, error) {
debugLog("fetching document at %q", path) debugLog("fetching document at %q", path)
return PathLoader(path) return PathLoader(path)
@ -312,12 +336,6 @@ func nextRef(startingNode interface{}, startingRef *Ref, ptr *jsonpointer.Pointe
return ret return ret
} }
func debugLog(msg string, args ...interface{}) {
if Debug {
log.Printf(msg, args...)
}
}
// normalize absolute path for cache. // normalize absolute path for cache.
// on Windows, drive letters should be converted to lower as scheme in net/url.URL // on Windows, drive letters should be converted to lower as scheme in net/url.URL
func normalizeAbsPath(path string) string { func normalizeAbsPath(path string) string {
@ -336,12 +354,17 @@ func normalizeAbsPath(path string) string {
// base could be a directory or a full file path // base could be a directory or a full file path
func normalizePaths(refPath, base string) string { func normalizePaths(refPath, base string) string {
refURL, _ := url.Parse(refPath) refURL, _ := url.Parse(refPath)
if path.IsAbs(refURL.Path) { if path.IsAbs(refURL.Path) || filepath.IsAbs(refPath) {
// refPath is actually absolute // refPath is actually absolute
if refURL.Host != "" { if refURL.Host != "" {
return refPath return refPath
} }
return filepath.FromSlash(refPath) parts := strings.Split(refPath, "#")
result := filepath.FromSlash(parts[0])
if len(parts) == 2 {
result += "#" + parts[1]
}
return result
} }
// relative refPath // relative refPath
@ -361,6 +384,59 @@ func normalizePaths(refPath, base string) string {
return baseURL.String() return baseURL.String()
} }
// denormalizePaths returns to simplest notation on file $ref,
// i.e. strips the absolute path and sets a path relative to the base path.
//
// This is currently used when we rewrite ref after a circular ref has been detected
func denormalizeFileRef(ref *Ref, relativeBase, originalRelativeBase string) *Ref {
debugLog("denormalizeFileRef for: %s", ref.String())
if ref.String() == "" || ref.IsRoot() || ref.HasFragmentOnly {
return ref
}
// strip relativeBase from URI
relativeBaseURL, _ := url.Parse(relativeBase)
relativeBaseURL.Fragment = ""
if relativeBaseURL.IsAbs() && strings.HasPrefix(ref.String(), relativeBase) {
// this should work for absolute URI (e.g. http://...): we have an exact match, just trim prefix
r, _ := NewRef(strings.TrimPrefix(ref.String(), relativeBase))
return &r
}
if relativeBaseURL.IsAbs() {
// other absolute URL get unchanged (i.e. with a non-empty scheme)
return ref
}
// for relative file URIs:
originalRelativeBaseURL, _ := url.Parse(originalRelativeBase)
originalRelativeBaseURL.Fragment = ""
if strings.HasPrefix(ref.String(), originalRelativeBaseURL.String()) {
// the resulting ref is in the expanded spec: return a local ref
r, _ := NewRef(strings.TrimPrefix(ref.String(), originalRelativeBaseURL.String()))
return &r
}
// check if we may set a relative path, considering the original base path for this spec.
// Example:
// spec is located at /mypath/spec.json
// my normalized ref points to: /mypath/item.json#/target
// expected result: item.json#/target
parts := strings.Split(ref.String(), "#")
relativePath, err := filepath.Rel(path.Dir(originalRelativeBaseURL.String()), parts[0])
if err != nil {
// there is no common ancestor (e.g. different drives on windows)
// leaves the ref unchanged
return ref
}
if len(parts) == 2 {
relativePath += "#" + parts[1]
}
r, _ := NewRef(relativePath)
return &r
}
// relativeBase could be an ABSOLUTE file path or an ABSOLUTE URL // relativeBase could be an ABSOLUTE file path or an ABSOLUTE URL
func normalizeFileRef(ref *Ref, relativeBase string) *Ref { func normalizeFileRef(ref *Ref, relativeBase string) *Ref {
// This is important for when the reference is pointing to the root schema // This is important for when the reference is pointing to the root schema
@ -369,8 +445,7 @@ func normalizeFileRef(ref *Ref, relativeBase string) *Ref {
return &r return &r
} }
refURL := ref.GetURL() debugLog("normalizing %s against %s", ref.String(), relativeBase)
debugLog("normalizing %s against %s (%s)", ref.String(), relativeBase, refURL.String())
s := normalizePaths(ref.String(), relativeBase) s := normalizePaths(ref.String(), relativeBase)
r, _ := NewRef(s) r, _ := NewRef(s)
@ -395,7 +470,7 @@ func (r *schemaLoader) resolveRef(ref *Ref, target interface{}, basePath string)
// it is pointing somewhere in the root. // it is pointing somewhere in the root.
root := r.root root := r.root
if (ref.IsRoot() || ref.HasFragmentOnly) && root == nil && basePath != "" { if (ref.IsRoot() || ref.HasFragmentOnly) && root == nil && basePath != "" {
if baseRef, err := NewRef(basePath); err == nil { if baseRef, erb := NewRef(basePath); erb == nil {
root, _, _, _ = r.load(baseRef.GetURL()) root, _, _, _ = r.load(baseRef.GetURL())
} }
} }
@ -430,9 +505,11 @@ func (r *schemaLoader) load(refURL *url.URL) (interface{}, url.URL, bool, error)
toFetch := *refURL toFetch := *refURL
toFetch.Fragment = "" toFetch.Fragment = ""
data, fromCache := r.cache.Get(toFetch.String()) normalized := normalizeAbsPath(toFetch.String())
data, fromCache := r.cache.Get(normalized)
if !fromCache { if !fromCache {
b, err := r.loadDoc(toFetch.String()) b, err := r.loadDoc(normalized)
if err != nil { if err != nil {
return nil, url.URL{}, false, err return nil, url.URL{}, false, err
} }
@ -440,7 +517,7 @@ func (r *schemaLoader) load(refURL *url.URL) (interface{}, url.URL, bool, error)
if err := json.Unmarshal(b, &data); err != nil { if err := json.Unmarshal(b, &data); err != nil {
return nil, url.URL{}, false, err return nil, url.URL{}, false, err
} }
r.cache.Set(toFetch.String(), data) r.cache.Set(normalized, data)
} }
return data, toFetch, fromCache, nil return data, toFetch, fromCache, nil
@ -468,7 +545,7 @@ func absPath(fname string) (string, error) {
// ExpandSpec expands the references in a swagger spec // ExpandSpec expands the references in a swagger spec
func ExpandSpec(spec *Swagger, options *ExpandOptions) error { func ExpandSpec(spec *Swagger, options *ExpandOptions) error {
resolver, err := defaultSchemaLoader(spec, options, nil) resolver, err := defaultSchemaLoader(spec, options, nil, nil)
// Just in case this ever returns an error. // Just in case this ever returns an error.
if shouldStopOnError(err, resolver.options) { if shouldStopOnError(err, resolver.options) {
return err return err
@ -484,7 +561,7 @@ func ExpandSpec(spec *Swagger, options *ExpandOptions) error {
for key, definition := range spec.Definitions { for key, definition := range spec.Definitions {
var def *Schema var def *Schema
var err error var err error
if def, err = expandSchema(definition, []string{fmt.Sprintf("#/defintions/%s", key)}, resolver, specBasePath); shouldStopOnError(err, resolver.options) { if def, err = expandSchema(definition, []string{fmt.Sprintf("#/definitions/%s", key)}, resolver, specBasePath); shouldStopOnError(err, resolver.options) {
return err return err
} }
if def != nil { if def != nil {
@ -531,25 +608,35 @@ func shouldStopOnError(err error, opts *ExpandOptions) bool {
return false return false
} }
// ExpandSchema expands the refs in the schema object with reference to the root object // baseForRoot loads in the cache the root document and produces a fake "root" base path entry
// go-openapi/validate uses this function // for further $ref resolution
// notice that it is impossible to reference a json scema in a different file other than root func baseForRoot(root interface{}, cache ResolutionCache) string {
func ExpandSchema(schema *Schema, root interface{}, cache ResolutionCache) error { // cache the root document to resolve $ref's
// Only save the root to a tmp file if it isn't nil. const rootBase = "root"
var base string
if root != nil { if root != nil {
base, _ = absPath("root") base, _ := absPath(rootBase)
normalizedBase := normalizeAbsPath(base)
debugLog("setting root doc in cache at: %s", normalizedBase)
if cache == nil { if cache == nil {
cache = resCache cache = resCache
} }
cache.Set(normalizeAbsPath(base), root) cache.Set(normalizedBase, root)
base = "root" return rootBase
} }
return ""
}
// ExpandSchema expands the refs in the schema object with reference to the root object
// go-openapi/validate uses this function
// notice that it is impossible to reference a json schema in a different file other than root
func ExpandSchema(schema *Schema, root interface{}, cache ResolutionCache) error {
opts := &ExpandOptions{ opts := &ExpandOptions{
RelativeBase: base, // when a root is specified, cache the root as an in-memory document for $ref retrieval
RelativeBase: baseForRoot(root, cache),
SkipSchemas: false, SkipSchemas: false,
ContinueOnError: false, ContinueOnError: false,
// when no base path is specified, remaining $ref (circular) are rendered with an absolute path
AbsoluteCircularRef: true,
} }
return ExpandSchemaWithBasePath(schema, cache, opts) return ExpandSchemaWithBasePath(schema, cache, opts)
} }
@ -565,7 +652,7 @@ func ExpandSchemaWithBasePath(schema *Schema, cache ResolutionCache, opts *Expan
basePath, _ = absPath(opts.RelativeBase) basePath, _ = absPath(opts.RelativeBase)
} }
resolver, err := defaultSchemaLoader(nil, opts, cache) resolver, err := defaultSchemaLoader(nil, opts, cache, nil)
if err != nil { if err != nil {
return err return err
} }
@ -617,8 +704,32 @@ func basePathFromSchemaID(oldBasePath, id string) string {
return u.String() return u.String()
} }
func isCircular(ref *Ref, basePath string, parentRefs ...string) bool { // isCircular detects cycles in sequences of $ref.
return basePath != "" && swag.ContainsStringsCI(parentRefs, ref.String()) // It relies on a private context (which needs not be locked).
func (r *schemaLoader) isCircular(ref *Ref, basePath string, parentRefs ...string) (foundCycle bool) {
normalizedRef := normalizePaths(ref.String(), basePath)
if _, ok := r.context.circulars[normalizedRef]; ok {
// circular $ref has been already detected in another explored cycle
foundCycle = true
return
}
foundCycle = swag.ContainsStringsCI(parentRefs, normalizedRef)
if foundCycle {
r.context.circulars[normalizedRef] = true
}
return
}
func updateBasePath(transitive *schemaLoader, resolver *schemaLoader, basePath string) string {
if transitive != resolver {
debugLog("got a new resolver")
if transitive.options != nil && transitive.options.RelativeBase != "" {
basePath, _ = absPath(transitive.options.RelativeBase)
debugLog("new basePath = %s", basePath)
}
}
return basePath
} }
func expandSchema(target Schema, parentRefs []string, resolver *schemaLoader, basePath string) (*Schema, error) { func expandSchema(target Schema, parentRefs []string, resolver *schemaLoader, basePath string) (*Schema, error) {
@ -634,6 +745,7 @@ func expandSchema(target Schema, parentRefs []string, resolver *schemaLoader, ba
otherwise the basePath should inherit the parent's */ otherwise the basePath should inherit the parent's */
// important: ID can be relative path // important: ID can be relative path
if target.ID != "" { if target.ID != "" {
debugLog("schema has ID: %s", target.ID)
// handling the case when id is a folder // handling the case when id is a folder
// remember that basePath has to be a file // remember that basePath has to be a file
refPath := target.ID refPath := target.ID
@ -645,7 +757,6 @@ func expandSchema(target Schema, parentRefs []string, resolver *schemaLoader, ba
} }
/* Explain here what this function does */ /* Explain here what this function does */
var t *Schema var t *Schema
/* if Ref is found, everything else doesn't matter */ /* if Ref is found, everything else doesn't matter */
/* Ref also changes the resolution scope of children expandSchema */ /* Ref also changes the resolution scope of children expandSchema */
@ -654,14 +765,21 @@ func expandSchema(target Schema, parentRefs []string, resolver *schemaLoader, ba
normalizedRef := normalizeFileRef(&target.Ref, basePath) normalizedRef := normalizeFileRef(&target.Ref, basePath)
normalizedBasePath := normalizedRef.RemoteURI() normalizedBasePath := normalizedRef.RemoteURI()
/* this means there is a circle in the recursion tree */ if resolver.isCircular(normalizedRef, basePath, parentRefs...) {
/* return the Ref */ // this means there is a cycle in the recursion tree: return the Ref
if isCircular(normalizedRef, basePath, parentRefs...) { // - circular refs cannot be expanded. We leave them as ref.
target.Ref = *normalizedRef // - denormalization means that a new local file ref is set relative to the original basePath
debugLog("shortcut circular ref: basePath: %s, normalizedPath: %s, normalized ref: %s",
basePath, normalizedBasePath, normalizedRef.String())
if !resolver.options.AbsoluteCircularRef {
target.Ref = *denormalizeFileRef(normalizedRef, normalizedBasePath, resolver.context.basePath)
} else {
target.Ref = *normalizedRef
}
return &target, nil return &target, nil
} }
debugLog("\nbasePath: %s", basePath) debugLog("basePath: %s", basePath)
if Debug { if Debug {
b, _ := json.Marshal(target) b, _ := json.Marshal(target)
debugLog("calling Resolve with target: %s", string(b)) debugLog("calling Resolve with target: %s", string(b))
@ -672,7 +790,15 @@ func expandSchema(target Schema, parentRefs []string, resolver *schemaLoader, ba
if t != nil { if t != nil {
parentRefs = append(parentRefs, normalizedRef.String()) parentRefs = append(parentRefs, normalizedRef.String())
return expandSchema(*t, parentRefs, resolver, normalizedBasePath) var err error
transitiveResolver, err := transitiveResolver(basePath, target.Ref, resolver)
if shouldStopOnError(err, resolver.options) {
return nil, err
}
basePath = updateBasePath(transitiveResolver, resolver, normalizedBasePath)
return expandSchema(*t, parentRefs, transitiveResolver, basePath)
} }
} }
@ -781,7 +907,7 @@ func derefPathItem(pathItem *PathItem, parentRefs []string, resolver *schemaLoad
normalizedRef := normalizeFileRef(&pathItem.Ref, basePath) normalizedRef := normalizeFileRef(&pathItem.Ref, basePath)
normalizedBasePath := normalizedRef.RemoteURI() normalizedBasePath := normalizedRef.RemoteURI()
if isCircular(normalizedRef, basePath, parentRefs...) { if resolver.isCircular(normalizedRef, basePath, parentRefs...) {
return nil return nil
} }
@ -807,9 +933,17 @@ func expandPathItem(pathItem *PathItem, resolver *schemaLoader, basePath string)
if err := derefPathItem(pathItem, parentRefs, resolver, basePath); shouldStopOnError(err, resolver.options) { if err := derefPathItem(pathItem, parentRefs, resolver, basePath); shouldStopOnError(err, resolver.options) {
return err return err
} }
if pathItem.Ref.String() != "" {
var err error
resolver, err = transitiveResolver(basePath, pathItem.Ref, resolver)
if shouldStopOnError(err, resolver.options) {
return err
}
}
pathItem.Ref = Ref{} pathItem.Ref = Ref{}
parentRefs = parentRefs[0:] // Currently unused:
//parentRefs = parentRefs[0:]
for idx := range pathItem.Parameters { for idx := range pathItem.Parameters {
if err := expandParameter(&(pathItem.Parameters[idx]), resolver, basePath); shouldStopOnError(err, resolver.options) { if err := expandParameter(&(pathItem.Parameters[idx]), resolver, basePath); shouldStopOnError(err, resolver.options) {
@ -867,19 +1001,68 @@ func expandOperation(op *Operation, resolver *schemaLoader, basePath string) err
return nil return nil
} }
// ExpandResponse expands a response based on a basepath func transitiveResolver(basePath string, ref Ref, resolver *schemaLoader) (*schemaLoader, error) {
// This is the exported version of expandResponse if ref.IsRoot() || ref.HasFragmentOnly {
// all refs inside response will be resolved relative to basePath return resolver, nil
func ExpandResponse(response *Response, basePath string) error {
opts := &ExpandOptions{
RelativeBase: basePath,
} }
resolver, err := defaultSchemaLoader(nil, opts, nil)
baseRef, _ := NewRef(basePath)
currentRef := normalizeFileRef(&ref, basePath)
// Set a new root to resolve against
if !strings.HasPrefix(currentRef.String(), baseRef.String()) {
rootURL := currentRef.GetURL()
rootURL.Fragment = ""
root, _ := resolver.cache.Get(rootURL.String())
var err error
// shallow copy of resolver options to set a new RelativeBase when
// traversing multiple documents
newOptions := resolver.options
newOptions.RelativeBase = rootURL.String()
debugLog("setting new root: %s", newOptions.RelativeBase)
resolver, err = defaultSchemaLoader(root, newOptions, resolver.cache, resolver.context)
if err != nil {
return nil, err
}
}
return resolver, nil
}
// ExpandResponseWithRoot expands a response based on a root document, not a fetchable document
func ExpandResponseWithRoot(response *Response, root interface{}, cache ResolutionCache) error {
opts := &ExpandOptions{
RelativeBase: baseForRoot(root, cache),
SkipSchemas: false,
ContinueOnError: false,
// when no base path is specified, remaining $ref (circular) are rendered with an absolute path
AbsoluteCircularRef: true,
}
resolver, err := defaultSchemaLoader(root, opts, nil, nil)
if err != nil { if err != nil {
return err return err
} }
return expandResponse(response, resolver, basePath) return expandResponse(response, resolver, opts.RelativeBase)
}
// ExpandResponse expands a response based on a basepath
// This is the exported version of expandResponse
// all refs inside response will be resolved relative to basePath
func ExpandResponse(response *Response, basePath string) error {
var specBasePath string
if basePath != "" {
specBasePath, _ = absPath(basePath)
}
opts := &ExpandOptions{
RelativeBase: specBasePath,
}
resolver, err := defaultSchemaLoader(nil, opts, nil, nil)
if err != nil {
return err
}
return expandResponse(response, resolver, opts.RelativeBase)
} }
func derefResponse(response *Response, parentRefs []string, resolver *schemaLoader, basePath string) error { func derefResponse(response *Response, parentRefs []string, resolver *schemaLoader, basePath string) error {
@ -889,7 +1072,7 @@ func derefResponse(response *Response, parentRefs []string, resolver *schemaLoad
normalizedRef := normalizeFileRef(&response.Ref, basePath) normalizedRef := normalizeFileRef(&response.Ref, basePath)
normalizedBasePath := normalizedRef.RemoteURI() normalizedBasePath := normalizedRef.RemoteURI()
if isCircular(normalizedRef, basePath, parentRefs...) { if resolver.isCircular(normalizedRef, basePath, parentRefs...) {
return nil return nil
} }
@ -910,16 +1093,31 @@ func expandResponse(response *Response, resolver *schemaLoader, basePath string)
if response == nil { if response == nil {
return nil return nil
} }
parentRefs := []string{} parentRefs := []string{}
if err := derefResponse(response, parentRefs, resolver, basePath); shouldStopOnError(err, resolver.options) { if err := derefResponse(response, parentRefs, resolver, basePath); shouldStopOnError(err, resolver.options) {
return err return err
} }
if response.Ref.String() != "" {
transitiveResolver, err := transitiveResolver(basePath, response.Ref, resolver)
if shouldStopOnError(err, transitiveResolver.options) {
return err
}
basePath = updateBasePath(transitiveResolver, resolver, basePath)
resolver = transitiveResolver
}
if response.Schema != nil && response.Schema.Ref.String() != "" {
// schema expanded to a $ref in another root
var ern error
response.Schema.Ref, ern = NewRef(normalizePaths(response.Schema.Ref.String(), response.Ref.RemoteURI()))
if ern != nil {
return ern
}
}
response.Ref = Ref{} response.Ref = Ref{}
parentRefs = parentRefs[0:] parentRefs = parentRefs[0:]
if !resolver.options.SkipSchemas && response.Schema != nil { if !resolver.options.SkipSchemas && response.Schema != nil {
parentRefs = append(parentRefs, response.Schema.Ref.String()) // parentRefs = append(parentRefs, response.Schema.Ref.String())
s, err := expandSchema(*response.Schema, parentRefs, resolver, basePath) s, err := expandSchema(*response.Schema, parentRefs, resolver, basePath)
if shouldStopOnError(err, resolver.options) { if shouldStopOnError(err, resolver.options) {
return err return err
@ -930,19 +1128,40 @@ func expandResponse(response *Response, resolver *schemaLoader, basePath string)
return nil return nil
} }
// ExpandParameter expands a parameter based on a basepath // ExpandParameterWithRoot expands a parameter based on a root document, not a fetchable document
// This is the exported version of expandParameter func ExpandParameterWithRoot(parameter *Parameter, root interface{}, cache ResolutionCache) error {
// all refs inside parameter will be resolved relative to basePath
func ExpandParameter(parameter *Parameter, basePath string) error {
opts := &ExpandOptions{ opts := &ExpandOptions{
RelativeBase: basePath, RelativeBase: baseForRoot(root, cache),
SkipSchemas: false,
ContinueOnError: false,
// when no base path is specified, remaining $ref (circular) are rendered with an absolute path
AbsoluteCircularRef: true,
} }
resolver, err := defaultSchemaLoader(nil, opts, nil) resolver, err := defaultSchemaLoader(root, opts, nil, nil)
if err != nil { if err != nil {
return err return err
} }
return expandParameter(parameter, resolver, basePath) return expandParameter(parameter, resolver, opts.RelativeBase)
}
// ExpandParameter expands a parameter based on a basepath
// This is the exported version of expandParameter
// all refs inside parameter will be resolved relative to basePath
func ExpandParameter(parameter *Parameter, basePath string) error {
var specBasePath string
if basePath != "" {
specBasePath, _ = absPath(basePath)
}
opts := &ExpandOptions{
RelativeBase: specBasePath,
}
resolver, err := defaultSchemaLoader(nil, opts, nil, nil)
if err != nil {
return err
}
return expandParameter(parameter, resolver, opts.RelativeBase)
} }
func derefParameter(parameter *Parameter, parentRefs []string, resolver *schemaLoader, basePath string) error { func derefParameter(parameter *Parameter, parentRefs []string, resolver *schemaLoader, basePath string) error {
@ -951,7 +1170,7 @@ func derefParameter(parameter *Parameter, parentRefs []string, resolver *schemaL
normalizedRef := normalizeFileRef(&parameter.Ref, basePath) normalizedRef := normalizeFileRef(&parameter.Ref, basePath)
normalizedBasePath := normalizedRef.RemoteURI() normalizedBasePath := normalizedRef.RemoteURI()
if isCircular(normalizedRef, basePath, parentRefs...) { if resolver.isCircular(normalizedRef, basePath, parentRefs...) {
return nil return nil
} }
@ -977,11 +1196,27 @@ func expandParameter(parameter *Parameter, resolver *schemaLoader, basePath stri
if err := derefParameter(parameter, parentRefs, resolver, basePath); shouldStopOnError(err, resolver.options) { if err := derefParameter(parameter, parentRefs, resolver, basePath); shouldStopOnError(err, resolver.options) {
return err return err
} }
if parameter.Ref.String() != "" {
transitiveResolver, err := transitiveResolver(basePath, parameter.Ref, resolver)
if shouldStopOnError(err, transitiveResolver.options) {
return err
}
basePath = updateBasePath(transitiveResolver, resolver, basePath)
resolver = transitiveResolver
}
if parameter.Schema != nil && parameter.Schema.Ref.String() != "" {
// schema expanded to a $ref in another root
var ern error
parameter.Schema.Ref, ern = NewRef(normalizePaths(parameter.Schema.Ref.String(), parameter.Ref.RemoteURI()))
if ern != nil {
return ern
}
}
parameter.Ref = Ref{} parameter.Ref = Ref{}
parentRefs = parentRefs[0:] parentRefs = parentRefs[0:]
if !resolver.options.SkipSchemas && parameter.Schema != nil { if !resolver.options.SkipSchemas && parameter.Schema != nil {
parentRefs = append(parentRefs, parameter.Schema.Ref.String())
s, err := expandSchema(*parameter.Schema, parentRefs, resolver, basePath) s, err := expandSchema(*parameter.Schema, parentRefs, resolver, basePath)
if shouldStopOnError(err, resolver.options) { if shouldStopOnError(err, resolver.options) {
return err return err

View File

@ -0,0 +1,16 @@
module github.com/go-openapi/spec
require (
github.com/PuerkitoBio/purell v1.1.0 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-openapi/jsonpointer v0.17.0
github.com/go-openapi/jsonreference v0.17.0
github.com/go-openapi/swag v0.17.0
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/testify v1.2.2
golang.org/x/net v0.0.0-20181005035420-146acd28ed58 // indirect
golang.org/x/text v0.3.0 // indirect
gopkg.in/yaml.v2 v2.2.1
)

View File

@ -0,0 +1,22 @@
github.com/PuerkitoBio/purell v1.1.0 h1:rmGxhojJlM0tuKtfdvliR84CFHljx9ag64t2xmVkjK4=
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-openapi/jsonpointer v0.17.0 h1:Bpl2DtZ6k7wKqfFs7e+4P08+M9I3FQgn09a1UsRUQbk=
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
github.com/go-openapi/jsonreference v0.17.0 h1:d/o7/fsLWWQZACbihvZxcyLQ59jfUVs7WOJv/ak7T7A=
github.com/go-openapi/jsonreference v0.17.0/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
github.com/go-openapi/swag v0.17.0 h1:7wu+dZ5k83kvUWeAb+WUkFiUhDzwGqzTR/NhWzeo1JU=
github.com/go-openapi/swag v0.17.0/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 h1:2gxZ0XQIU/5z3Z3bUBu+FXuk2pFbkN6tcwi/pjyaDic=
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
golang.org/x/net v0.0.0-20181005035420-146acd28ed58 h1:otZG8yDCO4LVps5+9bxOeNiCvgmOyt96J3roHTYs7oE=
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@ -22,6 +22,7 @@ import (
"github.com/go-openapi/swag" "github.com/go-openapi/swag"
) )
// HeaderProps describes a response header
type HeaderProps struct { type HeaderProps struct {
Description string `json:"description,omitempty"` Description string `json:"description,omitempty"`
} }
@ -153,7 +154,7 @@ func (h Header) MarshalJSON() ([]byte, error) {
return swag.ConcatJSON(b1, b2, b3), nil return swag.ConcatJSON(b1, b2, b3), nil
} }
// UnmarshalJSON marshal this from JSON // UnmarshalJSON unmarshals this header from JSON
func (h *Header) UnmarshalJSON(data []byte) error { func (h *Header) UnmarshalJSON(data []byte) error {
if err := json.Unmarshal(data, &h.CommonValidations); err != nil { if err := json.Unmarshal(data, &h.CommonValidations); err != nil {
return err return err
@ -164,32 +165,29 @@ func (h *Header) UnmarshalJSON(data []byte) error {
if err := json.Unmarshal(data, &h.VendorExtensible); err != nil { if err := json.Unmarshal(data, &h.VendorExtensible); err != nil {
return err return err
} }
if err := json.Unmarshal(data, &h.HeaderProps); err != nil { return json.Unmarshal(data, &h.HeaderProps)
return err
}
return nil
} }
// JSONLookup look up a value by the json property name // JSONLookup look up a value by the json property name
func (p Header) JSONLookup(token string) (interface{}, error) { func (h Header) JSONLookup(token string) (interface{}, error) {
if ex, ok := p.Extensions[token]; ok { if ex, ok := h.Extensions[token]; ok {
return &ex, nil return &ex, nil
} }
r, _, err := jsonpointer.GetForToken(p.CommonValidations, token) r, _, err := jsonpointer.GetForToken(h.CommonValidations, token)
if err != nil && !strings.HasPrefix(err.Error(), "object has no field") { if err != nil && !strings.HasPrefix(err.Error(), "object has no field") {
return nil, err return nil, err
} }
if r != nil { if r != nil {
return r, nil return r, nil
} }
r, _, err = jsonpointer.GetForToken(p.SimpleSchema, token) r, _, err = jsonpointer.GetForToken(h.SimpleSchema, token)
if err != nil && !strings.HasPrefix(err.Error(), "object has no field") { if err != nil && !strings.HasPrefix(err.Error(), "object has no field") {
return nil, err return nil, err
} }
if r != nil { if r != nil {
return r, nil return r, nil
} }
r, _, err = jsonpointer.GetForToken(p.HeaderProps, token) r, _, err = jsonpointer.GetForToken(h.HeaderProps, token)
return r, err return r, err
} }

View File

@ -52,14 +52,14 @@ func (e Extensions) GetBool(key string) (bool, bool) {
// GetStringSlice gets a string value from the extensions // GetStringSlice gets a string value from the extensions
func (e Extensions) GetStringSlice(key string) ([]string, bool) { func (e Extensions) GetStringSlice(key string) ([]string, bool) {
if v, ok := e[strings.ToLower(key)]; ok { if v, ok := e[strings.ToLower(key)]; ok {
arr, ok := v.([]interface{}) arr, isSlice := v.([]interface{})
if !ok { if !isSlice {
return nil, false return nil, false
} }
var strs []string var strs []string
for _, iface := range arr { for _, iface := range arr {
str, ok := iface.(string) str, isString := iface.(string)
if !ok { if !isString {
return nil, false return nil, false
} }
strs = append(strs, str) strs = append(strs, str)

View File

@ -22,6 +22,7 @@ import (
"github.com/go-openapi/swag" "github.com/go-openapi/swag"
) )
// SimpleSchema describe swagger simple schemas for parameters and headers
type SimpleSchema struct { type SimpleSchema struct {
Type string `json:"type,omitempty"` Type string `json:"type,omitempty"`
Format string `json:"format,omitempty"` Format string `json:"format,omitempty"`
@ -31,6 +32,7 @@ type SimpleSchema struct {
Example interface{} `json:"example,omitempty"` Example interface{} `json:"example,omitempty"`
} }
// TypeName return the type (or format) of a simple schema
func (s *SimpleSchema) TypeName() string { func (s *SimpleSchema) TypeName() string {
if s.Format != "" { if s.Format != "" {
return s.Format return s.Format
@ -38,6 +40,7 @@ func (s *SimpleSchema) TypeName() string {
return s.Type return s.Type
} }
// ItemsTypeName yields the type of items in a simple schema array
func (s *SimpleSchema) ItemsTypeName() string { func (s *SimpleSchema) ItemsTypeName() string {
if s.Items == nil { if s.Items == nil {
return "" return ""
@ -45,6 +48,7 @@ func (s *SimpleSchema) ItemsTypeName() string {
return s.Items.TypeName() return s.Items.TypeName()
} }
// CommonValidations describe common JSON-schema validations
type CommonValidations struct { type CommonValidations struct {
Maximum *float64 `json:"maximum,omitempty"` Maximum *float64 `json:"maximum,omitempty"`
ExclusiveMaximum bool `json:"exclusiveMaximum,omitempty"` ExclusiveMaximum bool `json:"exclusiveMaximum,omitempty"`
@ -212,18 +216,18 @@ func (i Items) MarshalJSON() ([]byte, error) {
} }
// JSONLookup look up a value by the json property name // JSONLookup look up a value by the json property name
func (p Items) JSONLookup(token string) (interface{}, error) { func (i Items) JSONLookup(token string) (interface{}, error) {
if token == "$ref" { if token == "$ref" {
return &p.Ref, nil return &i.Ref, nil
} }
r, _, err := jsonpointer.GetForToken(p.CommonValidations, token) r, _, err := jsonpointer.GetForToken(i.CommonValidations, token)
if err != nil && !strings.HasPrefix(err.Error(), "object has no field") { if err != nil && !strings.HasPrefix(err.Error(), "object has no field") {
return nil, err return nil, err
} }
if r != nil { if r != nil {
return r, nil return r, nil
} }
r, _, err = jsonpointer.GetForToken(p.SimpleSchema, token) r, _, err = jsonpointer.GetForToken(i.SimpleSchema, token)
return r, err return r, err
} }

View File

@ -21,6 +21,7 @@ import (
"github.com/go-openapi/swag" "github.com/go-openapi/swag"
) )
// OperationProps describes an operation
type OperationProps struct { type OperationProps struct {
Description string `json:"description,omitempty"` Description string `json:"description,omitempty"`
Consumes []string `json:"consumes,omitempty"` Consumes []string `json:"consumes,omitempty"`
@ -38,9 +39,9 @@ type OperationProps struct {
// MarshalJSON takes care of serializing operation properties to JSON // MarshalJSON takes care of serializing operation properties to JSON
// //
// We use a custom marhaller here to handle a special cases related // We use a custom marhaller here to handle a special cases related to
// the Security field. We need to preserve zero length slice // the Security field. We need to preserve zero length slice
// while omiting the field when the value is nil/unset. // while omitting the field when the value is nil/unset.
func (op OperationProps) MarshalJSON() ([]byte, error) { func (op OperationProps) MarshalJSON() ([]byte, error) {
type Alias OperationProps type Alias OperationProps
if op.Security == nil { if op.Security == nil {

View File

@ -64,6 +64,7 @@ func ParamRef(uri string) *Parameter {
return p return p
} }
// ParamProps describes the specific attributes of an operation parameter
type ParamProps struct { type ParamProps struct {
Description string `json:"description,omitempty"` Description string `json:"description,omitempty"`
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`

View File

@ -21,7 +21,7 @@ import (
"github.com/go-openapi/swag" "github.com/go-openapi/swag"
) )
// pathItemProps the path item specific properties // PathItemProps the path item specific properties
type PathItemProps struct { type PathItemProps struct {
Get *Operation `json:"get,omitempty"` Get *Operation `json:"get,omitempty"`
Put *Operation `json:"put,omitempty"` Put *Operation `json:"put,omitempty"`

View File

@ -39,15 +39,15 @@ type Response struct {
} }
// JSONLookup look up a value by the json property name // JSONLookup look up a value by the json property name
func (p Response) JSONLookup(token string) (interface{}, error) { func (r Response) JSONLookup(token string) (interface{}, error) {
if ex, ok := p.Extensions[token]; ok { if ex, ok := r.Extensions[token]; ok {
return &ex, nil return &ex, nil
} }
if token == "$ref" { if token == "$ref" {
return &p.Ref, nil return &r.Ref, nil
} }
r, _, err := jsonpointer.GetForToken(p.ResponseProps, token) ptr, _, err := jsonpointer.GetForToken(r.ResponseProps, token)
return r, err return ptr, err
} }
// UnmarshalJSON hydrates this items instance with the data from JSON // UnmarshalJSON hydrates this items instance with the data from JSON

View File

@ -85,11 +85,15 @@ func (r Responses) MarshalJSON() ([]byte, error) {
return concated, nil return concated, nil
} }
// ResponsesProps describes all responses for an operation.
// It tells what is the default response and maps all responses with a
// HTTP status code.
type ResponsesProps struct { type ResponsesProps struct {
Default *Response Default *Response
StatusCodeResponses map[int]Response StatusCodeResponses map[int]Response
} }
// MarshalJSON marshals responses as JSON
func (r ResponsesProps) MarshalJSON() ([]byte, error) { func (r ResponsesProps) MarshalJSON() ([]byte, error) {
toser := map[string]Response{} toser := map[string]Response{}
if r.Default != nil { if r.Default != nil {
@ -101,6 +105,7 @@ func (r ResponsesProps) MarshalJSON() ([]byte, error) {
return json.Marshal(toser) return json.Marshal(toser)
} }
// UnmarshalJSON unmarshals responses from JSON
func (r *ResponsesProps) UnmarshalJSON(data []byte) error { func (r *ResponsesProps) UnmarshalJSON(data []byte) error {
var res map[string]Response var res map[string]Response
if err := json.Unmarshal(data, &res); err != nil { if err := json.Unmarshal(data, &res); err != nil {

View File

@ -203,6 +203,7 @@ func (r *SchemaURL) fromMap(v map[string]interface{}) error {
// return nil // return nil
// } // }
// SchemaProps describes a JSON schema (draft 4)
type SchemaProps struct { type SchemaProps struct {
ID string `json:"id,omitempty"` ID string `json:"id,omitempty"`
Ref Ref `json:"-"` Ref Ref `json:"-"`
@ -240,6 +241,7 @@ type SchemaProps struct {
Definitions Definitions `json:"definitions,omitempty"` Definitions Definitions `json:"definitions,omitempty"`
} }
// SwaggerSchemaProps are additional properties supported by swagger schemas, but not JSON-schema (draft 4)
type SwaggerSchemaProps struct { type SwaggerSchemaProps struct {
Discriminator string `json:"discriminator,omitempty"` Discriminator string `json:"discriminator,omitempty"`
ReadOnly bool `json:"readOnly,omitempty"` ReadOnly bool `json:"readOnly,omitempty"`
@ -604,8 +606,8 @@ func (s *Schema) UnmarshalJSON(data []byte) error {
return err return err
} }
sch.Ref.fromMap(d) _ = sch.Ref.fromMap(d)
sch.Schema.fromMap(d) _ = sch.Schema.fromMap(d)
delete(d, "$ref") delete(d, "$ref")
delete(d, "$schema") delete(d, "$schema")

View File

@ -78,6 +78,7 @@ func OAuth2AccessToken(authorizationURL, tokenURL string) *SecurityScheme {
}} }}
} }
// SecuritySchemeProps describes a swagger security scheme in the securityDefinitions section
type SecuritySchemeProps struct { type SecuritySchemeProps struct {
Description string `json:"description,omitempty"` Description string `json:"description,omitempty"`
Type string `json:"type"` Type string `json:"type"`

View File

@ -67,6 +67,7 @@ func (s *Swagger) UnmarshalJSON(data []byte) error {
return nil return nil
} }
// SwaggerProps captures the top-level properties of an Api specification
type SwaggerProps struct { type SwaggerProps struct {
ID string `json:"id,omitempty"` ID string `json:"id,omitempty"`
Consumes []string `json:"consumes,omitempty"` Consumes []string `json:"consumes,omitempty"`

View File

@ -21,6 +21,7 @@ import (
"github.com/go-openapi/swag" "github.com/go-openapi/swag"
) )
// TagProps describe a tag entry in the top level tags section of a swagger spec
type TagProps struct { type TagProps struct {
Description string `json:"description,omitempty"` Description string `json:"description,omitempty"`
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`

View File

@ -1 +1,3 @@
secrets.yml secrets.yml
vendor
Godeps

View File

@ -0,0 +1,20 @@
linters-settings:
govet:
check-shadowing: true
golint:
min-confidence: 0
gocyclo:
min-complexity: 25
maligned:
suggest-new: true
dupl:
threshold: 100
goconst:
min-len: 3
min-occurrences: 2
linters:
enable-all: true
disable:
- maligned
- lll

View File

@ -1,14 +1,16 @@
language: go after_success:
- bash <(curl -s https://codecov.io/bash)
go: go:
- 1.8 - '1.9'
- 1.10.x
- 1.11.x
install: install:
- go get -u github.com/stretchr/testify - go get -u github.com/stretchr/testify
- go get -u github.com/mailru/easyjson - go get -u github.com/mailru/easyjson
- go get -u gopkg.in/yaml.v2 - go get -u gopkg.in/yaml.v2
script: language: go
- go test -v -race -cover -coverprofile=coverage.txt -covermode=atomic ./...
after_success:
- bash <(curl -s https://codecov.io/bash)
notifications: notifications:
slack: slack:
secure: QUWvCkBBK09GF7YtEvHHVt70JOkdlNBG0nIKu/5qc4/nW5HP8I2w0SEf/XR2je0eED1Qe3L/AfMCWwrEj+IUZc3l4v+ju8X8R3Lomhme0Eb0jd1MTMCuPcBT47YCj0M7RON7vXtbFfm1hFJ/jLe5+9FXz0hpXsR24PJc5ZIi/ogNwkaPqG4BmndzecpSh0vc2FJPZUD9LT0I09REY/vXR0oQAalLkW0asGD5taHZTUZq/kBpsNxaAFrLM23i4mUcf33M5fjLpvx5LRICrX/57XpBrDh2TooBU6Qj3CgoY0uPRYUmSNxbVx1czNzl2JtEpb5yjoxfVPQeg0BvQM00G8LJINISR+ohrjhkZmAqchDupAX+yFrxTtORa78CtnIL6z/aTNlgwwVD8kvL/1pFA/JWYmKDmz93mV/+6wubGzNSQCstzjkFA4/iZEKewKUoRIAi/fxyscP6L/rCpmY/4llZZvrnyTqVbt6URWpopUpH4rwYqreXAtJxJsfBJIeSmUIiDIOMGkCTvyTEW3fWGmGoqWtSHLoaWDyAIGb7azb+KvfpWtEcoPFWfSWU+LGee0A/YsUhBl7ADB9A0CJEuR8q4BPpKpfLwPKSiKSAXL7zDkyjExyhtgqbSl2jS+rKIHOZNL8JkCcTP2MKMVd563C5rC5FMKqu3S9m2b6380E= secure: QUWvCkBBK09GF7YtEvHHVt70JOkdlNBG0nIKu/5qc4/nW5HP8I2w0SEf/XR2je0eED1Qe3L/AfMCWwrEj+IUZc3l4v+ju8X8R3Lomhme0Eb0jd1MTMCuPcBT47YCj0M7RON7vXtbFfm1hFJ/jLe5+9FXz0hpXsR24PJc5ZIi/ogNwkaPqG4BmndzecpSh0vc2FJPZUD9LT0I09REY/vXR0oQAalLkW0asGD5taHZTUZq/kBpsNxaAFrLM23i4mUcf33M5fjLpvx5LRICrX/57XpBrDh2TooBU6Qj3CgoY0uPRYUmSNxbVx1czNzl2JtEpb5yjoxfVPQeg0BvQM00G8LJINISR+ohrjhkZmAqchDupAX+yFrxTtORa78CtnIL6z/aTNlgwwVD8kvL/1pFA/JWYmKDmz93mV/+6wubGzNSQCstzjkFA4/iZEKewKUoRIAi/fxyscP6L/rCpmY/4llZZvrnyTqVbt6URWpopUpH4rwYqreXAtJxJsfBJIeSmUIiDIOMGkCTvyTEW3fWGmGoqWtSHLoaWDyAIGb7azb+KvfpWtEcoPFWfSWU+LGee0A/YsUhBl7ADB9A0CJEuR8q4BPpKpfLwPKSiKSAXL7zDkyjExyhtgqbSl2jS+rKIHOZNL8JkCcTP2MKMVd563C5rC5FMKqu3S9m2b6380E=
script:
- go test -v -race -cover -coverprofile=coverage.txt -covermode=atomic ./...

View File

@ -1,12 +1,23 @@
# Swag [![Build Status](https://travis-ci.org/go-openapi/swag.svg?branch=master)](https://travis-ci.org/go-openapi/swag) [![codecov](https://codecov.io/gh/go-openapi/swag/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/swag) [![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io) # Swag [![Build Status](https://travis-ci.org/go-openapi/swag.svg?branch=master)](https://travis-ci.org/go-openapi/swag) [![codecov](https://codecov.io/gh/go-openapi/swag/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/swag) [![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io)
[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/swag/master/LICENSE) [![GoDoc](https://godoc.org/github.com/go-openapi/swag?status.svg)](http://godoc.org/github.com/go-openapi/swag) [![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/swag/master/LICENSE)
[![GoDoc](https://godoc.org/github.com/go-openapi/swag?status.svg)](http://godoc.org/github.com/go-openapi/swag)
[![GolangCI](https://golangci.com/badges/github.com/go-openapi/swag.svg)](https://golangci.com)
[![Go Report Card](https://goreportcard.com/badge/github.com/go-openapi/swag)](https://goreportcard.com/report/github.com/go-openapi/swag)
Contains a bunch of helper functions: Contains a bunch of helper functions for go-openapi and go-swagger projects.
* convert between value and pointers for builtins You may also use it standalone for your projects.
* convert from string to builtin
* convert between value and pointers for builtin types
* convert from string to builtin types (wraps strconv)
* fast json concatenation * fast json concatenation
* search in path * search in path
* load from file or http * load from file or http
* name manglin * name mangling
This repo has only few dependencies outside of the standard library:
* JSON utilities depend on github.com/mailru/easyjson
* YAML utilities depend on gopkg.in/yaml.v2

View File

@ -22,8 +22,9 @@ import (
// same as ECMA Number.MAX_SAFE_INTEGER and Number.MIN_SAFE_INTEGER // same as ECMA Number.MAX_SAFE_INTEGER and Number.MIN_SAFE_INTEGER
const ( const (
maxJSONFloat = float64(1<<53 - 1) // 9007199254740991.0 2^53 - 1 maxJSONFloat = float64(1<<53 - 1) // 9007199254740991.0 2^53 - 1
minJSONFloat = -float64(1<<53 - 1) //-9007199254740991.0 -2^53 - 1 minJSONFloat = -float64(1<<53 - 1) //-9007199254740991.0 -2^53 - 1
epsilon float64 = 1e-9
) )
// IsFloat64AJSONInteger allow for integers [-2^53, 2^53-1] inclusive // IsFloat64AJSONInteger allow for integers [-2^53, 2^53-1] inclusive
@ -31,21 +32,39 @@ func IsFloat64AJSONInteger(f float64) bool {
if math.IsNaN(f) || math.IsInf(f, 0) || f < minJSONFloat || f > maxJSONFloat { if math.IsNaN(f) || math.IsInf(f, 0) || f < minJSONFloat || f > maxJSONFloat {
return false return false
} }
fa := math.Abs(f)
g := float64(uint64(f))
ga := math.Abs(g)
return f == float64(int64(f)) || f == float64(uint64(f)) diff := math.Abs(f - g)
// more info: https://floating-point-gui.de/errors/comparison/#look-out-for-edge-cases
if f == g { // best case
return true
} else if f == float64(int64(f)) || f == float64(uint64(f)) { // optimistic case
return true
} else if f == 0 || g == 0 || diff < math.SmallestNonzeroFloat64 { // very close to 0 values
return diff < (epsilon * math.SmallestNonzeroFloat64)
}
// check the relative error
return diff/math.Min(fa+ga, math.MaxFloat64) < epsilon
} }
var evaluatesAsTrue = map[string]struct{}{ var evaluatesAsTrue map[string]struct{}
"true": struct{}{},
"1": struct{}{}, func init() {
"yes": struct{}{}, evaluatesAsTrue = map[string]struct{}{
"ok": struct{}{}, "true": {},
"y": struct{}{}, "1": {},
"on": struct{}{}, "yes": {},
"selected": struct{}{}, "ok": {},
"checked": struct{}{}, "y": {},
"t": struct{}{}, "on": {},
"enabled": struct{}{}, "selected": {},
"checked": {},
"t": {},
"enabled": {},
}
} }
// ConvertBool turn a string into a boolean // ConvertBool turn a string into a boolean

View File

@ -0,0 +1,33 @@
// Copyright 2015 go-swagger maintainers
//
// 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 swag contains a bunch of helper functions for go-openapi and go-swagger projects.
You may also use it standalone for your projects.
* convert between value and pointers for builtin types
* convert from string to builtin types (wraps strconv)
* fast json concatenation
* search in path
* load from file or http
* name mangling
This repo has only few dependencies outside of the standard library:
* JSON utilities depend on github.com/mailru/easyjson
* YAML utilities depend on gopkg.in/yaml.v2
*/
package swag

View File

@ -0,0 +1,9 @@
module github.com/go-openapi/swag
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/testify v1.2.2
gopkg.in/yaml.v2 v2.2.1
)

View File

@ -0,0 +1,9 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 h1:2gxZ0XQIU/5z3Z3bUBu+FXuk2pFbkN6tcwi/pjyaDic=
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@ -26,14 +26,21 @@ import (
"github.com/mailru/easyjson/jwriter" "github.com/mailru/easyjson/jwriter"
) )
// nullJSON represents a JSON object with null type
var nullJSON = []byte("null")
// DefaultJSONNameProvider the default cache for types // DefaultJSONNameProvider the default cache for types
var DefaultJSONNameProvider = NewNameProvider() var DefaultJSONNameProvider = NewNameProvider()
const comma = byte(',') const comma = byte(',')
var closers = map[byte]byte{ var closers map[byte]byte
'{': '}',
'[': ']', func init() {
closers = map[byte]byte{
'{': '}',
'[': ']',
}
} }
type ejMarshaler interface { type ejMarshaler interface {
@ -79,10 +86,7 @@ func DynamicJSONToStruct(data interface{}, target interface{}) error {
if err != nil { if err != nil {
return err return err
} }
if err := ReadJSON(b, target); err != nil { return ReadJSON(b, target)
return err
}
return nil
} }
// ConcatJSON concatenates multiple json objects efficiently // ConcatJSON concatenates multiple json objects efficiently
@ -90,17 +94,29 @@ func ConcatJSON(blobs ...[]byte) []byte {
if len(blobs) == 0 { if len(blobs) == 0 {
return nil return nil
} }
if len(blobs) == 1 {
last := len(blobs) - 1
for blobs[last] == nil || bytes.Equal(blobs[last], nullJSON) {
// strips trailing null objects
last = last - 1
if last < 0 {
// there was nothing but "null"s or nil...
return nil
}
}
if last == 0 {
return blobs[0] return blobs[0]
} }
last := len(blobs) - 1
var opening, closing byte var opening, closing byte
a := 0 var idx, a int
idx := 0
buf := bytes.NewBuffer(nil) buf := bytes.NewBuffer(nil)
for i, b := range blobs { for i, b := range blobs[:last+1] {
if b == nil || bytes.Equal(b, nullJSON) {
// a null object is in the list: skip it
continue
}
if len(b) > 0 && opening == 0 { // is this an array or an object? if len(b) > 0 && opening == 0 { // is this an array or an object?
opening, closing = b[0], closers[b[0]] opening, closing = b[0], closers[b[0]]
} }
@ -245,7 +261,7 @@ func (n *NameProvider) GetJSONNames(subject interface{}) []string {
names = n.makeNameIndex(tpe) names = n.makeNameIndex(tpe)
} }
var res []string res := make([]string, 0, len(names.jsonNames))
for k := range names.jsonNames { for k := range names.jsonNames {
res = append(res, k) res = append(res, k)
} }

View File

@ -43,7 +43,13 @@ func LoadStrategy(path string, local, remote func(string) ([]byte, error)) func(
if strings.HasPrefix(path, "http") { if strings.HasPrefix(path, "http") {
return remote return remote
} }
return func(pth string) ([]byte, error) { return local(filepath.FromSlash(pth)) } return func(pth string) ([]byte, error) {
upth, err := pathUnescape(pth)
if err != nil {
return nil, err
}
return local(filepath.FromSlash(upth))
}
} }
func loadHTTPBytes(timeout time.Duration) func(path string) ([]byte, error) { func loadHTTPBytes(timeout time.Duration) func(path string) ([]byte, error) {

View File

@ -0,0 +1,9 @@
// +build go1.8
package swag
import "net/url"
func pathUnescape(path string) (string, error) {
return url.PathUnescape(path)
}

View File

@ -0,0 +1,53 @@
// +build go1.9
package swag
import (
"sort"
"sync"
)
// indexOfInitialisms is a thread-safe implementation of the sorted index of initialisms.
// Since go1.9, this may be implemented with sync.Map.
type indexOfInitialisms struct {
sortMutex *sync.Mutex
index *sync.Map
}
func newIndexOfInitialisms() *indexOfInitialisms {
return &indexOfInitialisms{
sortMutex: new(sync.Mutex),
index: new(sync.Map),
}
}
func (m *indexOfInitialisms) load(initial map[string]bool) *indexOfInitialisms {
m.sortMutex.Lock()
defer m.sortMutex.Unlock()
for k, v := range initial {
m.index.Store(k, v)
}
return m
}
func (m *indexOfInitialisms) isInitialism(key string) bool {
_, ok := m.index.Load(key)
return ok
}
func (m *indexOfInitialisms) add(key string) *indexOfInitialisms {
m.index.Store(key, true)
return m
}
func (m *indexOfInitialisms) sorted() (result []string) {
m.sortMutex.Lock()
defer m.sortMutex.Unlock()
m.index.Range(func(key, value interface{}) bool {
k := key.(string)
result = append(result, k)
return true
})
sort.Sort(sort.Reverse(byLength(result)))
return
}

Some files were not shown because too many files have changed in this diff Show More