Update vendor dep

Signed-off-by: jwcesign <jiangwei115@huawei.com>
This commit is contained in:
jwcesign 2023-05-25 09:38:29 +08:00
parent 218a0b2400
commit a18702b1c8
167 changed files with 29937 additions and 1000 deletions

View File

@ -1,10 +1,21 @@
# Change history of go-restful
## [v3.9.0] - 20221-07-21
## [v3.10.1] - 2022-11-19
- fix broken 3.10.0 by using path package for joining paths
## [v3.10.0] - 2022-10-11 - BROKEN
- changed tokenizer to match std route match behavior; do not trimright the path (#511)
- Add MIME_ZIP (#512)
- Add MIME_ZIP and HEADER_ContentDisposition (#513)
- Changed how to get query parameter issue #510
## [v3.9.0] - 2022-07-21
- add support for http.Handler implementations to work as FilterFunction, issue #504 (thanks to https://github.com/ggicci)
## [v3.8.0] - 20221-06-06
## [v3.8.0] - 2022-06-06
- use exact matching of allowed domain entries, issue #489 (#493)
- this changes fixes [security] Authorization Bypass Through User-Controlled Key

View File

@ -7,12 +7,14 @@ package restful
const (
MIME_XML = "application/xml" // Accept or Content-Type used in Consumes() and/or Produces()
MIME_JSON = "application/json" // Accept or Content-Type used in Consumes() and/or Produces()
MIME_ZIP = "application/zip" // Accept or Content-Type used in Consumes() and/or Produces()
MIME_OCTET = "application/octet-stream" // If Content-Type is not present in request, use the default
HEADER_Allow = "Allow"
HEADER_Accept = "Accept"
HEADER_Origin = "Origin"
HEADER_ContentType = "Content-Type"
HEADER_ContentDisposition = "Content-Disposition"
HEADER_LastModified = "Last-Modified"
HEADER_AcceptEncoding = "Accept-Encoding"
HEADER_ContentEncoding = "Content-Encoding"

View File

@ -31,7 +31,8 @@ func NewRequest(httpRequest *http.Request) *Request {
// a "Unable to unmarshal content of type:" response is returned.
// Valid values are restful.MIME_JSON and restful.MIME_XML
// Example:
// restful.DefaultRequestContentType(restful.MIME_JSON)
//
// restful.DefaultRequestContentType(restful.MIME_JSON)
func DefaultRequestContentType(mime string) {
defaultRequestContentType = mime
}
@ -48,7 +49,7 @@ func (r *Request) PathParameters() map[string]string {
// QueryParameter returns the (first) Query parameter value by its name
func (r *Request) QueryParameter(name string) string {
return r.Request.FormValue(name)
return r.Request.URL.Query().Get(name)
}
// QueryParameters returns the all the query parameters values by name

View File

@ -109,6 +109,9 @@ func (r *Response) EntityWriter() (EntityReaderWriter, bool) {
if DefaultResponseMimeType == MIME_XML {
return entityAccessRegistry.accessorAt(MIME_XML)
}
if DefaultResponseMimeType == MIME_ZIP {
return entityAccessRegistry.accessorAt(MIME_ZIP)
}
// Fallback to whatever the route says it can produce.
// https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
for _, each := range r.routeProduces {

View File

@ -164,7 +164,7 @@ func tokenizePath(path string) []string {
if "/" == path {
return nil
}
return strings.Split(strings.Trim(path, "/"), "/")
return strings.Split(strings.TrimLeft(path, "/"), "/")
}
// for debugging
@ -176,3 +176,5 @@ func (r *Route) String() string {
func (r *Route) EnableContentEncoding(enabled bool) {
r.contentEncodingEnabled = &enabled
}
var TrimRightSlashEnabled = false

View File

@ -7,6 +7,7 @@ package restful
import (
"fmt"
"os"
"path"
"reflect"
"runtime"
"strings"
@ -46,11 +47,12 @@ type RouteBuilder struct {
// Do evaluates each argument with the RouteBuilder itself.
// This allows you to follow DRY principles without breaking the fluent programming style.
// Example:
// ws.Route(ws.DELETE("/{name}").To(t.deletePerson).Do(Returns200, Returns500))
//
// func Returns500(b *RouteBuilder) {
// b.Returns(500, "Internal Server Error", restful.ServiceError{})
// }
// ws.Route(ws.DELETE("/{name}").To(t.deletePerson).Do(Returns200, Returns500))
//
// func Returns500(b *RouteBuilder) {
// b.Returns(500, "Internal Server Error", restful.ServiceError{})
// }
func (b *RouteBuilder) Do(oneArgBlocks ...func(*RouteBuilder)) *RouteBuilder {
for _, each := range oneArgBlocks {
each(b)
@ -352,7 +354,7 @@ func (b *RouteBuilder) Build() Route {
}
func concatPath(path1, path2 string) string {
return strings.TrimRight(path1, "/") + "/" + strings.TrimLeft(path2, "/")
return path.Join(path1, path2)
}
var anonymousFuncCount int32

View File

@ -1,15 +0,0 @@
after_success:
- bash <(curl -s https://codecov.io/bash)
go:
- 1.14.x
- 1.15.x
install:
- GO111MODULE=off go get -u gotest.tools/gotestsum
env:
- GO111MODULE=on
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:
- gotestsum -f short-verbose -- -race -coverprofile=coverage.txt -covermode=atomic ./...

View File

@ -1,8 +1,6 @@
linters-settings:
govet:
check-shadowing: true
golint:
min-confidence: 0
gocyclo:
min-complexity: 30
maligned:
@ -12,6 +10,8 @@ linters-settings:
goconst:
min-len: 2
min-occurrences: 4
paralleltest:
ignore-missing: true
linters:
enable-all: true
disable:
@ -39,3 +39,12 @@ linters:
- nestif
- godot
- errorlint
- varcheck
- interfacer
- deadcode
- golint
- ifshort
- structcheck
- nosnakecase
- varnamelen
- exhaustruct

View File

@ -1,24 +0,0 @@
after_success:
- bash <(curl -s https://codecov.io/bash)
go:
- 1.14.x
- 1.x
install:
- go get gotest.tools/gotestsum
jobs:
include:
# include linting job, but only for latest go version and amd64 arch
- go: 1.x
arch: amd64
install:
go get github.com/golangci/golangci-lint/cmd/golangci-lint
script:
- golangci-lint run --new-from-rev master
env:
- GO111MODULE=on
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:
- gotestsum -f short-verbose -- -race -coverprofile=coverage.txt -covermode=atomic ./...

View File

@ -7,8 +7,8 @@ import (
)
const (
defaultHttpPort = ":80"
defaultHttpsPort = ":443"
defaultHTTPPort = ":80"
defaultHTTPSPort = ":443"
)
// Regular expressions used by the normalizations
@ -18,13 +18,14 @@ var rxDupSlashes = regexp.MustCompile(`/{2,}`)
// NormalizeURL will normalize the specified URL
// This was added to replace a previous call to the no longer maintained purell library:
// The call that was used looked like the following:
// url.Parse(purell.NormalizeURL(parsed, purell.FlagsSafe|purell.FlagRemoveDuplicateSlashes))
//
// url.Parse(purell.NormalizeURL(parsed, purell.FlagsSafe|purell.FlagRemoveDuplicateSlashes))
//
// To explain all that was included in the call above, purell.FlagsSafe was really just the following:
// - FlagLowercaseScheme
// - FlagLowercaseHost
// - FlagRemoveDefaultPort
// - FlagRemoveDuplicateSlashes (and this was mixed in with the |)
// - FlagLowercaseScheme
// - FlagLowercaseHost
// - FlagRemoveDefaultPort
// - FlagRemoveDuplicateSlashes (and this was mixed in with the |)
func NormalizeURL(u *url.URL) {
lowercaseScheme(u)
lowercaseHost(u)
@ -48,7 +49,7 @@ func removeDefaultPort(u *url.URL) {
if len(u.Host) > 0 {
scheme := strings.ToLower(u.Scheme)
u.Host = rxPort.ReplaceAllStringFunc(u.Host, func(val string) string {
if (scheme == "http" && val == defaultHttpPort) || (scheme == "https" && val == defaultHttpsPort) {
if (scheme == "http" && val == defaultHTTPPort) || (scheme == "https" && val == defaultHTTPSPort) {
return ""
}
return val

View File

@ -8,7 +8,6 @@ import (
"fmt"
"math"
"os"
"path/filepath"
"reflect"
"regexp"
"runtime"
@ -141,12 +140,11 @@ func CallerInfo() []string {
}
parts := strings.Split(file, "/")
file = parts[len(parts)-1]
if len(parts) > 1 {
filename := parts[len(parts)-1]
dir := parts[len(parts)-2]
if (dir != "assert" && dir != "mock" && dir != "require") || file == "mock_test.go" {
path, _ := filepath.Abs(file)
callers = append(callers, fmt.Sprintf("%s:%d", path, line))
if (dir != "assert" && dir != "mock" && dir != "require") || filename == "mock_test.go" {
callers = append(callers, fmt.Sprintf("%s:%d", file, line))
}
}
@ -530,7 +528,7 @@ func isNil(object interface{}) bool {
[]reflect.Kind{
reflect.Chan, reflect.Func,
reflect.Interface, reflect.Map,
reflect.Ptr, reflect.Slice},
reflect.Ptr, reflect.Slice, reflect.UnsafePointer},
kind)
if isNilableKind && value.IsNil() {
@ -818,49 +816,44 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok
return true // we consider nil to be equal to the nil set
}
defer func() {
if e := recover(); e != nil {
ok = false
}
}()
listKind := reflect.TypeOf(list).Kind()
subsetKind := reflect.TypeOf(subset).Kind()
if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map {
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...)
}
subsetKind := reflect.TypeOf(subset).Kind()
if subsetKind != reflect.Array && subsetKind != reflect.Slice && listKind != reflect.Map {
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...)
}
subsetValue := reflect.ValueOf(subset)
if subsetKind == reflect.Map && listKind == reflect.Map {
listValue := reflect.ValueOf(list)
subsetKeys := subsetValue.MapKeys()
subsetMap := reflect.ValueOf(subset)
actualMap := reflect.ValueOf(list)
for i := 0; i < len(subsetKeys); i++ {
subsetKey := subsetKeys[i]
subsetElement := subsetValue.MapIndex(subsetKey).Interface()
listElement := listValue.MapIndex(subsetKey).Interface()
for _, k := range subsetMap.MapKeys() {
ev := subsetMap.MapIndex(k)
av := actualMap.MapIndex(k)
if !ObjectsAreEqual(subsetElement, listElement) {
return Fail(t, fmt.Sprintf("\"%s\" does not contain \"%s\"", list, subsetElement), msgAndArgs...)
if !av.IsValid() {
return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, subset), msgAndArgs...)
}
if !ObjectsAreEqual(ev.Interface(), av.Interface()) {
return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, subset), msgAndArgs...)
}
}
return true
}
for i := 0; i < subsetValue.Len(); i++ {
element := subsetValue.Index(i).Interface()
subsetList := reflect.ValueOf(subset)
for i := 0; i < subsetList.Len(); i++ {
element := subsetList.Index(i).Interface()
ok, found := containsElement(list, element)
if !ok {
return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", list), msgAndArgs...)
return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", list), msgAndArgs...)
}
if !found {
return Fail(t, fmt.Sprintf("\"%s\" does not contain \"%s\"", list, element), msgAndArgs...)
return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, element), msgAndArgs...)
}
}
@ -879,34 +872,28 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{})
return Fail(t, "nil is the empty set which is a subset of every set", msgAndArgs...)
}
defer func() {
if e := recover(); e != nil {
ok = false
}
}()
listKind := reflect.TypeOf(list).Kind()
subsetKind := reflect.TypeOf(subset).Kind()
if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map {
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...)
}
subsetKind := reflect.TypeOf(subset).Kind()
if subsetKind != reflect.Array && subsetKind != reflect.Slice && listKind != reflect.Map {
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...)
}
subsetValue := reflect.ValueOf(subset)
if subsetKind == reflect.Map && listKind == reflect.Map {
listValue := reflect.ValueOf(list)
subsetKeys := subsetValue.MapKeys()
subsetMap := reflect.ValueOf(subset)
actualMap := reflect.ValueOf(list)
for i := 0; i < len(subsetKeys); i++ {
subsetKey := subsetKeys[i]
subsetElement := subsetValue.MapIndex(subsetKey).Interface()
listElement := listValue.MapIndex(subsetKey).Interface()
for _, k := range subsetMap.MapKeys() {
ev := subsetMap.MapIndex(k)
av := actualMap.MapIndex(k)
if !ObjectsAreEqual(subsetElement, listElement) {
if !av.IsValid() {
return true
}
if !ObjectsAreEqual(ev.Interface(), av.Interface()) {
return true
}
}
@ -914,8 +901,9 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{})
return Fail(t, fmt.Sprintf("%q is a subset of %q", subset, list), msgAndArgs...)
}
for i := 0; i < subsetValue.Len(); i++ {
element := subsetValue.Index(i).Interface()
subsetList := reflect.ValueOf(subset)
for i := 0; i < subsetList.Len(); i++ {
element := subsetList.Index(i).Interface()
ok, found := containsElement(list, element)
if !ok {
return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", list), msgAndArgs...)

View File

@ -218,16 +218,22 @@ func (c *Call) Unset() *Call {
foundMatchingCall := false
for i, call := range c.Parent.ExpectedCalls {
// in-place filter slice for calls to be removed - iterate from 0'th to last skipping unnecessary ones
var index int // write index
for _, call := range c.Parent.ExpectedCalls {
if call.Method == c.Method {
_, diffCount := call.Arguments.Diff(c.Arguments)
if diffCount == 0 {
foundMatchingCall = true
// Remove from ExpectedCalls
c.Parent.ExpectedCalls = append(c.Parent.ExpectedCalls[:i], c.Parent.ExpectedCalls[i+1:]...)
// Remove from ExpectedCalls - just skip it
continue
}
}
c.Parent.ExpectedCalls[index] = call
index++
}
// trim slice up to last copied index
c.Parent.ExpectedCalls = c.Parent.ExpectedCalls[:index]
if !foundMatchingCall {
unlockOnce.Do(c.unlock)

30
vendor/k8s.io/klog/v2/contextual.go generated vendored
View File

@ -70,11 +70,14 @@ func SetLogger(logger logr.Logger) {
// routing log entries through klogr into klog and then into the actual Logger
// backend.
func SetLoggerWithOptions(logger logr.Logger, opts ...LoggerOption) {
logging.logger = &logger
logging.loggerOptions = loggerOptions{}
for _, opt := range opts {
opt(&logging.loggerOptions)
}
logging.logger = &logWriter{
Logger: logger,
writeKlogBuffer: logging.loggerOptions.writeKlogBuffer,
}
}
// ContextualLogger determines whether the logger passed to
@ -93,6 +96,22 @@ func FlushLogger(flush func()) LoggerOption {
}
}
// WriteKlogBuffer sets a callback that will be invoked by klog to write output
// produced by non-structured log calls like Infof.
//
// The buffer will contain exactly the same data that klog normally would write
// into its own output stream(s). In particular this includes the header, if
// klog is configured to write one. The callback then can divert that data into
// its own output streams. The buffer may or may not end in a line break.
//
// Without such a callback, klog will call the logger's Info or Error method
// with just the message string (i.e. no header).
func WriteKlogBuffer(write func([]byte)) LoggerOption {
return func(o *loggerOptions) {
o.writeKlogBuffer = write
}
}
// LoggerOption implements the functional parameter paradigm for
// SetLoggerWithOptions.
type LoggerOption func(o *loggerOptions)
@ -100,6 +119,13 @@ type LoggerOption func(o *loggerOptions)
type loggerOptions struct {
contextualLogger bool
flush func()
writeKlogBuffer func([]byte)
}
// logWriter combines a logger (always set) with a write callback (optional).
type logWriter struct {
Logger
writeKlogBuffer func([]byte)
}
// ClearLogger removes a backing Logger implementation if one was set earlier
@ -152,7 +178,7 @@ func Background() Logger {
if logging.loggerOptions.contextualLogger {
// Is non-nil because logging.loggerOptions.contextualLogger is
// only true if a logger was set.
return *logging.logger
return logging.logger.Logger
}
return klogLogger

View File

@ -40,44 +40,33 @@ type Buffer struct {
next *Buffer
}
// Buffers manages the reuse of individual buffer instances. It is thread-safe.
type Buffers struct {
// mu protects the free list. It is separate from the main mutex
// so buffers can be grabbed and printed to without holding the main lock,
// for better parallelization.
mu sync.Mutex
// freeList is a list of byte buffers, maintained under mu.
freeList *Buffer
var buffers = sync.Pool{
New: func() interface{} {
return new(Buffer)
},
}
// GetBuffer returns a new, ready-to-use buffer.
func (bl *Buffers) GetBuffer() *Buffer {
bl.mu.Lock()
b := bl.freeList
if b != nil {
bl.freeList = b.next
}
bl.mu.Unlock()
if b == nil {
b = new(Buffer)
} else {
b.next = nil
b.Reset()
}
func GetBuffer() *Buffer {
b := buffers.Get().(*Buffer)
b.Reset()
return b
}
// PutBuffer returns a buffer to the free list.
func (bl *Buffers) PutBuffer(b *Buffer) {
func PutBuffer(b *Buffer) {
if b.Len() >= 256 {
// Let big buffers die a natural death.
// Let big buffers die a natural death, without relying on
// sync.Pool behavior. The documentation implies that items may
// get deallocated while stored there ("If the Pool holds the
// only reference when this [= be removed automatically]
// happens, the item might be deallocated."), but
// https://github.com/golang/go/issues/23199 leans more towards
// having such a size limit.
return
}
bl.mu.Lock()
b.next = bl.freeList
bl.freeList = b
bl.mu.Unlock()
buffers.Put(b)
}
// Some custom tiny helper functions to print the log header efficiently.
@ -121,7 +110,8 @@ func (buf *Buffer) someDigits(i, d int) int {
return copy(buf.Tmp[i:], buf.Tmp[j:])
}
// FormatHeader formats a log header using the provided file name and line number.
// FormatHeader formats a log header using the provided file name and line number
// and writes it into the buffer.
func (buf *Buffer) FormatHeader(s severity.Severity, file string, line int, now time.Time) {
if line < 0 {
line = 0 // not a real line number, but acceptable to someDigits
@ -157,3 +147,30 @@ func (buf *Buffer) FormatHeader(s severity.Severity, file string, line int, now
buf.Tmp[n+2] = ' '
buf.Write(buf.Tmp[:n+3])
}
// SprintHeader formats a log header and returns a string. This is a simpler
// version of FormatHeader for use in ktesting.
func (buf *Buffer) SprintHeader(s severity.Severity, now time.Time) string {
if s > severity.FatalLog {
s = severity.InfoLog // for safety.
}
// Avoid Fprintf, for speed. The format is so simple that we can do it quickly by hand.
// It's worth about 3X. Fprintf is hard.
_, month, day := now.Date()
hour, minute, second := now.Clock()
// Lmmdd hh:mm:ss.uuuuuu threadid file:line]
buf.Tmp[0] = severity.Char[s]
buf.twoDigits(1, int(month))
buf.twoDigits(3, day)
buf.Tmp[5] = ' '
buf.twoDigits(6, hour)
buf.Tmp[8] = ':'
buf.twoDigits(9, minute)
buf.Tmp[11] = ':'
buf.twoDigits(12, second)
buf.Tmp[14] = '.'
buf.nDigits(6, 15, now.Nanosecond()/1000, '0')
buf.Tmp[21] = ']'
return string(buf.Tmp[:22])
}

View File

@ -24,6 +24,10 @@ import (
"github.com/go-logr/logr"
)
type textWriter interface {
WriteText(*bytes.Buffer)
}
// WithValues implements LogSink.WithValues. The old key/value pairs are
// assumed to be well-formed, the new ones are checked and padded if
// necessary. It returns a new slice.
@ -91,11 +95,66 @@ func MergeKVs(first, second []interface{}) []interface{} {
return merged
}
type Formatter struct {
AnyToStringHook AnyToStringFunc
}
type AnyToStringFunc func(v interface{}) string
// MergeKVsInto is a variant of MergeKVs which directly formats the key/value
// pairs into a buffer.
func (f Formatter) MergeAndFormatKVs(b *bytes.Buffer, first, second []interface{}) {
if len(first) == 0 && len(second) == 0 {
// Nothing to do at all.
return
}
if len(first) == 0 && len(second)%2 == 0 {
// Nothing to be overridden, second slice is well-formed
// and can be used directly.
for i := 0; i < len(second); i += 2 {
f.KVFormat(b, second[i], second[i+1])
}
return
}
// Determine which keys are in the second slice so that we can skip
// them when iterating over the first one. The code intentionally
// favors performance over completeness: we assume that keys are string
// constants and thus compare equal when the string values are equal. A
// string constant being overridden by, for example, a fmt.Stringer is
// not handled.
overrides := map[interface{}]bool{}
for i := 0; i < len(second); i += 2 {
overrides[second[i]] = true
}
for i := 0; i < len(first); i += 2 {
key := first[i]
if overrides[key] {
continue
}
f.KVFormat(b, key, first[i+1])
}
// Round down.
l := len(second)
l = l / 2 * 2
for i := 1; i < l; i += 2 {
f.KVFormat(b, second[i-1], second[i])
}
if len(second)%2 == 1 {
f.KVFormat(b, second[len(second)-1], missingValue)
}
}
func MergeAndFormatKVs(b *bytes.Buffer, first, second []interface{}) {
Formatter{}.MergeAndFormatKVs(b, first, second)
}
const missingValue = "(MISSING)"
// KVListFormat serializes all key/value pairs into the provided buffer.
// A space gets inserted before the first pair and between each pair.
func KVListFormat(b *bytes.Buffer, keysAndValues ...interface{}) {
func (f Formatter) KVListFormat(b *bytes.Buffer, keysAndValues ...interface{}) {
for i := 0; i < len(keysAndValues); i += 2 {
var v interface{}
k := keysAndValues[i]
@ -104,69 +163,93 @@ func KVListFormat(b *bytes.Buffer, keysAndValues ...interface{}) {
} else {
v = missingValue
}
b.WriteByte(' ')
// Keys are assumed to be well-formed according to
// https://github.com/kubernetes/community/blob/master/contributors/devel/sig-instrumentation/migration-to-structured-logging.md#name-arguments
// for the sake of performance. Keys with spaces,
// special characters, etc. will break parsing.
if sK, ok := k.(string); ok {
// Avoid one allocation when the key is a string, which
// normally it should be.
b.WriteString(sK)
} else {
b.WriteString(fmt.Sprintf("%s", k))
}
// The type checks are sorted so that more frequently used ones
// come first because that is then faster in the common
// cases. In Kubernetes, ObjectRef (a Stringer) is more common
// than plain strings
// (https://github.com/kubernetes/kubernetes/pull/106594#issuecomment-975526235).
switch v := v.(type) {
case fmt.Stringer:
writeStringValue(b, true, StringerToString(v))
case string:
writeStringValue(b, true, v)
case error:
writeStringValue(b, true, ErrorToString(v))
case logr.Marshaler:
value := MarshalerToValue(v)
// A marshaler that returns a string is useful for
// delayed formatting of complex values. We treat this
// case like a normal string. This is useful for
// multi-line support.
//
// We could do this by recursively formatting a value,
// but that comes with the risk of infinite recursion
// if a marshaler returns itself. Instead we call it
// only once and rely on it returning the intended
// value directly.
switch value := value.(type) {
case string:
writeStringValue(b, true, value)
default:
writeStringValue(b, false, fmt.Sprintf("%+v", value))
}
case []byte:
// In https://github.com/kubernetes/klog/pull/237 it was decided
// to format byte slices with "%+q". The advantages of that are:
// - readable output if the bytes happen to be printable
// - non-printable bytes get represented as unicode escape
// sequences (\uxxxx)
//
// The downsides are that we cannot use the faster
// strconv.Quote here and that multi-line output is not
// supported. If developers know that a byte array is
// printable and they want multi-line output, they can
// convert the value to string before logging it.
b.WriteByte('=')
b.WriteString(fmt.Sprintf("%+q", v))
default:
writeStringValue(b, false, fmt.Sprintf("%+v", v))
}
f.KVFormat(b, k, v)
}
}
func KVListFormat(b *bytes.Buffer, keysAndValues ...interface{}) {
Formatter{}.KVListFormat(b, keysAndValues...)
}
// KVFormat serializes one key/value pair into the provided buffer.
// A space gets inserted before the pair.
func (f Formatter) KVFormat(b *bytes.Buffer, k, v interface{}) {
b.WriteByte(' ')
// Keys are assumed to be well-formed according to
// https://github.com/kubernetes/community/blob/master/contributors/devel/sig-instrumentation/migration-to-structured-logging.md#name-arguments
// for the sake of performance. Keys with spaces,
// special characters, etc. will break parsing.
if sK, ok := k.(string); ok {
// Avoid one allocation when the key is a string, which
// normally it should be.
b.WriteString(sK)
} else {
b.WriteString(fmt.Sprintf("%s", k))
}
// The type checks are sorted so that more frequently used ones
// come first because that is then faster in the common
// cases. In Kubernetes, ObjectRef (a Stringer) is more common
// than plain strings
// (https://github.com/kubernetes/kubernetes/pull/106594#issuecomment-975526235).
switch v := v.(type) {
case textWriter:
writeTextWriterValue(b, v)
case fmt.Stringer:
writeStringValue(b, true, StringerToString(v))
case string:
writeStringValue(b, true, v)
case error:
writeStringValue(b, true, ErrorToString(v))
case logr.Marshaler:
value := MarshalerToValue(v)
// A marshaler that returns a string is useful for
// delayed formatting of complex values. We treat this
// case like a normal string. This is useful for
// multi-line support.
//
// We could do this by recursively formatting a value,
// but that comes with the risk of infinite recursion
// if a marshaler returns itself. Instead we call it
// only once and rely on it returning the intended
// value directly.
switch value := value.(type) {
case string:
writeStringValue(b, true, value)
default:
writeStringValue(b, false, f.AnyToString(value))
}
case []byte:
// In https://github.com/kubernetes/klog/pull/237 it was decided
// to format byte slices with "%+q". The advantages of that are:
// - readable output if the bytes happen to be printable
// - non-printable bytes get represented as unicode escape
// sequences (\uxxxx)
//
// The downsides are that we cannot use the faster
// strconv.Quote here and that multi-line output is not
// supported. If developers know that a byte array is
// printable and they want multi-line output, they can
// convert the value to string before logging it.
b.WriteByte('=')
b.WriteString(fmt.Sprintf("%+q", v))
default:
writeStringValue(b, false, f.AnyToString(v))
}
}
func KVFormat(b *bytes.Buffer, k, v interface{}) {
Formatter{}.KVFormat(b, k, v)
}
// AnyToString is the historic fallback formatter.
func (f Formatter) AnyToString(v interface{}) string {
if f.AnyToStringHook != nil {
return f.AnyToStringHook(v)
}
return fmt.Sprintf("%+v", v)
}
// StringerToString converts a Stringer to a string,
// handling panics if they occur.
func StringerToString(s fmt.Stringer) (ret string) {
@ -203,6 +286,16 @@ func ErrorToString(err error) (ret string) {
return
}
func writeTextWriterValue(b *bytes.Buffer, v textWriter) {
b.WriteRune('=')
defer func() {
if err := recover(); err != nil {
fmt.Fprintf(b, `"<panic: %s>"`, err)
}
}()
v.WriteText(b)
}
func writeStringValue(b *bytes.Buffer, quote bool, v string) {
data := []byte(v)
index := bytes.IndexByte(data, '\n')

View File

@ -17,8 +17,10 @@ limitations under the License.
package klog
import (
"bytes"
"fmt"
"reflect"
"strings"
"github.com/go-logr/logr"
)
@ -31,11 +33,30 @@ type ObjectRef struct {
func (ref ObjectRef) String() string {
if ref.Namespace != "" {
return fmt.Sprintf("%s/%s", ref.Namespace, ref.Name)
var builder strings.Builder
builder.Grow(len(ref.Namespace) + len(ref.Name) + 1)
builder.WriteString(ref.Namespace)
builder.WriteRune('/')
builder.WriteString(ref.Name)
return builder.String()
}
return ref.Name
}
func (ref ObjectRef) WriteText(out *bytes.Buffer) {
out.WriteRune('"')
ref.writeUnquoted(out)
out.WriteRune('"')
}
func (ref ObjectRef) writeUnquoted(out *bytes.Buffer) {
if ref.Namespace != "" {
out.WriteString(ref.Namespace)
out.WriteRune('/')
}
out.WriteString(ref.Name)
}
// MarshalLog ensures that loggers with support for structured output will log
// as a struct by removing the String method via a custom type.
func (ref ObjectRef) MarshalLog() interface{} {
@ -117,31 +138,31 @@ var _ fmt.Stringer = kobjSlice{}
var _ logr.Marshaler = kobjSlice{}
func (ks kobjSlice) String() string {
objectRefs, err := ks.process()
if err != nil {
return err.Error()
objectRefs, errStr := ks.process()
if errStr != "" {
return errStr
}
return fmt.Sprintf("%v", objectRefs)
}
func (ks kobjSlice) MarshalLog() interface{} {
objectRefs, err := ks.process()
if err != nil {
return err.Error()
objectRefs, errStr := ks.process()
if errStr != "" {
return errStr
}
return objectRefs
}
func (ks kobjSlice) process() ([]interface{}, error) {
func (ks kobjSlice) process() (objs []interface{}, err string) {
s := reflect.ValueOf(ks.arg)
switch s.Kind() {
case reflect.Invalid:
// nil parameter, print as nil.
return nil, nil
return nil, ""
case reflect.Slice:
// Okay, handle below.
default:
return nil, fmt.Errorf("<KObjSlice needs a slice, got type %T>", ks.arg)
return nil, fmt.Sprintf("<KObjSlice needs a slice, got type %T>", ks.arg)
}
objectRefs := make([]interface{}, 0, s.Len())
for i := 0; i < s.Len(); i++ {
@ -151,8 +172,41 @@ func (ks kobjSlice) process() ([]interface{}, error) {
} else if v, ok := item.(KMetadata); ok {
objectRefs = append(objectRefs, KObj(v))
} else {
return nil, fmt.Errorf("<KObjSlice needs a slice of values implementing KMetadata, got type %T>", item)
return nil, fmt.Sprintf("<KObjSlice needs a slice of values implementing KMetadata, got type %T>", item)
}
}
return objectRefs, ""
}
var nilToken = []byte("<nil>")
func (ks kobjSlice) WriteText(out *bytes.Buffer) {
s := reflect.ValueOf(ks.arg)
switch s.Kind() {
case reflect.Invalid:
// nil parameter, print as empty slice.
out.WriteString("[]")
return
case reflect.Slice:
// Okay, handle below.
default:
fmt.Fprintf(out, `"<KObjSlice needs a slice, got type %T>"`, ks.arg)
return
}
out.Write([]byte{'['})
defer out.Write([]byte{']'})
for i := 0; i < s.Len(); i++ {
if i > 0 {
out.Write([]byte{' '})
}
item := s.Index(i).Interface()
if item == nil {
out.Write(nilToken)
} else if v, ok := item.(KMetadata); ok {
KObj(v).writeUnquoted(out)
} else {
fmt.Fprintf(out, "<KObjSlice needs a slice of values implementing KMetadata, got type %T>", item)
return
}
}
return objectRefs, nil
}

135
vendor/k8s.io/klog/v2/klog.go generated vendored
View File

@ -91,8 +91,6 @@ import (
"sync/atomic"
"time"
"github.com/go-logr/logr"
"k8s.io/klog/v2/internal/buffer"
"k8s.io/klog/v2/internal/clock"
"k8s.io/klog/v2/internal/dbg"
@ -453,7 +451,7 @@ type settings struct {
// logger is the global Logger chosen by users of klog, nil if
// none is available.
logger *Logger
logger *logWriter
// loggerOptions contains the options that were supplied for
// globalLogger.
@ -525,6 +523,11 @@ func (s settings) deepCopy() settings {
}
s.vmodule.filter = filter
if s.logger != nil {
logger := *s.logger
s.logger = &logger
}
return s
}
@ -532,11 +535,6 @@ func (s settings) deepCopy() settings {
type loggingT struct {
settings
// bufferCache maintains the free list. It uses its own mutex
// so buffers can be grabbed and printed to without holding the main lock,
// for better parallelization.
bufferCache buffer.Buffers
// flushD holds a flushDaemon that frequently flushes log file buffers.
// Uses its own mutex.
flushD *flushDaemon
@ -664,7 +662,7 @@ func (l *loggingT) header(s severity.Severity, depth int) (*buffer.Buffer, strin
// formatHeader formats a log header using the provided file name and line number.
func (l *loggingT) formatHeader(s severity.Severity, file string, line int) *buffer.Buffer {
buf := l.bufferCache.GetBuffer()
buf := buffer.GetBuffer()
if l.skipHeaders {
return buf
}
@ -673,17 +671,18 @@ func (l *loggingT) formatHeader(s severity.Severity, file string, line int) *buf
return buf
}
func (l *loggingT) println(s severity.Severity, logger *logr.Logger, filter LogFilter, args ...interface{}) {
func (l *loggingT) println(s severity.Severity, logger *logWriter, filter LogFilter, args ...interface{}) {
l.printlnDepth(s, logger, filter, 1, args...)
}
func (l *loggingT) printlnDepth(s severity.Severity, logger *logr.Logger, filter LogFilter, depth int, args ...interface{}) {
func (l *loggingT) printlnDepth(s severity.Severity, logger *logWriter, filter LogFilter, depth int, args ...interface{}) {
buf, file, line := l.header(s, depth)
// if logger is set, we clear the generated header as we rely on the backing
// logger implementation to print headers
if logger != nil {
l.bufferCache.PutBuffer(buf)
buf = l.bufferCache.GetBuffer()
// If a logger is set and doesn't support writing a formatted buffer,
// we clear the generated header as we rely on the backing
// logger implementation to print headers.
if logger != nil && logger.writeKlogBuffer == nil {
buffer.PutBuffer(buf)
buf = buffer.GetBuffer()
}
if filter != nil {
args = filter.Filter(args)
@ -692,17 +691,18 @@ func (l *loggingT) printlnDepth(s severity.Severity, logger *logr.Logger, filter
l.output(s, logger, buf, depth, file, line, false)
}
func (l *loggingT) print(s severity.Severity, logger *logr.Logger, filter LogFilter, args ...interface{}) {
func (l *loggingT) print(s severity.Severity, logger *logWriter, filter LogFilter, args ...interface{}) {
l.printDepth(s, logger, filter, 1, args...)
}
func (l *loggingT) printDepth(s severity.Severity, logger *logr.Logger, filter LogFilter, depth int, args ...interface{}) {
func (l *loggingT) printDepth(s severity.Severity, logger *logWriter, filter LogFilter, depth int, args ...interface{}) {
buf, file, line := l.header(s, depth)
// if logr is set, we clear the generated header as we rely on the backing
// logr implementation to print headers
if logger != nil {
l.bufferCache.PutBuffer(buf)
buf = l.bufferCache.GetBuffer()
// If a logger is set and doesn't support writing a formatted buffer,
// we clear the generated header as we rely on the backing
// logger implementation to print headers.
if logger != nil && logger.writeKlogBuffer == nil {
buffer.PutBuffer(buf)
buf = buffer.GetBuffer()
}
if filter != nil {
args = filter.Filter(args)
@ -714,17 +714,18 @@ func (l *loggingT) printDepth(s severity.Severity, logger *logr.Logger, filter L
l.output(s, logger, buf, depth, file, line, false)
}
func (l *loggingT) printf(s severity.Severity, logger *logr.Logger, filter LogFilter, format string, args ...interface{}) {
func (l *loggingT) printf(s severity.Severity, logger *logWriter, filter LogFilter, format string, args ...interface{}) {
l.printfDepth(s, logger, filter, 1, format, args...)
}
func (l *loggingT) printfDepth(s severity.Severity, logger *logr.Logger, filter LogFilter, depth int, format string, args ...interface{}) {
func (l *loggingT) printfDepth(s severity.Severity, logger *logWriter, filter LogFilter, depth int, format string, args ...interface{}) {
buf, file, line := l.header(s, depth)
// if logr is set, we clear the generated header as we rely on the backing
// logr implementation to print headers
if logger != nil {
l.bufferCache.PutBuffer(buf)
buf = l.bufferCache.GetBuffer()
// If a logger is set and doesn't support writing a formatted buffer,
// we clear the generated header as we rely on the backing
// logger implementation to print headers.
if logger != nil && logger.writeKlogBuffer == nil {
buffer.PutBuffer(buf)
buf = buffer.GetBuffer()
}
if filter != nil {
format, args = filter.FilterF(format, args)
@ -739,13 +740,14 @@ func (l *loggingT) printfDepth(s severity.Severity, logger *logr.Logger, filter
// printWithFileLine behaves like print but uses the provided file and line number. If
// alsoLogToStderr is true, the log message always appears on standard error; it
// will also appear in the log file unless --logtostderr is set.
func (l *loggingT) printWithFileLine(s severity.Severity, logger *logr.Logger, filter LogFilter, file string, line int, alsoToStderr bool, args ...interface{}) {
func (l *loggingT) printWithFileLine(s severity.Severity, logger *logWriter, filter LogFilter, file string, line int, alsoToStderr bool, args ...interface{}) {
buf := l.formatHeader(s, file, line)
// if logr is set, we clear the generated header as we rely on the backing
// logr implementation to print headers
if logger != nil {
l.bufferCache.PutBuffer(buf)
buf = l.bufferCache.GetBuffer()
// If a logger is set and doesn't support writing a formatted buffer,
// we clear the generated header as we rely on the backing
// logger implementation to print headers.
if logger != nil && logger.writeKlogBuffer == nil {
buffer.PutBuffer(buf)
buf = buffer.GetBuffer()
}
if filter != nil {
args = filter.Filter(args)
@ -758,7 +760,7 @@ func (l *loggingT) printWithFileLine(s severity.Severity, logger *logr.Logger, f
}
// if loggr is specified, will call loggr.Error, otherwise output with logging module.
func (l *loggingT) errorS(err error, logger *logr.Logger, filter LogFilter, depth int, msg string, keysAndValues ...interface{}) {
func (l *loggingT) errorS(err error, logger *logWriter, filter LogFilter, depth int, msg string, keysAndValues ...interface{}) {
if filter != nil {
msg, keysAndValues = filter.FilterS(msg, keysAndValues)
}
@ -770,7 +772,7 @@ func (l *loggingT) errorS(err error, logger *logr.Logger, filter LogFilter, dept
}
// if loggr is specified, will call loggr.Info, otherwise output with logging module.
func (l *loggingT) infoS(logger *logr.Logger, filter LogFilter, depth int, msg string, keysAndValues ...interface{}) {
func (l *loggingT) infoS(logger *logWriter, filter LogFilter, depth int, msg string, keysAndValues ...interface{}) {
if filter != nil {
msg, keysAndValues = filter.FilterS(msg, keysAndValues)
}
@ -785,7 +787,7 @@ func (l *loggingT) infoS(logger *logr.Logger, filter LogFilter, depth int, msg s
// set log severity by s
func (l *loggingT) printS(err error, s severity.Severity, depth int, msg string, keysAndValues ...interface{}) {
// Only create a new buffer if we don't have one cached.
b := l.bufferCache.GetBuffer()
b := buffer.GetBuffer()
// The message is always quoted, even if it contains line breaks.
// If developers want multi-line output, they should use a small, fixed
// message and put the multi-line output into a value.
@ -796,7 +798,7 @@ func (l *loggingT) printS(err error, s severity.Severity, depth int, msg string,
serialize.KVListFormat(&b.Buffer, keysAndValues...)
l.printDepth(s, logging.logger, nil, depth+1, &b.Buffer)
// Make the buffer available for reuse.
l.bufferCache.PutBuffer(b)
buffer.PutBuffer(b)
}
// redirectBuffer is used to set an alternate destination for the logs
@ -851,7 +853,7 @@ func LogToStderr(stderr bool) {
}
// output writes the data to the log files and releases the buffer.
func (l *loggingT) output(s severity.Severity, log *logr.Logger, buf *buffer.Buffer, depth int, file string, line int, alsoToStderr bool) {
func (l *loggingT) output(s severity.Severity, logger *logWriter, buf *buffer.Buffer, depth int, file string, line int, alsoToStderr bool) {
var isLocked = true
l.mu.Lock()
defer func() {
@ -867,13 +869,17 @@ func (l *loggingT) output(s severity.Severity, log *logr.Logger, buf *buffer.Buf
}
}
data := buf.Bytes()
if log != nil {
// TODO: set 'severity' and caller information as structured log info
// keysAndValues := []interface{}{"severity", severityName[s], "file", file, "line", line}
if s == severity.ErrorLog {
logging.logger.WithCallDepth(depth+3).Error(nil, string(data))
if logger != nil {
if logger.writeKlogBuffer != nil {
logger.writeKlogBuffer(data)
} else {
log.WithCallDepth(depth + 3).Info(string(data))
// TODO: set 'severity' and caller information as structured log info
// keysAndValues := []interface{}{"severity", severityName[s], "file", file, "line", line}
if s == severity.ErrorLog {
logger.WithCallDepth(depth+3).Error(nil, string(data))
} else {
logger.WithCallDepth(depth + 3).Info(string(data))
}
}
} else if l.toStderr {
os.Stderr.Write(data)
@ -948,7 +954,7 @@ func (l *loggingT) output(s severity.Severity, log *logr.Logger, buf *buffer.Buf
timeoutFlush(ExitFlushTimeout)
OsExit(255) // C++ uses -1, which is silly because it's anded with 255 anyway.
}
l.bufferCache.PutBuffer(buf)
buffer.PutBuffer(buf)
if stats := severityStats[s]; stats != nil {
atomic.AddInt64(&stats.lines, 1)
@ -1282,7 +1288,7 @@ func (l *loggingT) setV(pc uintptr) Level {
// See the documentation of V for more information.
type Verbose struct {
enabled bool
logr *logr.Logger
logger *logWriter
}
func newVerbose(level Level, b bool) Verbose {
@ -1290,7 +1296,7 @@ func newVerbose(level Level, b bool) Verbose {
return Verbose{b, nil}
}
v := logging.logger.V(int(level))
return Verbose{b, &v}
return Verbose{b, &logWriter{Logger: v, writeKlogBuffer: logging.loggerOptions.writeKlogBuffer}}
}
// V reports whether verbosity at the call site is at least the requested level.
@ -1313,6 +1319,13 @@ func newVerbose(level Level, b bool) Verbose {
// less than or equal to the value of the -vmodule pattern matching the source file
// containing the call.
func V(level Level) Verbose {
return VDepth(1, level)
}
// VDepth is a variant of V that accepts a number of stack frames that will be
// skipped when checking the -vmodule patterns. VDepth(0) is equivalent to
// V().
func VDepth(depth int, level Level) Verbose {
// This function tries hard to be cheap unless there's work to do.
// The fast path is two atomic loads and compares.
@ -1329,7 +1342,7 @@ func V(level Level) Verbose {
// but if V logging is enabled we're slow anyway.
logging.mu.Lock()
defer logging.mu.Unlock()
if runtime.Callers(2, logging.pcs[:]) == 0 {
if runtime.Callers(2+depth, logging.pcs[:]) == 0 {
return newVerbose(level, false)
}
// runtime.Callers returns "return PCs", but we want
@ -1357,7 +1370,7 @@ func (v Verbose) Enabled() bool {
// See the documentation of V for usage.
func (v Verbose) Info(args ...interface{}) {
if v.enabled {
logging.print(severity.InfoLog, v.logr, logging.filter, args...)
logging.print(severity.InfoLog, v.logger, logging.filter, args...)
}
}
@ -1365,7 +1378,7 @@ func (v Verbose) Info(args ...interface{}) {
// See the documentation of V for usage.
func (v Verbose) InfoDepth(depth int, args ...interface{}) {
if v.enabled {
logging.printDepth(severity.InfoLog, v.logr, logging.filter, depth, args...)
logging.printDepth(severity.InfoLog, v.logger, logging.filter, depth, args...)
}
}
@ -1373,7 +1386,7 @@ func (v Verbose) InfoDepth(depth int, args ...interface{}) {
// See the documentation of V for usage.
func (v Verbose) Infoln(args ...interface{}) {
if v.enabled {
logging.println(severity.InfoLog, v.logr, logging.filter, args...)
logging.println(severity.InfoLog, v.logger, logging.filter, args...)
}
}
@ -1381,7 +1394,7 @@ func (v Verbose) Infoln(args ...interface{}) {
// See the documentation of V for usage.
func (v Verbose) InfolnDepth(depth int, args ...interface{}) {
if v.enabled {
logging.printlnDepth(severity.InfoLog, v.logr, logging.filter, depth, args...)
logging.printlnDepth(severity.InfoLog, v.logger, logging.filter, depth, args...)
}
}
@ -1389,7 +1402,7 @@ func (v Verbose) InfolnDepth(depth int, args ...interface{}) {
// See the documentation of V for usage.
func (v Verbose) Infof(format string, args ...interface{}) {
if v.enabled {
logging.printf(severity.InfoLog, v.logr, logging.filter, format, args...)
logging.printf(severity.InfoLog, v.logger, logging.filter, format, args...)
}
}
@ -1397,7 +1410,7 @@ func (v Verbose) Infof(format string, args ...interface{}) {
// See the documentation of V for usage.
func (v Verbose) InfofDepth(depth int, format string, args ...interface{}) {
if v.enabled {
logging.printfDepth(severity.InfoLog, v.logr, logging.filter, depth, format, args...)
logging.printfDepth(severity.InfoLog, v.logger, logging.filter, depth, format, args...)
}
}
@ -1405,7 +1418,7 @@ func (v Verbose) InfofDepth(depth int, format string, args ...interface{}) {
// See the documentation of V for usage.
func (v Verbose) InfoS(msg string, keysAndValues ...interface{}) {
if v.enabled {
logging.infoS(v.logr, logging.filter, 0, msg, keysAndValues...)
logging.infoS(v.logger, logging.filter, 0, msg, keysAndValues...)
}
}
@ -1419,14 +1432,14 @@ func InfoSDepth(depth int, msg string, keysAndValues ...interface{}) {
// See the documentation of V for usage.
func (v Verbose) InfoSDepth(depth int, msg string, keysAndValues ...interface{}) {
if v.enabled {
logging.infoS(v.logr, logging.filter, depth, msg, keysAndValues...)
logging.infoS(v.logger, logging.filter, depth, msg, keysAndValues...)
}
}
// Deprecated: Use ErrorS instead.
func (v Verbose) Error(err error, msg string, args ...interface{}) {
if v.enabled {
logging.errorS(err, v.logr, logging.filter, 0, msg, args...)
logging.errorS(err, v.logger, logging.filter, 0, msg, args...)
}
}
@ -1434,7 +1447,7 @@ func (v Verbose) Error(err error, msg string, args ...interface{}) {
// See the documentation of V for usage.
func (v Verbose) ErrorS(err error, msg string, keysAndValues ...interface{}) {
if v.enabled {
logging.errorS(err, v.logr, logging.filter, 0, msg, keysAndValues...)
logging.errorS(err, v.logger, logging.filter, 0, msg, keysAndValues...)
}
}

12
vendor/k8s.io/klog/v2/klogr.go generated vendored
View File

@ -42,19 +42,21 @@ func (l *klogger) Init(info logr.RuntimeInfo) {
l.callDepth += info.CallDepth
}
func (l klogger) Info(level int, msg string, kvList ...interface{}) {
func (l *klogger) Info(level int, msg string, kvList ...interface{}) {
merged := serialize.MergeKVs(l.values, kvList)
if l.prefix != "" {
msg = l.prefix + ": " + msg
}
V(Level(level)).InfoSDepth(l.callDepth+1, msg, merged...)
// Skip this function.
VDepth(l.callDepth+1, Level(level)).InfoSDepth(l.callDepth+1, msg, merged...)
}
func (l klogger) Enabled(level int) bool {
return V(Level(level)).Enabled()
func (l *klogger) Enabled(level int) bool {
// Skip this function and logr.Logger.Info where Enabled is called.
return VDepth(l.callDepth+2, Level(level)).Enabled()
}
func (l klogger) Error(err error, msg string, kvList ...interface{}) {
func (l *klogger) Error(err error, msg string, kvList ...interface{}) {
merged := serialize.MergeKVs(l.values, kvList)
if l.prefix != "" {
msg = l.prefix + ": " + msg

View File

@ -327,6 +327,21 @@ func BuildOpenAPISpecFromRoutes(webServices []common.RouteContainer, config *com
return a.spec, nil
}
// BuildOpenAPIDefinitionsForResource builds a partial OpenAPI spec given a sample object and common.Config to customize it.
// BuildOpenAPIDefinitionsForResources returns the OpenAPI spec which includes the definitions for the
// passed type names.
func BuildOpenAPIDefinitionsForResources(config *common.Config, names ...string) (map[string]*spec.Schema, error) {
o := newOpenAPI(config)
// We can discard the return value of toSchema because all we care about is the side effect of calling it.
// All the models created for this resource get added to o.swagger.Definitions
for _, name := range names {
_, err := o.toSchema(name)
if err != nil {
return nil, err
}
}
return o.spec.Components.Schemas, nil
}
func (o *openAPI) findCommonParameters(routes []common.Route) (map[interface{}]*spec3.Parameter, error) {
commonParamsMap := make(map[interface{}]*spec3.Parameter, 0)
paramOpsCountByName := make(map[interface{}]int, 0)

View File

@ -246,38 +246,42 @@ var schemaTypeFormatMap = map[string]typeInfo{
// the spec does not need to be simple type,format) or can even return a simple type,format (e.g. IntOrString). For simple
// type formats, the benefit of adding OpenAPIDefinitionGetter interface is to keep both type and property documentation.
// Example:
// type Sample struct {
// ...
// // port of the server
// port IntOrString
// ...
// }
//
// type Sample struct {
// ...
// // port of the server
// port IntOrString
// ...
// }
//
// // IntOrString documentation...
// type IntOrString { ... }
//
// Adding IntOrString to this function:
// "port" : {
// format: "string",
// type: "int-or-string",
// Description: "port of the server"
// }
//
// "port" : {
// format: "string",
// type: "int-or-string",
// Description: "port of the server"
// }
//
// Implement OpenAPIDefinitionGetter for IntOrString:
//
// "port" : {
// $Ref: "#/definitions/IntOrString"
// Description: "port of the server"
// }
// "port" : {
// $Ref: "#/definitions/IntOrString"
// Description: "port of the server"
// }
//
// ...
// definitions:
// {
// "IntOrString": {
// format: "string",
// type: "int-or-string",
// Description: "IntOrString documentation..." // new
// }
// }
//
// {
// "IntOrString": {
// format: "string",
// type: "int-or-string",
// Description: "IntOrString documentation..." // new
// }
// }
func OpenAPITypeFormat(typeName string) (string, string) {
mapped, ok := schemaTypeFormatMap[typeName]
if !ok {

View File

@ -13,4 +13,3 @@ func AdaptWebServices(webServices []*restful.WebService) []common.RouteContainer
}
return containers
}

View File

@ -20,7 +20,6 @@ import (
"bytes"
"fmt"
"io"
"io/ioutil"
"os"
"sort"
@ -64,7 +63,7 @@ func (a apiViolationFile) VerifyFile(f *generator.File, path string) error {
path = a.unmangledPath
formatted := f.Body.Bytes()
existing, err := ioutil.ReadFile(path)
existing, err := os.ReadFile(path)
if err != nil {
return fmt.Errorf("unable to read file %q for comparison: %v", path, err)
}

View File

@ -55,6 +55,10 @@ func newEnumContext(c *generator.Context) *enumContext {
// If the given type is a known enum type, returns the enumType, true
// Otherwise, returns nil, false
func (ec *enumContext) EnumType(t *types.Type) (enum *enumType, isEnum bool) {
// if t is a pointer, use its underlying type instead
if t.Kind == types.Pointer {
t = t.Elem
}
enum, ok := ec.enumTypes[t.Name]
return enum, ok
}
@ -74,9 +78,12 @@ func (et *enumType) ValueStrings() []string {
// DescriptionLines returns a description of the enum in this format:
//
// Possible enum values:
// - `"value1"` description 1
// - `"value2"` description 2
// - `"value1"` description 1
// - `"value2"` description 2
func (et *enumType) DescriptionLines() []string {
if len(et.Values) == 0 {
return nil
}
var lines []string
for _, value := range et.Values {
lines = append(lines, value.Description())
@ -90,9 +97,9 @@ func parseEnums(c *generator.Context) enumMap {
// First, find the builtin "string" type
stringType := c.Universe.Type(types.Name{Name: "string"})
// find all enum types.
enumTypes := make(enumMap)
for _, p := range c.Universe {
// find all enum types.
for _, t := range p.Types {
if isEnumType(stringType, t) {
if _, ok := enumTypes[t.Name]; !ok {
@ -102,7 +109,10 @@ func parseEnums(c *generator.Context) enumMap {
}
}
}
// find all enum values from constants, and try to match each with its type.
}
// find all enum values from constants, and try to match each with its type.
for _, p := range c.Universe {
for _, c := range p.Constants {
enumType := c.Underlying
if _, ok := enumTypes[enumType.Name]; ok {
@ -125,7 +135,7 @@ func (et *enumType) appendValue(value *enumValue) {
// Description returns the description line for the enumValue
// with the format:
// - `"FooValue"` is the Foo value
// - `"FooValue"` is the Foo value
func (ev *enumValue) Description() string {
comment := strings.TrimSpace(ev.Comment)
// The comment should starts with the type name, trim it first.

View File

@ -686,7 +686,7 @@ func (g openAPITypeWriter) generateProperty(m *types.Member, parent *types.Type)
g.generateSimpleProperty(typeString, format)
if enumType, isEnum := g.enumContext.EnumType(m.Type); isEnum {
// original type is an enum, add "Enum: " and the values
g.Do("Enum: []interface{}{$.$}", strings.Join(enumType.ValueStrings(), ", "))
g.Do("Enum: []interface{}{$.$},\n", strings.Join(enumType.ValueStrings(), ", "))
}
g.Do("},\n},\n", nil)
return nil

View File

@ -56,24 +56,29 @@ Go field names must be CamelCase. JSON field names must be camelCase. Other than
initial letter, the two should almost always match. No underscores nor dashes in either.
This rule verifies the convention "Other than capitalization of the initial letter, the two should almost always match."
Examples (also in unit test):
Go name | JSON name | match
podSpec false
PodSpec podSpec true
PodSpec PodSpec false
podSpec podSpec false
PodSpec spec false
Spec podSpec false
JSONSpec jsonSpec true
JSONSpec jsonspec false
HTTPJSONSpec httpJSONSpec true
Go name | JSON name | match
podSpec false
PodSpec podSpec true
PodSpec PodSpec false
podSpec podSpec false
PodSpec spec false
Spec podSpec false
JSONSpec jsonSpec true
JSONSpec jsonspec false
HTTPJSONSpec httpJSONSpec true
NOTE: this validator cannot tell two sequential all-capital words from one word, therefore the case below
is also considered matched.
HTTPJSONSpec httpjsonSpec true
HTTPJSONSpec httpjsonSpec true
NOTE: JSON names in jsonNameBlacklist should skip evaluation
true
podSpec true
podSpec - true
podSpec metadata true
true
podSpec true
podSpec - true
podSpec metadata true
*/
type NamesMatch struct{}
@ -114,14 +119,15 @@ func (n *NamesMatch) Validate(t *types.Type) ([]string, error) {
// namesMatch evaluates if goName and jsonName match the API rule
// TODO: Use an off-the-shelf CamelCase solution instead of implementing this logic. The following existing
// packages have been tried out:
// github.com/markbates/inflect
// github.com/segmentio/go-camelcase
// github.com/iancoleman/strcase
// github.com/fatih/camelcase
// Please see https://github.com/kubernetes/kube-openapi/pull/83#issuecomment-400842314 for more details
// about why they don't satisfy our need. What we need can be a function that detects an acronym at the
// beginning of a string.
//
// packages have been tried out:
// github.com/markbates/inflect
// github.com/segmentio/go-camelcase
// github.com/iancoleman/strcase
// github.com/fatih/camelcase
// Please see https://github.com/kubernetes/kube-openapi/pull/83#issuecomment-400842314 for more details
// about why they don't satisfy our need. What we need can be a function that detects an acronym at the
// beginning of a string.
func namesMatch(goName, jsonName string) bool {
if jsonNameBlacklist.Has(jsonName) {
return true

View File

@ -18,11 +18,8 @@ package handler
import (
"bytes"
"compress/gzip"
"crypto/sha512"
"encoding/json"
"fmt"
"mime"
"net/http"
"strconv"
"sync"
@ -42,12 +39,9 @@ import (
)
const (
jsonExt = ".json"
mimeJson = "application/json"
// TODO(mehdy): change @68f4ded to a version tag when gnostic add version tags.
mimePb = "application/com.github.googleapis.gnostic.OpenAPIv2@68f4ded+protobuf"
mimePbGz = "application/x-gzip"
subTypeProtobufDeprecated = "com.github.proto-openapi.spec.v2@v1.0+protobuf"
subTypeProtobuf = "com.github.proto-openapi.spec.v2.v1.0+protobuf"
subTypeJSON = "json"
)
func computeETag(data []byte) string {
@ -70,12 +64,6 @@ type OpenAPIService struct {
etagCache handler.HandlerCache
}
func init() {
mime.AddExtensionType(".json", mimeJson)
mime.AddExtensionType(".pb-v1", mimePb)
mime.AddExtensionType(".gz", mimePbGz)
}
// NewOpenAPIService builds an OpenAPIService starting with the given spec.
func NewOpenAPIService(spec *spec.Swagger) (*OpenAPIService, error) {
o := &OpenAPIService{}
@ -117,7 +105,7 @@ func (o *OpenAPIService) UpdateSpec(openapiSpec *spec.Swagger) (err error) {
o.rwMutex.Lock()
defer o.rwMutex.Unlock()
o.jsonCache = o.jsonCache.New(func() ([]byte, error) {
return json.Marshal(openapiSpec)
return openapiSpec.MarshalJSON()
})
o.protoCache = o.protoCache.New(func() ([]byte, error) {
json, err := o.jsonCache.Get()
@ -146,14 +134,6 @@ func ToProtoBinary(json []byte) ([]byte, error) {
return proto.Marshal(document)
}
func toGzip(data []byte) []byte {
var buf bytes.Buffer
zw := gzip.NewWriter(&buf)
zw.Write(data)
zw.Close()
return buf.Bytes()
}
// RegisterOpenAPIVersionedService registers a handler to provide access to provided swagger spec.
//
// Deprecated: use OpenAPIService.RegisterOpenAPIVersionedService instead.
@ -168,12 +148,14 @@ func RegisterOpenAPIVersionedService(spec *spec.Swagger, servePath string, handl
// RegisterOpenAPIVersionedService registers a handler to provide access to provided swagger spec.
func (o *OpenAPIService) RegisterOpenAPIVersionedService(servePath string, handler common.PathHandler) error {
accepted := []struct {
Type string
SubType string
GetDataAndETag func() ([]byte, string, time.Time, error)
Type string
SubType string
ReturnedContentType string
GetDataAndETag func() ([]byte, string, time.Time, error)
}{
{"application", "json", o.getSwaggerBytes},
{"application", "com.github.proto-openapi.spec.v2@v1.0+protobuf", o.getSwaggerPbBytes},
{"application", subTypeJSON, "application/" + subTypeJSON, o.getSwaggerBytes},
{"application", subTypeProtobufDeprecated, "application/" + subTypeProtobuf, o.getSwaggerPbBytes},
{"application", subTypeProtobuf, "application/" + subTypeProtobuf, o.getSwaggerPbBytes},
}
handler.Handle(servePath, gziphandler.GzipHandler(http.HandlerFunc(
@ -192,7 +174,6 @@ func (o *OpenAPIService) RegisterOpenAPIVersionedService(servePath string, handl
if clause.SubType != accepts.SubType && clause.SubType != "*" {
continue
}
// serve the first matching media type in the sorted clause list
data, etag, lastModified, err := accepts.GetDataAndETag()
if err != nil {
@ -203,6 +184,9 @@ func (o *OpenAPIService) RegisterOpenAPIVersionedService(servePath string, handl
return
}
}
// Set Content-Type header in the reponse
w.Header().Set("Content-Type", accepts.ReturnedContentType)
// ETag must be enclosed in double quotes: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag
w.Header().Set("Etag", strconv.Quote(etag))
// ServeContent will take care of caching using eTag.

View File

@ -21,7 +21,6 @@ import (
"crypto/sha512"
"encoding/json"
"fmt"
"mime"
"net/http"
"net/url"
"path"
@ -41,15 +40,9 @@ import (
)
const (
jsonExt = ".json"
mimeJson = "application/json"
// TODO(mehdy): change @68f4ded to a version tag when gnostic add version tags.
mimePb = "application/com.github.googleapis.gnostic.OpenAPIv3@68f4ded+protobuf"
mimePbGz = "application/x-gzip"
subTypeProtobuf = "com.github.proto-openapi.spec.v3@v1.0+protobuf"
subTypeJSON = "json"
subTypeProtobufDeprecated = "com.github.proto-openapi.spec.v3@v1.0+protobuf"
subTypeProtobuf = "com.github.proto-openapi.spec.v3.v1.0+protobuf"
subTypeJSON = "json"
)
// OpenAPIV3Discovery is the format of the Discovery document for OpenAPI V3
@ -84,12 +77,6 @@ type OpenAPIV3Group struct {
etagCache handler.HandlerCache
}
func init() {
mime.AddExtensionType(".json", mimeJson)
mime.AddExtensionType(".pb-v1", mimePb)
mime.AddExtensionType(".gz", mimePbGz)
}
func computeETag(data []byte) string {
if data == nil {
return ""
@ -154,7 +141,7 @@ func (o *OpenAPIService) getSingleGroupBytes(getType string, group string) ([]by
}
etagBytes, err := v.etagCache.Get()
return specBytes, string(etagBytes), v.lastModified, err
} else if getType == subTypeProtobuf {
} else if getType == subTypeProtobuf || getType == subTypeProtobufDeprecated {
specPb, err := v.pbCache.Get()
if err != nil {
return nil, "", v.lastModified, err
@ -191,6 +178,8 @@ func ToV3ProtoBinary(json []byte) ([]byte, error) {
func (o *OpenAPIService) HandleDiscovery(w http.ResponseWriter, r *http.Request) {
data, _ := o.getGroupBytes()
w.Header().Set("Etag", strconv.Quote(computeETag(data)))
w.Header().Set("Content-Type", "application/json")
http.ServeContent(w, r, "/openapi/v3", time.Now(), bytes.NewReader(data))
}
@ -210,11 +199,13 @@ func (o *OpenAPIService) HandleGroupVersion(w http.ResponseWriter, r *http.Reque
}
accepted := []struct {
Type string
SubType string
Type string
SubType string
ReturnedContentType string
}{
{"application", subTypeJSON},
{"application", subTypeProtobuf},
{"application", subTypeJSON, "application/" + subTypeJSON},
{"application", subTypeProtobuf, "application/" + subTypeProtobuf},
{"application", subTypeProtobufDeprecated, "application/" + subTypeProtobuf},
}
for _, clause := range clauses {
@ -229,6 +220,9 @@ func (o *OpenAPIService) HandleGroupVersion(w http.ResponseWriter, r *http.Reque
if err != nil {
return
}
// Set Content-Type header in the reponse
w.Header().Set("Content-Type", accepts.ReturnedContentType)
// ETag must be enclosed in double quotes: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag
w.Header().Set("Etag", strconv.Quote(etag))

View File

@ -18,3 +18,7 @@ package internal
// Used by tests to selectively disable experimental JSON unmarshaler
var UseOptimizedJSONUnmarshaling bool = true
var UseOptimizedJSONUnmarshalingV3 bool = true
// Used by tests to selectively disable experimental JSON marshaler
var UseOptimizedJSONMarshaling bool = true

View File

@ -0,0 +1,65 @@
/*
Copyright 2023 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 internal
import (
"github.com/go-openapi/jsonreference"
jsonv2 "k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json"
)
// DeterministicMarshal calls the jsonv2 library with the deterministic
// flag in order to have stable marshaling.
func DeterministicMarshal(in any) ([]byte, error) {
return jsonv2.MarshalOptions{Deterministic: true}.Marshal(jsonv2.EncodeOptions{}, in)
}
// JSONRefFromMap populates a json reference object if the map v contains a $ref key.
func JSONRefFromMap(jsonRef *jsonreference.Ref, v map[string]interface{}) error {
if v == nil {
return nil
}
if vv, ok := v["$ref"]; ok {
if str, ok := vv.(string); ok {
ref, err := jsonreference.New(str)
if err != nil {
return err
}
*jsonRef = ref
}
}
return nil
}
// SanitizeExtensions sanitizes the input map such that non extension
// keys (non x-*, X-*) keys are dropped from the map. Returns the new
// modified map, or nil if the map is now empty.
func SanitizeExtensions(e map[string]interface{}) map[string]interface{} {
for k := range e {
if !IsExtensionKey(k) {
delete(e, k)
}
}
if len(e) == 0 {
e = nil
}
return e
}
// IsExtensionKey returns true if the input string is of format x-* or X-*
func IsExtensionKey(k string) bool {
return len(k) > 1 && (k[0] == 'x' || k[0] == 'X') && k[1] == '-'
}

View File

@ -34,6 +34,13 @@ type MarshalOptions struct {
// unknown JSON object members.
DiscardUnknownMembers bool
// Deterministic specifies that the same input value will be serialized
// as the exact same output bytes. Different processes of
// the same program will serialize equal values to the same bytes,
// but different versions of the same program are not guaranteed
// to produce the exact same sequence of bytes.
Deterministic bool
// formatDepth is the depth at which we respect the format flag.
formatDepth int
// format is custom formatting for the value at the specified depth.

View File

@ -62,7 +62,7 @@ func unmarshalValueAny(uo UnmarshalOptions, dec *Decoder) (any, error) {
}
return dec.stringCache.make(val), nil
case '0':
fv, _ := parseFloat(val, 64) // ignore error since readValue gaurantees val is valid
fv, _ := parseFloat(val, 64) // ignore error since readValue guarantees val is valid
return fv, nil
default:
panic("BUG: invalid kind: " + k.String())
@ -99,13 +99,32 @@ func marshalObjectAny(mo MarshalOptions, enc *Encoder, obj map[string]any) error
if !enc.options.AllowInvalidUTF8 {
enc.tokens.last.disableNamespace()
}
for name, val := range obj {
if err := enc.WriteToken(String(name)); err != nil {
return err
if !mo.Deterministic || len(obj) <= 1 {
for name, val := range obj {
if err := enc.WriteToken(String(name)); err != nil {
return err
}
if err := marshalValueAny(mo, enc, val); err != nil {
return err
}
}
if err := marshalValueAny(mo, enc, val); err != nil {
return err
} else {
names := getStrings(len(obj))
var i int
for name := range obj {
(*names)[i] = name
i++
}
names.Sort()
for _, name := range *names {
if err := enc.WriteToken(String(name)); err != nil {
return err
}
if err := marshalValueAny(mo, enc, obj[name]); err != nil {
return err
}
}
putStrings(names)
}
if err := enc.WriteToken(ObjectEnd); err != nil {
return err

View File

@ -5,6 +5,7 @@
package json
import (
"bytes"
"encoding/base32"
"encoding/base64"
"encoding/hex"
@ -12,6 +13,7 @@ import (
"fmt"
"math"
"reflect"
"sort"
"strconv"
"sync"
)
@ -228,13 +230,7 @@ func makeBytesArshaler(t reflect.Type, fncs *arshaler) *arshaler {
}
}
val := enc.UnusedBuffer()
var b []byte
if va.Kind() == reflect.Array {
// TODO(https://go.dev/issue/47066): Avoid reflect.Value.Slice.
b = va.Slice(0, va.Len()).Bytes()
} else {
b = va.Bytes()
}
b := va.Bytes()
n := len(`"`) + encodedLen(len(b)) + len(`"`)
if cap(val) < n {
val = make([]byte, n)
@ -248,19 +244,19 @@ func makeBytesArshaler(t reflect.Type, fncs *arshaler) *arshaler {
}
unmarshalDefault := fncs.unmarshal
fncs.unmarshal = func(uo UnmarshalOptions, dec *Decoder, va addressableValue) error {
decode, decodedLen := decodeBase64, decodedLenBase64
decode, decodedLen, encodedLen := decodeBase64, decodedLenBase64, encodedLenBase64
if uo.format != "" && uo.formatDepth == dec.tokens.depth() {
switch uo.format {
case "base64":
decode, decodedLen = decodeBase64, decodedLenBase64
decode, decodedLen, encodedLen = decodeBase64, decodedLenBase64, encodedLenBase64
case "base64url":
decode, decodedLen = decodeBase64URL, decodedLenBase64URL
decode, decodedLen, encodedLen = decodeBase64URL, decodedLenBase64URL, encodedLenBase64URL
case "base32":
decode, decodedLen = decodeBase32, decodedLenBase32
decode, decodedLen, encodedLen = decodeBase32, decodedLenBase32, encodedLenBase32
case "base32hex":
decode, decodedLen = decodeBase32Hex, decodedLenBase32Hex
decode, decodedLen, encodedLen = decodeBase32Hex, decodedLenBase32Hex, encodedLenBase32Hex
case "base16", "hex":
decode, decodedLen = decodeBase16, decodedLenBase16
decode, decodedLen, encodedLen = decodeBase16, decodedLenBase16, encodedLenBase16
case "array":
uo.format = ""
return unmarshalDefault(uo, dec, va)
@ -290,23 +286,28 @@ func makeBytesArshaler(t reflect.Type, fncs *arshaler) *arshaler {
n--
}
n = decodedLen(n)
var b []byte
b := va.Bytes()
if va.Kind() == reflect.Array {
// TODO(https://go.dev/issue/47066): Avoid reflect.Value.Slice.
b = va.Slice(0, va.Len()).Bytes()
if n != len(b) {
err := fmt.Errorf("decoded base64 length of %d mismatches array length of %d", n, len(b))
return &SemanticError{action: "unmarshal", JSONKind: k, GoType: t, Err: err}
}
} else {
b = va.Bytes()
if b == nil || cap(b) < n {
b = make([]byte, n)
} else {
b = b[:n]
}
}
if _, err := decode(b, val); err != nil {
n2, err := decode(b, val)
if err == nil && len(val) != encodedLen(n2) {
// TODO(https://go.dev/issue/53845): RFC 4648, section 3.3,
// specifies that non-alphabet characters must be rejected.
// Unfortunately, the "base32" and "base64" packages allow
// '\r' and '\n' characters by default.
err = errors.New("illegal data at input byte " + strconv.Itoa(bytes.IndexAny(val, "\r\n")))
}
if err != nil {
return &SemanticError{action: "unmarshal", JSONKind: k, GoType: t, Err: err}
}
if va.Kind() == reflect.Slice {
@ -412,7 +413,7 @@ func makeUintArshaler(t reflect.Type) *arshaler {
return nil
}
x := math.Float64frombits(uint64(va.Uint()))
x := math.Float64frombits(va.Uint())
return enc.writeNumber(x, rawUintNumber, mo.StringifyNumbers)
}
fncs.unmarshal = func(uo UnmarshalOptions, dec *Decoder, va addressableValue) error {
@ -450,7 +451,7 @@ func makeUintArshaler(t reflect.Type) *arshaler {
err := fmt.Errorf("cannot parse %q as unsigned integer: %w", val, strconv.ErrRange)
return &SemanticError{action: "unmarshal", JSONKind: k, GoType: t, Err: err}
}
va.SetUint(uint64(n))
va.SetUint(n)
return nil
}
return &SemanticError{action: "unmarshal", JSONKind: k, GoType: t}
@ -549,23 +550,9 @@ func makeFloatArshaler(t reflect.Type) *arshaler {
return &fncs
}
var mapIterPool = sync.Pool{
New: func() any { return new(reflect.MapIter) },
}
func getMapIter(mv reflect.Value) *reflect.MapIter {
iter := mapIterPool.Get().(*reflect.MapIter)
iter.Reset(mv)
return iter
}
func putMapIter(iter *reflect.MapIter) {
iter.Reset(reflect.Value{}) // allow underlying map to be garbage collected
mapIterPool.Put(iter)
}
func makeMapArshaler(t reflect.Type) *arshaler {
// NOTE: The logic below disables namespaces for tracking duplicate names
// when handling map keys with a unique represention.
// when handling map keys with a unique representation.
// NOTE: Values retrieved from a map are not addressable,
// so we shallow copy the values to make them addressable and
@ -641,24 +628,76 @@ func makeMapArshaler(t reflect.Type) *arshaler {
enc.tokens.last.disableNamespace()
}
// NOTE: Map entries are serialized in a non-deterministic order.
// Users that need stable output should call RawValue.Canonicalize.
// TODO(go1.19): Remove use of a sync.Pool with reflect.MapIter.
// Calling reflect.Value.MapRange no longer allocates.
// See https://go.dev/cl/400675.
iter := getMapIter(va.Value)
defer putMapIter(iter)
for iter.Next() {
k.SetIterKey(iter)
if err := marshalKey(mko, enc, k); err != nil {
// TODO: If err is errMissingName, then wrap it as a
// SemanticError since this key type cannot be serialized
// as a JSON string.
return err
switch {
case !mo.Deterministic || n <= 1:
for iter := va.Value.MapRange(); iter.Next(); {
k.SetIterKey(iter)
if err := marshalKey(mko, enc, k); err != nil {
// TODO: If err is errMissingName, then wrap it as a
// SemanticError since this key type cannot be serialized
// as a JSON string.
return err
}
v.SetIterValue(iter)
if err := marshalVal(mo, enc, v); err != nil {
return err
}
}
v.SetIterValue(iter)
if err := marshalVal(mo, enc, v); err != nil {
return err
case !nonDefaultKey && t.Key().Kind() == reflect.String:
names := getStrings(n)
for i, iter := 0, va.Value.MapRange(); i < n && iter.Next(); i++ {
k.SetIterKey(iter)
(*names)[i] = k.String()
}
names.Sort()
for _, name := range *names {
if err := enc.WriteToken(String(name)); err != nil {
return err
}
// TODO(https://go.dev/issue/57061): Use v.SetMapIndexOf.
k.SetString(name)
v.Set(va.MapIndex(k.Value))
if err := marshalVal(mo, enc, v); err != nil {
return err
}
}
putStrings(names)
default:
type member struct {
name string // unquoted name
key addressableValue
}
members := make([]member, n)
keys := reflect.MakeSlice(reflect.SliceOf(t.Key()), n, n)
for i, iter := 0, va.Value.MapRange(); i < n && iter.Next(); i++ {
// Marshal the member name.
k := addressableValue{keys.Index(i)} // indexed slice element is always addressable
k.SetIterKey(iter)
if err := marshalKey(mko, enc, k); err != nil {
// TODO: If err is errMissingName, then wrap it as a
// SemanticError since this key type cannot be serialized
// as a JSON string.
return err
}
name := enc.unwriteOnlyObjectMemberName()
members[i] = member{name, k}
}
// TODO: If AllowDuplicateNames is enabled, then sort according
// to reflect.Value as well if the names are equal.
// See internal/fmtsort.
// TODO(https://go.dev/issue/47619): Use slices.SortFunc instead.
sort.Slice(members, func(i, j int) bool {
return lessUTF16(members[i].name, members[j].name)
})
for _, member := range members {
if err := enc.WriteToken(String(member.name)); err != nil {
return err
}
// TODO(https://go.dev/issue/57061): Use v.SetMapIndexOf.
v.Set(va.MapIndex(member.key.Value))
if err := marshalVal(mo, enc, v); err != nil {
return err
}
}
}
}
@ -856,7 +895,7 @@ func makeStructArshaler(t reflect.Type) *arshaler {
// 2. The object namespace is guaranteed to be disabled.
// 3. The object name is guaranteed to be valid and pre-escaped.
// 4. There is no need to flush the buffer (for unwrite purposes).
// 5. There is no possibility of an error occuring.
// 5. There is no possibility of an error occurring.
if optimizeCommon {
// Append any delimiters or optional whitespace.
if enc.tokens.last.length() > 0 {
@ -996,7 +1035,7 @@ func makeStructArshaler(t reflect.Type) *arshaler {
if fields.inlinedFallback == nil {
// Skip unknown value since we have no place to store it.
if err := dec.skipValue(); err != nil {
if err := dec.SkipValue(); err != nil {
return err
}
} else {

View File

@ -5,6 +5,7 @@
package json
import (
"bytes"
"errors"
"reflect"
)
@ -89,35 +90,61 @@ func marshalInlinedFallbackAll(mo MarshalOptions, enc *Encoder, va addressableVa
}
return nil
} else {
if v.Len() == 0 {
m := v // must be a map[string]V
n := m.Len()
if n == 0 {
return nil
}
m := v
mk := newAddressableValue(stringType)
mv := newAddressableValue(m.Type().Elem())
for iter := m.MapRange(); iter.Next(); {
b, err := appendString(enc.UnusedBuffer(), iter.Key().String(), !enc.options.AllowInvalidUTF8, nil)
marshalKey := func(mk addressableValue) error {
b, err := appendString(enc.UnusedBuffer(), mk.String(), !enc.options.AllowInvalidUTF8, nil)
if err != nil {
return err
}
if insertUnquotedName != nil {
isVerbatim := consumeSimpleString(b) == len(b)
isVerbatim := bytes.IndexByte(b, '\\') < 0
name := unescapeStringMayCopy(b, isVerbatim)
if !insertUnquotedName(name) {
return &SyntacticError{str: "duplicate name " + string(b) + " in object"}
}
}
if err := enc.WriteValue(b); err != nil {
return err
return enc.WriteValue(b)
}
marshalVal := f.fncs.marshal
if mo.Marshalers != nil {
marshalVal, _ = mo.Marshalers.lookup(marshalVal, mv.Type())
}
if !mo.Deterministic || n <= 1 {
for iter := m.MapRange(); iter.Next(); {
mk.SetIterKey(iter)
if err := marshalKey(mk); err != nil {
return err
}
mv.Set(iter.Value())
if err := marshalVal(mo, enc, mv); err != nil {
return err
}
}
mv.Set(iter.Value())
marshal := f.fncs.marshal
if mo.Marshalers != nil {
marshal, _ = mo.Marshalers.lookup(marshal, mv.Type())
} else {
names := getStrings(n)
for i, iter := 0, m.Value.MapRange(); i < n && iter.Next(); i++ {
mk.SetIterKey(iter)
(*names)[i] = mk.String()
}
if err := marshal(mo, enc, mv); err != nil {
return err
names.Sort()
for _, name := range *names {
mk.SetString(name)
if err := marshalKey(mk); err != nil {
return err
}
// TODO(https://go.dev/issue/57061): Use mv.SetMapIndexOf.
mv.Set(m.MapIndex(mk.Value))
if err := marshalVal(mo, enc, mv); err != nil {
return err
}
}
putStrings(names)
}
return nil
}
@ -162,7 +189,7 @@ func unmarshalInlinedFallbackNext(uo UnmarshalOptions, dec *Decoder, va addressa
} else {
name := string(unquotedName) // TODO: Intern this?
m := v
m := v // must be a map[string]V
if m.IsNil() {
m.Set(reflect.MakeMap(m.Type()))
}

View File

@ -21,8 +21,8 @@ var (
)
// MarshalerV1 is implemented by types that can marshal themselves.
// It is recommended that types implement MarshalerV2 unless
// the implementation is trying to avoid a hard dependency on this package.
// It is recommended that types implement MarshalerV2 unless the implementation
// is trying to avoid a hard dependency on the "jsontext" package.
//
// It is recommended that implementations return a buffer that is safe
// for the caller to retain and potentially mutate.

View File

@ -5,6 +5,7 @@
package json
import (
"errors"
"fmt"
"reflect"
"strings"
@ -85,25 +86,39 @@ func makeTimeArshaler(fncs *arshaler, t reflect.Type) *arshaler {
fncs.nonDefault = true
fncs.marshal = func(mo MarshalOptions, enc *Encoder, va addressableValue) error {
format := time.RFC3339Nano
isRFC3339 := true
if mo.format != "" && mo.formatDepth == enc.tokens.depth() {
var err error
format, err = checkTimeFormat(mo.format)
format, isRFC3339, err = checkTimeFormat(mo.format)
if err != nil {
return &SemanticError{action: "marshal", GoType: t, Err: err}
}
}
tt := va.Interface().(time.Time)
if y := tt.Year(); y < 0 || y >= 10000 {
// RFC 3339 is clear that years are 4 digits exactly.
// See https://go.dev/issue/4556#c15 for more discussion.
err := fmt.Errorf("year %d outside of range [0,9999]", y)
return &SemanticError{action: "marshal", GoType: t, Err: err}
}
b := enc.UnusedBuffer()
b = append(b, '"')
b = tt.AppendFormat(b, format)
b = append(b, '"')
if isRFC3339 {
// Not all Go timestamps can be represented as valid RFC 3339.
// Explicitly check for these edge cases.
// See https://go.dev/issue/4556 and https://go.dev/issue/54580.
var err error
switch b := b[len(`"`) : len(b)-len(`"`)]; {
case b[len("9999")] != '-': // year must be exactly 4 digits wide
err = errors.New("year outside of range [0,9999]")
case b[len(b)-1] != 'Z':
c := b[len(b)-len("Z07:00")]
if ('0' <= c && c <= '9') || parseDec2(b[len(b)-len("07:00"):]) >= 24 {
err = errors.New("timezone hour outside of range [0,23]")
}
}
if err != nil {
return &SemanticError{action: "marshal", GoType: t, Err: err}
}
return enc.WriteValue(b) // RFC 3339 never needs JSON escaping
}
// The format may contain special characters that need escaping.
// Verify that the result is a valid JSON string (common case),
// otherwise escape the string correctly (slower case).
@ -113,10 +128,11 @@ func makeTimeArshaler(fncs *arshaler, t reflect.Type) *arshaler {
return enc.WriteValue(b)
}
fncs.unmarshal = func(uo UnmarshalOptions, dec *Decoder, va addressableValue) error {
format := time.RFC3339Nano
format := time.RFC3339
isRFC3339 := true
if uo.format != "" && uo.formatDepth == dec.tokens.depth() {
var err error
format, err = checkTimeFormat(uo.format)
format, isRFC3339, err = checkTimeFormat(uo.format)
if err != nil {
return &SemanticError{action: "unmarshal", GoType: t, Err: err}
}
@ -136,6 +152,29 @@ func makeTimeArshaler(fncs *arshaler, t reflect.Type) *arshaler {
case '"':
val = unescapeStringMayCopy(val, flags.isVerbatim())
tt2, err := time.Parse(format, string(val))
if isRFC3339 && err == nil {
// TODO(https://go.dev/issue/54580): RFC 3339 specifies
// the exact grammar of a valid timestamp. However,
// the parsing functionality in "time" is too loose and
// incorrectly accepts invalid timestamps as valid.
// Remove these manual checks when "time" checks it for us.
newParseError := func(layout, value, layoutElem, valueElem, message string) error {
return &time.ParseError{Layout: layout, Value: value, LayoutElem: layoutElem, ValueElem: valueElem, Message: message}
}
switch {
case val[len("2006-01-02T")+1] == ':': // hour must be two digits
err = newParseError(format, string(val), "15", string(val[len("2006-01-02T"):][:1]), "")
case val[len("2006-01-02T15:04:05")] == ',': // sub-second separator must be a period
err = newParseError(format, string(val), ".", ",", "")
case val[len(val)-1] != 'Z':
switch {
case parseDec2(val[len(val)-len("07:00"):]) >= 24: // timezone hour must be in range
err = newParseError(format, string(val), "Z07:00", string(val[len(val)-len("Z07:00"):]), ": timezone hour out of range")
case parseDec2(val[len(val)-len("00"):]) >= 60: // timezone minute must be in range
err = newParseError(format, string(val), "Z07:00", string(val[len(val)-len("Z07:00"):]), ": timezone minute out of range")
}
}
}
if err != nil {
return &SemanticError{action: "unmarshal", JSONKind: k, GoType: t, Err: err}
}
@ -149,48 +188,54 @@ func makeTimeArshaler(fncs *arshaler, t reflect.Type) *arshaler {
return fncs
}
func checkTimeFormat(format string) (string, error) {
func checkTimeFormat(format string) (string, bool, error) {
// We assume that an exported constant in the time package will
// always start with an uppercase ASCII letter.
if len(format) > 0 && 'A' <= format[0] && format[0] <= 'Z' {
switch format {
case "ANSIC":
return time.ANSIC, nil
return time.ANSIC, false, nil
case "UnixDate":
return time.UnixDate, nil
return time.UnixDate, false, nil
case "RubyDate":
return time.RubyDate, nil
return time.RubyDate, false, nil
case "RFC822":
return time.RFC822, nil
return time.RFC822, false, nil
case "RFC822Z":
return time.RFC822Z, nil
return time.RFC822Z, false, nil
case "RFC850":
return time.RFC850, nil
return time.RFC850, false, nil
case "RFC1123":
return time.RFC1123, nil
return time.RFC1123, false, nil
case "RFC1123Z":
return time.RFC1123Z, nil
return time.RFC1123Z, false, nil
case "RFC3339":
return time.RFC3339, nil
return time.RFC3339, true, nil
case "RFC3339Nano":
return time.RFC3339Nano, nil
return time.RFC3339Nano, true, nil
case "Kitchen":
return time.Kitchen, nil
return time.Kitchen, false, nil
case "Stamp":
return time.Stamp, nil
return time.Stamp, false, nil
case "StampMilli":
return time.StampMilli, nil
return time.StampMilli, false, nil
case "StampMicro":
return time.StampMicro, nil
return time.StampMicro, false, nil
case "StampNano":
return time.StampNano, nil
return time.StampNano, false, nil
default:
// Reject any format that is an exported Go identifier in case
// new format constants are added to the time package.
if strings.TrimFunc(format, isLetterOrDigit) == "" {
return "", fmt.Errorf("undefined format layout: %v", format)
return "", false, fmt.Errorf("undefined format layout: %v", format)
}
}
}
return format, nil
return format, false, nil
}
// parseDec2 parses b as an unsigned, base-10, 2-digit number.
// It panics if len(b) < 2. The result is undefined if digits are not base-10.
func parseDec2(b []byte) byte {
return 10*(b[0]-'0') + (b[1] - '0')
}

View File

@ -347,9 +347,9 @@ func (d *Decoder) PeekKind() Kind {
return next
}
// skipValue is semantically equivalent to calling ReadValue and discarding
// SkipValue is semantically equivalent to calling ReadValue and discarding
// the result except that memory is not wasted trying to hold the entire result.
func (d *Decoder) skipValue() error {
func (d *Decoder) SkipValue() error {
switch d.PeekKind() {
case '{', '[':
// For JSON objects and arrays, keep skipping all tokens
@ -374,7 +374,7 @@ func (d *Decoder) skipValue() error {
}
// ReadToken reads the next Token, advancing the read offset.
// The returned token is only valid until the next Peek or Read call.
// The returned token is only valid until the next Peek, Read, or Skip call.
// It returns io.EOF if there are no more tokens.
func (d *Decoder) ReadToken() (Token, error) {
// Determine the next kind.
@ -585,7 +585,7 @@ func (f valueFlags) isCanonical() bool { return f&stringNonCanonical == 0 }
// ReadValue returns the next raw JSON value, advancing the read offset.
// The value is stripped of any leading or trailing whitespace.
// The returned value is only valid until the next Peek or Read call and
// The returned value is only valid until the next Peek, Read, or Skip call and
// may not be mutated while the Decoder remains in use.
// If the decoder is currently at the end token for an object or array,
// then it reports a SyntacticError and the internal state remains unchanged.
@ -1013,7 +1013,7 @@ func (d *Decoder) InputOffset() int64 {
// UnreadBuffer returns the data remaining in the unread buffer,
// which may contain zero or more bytes.
// The returned buffer must not be mutated while Decoder continues to be used.
// The buffer contents are valid until the next Peek or Read call.
// The buffer contents are valid until the next Peek, Read, or Skip call.
func (d *Decoder) UnreadBuffer() []byte {
return d.unreadBuffer()
}
@ -1213,7 +1213,7 @@ func consumeStringResumable(flags *valueFlags, b []byte, resumeOffset int, valid
return n, &SyntacticError{str: "invalid escape sequence " + strconv.Quote(string(b[n:n+6])) + " within string"}
}
// Only certain control characters can use the \uFFFF notation
// for canonical formating (per RFC 8785, section 3.2.2.2.).
// for canonical formatting (per RFC 8785, section 3.2.2.2.).
switch v1 {
// \uFFFF notation not permitted for these characters.
case '\b', '\f', '\n', '\r', '\t':

View File

@ -8,8 +8,7 @@
// primitive data types such as booleans, strings, and numbers,
// in addition to structured data types such as objects and arrays.
//
//
// Terminology
// # Terminology
//
// This package uses the terms "encode" and "decode" for syntactic functionality
// that is concerned with processing JSON based on its grammar, and
@ -32,8 +31,7 @@
//
// See RFC 8259 for more information.
//
//
// Specifications
// # Specifications
//
// Relevant specifications include RFC 4627, RFC 7159, RFC 7493, RFC 8259,
// and RFC 8785. Each RFC is generally a stricter subset of another RFC.
@ -60,8 +58,7 @@
// In particular, it makes specific choices about behavior that RFC 8259
// leaves as undefined in order to ensure greater interoperability.
//
//
// JSON Representation of Go structs
// # JSON Representation of Go structs
//
// A Go struct is naturally represented as a JSON object,
// where each Go struct field corresponds with a JSON object member.

View File

@ -347,6 +347,30 @@ func (e *Encoder) unwriteEmptyObjectMember(prevName *string) bool {
return true
}
// unwriteOnlyObjectMemberName unwrites the only object member name
// and returns the unquoted name.
func (e *Encoder) unwriteOnlyObjectMemberName() string {
if last := e.tokens.last; !last.isObject() || last.length() != 1 {
panic("BUG: must be called on an object after writing first name")
}
// Unwrite the name and whitespace.
b := trimSuffixString(e.buf)
isVerbatim := bytes.IndexByte(e.buf[len(b):], '\\') < 0
name := string(unescapeStringMayCopy(e.buf[len(b):], isVerbatim))
e.buf = trimSuffixWhitespace(b)
// Undo state changes.
e.tokens.last.decrement()
if !e.options.AllowDuplicateNames {
if e.tokens.last.isActiveNamespace() {
e.namespaces.last().removeLast()
}
e.names.clearLast()
}
return name
}
func trimSuffixWhitespace(b []byte) []byte {
// NOTE: The arguments and logic are kept simple to keep this inlineable.
n := len(b) - 1

View File

@ -8,6 +8,7 @@ import (
"bytes"
"io"
"math/bits"
"sort"
"sync"
)
@ -148,3 +149,34 @@ func putStreamingDecoder(d *Decoder) {
streamingDecoderPool.Put(d)
}
}
var stringsPools = &sync.Pool{New: func() any { return new(stringSlice) }}
type stringSlice []string
// getStrings returns a non-nil pointer to a slice with length n.
func getStrings(n int) *stringSlice {
s := stringsPools.Get().(*stringSlice)
if cap(*s) < n {
*s = make([]string, n)
}
*s = (*s)[:n]
return s
}
func putStrings(s *stringSlice) {
if cap(*s) > 1<<10 {
*s = nil // avoid pinning arbitrarily large amounts of memory
}
stringsPools.Put(s)
}
// Sort sorts the string slice according to RFC 8785, section 3.2.3.
func (ss *stringSlice) Sort() {
// TODO(https://go.dev/issue/47619): Use slices.SortFunc instead.
sort.Sort(ss)
}
func (ss *stringSlice) Len() int { return len(*ss) }
func (ss *stringSlice) Less(i, j int) bool { return lessUTF16((*ss)[i], (*ss)[j]) }
func (ss *stringSlice) Swap(i, j int) { (*ss)[i], (*ss)[j] = (*ss)[j], (*ss)[i] }

View File

@ -721,7 +721,7 @@ func (s *uintSet) has(i uint) bool {
return s.lo.has(i)
} else {
i -= 64
iHi, iLo := int(i/64), uint(i%64)
iHi, iLo := int(i/64), i%64
return iHi < len(s.hi) && s.hi[iHi].has(iLo)
}
}
@ -735,7 +735,7 @@ func (s *uintSet) insert(i uint) bool {
return !has
} else {
i -= 64
iHi, iLo := int(i/64), uint(i%64)
iHi, iLo := int(i/64), i%64
if iHi >= len(s.hi) {
s.hi = append(s.hi, make([]uintSet64, iHi+1-len(s.hi))...)
s.hi = s.hi[:cap(s.hi)]

View File

@ -112,7 +112,7 @@ func Bool(b bool) Token {
return False
}
// String construct a Token representing a JSON string.
// String constructs a Token representing a JSON string.
// The provided string should contain valid UTF-8, otherwise invalid characters
// may be mangled as the Unicode replacement character.
func String(s string) Token {
@ -225,7 +225,7 @@ func (t Token) appendString(dst []byte, validateUTF8, preserveRaw bool, escapeRu
}
// String returns the unescaped string value for a JSON string.
// For other JSON kinds, this returns the raw JSON represention.
// For other JSON kinds, this returns the raw JSON representation.
func (t Token) String() string {
// This is inlinable to take advantage of "function outlining".
// This avoids an allocation for the string(b) conversion
@ -373,10 +373,10 @@ func (t Token) Int() int64 {
case 'i':
return int64(t.num)
case 'u':
if uint64(t.num) > maxInt64 {
if t.num > maxInt64 {
return maxInt64
}
return int64(uint64(t.num))
return int64(t.num)
}
}
@ -425,7 +425,7 @@ func (t Token) Uint() uint64 {
// Handle exact integer value.
switch t.str[0] {
case 'u':
return uint64(t.num)
return t.num
case 'i':
if int64(t.num) < minUint64 {
return minUint64

View File

@ -263,7 +263,7 @@ func reorderObjects(d *Decoder, scratch *[]byte) {
afterValue := d.InputOffset()
if isSorted && len(*members) > 0 {
isSorted = lessUTF16(prevName, name)
isSorted = lessUTF16(prevName, []byte(name))
}
*members = append(*members, memberName{name, beforeName, afterValue})
prevName = name
@ -317,7 +317,7 @@ func reorderObjects(d *Decoder, scratch *[]byte) {
// to the UTF-16 codepoints of the UTF-8 encoded input strings.
// This implements the ordering specified in RFC 8785, section 3.2.3.
// The inputs must be valid UTF-8, otherwise this may panic.
func lessUTF16(x, y []byte) bool {
func lessUTF16[Bytes []byte | string](x, y Bytes) bool {
// NOTE: This is an optimized, allocation-free implementation
// of lessUTF16Simple in fuzz_test.go. FuzzLessUTF16 verifies that the
// two implementations agree on the result of comparing any two strings.
@ -326,8 +326,13 @@ func lessUTF16(x, y []byte) bool {
return ('\u0000' <= r && r <= '\uD7FF') || ('\uE000' <= r && r <= '\uFFFF')
}
var invalidUTF8 bool
x0, y0 := x, y
for {
if len(x) == 0 || len(y) == 0 {
if len(x) == len(y) && invalidUTF8 {
return string(x0) < string(y0)
}
return len(x) < len(y)
}
@ -341,35 +346,36 @@ func lessUTF16(x, y []byte) bool {
}
// Decode next pair of runes as UTF-8.
rx, nx := utf8.DecodeRune(x)
ry, ny := utf8.DecodeRune(y)
// TODO(https://go.dev/issue/56948): Use a generic implementation
// of utf8.DecodeRune, or rely on a compiler optimization to statically
// hide the cost of a type switch (https://go.dev/issue/57072).
var rx, ry rune
var nx, ny int
switch any(x).(type) {
case string:
rx, nx = utf8.DecodeRuneInString(string(x))
ry, ny = utf8.DecodeRuneInString(string(y))
case []byte:
rx, nx = utf8.DecodeRune([]byte(x))
ry, ny = utf8.DecodeRune([]byte(y))
}
selfx := isUTF16Self(rx)
selfy := isUTF16Self(ry)
switch {
// Both runes encode as either a single or surrogate pair
// of UTF-16 codepoints.
case isUTF16Self(rx) == isUTF16Self(ry):
if rx != ry {
return rx < ry
}
// The x rune is a single UTF-16 codepoint, while
// the y rune is a surrogate pair of UTF-16 codepoints.
case isUTF16Self(rx):
ry, _ := utf16.EncodeRune(ry)
if rx != ry {
return rx < ry
}
panic("BUG: invalid UTF-8") // implies rx is an unpaired surrogate half
case selfx && !selfy:
ry, _ = utf16.EncodeRune(ry)
// The y rune is a single UTF-16 codepoint, while
// the x rune is a surrogate pair of UTF-16 codepoints.
case isUTF16Self(ry):
rx, _ := utf16.EncodeRune(rx)
if rx != ry {
return rx < ry
}
panic("BUG: invalid UTF-8") // implies ry is an unpaired surrogate half
case selfy && !selfx:
rx, _ = utf16.EncodeRune(rx)
}
if rx != ry {
return rx < ry
}
invalidUTF8 = invalidUTF8 || (rx == utf8.RuneError && nx == 1) || (ry == utf8.RuneError && ny == 1)
x, y = x[nx:], y[ny:]
}
}

260
vendor/k8s.io/kube-openapi/pkg/schemaconv/openapi.go generated vendored Normal file
View File

@ -0,0 +1,260 @@
/*
Copyright 2022 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 schemaconv
import (
"errors"
"path"
"strings"
"k8s.io/kube-openapi/pkg/validation/spec"
"sigs.k8s.io/structured-merge-diff/v4/schema"
)
// ToSchemaFromOpenAPI converts a directory of OpenAPI schemas to an smd Schema.
// - models: a map from definition name to OpenAPI V3 structural schema for each definition.
// Key in map is used to resolve references in the schema.
// - preserveUnknownFields: flag indicating whether unknown fields in all schemas should be preserved.
// - returns: nil and an error if there is a parse error, or if schema does not satisfy a
// required structural schema invariant for conversion. If no error, returns
// a new smd schema.
//
// Schema should be validated as structural before using with this function, or
// there may be information lost.
func ToSchemaFromOpenAPI(models map[string]*spec.Schema, preserveUnknownFields bool) (*schema.Schema, error) {
c := convert{
preserveUnknownFields: preserveUnknownFields,
output: &schema.Schema{},
}
for name, spec := range models {
// Skip/Ignore top-level references
if len(spec.Ref.String()) > 0 {
continue
}
var a schema.Atom
// Hard-coded schemas for now as proto_models implementation functions.
// https://github.com/kubernetes/kube-openapi/issues/364
if name == quantityResource {
a = schema.Atom{
Scalar: untypedDef.Atom.Scalar,
}
} else if name == rawExtensionResource {
a = untypedDef.Atom
} else {
c2 := c.push(name, &a)
c2.visitSpec(spec)
c.pop(c2)
}
c.insertTypeDef(name, a)
}
if len(c.errorMessages) > 0 {
return nil, errors.New(strings.Join(c.errorMessages, "\n"))
}
c.addCommonTypes()
return c.output, nil
}
func (c *convert) visitSpec(m *spec.Schema) {
// Check if this schema opts its descendants into preserve-unknown-fields
if p, ok := m.Extensions["x-kubernetes-preserve-unknown-fields"]; ok && p == true {
c.preserveUnknownFields = true
}
a := c.top()
*a = c.parseSchema(m)
}
func (c *convert) parseSchema(m *spec.Schema) schema.Atom {
// k8s-generated OpenAPI specs have historically used only one value for
// type and starting with OpenAPIV3 it is only allowed to be
// a single string.
typ := ""
if len(m.Type) > 0 {
typ = m.Type[0]
}
// Structural Schemas produced by kubernetes follow very specific rules which
// we can use to infer the SMD type:
switch typ {
case "":
// According to Swagger docs:
// https://swagger.io/docs/specification/data-models/data-types/#any
//
// If no type is specified, it is equivalent to accepting any type.
return schema.Atom{
Scalar: ptr(schema.Scalar("untyped")),
List: c.parseList(m),
Map: c.parseObject(m),
}
case "object":
return schema.Atom{
Map: c.parseObject(m),
}
case "array":
return schema.Atom{
List: c.parseList(m),
}
case "integer", "boolean", "number", "string":
return convertPrimitive(typ, m.Format)
default:
c.reportError("unrecognized type: '%v'", typ)
return schema.Atom{
Scalar: ptr(schema.Scalar("untyped")),
}
}
}
func (c *convert) makeOpenAPIRef(specSchema *spec.Schema) schema.TypeRef {
refString := specSchema.Ref.String()
// Special-case handling for $ref stored inside a single-element allOf
if len(refString) == 0 && len(specSchema.AllOf) == 1 && len(specSchema.AllOf[0].Ref.String()) > 0 {
refString = specSchema.AllOf[0].Ref.String()
}
if _, n := path.Split(refString); len(n) > 0 {
//!TODO: Refactor the field ElementRelationship override
// we can generate the types with overrides ahead of time rather than
// requiring the hacky runtime support
// (could just create a normalized key struct containing all customizations
// to deduplicate)
mapRelationship, err := getMapElementRelationship(specSchema.Extensions)
if err != nil {
c.reportError(err.Error())
}
if len(mapRelationship) > 0 {
return schema.TypeRef{
NamedType: &n,
ElementRelationship: &mapRelationship,
}
}
return schema.TypeRef{
NamedType: &n,
}
}
var inlined schema.Atom
// compute the type inline
c2 := c.push("inlined in "+c.currentName, &inlined)
c2.preserveUnknownFields = c.preserveUnknownFields
c2.visitSpec(specSchema)
c.pop(c2)
return schema.TypeRef{
Inlined: inlined,
}
}
func (c *convert) parseObject(s *spec.Schema) *schema.Map {
var fields []schema.StructField
for name, member := range s.Properties {
fields = append(fields, schema.StructField{
Name: name,
Type: c.makeOpenAPIRef(&member),
Default: member.Default,
})
}
// AdditionalProperties informs the schema of any "unknown" keys
// Unknown keys are enforced by the ElementType field.
elementType := func() schema.TypeRef {
if s.AdditionalProperties == nil {
// According to openAPI spec, an object without properties and without
// additionalProperties is assumed to be a free-form object.
if c.preserveUnknownFields || len(s.Properties) == 0 {
return schema.TypeRef{
NamedType: &deducedName,
}
}
// If properties are specified, do not implicitly allow unknown
// fields
return schema.TypeRef{}
} else if s.AdditionalProperties.Schema != nil {
// Unknown fields use the referred schema
return c.makeOpenAPIRef(s.AdditionalProperties.Schema)
} else if s.AdditionalProperties.Allows {
// A boolean instead of a schema was provided. Deduce the
// type from the value provided at runtime.
return schema.TypeRef{
NamedType: &deducedName,
}
} else {
// Additional Properties are explicitly disallowed by the user.
// Ensure element type is empty.
return schema.TypeRef{}
}
}()
relationship, err := getMapElementRelationship(s.Extensions)
if err != nil {
c.reportError(err.Error())
}
return &schema.Map{
Fields: fields,
ElementRelationship: relationship,
ElementType: elementType,
}
}
func (c *convert) parseList(s *spec.Schema) *schema.List {
relationship, mapKeys, err := getListElementRelationship(s.Extensions)
if err != nil {
c.reportError(err.Error())
}
elementType := func() schema.TypeRef {
if s.Items != nil {
if s.Items.Schema == nil || s.Items.Len() != 1 {
c.reportError("structural schema arrays must have exactly one member subtype")
return schema.TypeRef{
NamedType: &deducedName,
}
}
subSchema := s.Items.Schema
if subSchema == nil {
subSchema = &s.Items.Schemas[0]
}
return c.makeOpenAPIRef(subSchema)
} else if len(s.Type) > 0 && len(s.Type[0]) > 0 {
c.reportError("`items` must be specified on arrays")
}
// A list with no items specified is treated as "untyped".
return schema.TypeRef{
NamedType: &untypedName,
}
}()
return &schema.List{
ElementRelationship: relationship,
Keys: mapKeys,
ElementType: elementType,
}
}

View File

@ -0,0 +1,178 @@
/*
Copyright 2022 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 schemaconv
import (
"errors"
"path"
"strings"
"k8s.io/kube-openapi/pkg/util/proto"
"sigs.k8s.io/structured-merge-diff/v4/schema"
)
// ToSchema converts openapi definitions into a schema suitable for structured
// merge (i.e. kubectl apply v2).
func ToSchema(models proto.Models) (*schema.Schema, error) {
return ToSchemaWithPreserveUnknownFields(models, false)
}
// ToSchemaWithPreserveUnknownFields converts openapi definitions into a schema suitable for structured
// merge (i.e. kubectl apply v2), it will preserve unknown fields if specified.
func ToSchemaWithPreserveUnknownFields(models proto.Models, preserveUnknownFields bool) (*schema.Schema, error) {
c := convert{
preserveUnknownFields: preserveUnknownFields,
output: &schema.Schema{},
}
for _, name := range models.ListModels() {
model := models.LookupModel(name)
var a schema.Atom
c2 := c.push(name, &a)
model.Accept(c2)
c.pop(c2)
c.insertTypeDef(name, a)
}
if len(c.errorMessages) > 0 {
return nil, errors.New(strings.Join(c.errorMessages, "\n"))
}
c.addCommonTypes()
return c.output, nil
}
func (c *convert) makeRef(model proto.Schema, preserveUnknownFields bool) schema.TypeRef {
var tr schema.TypeRef
if r, ok := model.(*proto.Ref); ok {
if r.Reference() == "io.k8s.apimachinery.pkg.runtime.RawExtension" {
return schema.TypeRef{
NamedType: &untypedName,
}
}
// reference a named type
_, n := path.Split(r.Reference())
tr.NamedType = &n
mapRelationship, err := getMapElementRelationship(model.GetExtensions())
if err != nil {
c.reportError(err.Error())
}
// empty string means unset.
if len(mapRelationship) > 0 {
tr.ElementRelationship = &mapRelationship
}
} else {
// compute the type inline
c2 := c.push("inlined in "+c.currentName, &tr.Inlined)
c2.preserveUnknownFields = preserveUnknownFields
model.Accept(c2)
c.pop(c2)
if tr == (schema.TypeRef{}) {
// emit warning?
tr.NamedType = &untypedName
}
}
return tr
}
func (c *convert) VisitKind(k *proto.Kind) {
preserveUnknownFields := c.preserveUnknownFields
if p, ok := k.GetExtensions()["x-kubernetes-preserve-unknown-fields"]; ok && p == true {
preserveUnknownFields = true
}
a := c.top()
a.Map = &schema.Map{}
for _, name := range k.FieldOrder {
member := k.Fields[name]
tr := c.makeRef(member, preserveUnknownFields)
a.Map.Fields = append(a.Map.Fields, schema.StructField{
Name: name,
Type: tr,
Default: member.GetDefault(),
})
}
unions, err := makeUnions(k.GetExtensions())
if err != nil {
c.reportError(err.Error())
return
}
// TODO: We should check that the fields and discriminator
// specified in the union are actual fields in the struct.
a.Map.Unions = unions
if preserveUnknownFields {
a.Map.ElementType = schema.TypeRef{
NamedType: &deducedName,
}
}
a.Map.ElementRelationship, err = getMapElementRelationship(k.GetExtensions())
if err != nil {
c.reportError(err.Error())
}
}
func (c *convert) VisitArray(a *proto.Array) {
relationship, mapKeys, err := getListElementRelationship(a.GetExtensions())
if err != nil {
c.reportError(err.Error())
}
atom := c.top()
atom.List = &schema.List{
ElementType: c.makeRef(a.SubType, c.preserveUnknownFields),
ElementRelationship: relationship,
Keys: mapKeys,
}
}
func (c *convert) VisitMap(m *proto.Map) {
relationship, err := getMapElementRelationship(m.GetExtensions())
if err != nil {
c.reportError(err.Error())
}
a := c.top()
a.Map = &schema.Map{
ElementType: c.makeRef(m.SubType, c.preserveUnknownFields),
ElementRelationship: relationship,
}
}
func (c *convert) VisitPrimitive(p *proto.Primitive) {
a := c.top()
if c.currentName == quantityResource {
a.Scalar = ptr(schema.Scalar("untyped"))
} else {
*a = convertPrimitive(p.Type, p.Format)
}
}
func (c *convert) VisitArbitrary(a *proto.Arbitrary) {
*c.top() = deducedDef.Atom
}
func (c *convert) VisitReference(proto.Reference) {
// Do nothing, we handle references specially
}

View File

@ -17,43 +17,18 @@ limitations under the License.
package schemaconv
import (
"errors"
"fmt"
"path"
"sort"
"strings"
"k8s.io/kube-openapi/pkg/util/proto"
"sigs.k8s.io/structured-merge-diff/v4/schema"
)
const (
quantityResource = "io.k8s.apimachinery.pkg.api.resource.Quantity"
quantityResource = "io.k8s.apimachinery.pkg.api.resource.Quantity"
rawExtensionResource = "io.k8s.apimachinery.pkg.runtime.RawExtension"
)
// ToSchema converts openapi definitions into a schema suitable for structured
// merge (i.e. kubectl apply v2).
func ToSchema(models proto.Models) (*schema.Schema, error) {
return ToSchemaWithPreserveUnknownFields(models, false)
}
// ToSchemaWithPreserveUnknownFields converts openapi definitions into a schema suitable for structured
// merge (i.e. kubectl apply v2), it will preserve unknown fields if specified.
func ToSchemaWithPreserveUnknownFields(models proto.Models, preserveUnknownFields bool) (*schema.Schema, error) {
c := convert{
input: models,
preserveUnknownFields: preserveUnknownFields,
output: &schema.Schema{},
}
if err := c.convertAll(); err != nil {
return nil, err
}
c.addCommonTypes()
return c.output, nil
}
type convert struct {
input proto.Models
preserveUnknownFields bool
output *schema.Schema
@ -64,7 +39,6 @@ type convert struct {
func (c *convert) push(name string, a *schema.Atom) *convert {
return &convert{
input: c.input,
preserveUnknownFields: c.preserveUnknownFields,
output: c.output,
currentName: name,
@ -78,30 +52,17 @@ func (c *convert) pop(c2 *convert) {
c.errorMessages = append(c.errorMessages, c2.errorMessages...)
}
func (c *convert) convertAll() error {
for _, name := range c.input.ListModels() {
model := c.input.LookupModel(name)
c.insertTypeDef(name, model)
}
if len(c.errorMessages) > 0 {
return errors.New(strings.Join(c.errorMessages, "\n"))
}
return nil
}
func (c *convert) reportError(format string, args ...interface{}) {
c.errorMessages = append(c.errorMessages,
c.currentName+": "+fmt.Sprintf(format, args...),
)
}
func (c *convert) insertTypeDef(name string, model proto.Schema) {
func (c *convert) insertTypeDef(name string, atom schema.Atom) {
def := schema.TypeDef{
Name: name,
Atom: atom,
}
c2 := c.push(name, &def.Atom)
model.Accept(c2)
c.pop(c2)
if def.Atom == (schema.Atom{}) {
// This could happen if there were a top-level reference.
return
@ -156,46 +117,6 @@ var deducedDef schema.TypeDef = schema.TypeDef{
},
}
func (c *convert) makeRef(model proto.Schema, preserveUnknownFields bool) schema.TypeRef {
var tr schema.TypeRef
if r, ok := model.(*proto.Ref); ok {
if r.Reference() == "io.k8s.apimachinery.pkg.runtime.RawExtension" {
return schema.TypeRef{
NamedType: &untypedName,
}
}
// reference a named type
_, n := path.Split(r.Reference())
tr.NamedType = &n
ext := model.GetExtensions()
if val, ok := ext["x-kubernetes-map-type"]; ok {
switch val {
case "atomic":
relationship := schema.Atomic
tr.ElementRelationship = &relationship
case "granular":
relationship := schema.Separable
tr.ElementRelationship = &relationship
default:
c.reportError("unknown map type %v", val)
}
}
} else {
// compute the type inline
c2 := c.push("inlined in "+c.currentName, &tr.Inlined)
c2.preserveUnknownFields = preserveUnknownFields
model.Accept(c2)
c.pop(c2)
if tr == (schema.TypeRef{}) {
// emit warning?
tr.NamedType = &untypedName
}
}
return tr
}
func makeUnions(extensions map[string]interface{}) ([]schema.Union, error) {
schemaUnions := []schema.Union{}
if iunions, ok := extensions["x-kubernetes-unions"]; ok {
@ -299,52 +220,6 @@ func makeUnion(extensions map[string]interface{}) (schema.Union, error) {
return union, nil
}
func (c *convert) VisitKind(k *proto.Kind) {
preserveUnknownFields := c.preserveUnknownFields
if p, ok := k.GetExtensions()["x-kubernetes-preserve-unknown-fields"]; ok && p == true {
preserveUnknownFields = true
}
a := c.top()
a.Map = &schema.Map{}
for _, name := range k.FieldOrder {
member := k.Fields[name]
tr := c.makeRef(member, preserveUnknownFields)
a.Map.Fields = append(a.Map.Fields, schema.StructField{
Name: name,
Type: tr,
Default: member.GetDefault(),
})
}
unions, err := makeUnions(k.GetExtensions())
if err != nil {
c.reportError(err.Error())
return
}
// TODO: We should check that the fields and discriminator
// specified in the union are actual fields in the struct.
a.Map.Unions = unions
if preserveUnknownFields {
a.Map.ElementType = schema.TypeRef{
NamedType: &deducedName,
}
}
ext := k.GetExtensions()
if val, ok := ext["x-kubernetes-map-type"]; ok {
switch val {
case "atomic":
a.Map.ElementRelationship = schema.Atomic
case "granular":
a.Map.ElementRelationship = schema.Separable
default:
c.reportError("unknown map type %v", val)
}
}
}
func toStringSlice(o interface{}) (out []string, ok bool) {
switch t := o.(type) {
case []interface{}:
@ -355,117 +230,108 @@ func toStringSlice(o interface{}) (out []string, ok bool) {
}
}
return out, true
case []string:
return t, true
}
return nil, false
}
func (c *convert) VisitArray(a *proto.Array) {
atom := c.top()
atom.List = &schema.List{
ElementRelationship: schema.Atomic,
}
l := atom.List
l.ElementType = c.makeRef(a.SubType, c.preserveUnknownFields)
ext := a.GetExtensions()
if val, ok := ext["x-kubernetes-list-type"]; ok {
if val == "atomic" {
l.ElementRelationship = schema.Atomic
} else if val == "set" {
l.ElementRelationship = schema.Associative
} else if val == "map" {
l.ElementRelationship = schema.Associative
if keys, ok := ext["x-kubernetes-list-map-keys"]; ok {
if keyNames, ok := toStringSlice(keys); ok {
l.Keys = keyNames
} else {
c.reportError("uninterpreted map keys: %#v", keys)
}
} else {
c.reportError("missing map keys")
}
} else {
c.reportError("unknown list type %v", val)
l.ElementRelationship = schema.Atomic
}
} else if val, ok := ext["x-kubernetes-patch-strategy"]; ok {
if val == "merge" || val == "merge,retainKeys" {
l.ElementRelationship = schema.Associative
if key, ok := ext["x-kubernetes-patch-merge-key"]; ok {
if keyName, ok := key.(string); ok {
l.Keys = []string{keyName}
} else {
c.reportError("uninterpreted merge key: %#v", key)
}
} else {
// It's not an error for this to be absent, it
// means it's a set.
}
} else if val == "retainKeys" {
} else {
c.reportError("unknown patch strategy %v", val)
l.ElementRelationship = schema.Atomic
}
}
}
func (c *convert) VisitMap(m *proto.Map) {
a := c.top()
a.Map = &schema.Map{}
a.Map.ElementType = c.makeRef(m.SubType, c.preserveUnknownFields)
ext := m.GetExtensions()
if val, ok := ext["x-kubernetes-map-type"]; ok {
switch val {
case "atomic":
a.Map.ElementRelationship = schema.Atomic
case "granular":
a.Map.ElementRelationship = schema.Separable
default:
c.reportError("unknown map type %v", val)
}
}
}
func ptr(s schema.Scalar) *schema.Scalar { return &s }
func (c *convert) VisitPrimitive(p *proto.Primitive) {
a := c.top()
if c.currentName == quantityResource {
a.Scalar = ptr(schema.Scalar("untyped"))
} else {
switch p.Type {
case proto.Integer:
a.Scalar = ptr(schema.Numeric)
case proto.Number:
a.Scalar = ptr(schema.Numeric)
case proto.String:
switch p.Format {
case "":
a.Scalar = ptr(schema.String)
case "byte":
// byte really means []byte and is encoded as a string.
a.Scalar = ptr(schema.String)
case "int-or-string":
a.Scalar = ptr(schema.Scalar("untyped"))
case "date-time":
a.Scalar = ptr(schema.Scalar("untyped"))
default:
a.Scalar = ptr(schema.Scalar("untyped"))
}
case proto.Boolean:
a.Scalar = ptr(schema.Boolean)
// Basic conversion functions to convert OpenAPI schema definitions to
// SMD Schema atoms
func convertPrimitive(typ string, format string) (a schema.Atom) {
switch typ {
case "integer":
a.Scalar = ptr(schema.Numeric)
case "number":
a.Scalar = ptr(schema.Numeric)
case "string":
switch format {
case "":
a.Scalar = ptr(schema.String)
case "byte":
// byte really means []byte and is encoded as a string.
a.Scalar = ptr(schema.String)
case "int-or-string":
a.Scalar = ptr(schema.Scalar("untyped"))
case "date-time":
a.Scalar = ptr(schema.Scalar("untyped"))
default:
a.Scalar = ptr(schema.Scalar("untyped"))
}
case "boolean":
a.Scalar = ptr(schema.Boolean)
default:
a.Scalar = ptr(schema.Scalar("untyped"))
}
return a
}
func getListElementRelationship(ext map[string]any) (schema.ElementRelationship, []string, error) {
if val, ok := ext["x-kubernetes-list-type"]; ok {
switch val {
case "atomic":
return schema.Atomic, nil, nil
case "set":
return schema.Associative, nil, nil
case "map":
keys, ok := ext["x-kubernetes-list-map-keys"]
if !ok {
return schema.Associative, nil, fmt.Errorf("missing map keys")
}
keyNames, ok := toStringSlice(keys)
if !ok {
return schema.Associative, nil, fmt.Errorf("uninterpreted map keys: %#v", keys)
}
return schema.Associative, keyNames, nil
default:
return schema.Atomic, nil, fmt.Errorf("unknown list type %v", val)
}
} else if val, ok := ext["x-kubernetes-patch-strategy"]; ok {
switch val {
case "merge", "merge,retainKeys":
if key, ok := ext["x-kubernetes-patch-merge-key"]; ok {
keyName, ok := key.(string)
if !ok {
return schema.Associative, nil, fmt.Errorf("uninterpreted merge key: %#v", key)
}
return schema.Associative, []string{keyName}, nil
}
// It's not an error for x-kubernetes-patch-merge-key to be absent,
// it means it's a set
return schema.Associative, nil, nil
case "retainKeys":
return schema.Atomic, nil, nil
default:
return schema.Atomic, nil, fmt.Errorf("unknown patch strategy %v", val)
}
}
// Treat as atomic by default
return schema.Atomic, nil, nil
}
// Returns map element relationship if specified, or empty string if unspecified
func getMapElementRelationship(ext map[string]any) (schema.ElementRelationship, error) {
val, ok := ext["x-kubernetes-map-type"]
if !ok {
// unset Map element relationship
return "", nil
}
switch val {
case "atomic":
return schema.Atomic, nil
case "granular":
return schema.Separable, nil
default:
return "", fmt.Errorf("unknown map type %v", val)
}
}
func (c *convert) VisitArbitrary(a *proto.Arbitrary) {
*c.top() = deducedDef.Atom
}
func (c *convert) VisitReference(proto.Reference) {
// Do nothing, we handle references specially
}

View File

@ -18,7 +18,10 @@ package spec3
import (
"encoding/json"
"github.com/go-openapi/swag"
"k8s.io/kube-openapi/pkg/internal"
jsonv2 "k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json"
"k8s.io/kube-openapi/pkg/validation/spec"
)
@ -41,6 +44,9 @@ func (e *Encoding) MarshalJSON() ([]byte, error) {
}
func (e *Encoding) UnmarshalJSON(data []byte) error {
if internal.UseOptimizedJSONUnmarshalingV3 {
return jsonv2.Unmarshal(data, e)
}
if err := json.Unmarshal(data, &e.EncodingProps); err != nil {
return err
}
@ -50,6 +56,20 @@ func (e *Encoding) UnmarshalJSON(data []byte) error {
return nil
}
func (e *Encoding) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
var x struct {
spec.Extensions
EncodingProps
}
if err := opts.UnmarshalNext(dec, &x); err != nil {
return err
}
e.Extensions = internal.SanitizeExtensions(x.Extensions)
e.EncodingProps = x.EncodingProps
return nil
}
type EncodingProps struct {
// Content Type for encoding a specific property
ContentType string `json:"contentType,omitempty"`
@ -58,7 +78,7 @@ type EncodingProps struct {
// Describes how a specific property value will be serialized depending on its type
Style string `json:"style,omitempty"`
// When this is true, property values of type array or object generate separate parameters for each value of the array, or key-value-pair of the map. For other types of properties this property has no effect
Explode string `json:"explode,omitempty"`
Explode bool `json:"explode,omitempty"`
// AllowReserved determines whether the parameter value SHOULD allow reserved characters, as defined by RFC3986
AllowReserved bool `json:"allowReserved,omitempty"`
}

View File

@ -19,8 +19,11 @@ package spec3
import (
"encoding/json"
"k8s.io/kube-openapi/pkg/validation/spec"
"github.com/go-openapi/swag"
"k8s.io/kube-openapi/pkg/internal"
jsonv2 "k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json"
"k8s.io/kube-openapi/pkg/validation/spec"
)
// Example https://swagger.io/specification/#example-object
@ -49,6 +52,9 @@ func (e *Example) MarshalJSON() ([]byte, error) {
}
func (e *Example) UnmarshalJSON(data []byte) error {
if internal.UseOptimizedJSONUnmarshalingV3 {
return jsonv2.Unmarshal(data, e)
}
if err := json.Unmarshal(data, &e.Refable); err != nil {
return err
}
@ -61,6 +67,23 @@ func (e *Example) UnmarshalJSON(data []byte) error {
return nil
}
func (e *Example) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
var x struct {
spec.Extensions
ExampleProps
}
if err := opts.UnmarshalNext(dec, &x); err != nil {
return err
}
if err := internal.JSONRefFromMap(&e.Ref.Ref, x.Extensions); err != nil {
return err
}
e.Extensions = internal.SanitizeExtensions(x.Extensions)
e.ExampleProps = x.ExampleProps
return nil
}
type ExampleProps struct {
// Summary holds a short description of the example
Summary string `json:"summary,omitempty"`

View File

@ -18,8 +18,11 @@ package spec3
import (
"encoding/json"
"k8s.io/kube-openapi/pkg/validation/spec"
"github.com/go-openapi/swag"
"k8s.io/kube-openapi/pkg/internal"
jsonv2 "k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json"
"k8s.io/kube-openapi/pkg/validation/spec"
)
type ExternalDocumentation struct {
@ -48,6 +51,9 @@ func (e *ExternalDocumentation) MarshalJSON() ([]byte, error) {
}
func (e *ExternalDocumentation) UnmarshalJSON(data []byte) error {
if internal.UseOptimizedJSONUnmarshalingV3 {
return jsonv2.Unmarshal(data, e)
}
if err := json.Unmarshal(data, &e.ExternalDocumentationProps); err != nil {
return err
}
@ -56,3 +62,16 @@ func (e *ExternalDocumentation) UnmarshalJSON(data []byte) error {
}
return nil
}
func (e *ExternalDocumentation) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
var x struct {
spec.Extensions
ExternalDocumentationProps
}
if err := opts.UnmarshalNext(dec, &x); err != nil {
return err
}
e.Extensions = internal.SanitizeExtensions(x.Extensions)
e.ExternalDocumentationProps = x.ExternalDocumentationProps
return nil
}

254
vendor/k8s.io/kube-openapi/pkg/spec3/fuzz.go generated vendored Normal file
View File

@ -0,0 +1,254 @@
package spec3
import (
"math/rand"
"strings"
fuzz "github.com/google/gofuzz"
"k8s.io/kube-openapi/pkg/validation/spec"
)
// refChance is the chance that a particular component will use a $ref
// instead of fuzzed. Expressed as a fraction 1/n, currently there is
// a 1/3 chance that a ref will be used.
const refChance = 3
const alphaNumChars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
func randAlphanumString() string {
arr := make([]string, rand.Intn(10)+5)
for i := 0; i < len(arr); i++ {
arr[i] = string(alphaNumChars[rand.Intn(len(alphaNumChars))])
}
return strings.Join(arr, "")
}
var OpenAPIV3FuzzFuncs []interface{} = []interface{}{
func(s *string, c fuzz.Continue) {
// All OpenAPI V3 map keys must follow the corresponding
// regex. Note that this restricts the range for all other
// string values as well.
str := randAlphanumString()
*s = str
},
func(o *OpenAPI, c fuzz.Continue) {
c.FuzzNoCustom(o)
o.Version = "3.0.0"
},
func(r *interface{}, c fuzz.Continue) {
switch c.Intn(3) {
case 0:
*r = nil
case 1:
n := c.RandString() + "x"
*r = n
case 2:
n := c.Float64()
*r = n
}
},
func(v **spec.Info, c fuzz.Continue) {
// Info is never nil
*v = &spec.Info{}
c.FuzzNoCustom(*v)
(*v).Title = c.RandString() + "x"
},
func(v *Paths, c fuzz.Continue) {
c.Fuzz(&v.VendorExtensible)
num := c.Intn(5)
if num > 0 {
v.Paths = make(map[string]*Path)
}
for i := 0; i < num; i++ {
val := Path{}
c.Fuzz(&val)
v.Paths["/"+c.RandString()] = &val
}
},
func(v *SecurityScheme, c fuzz.Continue) {
if c.Intn(refChance) == 0 {
c.Fuzz(&v.Refable)
return
}
switch c.Intn(4) {
case 0:
v.Type = "apiKey"
v.Name = c.RandString() + "x"
switch c.Intn(3) {
case 0:
v.In = "query"
case 1:
v.In = "header"
case 2:
v.In = "cookie"
}
case 1:
v.Type = "http"
case 2:
v.Type = "oauth2"
v.Flows = make(map[string]*OAuthFlow)
flow := OAuthFlow{}
flow.AuthorizationUrl = c.RandString() + "x"
v.Flows["implicit"] = &flow
flow.Scopes = make(map[string]string)
flow.Scopes["foo"] = "bar"
case 3:
v.Type = "openIdConnect"
v.OpenIdConnectUrl = "https://" + c.RandString()
}
v.Scheme = "basic"
},
func(v *spec.Ref, c fuzz.Continue) {
switch c.Intn(7) {
case 0:
*v = spec.MustCreateRef("#/components/schemas/" + randAlphanumString())
case 1:
*v = spec.MustCreateRef("#/components/responses/" + randAlphanumString())
case 2:
*v = spec.MustCreateRef("#/components/headers/" + randAlphanumString())
case 3:
*v = spec.MustCreateRef("#/components/securitySchemes/" + randAlphanumString())
case 5:
*v = spec.MustCreateRef("#/components/parameters/" + randAlphanumString())
case 6:
*v = spec.MustCreateRef("#/components/requestBodies/" + randAlphanumString())
}
},
func(v *Parameter, c fuzz.Continue) {
if c.Intn(refChance) == 0 {
c.Fuzz(&v.Refable)
return
}
c.Fuzz(&v.ParameterProps)
c.Fuzz(&v.VendorExtensible)
switch c.Intn(3) {
case 0:
// Header param
v.In = "query"
case 1:
v.In = "header"
case 2:
v.In = "cookie"
}
},
func(v *RequestBody, c fuzz.Continue) {
if c.Intn(refChance) == 0 {
c.Fuzz(&v.Refable)
return
}
c.Fuzz(&v.RequestBodyProps)
c.Fuzz(&v.VendorExtensible)
},
func(v *Header, c fuzz.Continue) {
if c.Intn(refChance) == 0 {
c.Fuzz(&v.Refable)
return
}
c.Fuzz(&v.HeaderProps)
c.Fuzz(&v.VendorExtensible)
},
func(v *ResponsesProps, c fuzz.Continue) {
c.Fuzz(&v.Default)
n := c.Intn(5)
for i := 0; i < n; i++ {
r2 := Response{}
c.Fuzz(&r2)
// HTTP Status code in 100-599 Range
code := c.Intn(500) + 100
v.StatusCodeResponses = make(map[int]*Response)
v.StatusCodeResponses[code] = &r2
}
},
func(v *Response, c fuzz.Continue) {
if c.Intn(refChance) == 0 {
c.Fuzz(&v.Refable)
return
}
c.Fuzz(&v.ResponseProps)
c.Fuzz(&v.VendorExtensible)
},
func(v *spec.Extensions, c fuzz.Continue) {
numChildren := c.Intn(5)
for i := 0; i < numChildren; i++ {
if *v == nil {
*v = spec.Extensions{}
}
(*v)["x-"+c.RandString()] = c.RandString()
}
},
func(v *spec.ExternalDocumentation, c fuzz.Continue) {
c.Fuzz(&v.Description)
v.URL = "https://" + randAlphanumString()
},
func(v *spec.SchemaURL, c fuzz.Continue) {
*v = spec.SchemaURL("https://" + randAlphanumString())
},
func(v *spec.SchemaOrBool, c fuzz.Continue) {
*v = spec.SchemaOrBool{}
if c.RandBool() {
v.Allows = c.RandBool()
} else {
v.Schema = &spec.Schema{}
v.Allows = true
c.Fuzz(&v.Schema)
}
},
func(v *spec.SchemaOrArray, c fuzz.Continue) {
*v = spec.SchemaOrArray{}
if c.RandBool() {
schema := spec.Schema{}
c.Fuzz(&schema)
v.Schema = &schema
} else {
v.Schemas = []spec.Schema{}
numChildren := c.Intn(5)
for i := 0; i < numChildren; i++ {
schema := spec.Schema{}
c.Fuzz(&schema)
v.Schemas = append(v.Schemas, schema)
}
}
},
func(v *spec.SchemaOrStringArray, c fuzz.Continue) {
if c.RandBool() {
*v = spec.SchemaOrStringArray{}
if c.RandBool() {
c.Fuzz(&v.Property)
} else {
c.Fuzz(&v.Schema)
}
}
},
func(v *spec.Schema, c fuzz.Continue) {
if c.Intn(refChance) == 0 {
c.Fuzz(&v.Ref)
return
}
if c.RandBool() {
// file schema
c.Fuzz(&v.Default)
c.Fuzz(&v.Description)
c.Fuzz(&v.Example)
c.Fuzz(&v.ExternalDocs)
c.Fuzz(&v.Format)
c.Fuzz(&v.ReadOnly)
c.Fuzz(&v.Required)
c.Fuzz(&v.Title)
v.Type = spec.StringOrArray{"file"}
} else {
// normal schema
c.Fuzz(&v.SchemaProps)
c.Fuzz(&v.SwaggerSchemaProps)
c.Fuzz(&v.VendorExtensible)
c.Fuzz(&v.ExtraProps)
}
},
}

View File

@ -20,6 +20,8 @@ import (
"encoding/json"
"github.com/go-openapi/swag"
"k8s.io/kube-openapi/pkg/internal"
jsonv2 "k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json"
"k8s.io/kube-openapi/pkg/validation/spec"
)
@ -50,6 +52,9 @@ func (h *Header) MarshalJSON() ([]byte, error) {
}
func (h *Header) UnmarshalJSON(data []byte) error {
if internal.UseOptimizedJSONUnmarshalingV3 {
return jsonv2.Unmarshal(data, h)
}
if err := json.Unmarshal(data, &h.Refable); err != nil {
return err
}
@ -63,6 +68,22 @@ func (h *Header) UnmarshalJSON(data []byte) error {
return nil
}
func (h *Header) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
var x struct {
spec.Extensions
HeaderProps
}
if err := opts.UnmarshalNext(dec, &x); err != nil {
return err
}
if err := internal.JSONRefFromMap(&h.Ref.Ref, x.Extensions); err != nil {
return err
}
h.Extensions = internal.SanitizeExtensions(x.Extensions)
h.HeaderProps = x.HeaderProps
return nil
}
// HeaderProps a struct that describes a header object
type HeaderProps struct {
// Description holds a brief description of the parameter

View File

@ -18,7 +18,10 @@ package spec3
import (
"encoding/json"
"github.com/go-openapi/swag"
"k8s.io/kube-openapi/pkg/internal"
jsonv2 "k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json"
"k8s.io/kube-openapi/pkg/validation/spec"
)
@ -44,6 +47,9 @@ func (m *MediaType) MarshalJSON() ([]byte, error) {
}
func (m *MediaType) UnmarshalJSON(data []byte) error {
if internal.UseOptimizedJSONUnmarshalingV3 {
return jsonv2.Unmarshal(data, m)
}
if err := json.Unmarshal(data, &m.MediaTypeProps); err != nil {
return err
}
@ -53,10 +59,24 @@ func (m *MediaType) UnmarshalJSON(data []byte) error {
return nil
}
func (m *MediaType) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
var x struct {
spec.Extensions
MediaTypeProps
}
if err := opts.UnmarshalNext(dec, &x); err != nil {
return err
}
m.Extensions = internal.SanitizeExtensions(x.Extensions)
m.MediaTypeProps = x.MediaTypeProps
return nil
}
// MediaTypeProps a struct that allows you to specify content format, more at https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#mediaTypeObject
type MediaTypeProps struct {
// Schema holds the schema defining the type used for the media type
Schema *spec.Schema `json:"schema,omitempty"`
Schema *spec.Schema `json:"schema,omitempty"`
// Example of the media type
Example interface{} `json:"example,omitempty"`
// Examples of the media type. Each example object should match the media type and specific schema if present

View File

@ -19,8 +19,10 @@ package spec3
import (
"encoding/json"
"k8s.io/kube-openapi/pkg/validation/spec"
"github.com/go-openapi/swag"
"k8s.io/kube-openapi/pkg/internal"
jsonv2 "k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json"
"k8s.io/kube-openapi/pkg/validation/spec"
)
// Operation describes a single API operation on a path, more at https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#operationObject
@ -46,12 +48,28 @@ func (o *Operation) MarshalJSON() ([]byte, error) {
// UnmarshalJSON hydrates this items instance with the data from JSON
func (o *Operation) UnmarshalJSON(data []byte) error {
if internal.UseOptimizedJSONUnmarshalingV3 {
return jsonv2.Unmarshal(data, o)
}
if err := json.Unmarshal(data, &o.OperationProps); err != nil {
return err
}
return json.Unmarshal(data, &o.VendorExtensible)
}
func (o *Operation) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
var x struct {
spec.Extensions
OperationProps
}
if err := opts.UnmarshalNext(dec, &x); err != nil {
return err
}
o.Extensions = internal.SanitizeExtensions(x.Extensions)
o.OperationProps = x.OperationProps
return nil
}
// OperationProps describes a single API operation on a path, more at https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#operationObject
type OperationProps struct {
// Tags holds a list of tags for API documentation control
@ -73,7 +91,7 @@ type OperationProps struct {
// Deprecated declares this operation to be deprecated
Deprecated bool `json:"deprecated,omitempty"`
// SecurityRequirement holds a declaration of which security mechanisms can be used for this operation
SecurityRequirement []*SecurityRequirement `json:"security,omitempty"`
SecurityRequirement []map[string][]string `json:"security,omitempty"`
// Servers contains an alternative server array to service this operation
Servers []*Server `json:"servers,omitempty"`
}

View File

@ -20,6 +20,8 @@ import (
"encoding/json"
"github.com/go-openapi/swag"
"k8s.io/kube-openapi/pkg/internal"
jsonv2 "k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json"
"k8s.io/kube-openapi/pkg/validation/spec"
)
@ -50,6 +52,10 @@ func (p *Parameter) MarshalJSON() ([]byte, error) {
}
func (p *Parameter) UnmarshalJSON(data []byte) error {
if internal.UseOptimizedJSONUnmarshalingV3 {
return jsonv2.Unmarshal(data, p)
}
if err := json.Unmarshal(data, &p.Refable); err != nil {
return err
}
@ -63,6 +69,22 @@ func (p *Parameter) UnmarshalJSON(data []byte) error {
return nil
}
func (p *Parameter) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
var x struct {
spec.Extensions
ParameterProps
}
if err := opts.UnmarshalNext(dec, &x); err != nil {
return err
}
if err := internal.JSONRefFromMap(&p.Ref.Ref, x.Extensions); err != nil {
return err
}
p.Extensions = internal.SanitizeExtensions(x.Extensions)
p.ParameterProps = x.ParameterProps
return nil
}
// ParameterProps a struct that describes a single operation parameter, more at https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#parameterObject
type ParameterProps struct {
// Name holds the name of the parameter

View File

@ -18,10 +18,13 @@ package spec3
import (
"encoding/json"
"fmt"
"strings"
"k8s.io/kube-openapi/pkg/validation/spec"
"github.com/go-openapi/swag"
"k8s.io/kube-openapi/pkg/internal"
jsonv2 "k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json"
"k8s.io/kube-openapi/pkg/validation/spec"
)
// Paths describes the available paths and operations for the API, more at https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#pathsObject
@ -45,6 +48,9 @@ func (p *Paths) MarshalJSON() ([]byte, error) {
// UnmarshalJSON hydrates this items instance with the data from JSON
func (p *Paths) UnmarshalJSON(data []byte) error {
if internal.UseOptimizedJSONUnmarshalingV3 {
return jsonv2.Unmarshal(data, p)
}
var res map[string]json.RawMessage
if err := json.Unmarshal(data, &res); err != nil {
return err
@ -74,6 +80,59 @@ func (p *Paths) UnmarshalJSON(data []byte) error {
return nil
}
func (p *Paths) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
tok, err := dec.ReadToken()
if err != nil {
return err
}
switch k := tok.Kind(); k {
case 'n':
*p = Paths{}
return nil
case '{':
for {
tok, err := dec.ReadToken()
if err != nil {
return err
}
if tok.Kind() == '}' {
return nil
}
switch k := tok.String(); {
case internal.IsExtensionKey(k):
var ext any
if err := opts.UnmarshalNext(dec, &ext); err != nil {
return err
}
if p.Extensions == nil {
p.Extensions = make(map[string]any)
}
p.Extensions[k] = ext
case len(k) > 0 && k[0] == '/':
pi := Path{}
if err := opts.UnmarshalNext(dec, &pi); err != nil {
return err
}
if p.Paths == nil {
p.Paths = make(map[string]*Path)
}
p.Paths[k] = &pi
default:
_, err := dec.ReadValue() // skip value
if err != nil {
return err
}
}
}
default:
return fmt.Errorf("unknown JSON kind: %v", k)
}
}
// Path describes the operations available on a single path, more at https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#pathItemObject
//
// Note that this struct is actually a thin wrapper around PathProps to make it referable and extensible
@ -101,6 +160,9 @@ func (p *Path) MarshalJSON() ([]byte, error) {
}
func (p *Path) UnmarshalJSON(data []byte) error {
if internal.UseOptimizedJSONUnmarshalingV3 {
return jsonv2.Unmarshal(data, p)
}
if err := json.Unmarshal(data, &p.Refable); err != nil {
return err
}
@ -113,6 +175,24 @@ func (p *Path) UnmarshalJSON(data []byte) error {
return nil
}
func (p *Path) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
var x struct {
spec.Extensions
PathProps
}
if err := opts.UnmarshalNext(dec, &x); err != nil {
return err
}
if err := internal.JSONRefFromMap(&p.Ref.Ref, x.Extensions); err != nil {
return err
}
p.Extensions = internal.SanitizeExtensions(x.Extensions)
p.PathProps = x.PathProps
return nil
}
// PathProps describes the operations available on a single path, more at https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#pathItemObject
type PathProps struct {
// Summary holds a summary for all operations in this path

View File

@ -19,8 +19,10 @@ package spec3
import (
"encoding/json"
"k8s.io/kube-openapi/pkg/validation/spec"
"github.com/go-openapi/swag"
"k8s.io/kube-openapi/pkg/internal"
jsonv2 "k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json"
"k8s.io/kube-openapi/pkg/validation/spec"
)
// RequestBody describes a single request body, more at https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#requestBodyObject
@ -50,6 +52,9 @@ func (r *RequestBody) MarshalJSON() ([]byte, error) {
}
func (r *RequestBody) UnmarshalJSON(data []byte) error {
if internal.UseOptimizedJSONUnmarshalingV3 {
return jsonv2.Unmarshal(data, r)
}
if err := json.Unmarshal(data, &r.Refable); err != nil {
return err
}
@ -71,3 +76,19 @@ type RequestBodyProps struct {
// Required determines if the request body is required in the request
Required bool `json:"required,omitempty"`
}
func (r *RequestBody) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
var x struct {
spec.Extensions
RequestBodyProps
}
if err := opts.UnmarshalNext(dec, &x); err != nil {
return err
}
if err := internal.JSONRefFromMap(&r.Ref.Ref, x.Extensions); err != nil {
return err
}
r.Extensions = internal.SanitizeExtensions(x.Extensions)
r.RequestBodyProps = x.RequestBodyProps
return nil
}

View File

@ -18,10 +18,13 @@ package spec3
import (
"encoding/json"
"fmt"
"strconv"
"k8s.io/kube-openapi/pkg/validation/spec"
"github.com/go-openapi/swag"
"k8s.io/kube-openapi/pkg/internal"
jsonv2 "k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json"
"k8s.io/kube-openapi/pkg/validation/spec"
)
// Responses holds the list of possible responses as they are returned from executing this operation
@ -46,13 +49,15 @@ func (r *Responses) MarshalJSON() ([]byte, error) {
}
func (r *Responses) UnmarshalJSON(data []byte) error {
if internal.UseOptimizedJSONUnmarshalingV3 {
return jsonv2.Unmarshal(data, r)
}
if err := json.Unmarshal(data, &r.ResponsesProps); err != nil {
return err
}
if err := json.Unmarshal(data, &r.VendorExtensible); err != nil {
return err
}
return nil
}
@ -78,25 +83,91 @@ func (r ResponsesProps) MarshalJSON() ([]byte, error) {
// UnmarshalJSON unmarshals responses from JSON
func (r *ResponsesProps) UnmarshalJSON(data []byte) error {
var res map[string]*Response
if internal.UseOptimizedJSONUnmarshalingV3 {
return jsonv2.Unmarshal(data, r)
}
var res map[string]json.RawMessage
if err := json.Unmarshal(data, &res); err != nil {
return nil
return err
}
if v, ok := res["default"]; ok {
r.Default = v
value := Response{}
if err := json.Unmarshal(v, &value); err != nil {
return err
}
r.Default = &value
delete(res, "default")
}
for k, v := range res {
// Take all integral keys
if nk, err := strconv.Atoi(k); err == nil {
if r.StatusCodeResponses == nil {
r.StatusCodeResponses = map[int]*Response{}
}
r.StatusCodeResponses[nk] = v
value := Response{}
if err := json.Unmarshal(v, &value); err != nil {
return err
}
r.StatusCodeResponses[nk] = &value
}
}
return nil
}
func (r *Responses) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) (err error) {
tok, err := dec.ReadToken()
if err != nil {
return err
}
switch k := tok.Kind(); k {
case 'n':
*r = Responses{}
return nil
case '{':
for {
tok, err := dec.ReadToken()
if err != nil {
return err
}
if tok.Kind() == '}' {
return nil
}
switch k := tok.String(); {
case internal.IsExtensionKey(k):
var ext any
if err := opts.UnmarshalNext(dec, &ext); err != nil {
return err
}
if r.Extensions == nil {
r.Extensions = make(map[string]any)
}
r.Extensions[k] = ext
case k == "default":
resp := Response{}
if err := opts.UnmarshalNext(dec, &resp); err != nil {
return err
}
r.ResponsesProps.Default = &resp
default:
if nk, err := strconv.Atoi(k); err == nil {
resp := Response{}
if err := opts.UnmarshalNext(dec, &resp); err != nil {
return err
}
if r.StatusCodeResponses == nil {
r.StatusCodeResponses = map[int]*Response{}
}
r.StatusCodeResponses[nk] = &resp
}
}
}
default:
return fmt.Errorf("unknown JSON kind: %v", k)
}
}
// Response describes a single response from an API Operation, more at https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#responseObject
//
// Note that this struct is actually a thin wrapper around ResponseProps to make it referable and extensible
@ -124,6 +195,9 @@ func (r *Response) MarshalJSON() ([]byte, error) {
}
func (r *Response) UnmarshalJSON(data []byte) error {
if internal.UseOptimizedJSONUnmarshalingV3 {
return jsonv2.Unmarshal(data, r)
}
if err := json.Unmarshal(data, &r.Refable); err != nil {
return err
}
@ -133,7 +207,22 @@ func (r *Response) UnmarshalJSON(data []byte) error {
if err := json.Unmarshal(data, &r.VendorExtensible); err != nil {
return err
}
return nil
}
func (r *Response) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
var x struct {
spec.Extensions
ResponseProps
}
if err := opts.UnmarshalNext(dec, &x); err != nil {
return err
}
if err := internal.JSONRefFromMap(&r.Ref.Ref, x.Extensions); err != nil {
return err
}
r.Extensions = internal.SanitizeExtensions(x.Extensions)
r.ResponseProps = x.ResponseProps
return nil
}
@ -149,7 +238,6 @@ type ResponseProps struct {
Links map[string]*Link `json:"links,omitempty"`
}
// Link represents a possible design-time link for a response, more at https://swagger.io/specification/#link-object
type Link struct {
spec.Refable
@ -175,6 +263,9 @@ func (r *Link) MarshalJSON() ([]byte, error) {
}
func (r *Link) UnmarshalJSON(data []byte) error {
if internal.UseOptimizedJSONUnmarshalingV3 {
return jsonv2.Unmarshal(data, r)
}
if err := json.Unmarshal(data, &r.Refable); err != nil {
return err
}
@ -188,6 +279,22 @@ func (r *Link) UnmarshalJSON(data []byte) error {
return nil
}
func (l *Link) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
var x struct {
spec.Extensions
LinkProps
}
if err := opts.UnmarshalNext(dec, &x); err != nil {
return err
}
if err := internal.JSONRefFromMap(&l.Ref.Ref, x.Extensions); err != nil {
return err
}
l.Extensions = internal.SanitizeExtensions(x.Extensions)
l.LinkProps = x.LinkProps
return nil
}
// LinkProps describes a single response from an API Operation, more at https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#responseObject
type LinkProps struct {
// OperationId is the name of an existing, resolvable OAS operation

View File

@ -1,56 +0,0 @@
/*
Copyright 2021 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 spec3
import (
"encoding/json"
"k8s.io/kube-openapi/pkg/validation/spec"
"github.com/go-openapi/swag"
)
// SecurityRequirementProps describes the required security schemes to execute an operation, more at https://swagger.io/specification/#security-requirement-object
//
// Note that this struct is actually a thin wrapper around SecurityRequirementProps to make it referable and extensible
type SecurityRequirement struct {
SecurityRequirementProps
spec.VendorExtensible
}
// MarshalJSON is a custom marshal function that knows how to encode SecurityRequirement as JSON
func (s *SecurityRequirement) MarshalJSON() ([]byte, error) {
b1, err := json.Marshal(s.SecurityRequirementProps)
if err != nil {
return nil, err
}
b2, err := json.Marshal(s.VendorExtensible)
if err != nil {
return nil, err
}
return swag.ConcatJSON(b1, b2), nil
}
// UnmarshalJSON hydrates this items instance with the data from JSON
func (s *SecurityRequirement) UnmarshalJSON(data []byte) error {
if err := json.Unmarshal(data, &s.SecurityRequirementProps); err != nil {
return err
}
return json.Unmarshal(data, &s.VendorExtensible)
}
// SecurityRequirementProps describes the required security schemes to execute an operation, more at https://swagger.io/specification/#security-requirement-object
type SecurityRequirementProps map[string][]string

View File

@ -19,8 +19,8 @@ package spec3
import (
"encoding/json"
"k8s.io/kube-openapi/pkg/validation/spec"
"github.com/go-openapi/swag"
"k8s.io/kube-openapi/pkg/validation/spec"
)
// SecurityScheme defines reusable Security Scheme Object, more at https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#securitySchemeObject

View File

@ -18,9 +18,11 @@ package spec3
import (
"encoding/json"
"k8s.io/kube-openapi/pkg/validation/spec"
"github.com/go-openapi/swag"
"github.com/go-openapi/swag"
"k8s.io/kube-openapi/pkg/internal"
jsonv2 "k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json"
"k8s.io/kube-openapi/pkg/validation/spec"
)
type Server struct {
@ -51,6 +53,10 @@ func (s *Server) MarshalJSON() ([]byte, error) {
}
func (s *Server) UnmarshalJSON(data []byte) error {
if internal.UseOptimizedJSONUnmarshalingV3 {
return jsonv2.Unmarshal(data, s)
}
if err := json.Unmarshal(data, &s.ServerProps); err != nil {
return err
}
@ -60,6 +66,20 @@ func (s *Server) UnmarshalJSON(data []byte) error {
return nil
}
func (s *Server) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
var x struct {
spec.Extensions
ServerProps
}
if err := opts.UnmarshalNext(dec, &x); err != nil {
return err
}
s.Extensions = internal.SanitizeExtensions(x.Extensions)
s.ServerProps = x.ServerProps
return nil
}
type ServerVariable struct {
ServerVariableProps
spec.VendorExtensible
@ -88,6 +108,9 @@ func (s *ServerVariable) MarshalJSON() ([]byte, error) {
}
func (s *ServerVariable) UnmarshalJSON(data []byte) error {
if internal.UseOptimizedJSONUnmarshalingV3 {
return jsonv2.Unmarshal(data, s)
}
if err := json.Unmarshal(data, &s.ServerVariableProps); err != nil {
return err
}
@ -96,3 +119,17 @@ func (s *ServerVariable) UnmarshalJSON(data []byte) error {
}
return nil
}
func (s *ServerVariable) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decoder) error {
var x struct {
spec.Extensions
ServerVariableProps
}
if err := opts.UnmarshalNext(dec, &x); err != nil {
return err
}
s.Extensions = internal.SanitizeExtensions(x.Extensions)
s.ServerVariableProps = x.ServerVariableProps
return nil
}

View File

@ -17,6 +17,10 @@ limitations under the License.
package spec3
import (
"encoding/json"
"k8s.io/kube-openapi/pkg/internal"
jsonv2 "k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json"
"k8s.io/kube-openapi/pkg/validation/spec"
)
@ -35,3 +39,12 @@ type OpenAPI struct {
// ExternalDocs holds additional external documentation
ExternalDocs *ExternalDocumentation `json:"externalDocs,omitempty"`
}
func (o *OpenAPI) UnmarshalJSON(data []byte) error {
type OpenAPIWithNoFunctions OpenAPI
p := (*OpenAPIWithNoFunctions)(o)
if internal.UseOptimizedJSONUnmarshalingV3 {
return jsonv2.Unmarshal(data, &p)
}
return json.Unmarshal(data, &p)
}

View File

@ -120,7 +120,7 @@ func (d *Definitions) ParseSchemaV3(s *openapi_v3.Schema, path *Path) (Schema, e
switch s.GetType() {
case object:
for _, extension := range s.GetSpecificationExtension() {
if extension.Name == "x-kuberentes-group-version-kind" {
if extension.Name == "x-kubernetes-group-version-kind" {
// Objects with x-kubernetes-group-version-kind are always top
// level types.
return d.parseV3Kind(s, path)
@ -285,7 +285,7 @@ func parseV3Interface(def *yaml.Node) (interface{}, error) {
func (d *Definitions) parseV3BaseSchema(s *openapi_v3.Schema, path *Path) (*BaseSchema, error) {
if s == nil {
return nil, fmt.Errorf("cannot initializae BaseSchema from nil")
return nil, fmt.Errorf("cannot initialize BaseSchema from nil")
}
def, err := parseV3Interface(s.GetDefault().ToRawInfo())

View File

@ -17,7 +17,6 @@ limitations under the License.
package testing
import (
"io/ioutil"
"os"
"sync"
@ -42,7 +41,7 @@ func (f *Fake) OpenAPISchema() (*openapi_v2.Document, error) {
f.err = err
return
}
spec, err := ioutil.ReadFile(f.Path)
spec, err := os.ReadFile(f.Path)
if err != nil {
f.err = err
return

View File

@ -17,7 +17,6 @@ limitations under the License.
package testing
import (
"io/ioutil"
"os"
"path/filepath"
"sync"
@ -47,7 +46,7 @@ func (f *FakeV3) OpenAPIV3Schema(groupVersion string) (*openapi_v3.Document, err
if err != nil {
return nil, err
}
spec, err := ioutil.ReadFile(filepath.Join(f.Path, groupVersion+".json"))
spec, err := os.ReadFile(filepath.Join(f.Path, groupVersion+".json"))
if err != nil {
return nil, err
}

View File

@ -30,6 +30,7 @@ import (
// in GetCanonicalTypeName means Go type names with full package path.
//
// Examples of REST friendly OpenAPI name:
//
// Input: k8s.io/api/core/v1.Pod
// Output: io.k8s.api.core.v1.Pod
//
@ -45,6 +46,7 @@ func ToCanonicalName(name string) string {
// ToRESTFriendlyName converts Golang package/type canonical name into REST friendly OpenAPI name.
//
// Examples of REST friendly OpenAPI name:
//
// Input: k8s.io/api/core/v1.Pod
// Output: io.k8s.api.core.v1.Pod
//
@ -71,18 +73,21 @@ func ToRESTFriendlyName(name string) string {
// OpenAPI canonical names are Go type names with full package path, for uniquely indentifying
// a model / Go type. If a Go type is vendored from another package, only the path after "/vendor/"
// should be used. For custom resource definition (CRD), the canonical name is expected to be
// group/version.kind
//
// group/version.kind
//
// Examples of canonical name:
// Go type: k8s.io/kubernetes/pkg/apis/core.Pod
// CRD: csi.storage.k8s.io/v1alpha1.CSINodeInfo
//
// Go type: k8s.io/kubernetes/pkg/apis/core.Pod
// CRD: csi.storage.k8s.io/v1alpha1.CSINodeInfo
//
// Example for vendored Go type:
// Original full path: k8s.io/kubernetes/vendor/k8s.io/api/core/v1.Pod
// Canonical name: k8s.io/api/core/v1.Pod
//
// Original full path: vendor/k8s.io/api/core/v1.Pod
// Canonical name: k8s.io/api/core/v1.Pod
// Original full path: k8s.io/kubernetes/vendor/k8s.io/api/core/v1.Pod
// Canonical name: k8s.io/api/core/v1.Pod
//
// Original full path: vendor/k8s.io/api/core/v1.Pod
// Canonical name: k8s.io/api/core/v1.Pod
type OpenAPICanonicalTypeNamer interface {
OpenAPICanonicalTypeName() string
}

View File

@ -43,6 +43,9 @@ type Header struct {
// MarshalJSON marshal this to JSON
func (h Header) MarshalJSON() ([]byte, error) {
if internal.UseOptimizedJSONMarshaling {
return internal.DeterministicMarshal(h)
}
b1, err := json.Marshal(h.CommonValidations)
if err != nil {
return nil, err
@ -62,6 +65,20 @@ func (h Header) MarshalJSON() ([]byte, error) {
return swag.ConcatJSON(b1, b2, b3, b4), nil
}
func (h Header) MarshalNextJSON(opts jsonv2.MarshalOptions, enc *jsonv2.Encoder) error {
var x struct {
CommonValidations commonValidationsOmitZero `json:",inline"`
SimpleSchema simpleSchemaOmitZero `json:",inline"`
Extensions
HeaderProps
}
x.CommonValidations = commonValidationsOmitZero(h.CommonValidations)
x.SimpleSchema = simpleSchemaOmitZero(h.SimpleSchema)
x.Extensions = internal.SanitizeExtensions(h.Extensions)
x.HeaderProps = h.HeaderProps
return opts.MarshalNext(enc, x)
}
// UnmarshalJSON unmarshals this header from JSON
func (h *Header) UnmarshalJSON(data []byte) error {
if internal.UseOptimizedJSONUnmarshaling {
@ -94,12 +111,8 @@ func (h *Header) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Dec
h.CommonValidations = x.CommonValidations
h.SimpleSchema = x.SimpleSchema
h.Extensions = x.Extensions
h.Extensions = internal.SanitizeExtensions(x.Extensions)
h.HeaderProps = x.HeaderProps
h.Extensions.sanitize()
if len(h.Extensions) == 0 {
h.Extensions = nil
}
return nil
}

View File

@ -89,17 +89,9 @@ func (e Extensions) GetObject(key string, out interface{}) error {
return nil
}
func (e Extensions) sanitize() {
for k := range e {
if !isExtensionKey(k) {
delete(e, k)
}
}
}
func (e Extensions) sanitizeWithExtra() (extra map[string]any) {
for k, v := range e {
if !isExtensionKey(k) {
if !internal.IsExtensionKey(k) {
if extra == nil {
extra = make(map[string]any)
}
@ -110,10 +102,6 @@ func (e Extensions) sanitizeWithExtra() (extra map[string]any) {
return extra
}
func isExtensionKey(k string) bool {
return len(k) > 1 && (k[0] == 'x' || k[0] == 'X') && k[1] == '-'
}
// VendorExtensible composition block.
type VendorExtensible struct {
Extensions Extensions
@ -181,6 +169,9 @@ type Info struct {
// MarshalJSON marshal this to JSON
func (i Info) MarshalJSON() ([]byte, error) {
if internal.UseOptimizedJSONMarshaling {
return internal.DeterministicMarshal(i)
}
b1, err := json.Marshal(i.InfoProps)
if err != nil {
return nil, err
@ -192,6 +183,16 @@ func (i Info) MarshalJSON() ([]byte, error) {
return swag.ConcatJSON(b1, b2), nil
}
func (i Info) MarshalNextJSON(opts jsonv2.MarshalOptions, enc *jsonv2.Encoder) error {
var x struct {
Extensions
InfoProps
}
x.Extensions = i.Extensions
x.InfoProps = i.InfoProps
return opts.MarshalNext(enc, x)
}
// UnmarshalJSON marshal this from JSON
func (i *Info) UnmarshalJSON(data []byte) error {
if internal.UseOptimizedJSONUnmarshaling {
@ -212,11 +213,7 @@ func (i *Info) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decod
if err := opts.UnmarshalNext(dec, &x); err != nil {
return err
}
x.Extensions.sanitize()
if len(x.Extensions) == 0 {
x.Extensions = nil
}
i.VendorExtensible.Extensions = x.Extensions
i.Extensions = internal.SanitizeExtensions(x.Extensions)
i.InfoProps = x.InfoProps
return nil
}

View File

@ -37,6 +37,18 @@ type SimpleSchema struct {
Example interface{} `json:"example,omitempty"`
}
// Marshaling structure only, always edit along with corresponding
// struct (or compilation will fail).
type simpleSchemaOmitZero struct {
Type string `json:"type,omitempty"`
Nullable bool `json:"nullable,omitzero"`
Format string `json:"format,omitempty"`
Items *Items `json:"items,omitzero"`
CollectionFormat string `json:"collectionFormat,omitempty"`
Default interface{} `json:"default,omitempty"`
Example interface{} `json:"example,omitempty"`
}
// CommonValidations describe common JSON-schema validations
type CommonValidations struct {
Maximum *float64 `json:"maximum,omitempty"`
@ -53,6 +65,23 @@ type CommonValidations struct {
Enum []interface{} `json:"enum,omitempty"`
}
// Marshaling structure only, always edit along with corresponding
// struct (or compilation will fail).
type commonValidationsOmitZero struct {
Maximum *float64 `json:"maximum,omitempty"`
ExclusiveMaximum bool `json:"exclusiveMaximum,omitzero"`
Minimum *float64 `json:"minimum,omitempty"`
ExclusiveMinimum bool `json:"exclusiveMinimum,omitzero"`
MaxLength *int64 `json:"maxLength,omitempty"`
MinLength *int64 `json:"minLength,omitempty"`
Pattern string `json:"pattern,omitempty"`
MaxItems *int64 `json:"maxItems,omitempty"`
MinItems *int64 `json:"minItems,omitempty"`
UniqueItems bool `json:"uniqueItems,omitzero"`
MultipleOf *float64 `json:"multipleOf,omitempty"`
Enum []interface{} `json:"enum,omitempty"`
}
// Items a limited subset of JSON-Schema's items object.
// It is used by parameter definitions that are not located in "body".
//
@ -105,18 +134,18 @@ func (i *Items) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Deco
if err := i.Refable.Ref.fromMap(x.Extensions); err != nil {
return err
}
x.Extensions.sanitize()
if len(x.Extensions) == 0 {
x.Extensions = nil
}
i.CommonValidations = x.CommonValidations
i.SimpleSchema = x.SimpleSchema
i.VendorExtensible.Extensions = x.Extensions
i.Extensions = internal.SanitizeExtensions(x.Extensions)
return nil
}
// MarshalJSON converts this items object to JSON
func (i Items) MarshalJSON() ([]byte, error) {
if internal.UseOptimizedJSONMarshaling {
return internal.DeterministicMarshal(i)
}
b1, err := json.Marshal(i.CommonValidations)
if err != nil {
return nil, err
@ -135,3 +164,17 @@ func (i Items) MarshalJSON() ([]byte, error) {
}
return swag.ConcatJSON(b4, b3, b1, b2), nil
}
func (i Items) MarshalNextJSON(opts jsonv2.MarshalOptions, enc *jsonv2.Encoder) error {
var x struct {
CommonValidations commonValidationsOmitZero `json:",inline"`
SimpleSchema simpleSchemaOmitZero `json:",inline"`
Ref string `json:"$ref,omitempty"`
Extensions
}
x.CommonValidations = commonValidationsOmitZero(i.CommonValidations)
x.SimpleSchema = simpleSchemaOmitZero(i.SimpleSchema)
x.Ref = i.Refable.Ref.String()
x.Extensions = internal.SanitizeExtensions(i.Extensions)
return opts.MarshalNext(enc, x)
}

View File

@ -42,6 +42,23 @@ type OperationProps struct {
Responses *Responses `json:"responses,omitempty"`
}
// Marshaling structure only, always edit along with corresponding
// struct (or compilation will fail).
type operationPropsOmitZero struct {
Description string `json:"description,omitempty"`
Consumes []string `json:"consumes,omitempty"`
Produces []string `json:"produces,omitempty"`
Schemes []string `json:"schemes,omitempty"`
Tags []string `json:"tags,omitempty"`
Summary string `json:"summary,omitempty"`
ExternalDocs *ExternalDocumentation `json:"externalDocs,omitzero"`
ID string `json:"operationId,omitempty"`
Deprecated bool `json:"deprecated,omitempty,omitzero"`
Security []map[string][]string `json:"security,omitempty"`
Parameters []Parameter `json:"parameters,omitempty"`
Responses *Responses `json:"responses,omitzero"`
}
// MarshalJSON takes care of serializing operation properties to JSON
//
// We use a custom marhaller here to handle a special cases related to
@ -96,17 +113,16 @@ func (o *Operation) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.
if err := opts.UnmarshalNext(dec, &x); err != nil {
return err
}
x.Extensions.sanitize()
if len(x.Extensions) == 0 {
x.Extensions = nil
}
o.VendorExtensible.Extensions = x.Extensions
o.Extensions = internal.SanitizeExtensions(x.Extensions)
o.OperationProps = OperationProps(x.OperationPropsNoMethods)
return nil
}
// MarshalJSON converts this items object to JSON
func (o Operation) MarshalJSON() ([]byte, error) {
if internal.UseOptimizedJSONMarshaling {
return internal.DeterministicMarshal(o)
}
b1, err := json.Marshal(o.OperationProps)
if err != nil {
return nil, err
@ -118,3 +134,13 @@ func (o Operation) MarshalJSON() ([]byte, error) {
concated := swag.ConcatJSON(b1, b2)
return concated, nil
}
func (o Operation) MarshalNextJSON(opts jsonv2.MarshalOptions, enc *jsonv2.Encoder) error {
var x struct {
Extensions
OperationProps operationPropsOmitZero `json:",inline"`
}
x.Extensions = internal.SanitizeExtensions(o.Extensions)
x.OperationProps = operationPropsOmitZero(o.OperationProps)
return opts.MarshalNext(enc, x)
}

View File

@ -36,6 +36,17 @@ type ParamProps struct {
AllowEmptyValue bool `json:"allowEmptyValue,omitempty"`
}
// Marshaling structure only, always edit along with corresponding
// struct (or compilation will fail).
type paramPropsOmitZero struct {
Description string `json:"description,omitempty"`
Name string `json:"name,omitempty"`
In string `json:"in,omitempty"`
Required bool `json:"required,omitzero"`
Schema *Schema `json:"schema,omitzero"`
AllowEmptyValue bool `json:"allowEmptyValue,omitzero"`
}
// Parameter a unique parameter is defined by a combination of a [name](#parameterName) and [location](#parameterIn).
//
// There are five possible parameter types.
@ -109,19 +120,18 @@ func (p *Parameter) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.
if err := p.Refable.Ref.fromMap(x.Extensions); err != nil {
return err
}
x.Extensions.sanitize()
if len(x.Extensions) == 0 {
x.Extensions = nil
}
p.CommonValidations = x.CommonValidations
p.SimpleSchema = x.SimpleSchema
p.VendorExtensible.Extensions = x.Extensions
p.Extensions = internal.SanitizeExtensions(x.Extensions)
p.ParamProps = x.ParamProps
return nil
}
// MarshalJSON converts this items object to JSON
func (p Parameter) MarshalJSON() ([]byte, error) {
if internal.UseOptimizedJSONMarshaling {
return internal.DeterministicMarshal(p)
}
b1, err := json.Marshal(p.CommonValidations)
if err != nil {
return nil, err
@ -144,3 +154,19 @@ func (p Parameter) MarshalJSON() ([]byte, error) {
}
return swag.ConcatJSON(b3, b1, b2, b4, b5), nil
}
func (p Parameter) MarshalNextJSON(opts jsonv2.MarshalOptions, enc *jsonv2.Encoder) error {
var x struct {
CommonValidations commonValidationsOmitZero `json:",inline"`
SimpleSchema simpleSchemaOmitZero `json:",inline"`
ParamProps paramPropsOmitZero `json:",inline"`
Ref string `json:"$ref,omitempty"`
Extensions
}
x.CommonValidations = commonValidationsOmitZero(p.CommonValidations)
x.SimpleSchema = simpleSchemaOmitZero(p.SimpleSchema)
x.Extensions = internal.SanitizeExtensions(p.Extensions)
x.ParamProps = paramPropsOmitZero(p.ParamProps)
x.Ref = p.Refable.Ref.String()
return opts.MarshalNext(enc, x)
}

View File

@ -70,24 +70,20 @@ func (p *PathItem) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.D
if err := opts.UnmarshalNext(dec, &x); err != nil {
return err
}
p.Extensions = x.Extensions
p.PathItemProps = x.PathItemProps
if err := p.Refable.Ref.fromMap(p.Extensions); err != nil {
if err := p.Refable.Ref.fromMap(x.Extensions); err != nil {
return err
}
p.Extensions.sanitize()
if len(p.Extensions) == 0 {
p.Extensions = nil
}
p.Extensions = internal.SanitizeExtensions(x.Extensions)
p.PathItemProps = x.PathItemProps
return nil
}
// MarshalJSON converts this items object to JSON
func (p PathItem) MarshalJSON() ([]byte, error) {
if internal.UseOptimizedJSONMarshaling {
return internal.DeterministicMarshal(p)
}
b3, err := json.Marshal(p.Refable)
if err != nil {
return nil, err
@ -103,3 +99,15 @@ func (p PathItem) MarshalJSON() ([]byte, error) {
concated := swag.ConcatJSON(b3, b4, b5)
return concated, nil
}
func (p PathItem) MarshalNextJSON(opts jsonv2.MarshalOptions, enc *jsonv2.Encoder) error {
var x struct {
Ref string `json:"$ref,omitempty"`
Extensions
PathItemProps
}
x.Ref = p.Refable.Ref.String()
x.Extensions = internal.SanitizeExtensions(p.Extensions)
x.PathItemProps = p.PathItemProps
return opts.MarshalNext(enc, x)
}

View File

@ -92,7 +92,7 @@ func (p *Paths) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Deco
}
switch k := tok.String(); {
case isExtensionKey(k):
case internal.IsExtensionKey(k):
ext = nil
if err := opts.UnmarshalNext(dec, &ext); err != nil {
return err
@ -114,7 +114,9 @@ func (p *Paths) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Deco
p.Paths[k] = pi
default:
_, err := dec.ReadValue() // skip value
return err
if err != nil {
return err
}
}
}
default:
@ -124,6 +126,9 @@ func (p *Paths) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Deco
// MarshalJSON converts this items object to JSON
func (p Paths) MarshalJSON() ([]byte, error) {
if internal.UseOptimizedJSONMarshaling {
return internal.DeterministicMarshal(p)
}
b1, err := json.Marshal(p.VendorExtensible)
if err != nil {
return nil, err
@ -142,3 +147,18 @@ func (p Paths) MarshalJSON() ([]byte, error) {
concated := swag.ConcatJSON(b1, b2)
return concated, nil
}
func (p Paths) MarshalNextJSON(opts jsonv2.MarshalOptions, enc *jsonv2.Encoder) error {
m := make(map[string]any, len(p.Extensions)+len(p.Paths))
for k, v := range p.Extensions {
if internal.IsExtensionKey(k) {
m[k] = v
}
}
for k, v := range p.Paths {
if strings.HasPrefix(k, "/") {
m[k] = v
}
}
return opts.MarshalNext(enc, m)
}

View File

@ -21,6 +21,8 @@ import (
"path/filepath"
"github.com/go-openapi/jsonreference"
"k8s.io/kube-openapi/pkg/internal"
)
// Refable is a struct for things that accept a $ref property
@ -149,19 +151,5 @@ func (r *Ref) UnmarshalJSON(d []byte) error {
}
func (r *Ref) fromMap(v map[string]interface{}) error {
if v == nil {
return nil
}
if vv, ok := v["$ref"]; ok {
if str, ok := vv.(string); ok {
ref, err := jsonreference.New(str)
if err != nil {
return err
}
*r = Ref{Ref: ref}
}
}
return nil
return internal.JSONRefFromMap(&r.Ref, v)
}

View File

@ -30,6 +30,15 @@ type ResponseProps struct {
Examples map[string]interface{} `json:"examples,omitempty"`
}
// Marshaling structure only, always edit along with corresponding
// struct (or compilation will fail).
type responsePropsOmitZero struct {
Description string `json:"description,omitempty"`
Schema *Schema `json:"schema,omitzero"`
Headers map[string]Header `json:"headers,omitempty"`
Examples map[string]interface{} `json:"examples,omitempty"`
}
// Response describes a single response from an API Operation.
//
// For more information: http://goo.gl/8us55a#responseObject
@ -68,23 +77,20 @@ func (r *Response) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.D
return err
}
r.Extensions = x.Extensions
r.ResponseProps = x.ResponseProps
if err := r.Refable.Ref.fromMap(r.Extensions); err != nil {
if err := r.Refable.Ref.fromMap(x.Extensions); err != nil {
return err
}
r.Extensions.sanitize()
if len(r.Extensions) == 0 {
r.Extensions = nil
}
r.Extensions = internal.SanitizeExtensions(x.Extensions)
r.ResponseProps = x.ResponseProps
return nil
}
// MarshalJSON converts this items object to JSON
func (r Response) MarshalJSON() ([]byte, error) {
if internal.UseOptimizedJSONMarshaling {
return internal.DeterministicMarshal(r)
}
b1, err := json.Marshal(r.ResponseProps)
if err != nil {
return nil, err
@ -100,6 +106,18 @@ func (r Response) MarshalJSON() ([]byte, error) {
return swag.ConcatJSON(b1, b2, b3), nil
}
func (r Response) MarshalNextJSON(opts jsonv2.MarshalOptions, enc *jsonv2.Encoder) error {
var x struct {
Ref string `json:"$ref,omitempty"`
Extensions
ResponseProps responsePropsOmitZero `json:",inline"`
}
x.Ref = r.Refable.Ref.String()
x.Extensions = internal.SanitizeExtensions(r.Extensions)
x.ResponseProps = responsePropsOmitZero(r.ResponseProps)
return opts.MarshalNext(enc, x)
}
// NewResponse creates a new response instance
func NewResponse() *Response {
return new(Response)

View File

@ -63,6 +63,9 @@ func (r *Responses) UnmarshalJSON(data []byte) error {
// MarshalJSON converts this items object to JSON
func (r Responses) MarshalJSON() ([]byte, error) {
if internal.UseOptimizedJSONMarshaling {
return internal.DeterministicMarshal(r)
}
b1, err := json.Marshal(r.ResponsesProps)
if err != nil {
return nil, err
@ -75,6 +78,25 @@ func (r Responses) MarshalJSON() ([]byte, error) {
return concated, nil
}
func (r Responses) MarshalNextJSON(opts jsonv2.MarshalOptions, enc *jsonv2.Encoder) error {
type ArbitraryKeys map[string]interface{}
var x struct {
ArbitraryKeys
Default *Response `json:"default,omitempty"`
}
x.ArbitraryKeys = make(map[string]any, len(r.Extensions)+len(r.StatusCodeResponses))
for k, v := range r.Extensions {
if internal.IsExtensionKey(k) {
x.ArbitraryKeys[k] = v
}
}
for k, v := range r.StatusCodeResponses {
x.ArbitraryKeys[strconv.Itoa(k)] = v
}
x.Default = r.Default
return opts.MarshalNext(enc, x)
}
// ResponsesProps describes all responses for an operation.
// It tells what is the default response and maps all responses with a
// HTTP status code.
@ -148,7 +170,7 @@ func (r *Responses) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.
return nil
}
switch k := tok.String(); {
case isExtensionKey(k):
case internal.IsExtensionKey(k):
ext = nil
if err := opts.UnmarshalNext(dec, &ext); err != nil {
return err

View File

@ -196,6 +196,46 @@ type SchemaProps struct {
Definitions Definitions `json:"definitions,omitempty"`
}
// Marshaling structure only, always edit along with corresponding
// struct (or compilation will fail).
type schemaPropsOmitZero struct {
ID string `json:"id,omitempty"`
Ref Ref `json:"-"`
Schema SchemaURL `json:"-"`
Description string `json:"description,omitempty"`
Type StringOrArray `json:"type,omitzero"`
Nullable bool `json:"nullable,omitzero"`
Format string `json:"format,omitempty"`
Title string `json:"title,omitempty"`
Default interface{} `json:"default,omitzero"`
Maximum *float64 `json:"maximum,omitempty"`
ExclusiveMaximum bool `json:"exclusiveMaximum,omitzero"`
Minimum *float64 `json:"minimum,omitempty"`
ExclusiveMinimum bool `json:"exclusiveMinimum,omitzero"`
MaxLength *int64 `json:"maxLength,omitempty"`
MinLength *int64 `json:"minLength,omitempty"`
Pattern string `json:"pattern,omitempty"`
MaxItems *int64 `json:"maxItems,omitempty"`
MinItems *int64 `json:"minItems,omitempty"`
UniqueItems bool `json:"uniqueItems,omitzero"`
MultipleOf *float64 `json:"multipleOf,omitempty"`
Enum []interface{} `json:"enum,omitempty"`
MaxProperties *int64 `json:"maxProperties,omitempty"`
MinProperties *int64 `json:"minProperties,omitempty"`
Required []string `json:"required,omitempty"`
Items *SchemaOrArray `json:"items,omitzero"`
AllOf []Schema `json:"allOf,omitempty"`
OneOf []Schema `json:"oneOf,omitempty"`
AnyOf []Schema `json:"anyOf,omitempty"`
Not *Schema `json:"not,omitzero"`
Properties map[string]Schema `json:"properties,omitempty"`
AdditionalProperties *SchemaOrBool `json:"additionalProperties,omitzero"`
PatternProperties map[string]Schema `json:"patternProperties,omitempty"`
Dependencies Dependencies `json:"dependencies,omitempty"`
AdditionalItems *SchemaOrBool `json:"additionalItems,omitzero"`
Definitions Definitions `json:"definitions,omitempty"`
}
// SwaggerSchemaProps are additional properties supported by swagger schemas, but not JSON-schema (draft 4)
type SwaggerSchemaProps struct {
Discriminator string `json:"discriminator,omitempty"`
@ -204,6 +244,15 @@ type SwaggerSchemaProps struct {
Example interface{} `json:"example,omitempty"`
}
// Marshaling structure only, always edit along with corresponding
// struct (or compilation will fail).
type swaggerSchemaPropsOmitZero struct {
Discriminator string `json:"discriminator,omitempty"`
ReadOnly bool `json:"readOnly,omitzero"`
ExternalDocs *ExternalDocumentation `json:"externalDocs,omitzero"`
Example interface{} `json:"example,omitempty"`
}
// Schema the schema object allows the definition of input and output data types.
// These types can be objects, but also primitives and arrays.
// This object is based on the [JSON Schema Specification Draft 4](http://json-schema.org/)
@ -434,6 +483,9 @@ func (s *Schema) WithExternalDocs(description, url string) *Schema {
// MarshalJSON marshal this to JSON
func (s Schema) MarshalJSON() ([]byte, error) {
if internal.UseOptimizedJSONMarshaling {
return internal.DeterministicMarshal(s)
}
b1, err := json.Marshal(s.SchemaProps)
if err != nil {
return nil, fmt.Errorf("schema props %v", err)
@ -465,6 +517,31 @@ func (s Schema) MarshalJSON() ([]byte, error) {
return swag.ConcatJSON(b1, b2, b3, b4, b5, b6), nil
}
func (s Schema) MarshalNextJSON(opts jsonv2.MarshalOptions, enc *jsonv2.Encoder) error {
type ArbitraryKeys map[string]interface{}
var x struct {
ArbitraryKeys
SchemaProps schemaPropsOmitZero `json:",inline"`
SwaggerSchemaProps swaggerSchemaPropsOmitZero `json:",inline"`
Schema string `json:"$schema,omitempty"`
Ref string `json:"$ref,omitempty"`
}
x.ArbitraryKeys = make(map[string]any, len(s.Extensions)+len(s.ExtraProps))
for k, v := range s.Extensions {
if internal.IsExtensionKey(k) {
x.ArbitraryKeys[k] = v
}
}
for k, v := range s.ExtraProps {
x.ArbitraryKeys[k] = v
}
x.SchemaProps = schemaPropsOmitZero(s.SchemaProps)
x.SwaggerSchemaProps = swaggerSchemaPropsOmitZero(s.SwaggerSchemaProps)
x.Ref = s.Ref.String()
x.Schema = string(s.Schema)
return opts.MarshalNext(enc, x)
}
// UnmarshalJSON marshal this from JSON
func (s *Schema) UnmarshalJSON(data []byte) error {
if internal.UseOptimizedJSONUnmarshaling {
@ -547,7 +624,7 @@ func (s *Schema) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Dec
}
s.ExtraProps = x.Extensions.sanitizeWithExtra()
s.VendorExtensible.Extensions = x.Extensions
s.Extensions = internal.SanitizeExtensions(x.Extensions)
s.SchemaProps = x.SchemaProps
s.SwaggerSchemaProps = x.SwaggerSchemaProps
return nil

View File

@ -18,6 +18,7 @@ import (
"encoding/json"
"github.com/go-openapi/swag"
"k8s.io/kube-openapi/pkg/internal"
jsonv2 "k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json"
)
@ -45,6 +46,9 @@ type SecurityScheme struct {
// MarshalJSON marshal this to JSON
func (s SecurityScheme) MarshalJSON() ([]byte, error) {
if internal.UseOptimizedJSONMarshaling {
return internal.DeterministicMarshal(s)
}
b1, err := json.Marshal(s.SecuritySchemeProps)
if err != nil {
return nil, err
@ -56,6 +60,16 @@ func (s SecurityScheme) MarshalJSON() ([]byte, error) {
return swag.ConcatJSON(b1, b2), nil
}
func (s SecurityScheme) MarshalNextJSON(opts jsonv2.MarshalOptions, enc *jsonv2.Encoder) error {
var x struct {
Extensions
SecuritySchemeProps
}
x.Extensions = internal.SanitizeExtensions(s.Extensions)
x.SecuritySchemeProps = s.SecuritySchemeProps
return opts.MarshalNext(enc, x)
}
// UnmarshalJSON marshal this from JSON
func (s *SecurityScheme) UnmarshalJSON(data []byte) error {
if err := json.Unmarshal(data, &s.SecuritySchemeProps); err != nil {
@ -72,11 +86,7 @@ func (s *SecurityScheme) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *js
if err := opts.UnmarshalNext(dec, &x); err != nil {
return err
}
x.Extensions.sanitize()
if len(x.Extensions) == 0 {
x.Extensions = nil
}
s.VendorExtensible.Extensions = x.Extensions
s.Extensions = internal.SanitizeExtensions(x.Extensions)
s.SecuritySchemeProps = x.SecuritySchemeProps
return nil
}

View File

@ -35,6 +35,9 @@ type Swagger struct {
// MarshalJSON marshals this swagger structure to json
func (s Swagger) MarshalJSON() ([]byte, error) {
if internal.UseOptimizedJSONMarshaling {
return internal.DeterministicMarshal(s)
}
b1, err := json.Marshal(s.SwaggerProps)
if err != nil {
return nil, err
@ -46,12 +49,22 @@ func (s Swagger) MarshalJSON() ([]byte, error) {
return swag.ConcatJSON(b1, b2), nil
}
// MarshalJSON marshals this swagger structure to json
func (s Swagger) MarshalNextJSON(opts jsonv2.MarshalOptions, enc *jsonv2.Encoder) error {
var x struct {
Extensions
SwaggerProps
}
x.Extensions = internal.SanitizeExtensions(s.Extensions)
x.SwaggerProps = s.SwaggerProps
return opts.MarshalNext(enc, x)
}
// UnmarshalJSON unmarshals a swagger spec from json
func (s *Swagger) UnmarshalJSON(data []byte) error {
if internal.UseOptimizedJSONUnmarshaling {
return jsonv2.Unmarshal(data, s)
}
var sw Swagger
if err := json.Unmarshal(data, &sw.SwaggerProps); err != nil {
return err
@ -75,15 +88,8 @@ func (s *Swagger) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.De
if err := opts.UnmarshalNext(dec, &x); err != nil {
return err
}
s.Extensions = x.Extensions
s.Extensions = internal.SanitizeExtensions(x.Extensions)
s.SwaggerProps = x.SwaggerProps
s.Extensions.sanitize()
if len(s.Extensions) == 0 {
s.Extensions = nil
}
return nil
}
@ -126,6 +132,9 @@ var jsFalse = []byte("false")
// MarshalJSON convert this object to JSON
func (s SchemaOrBool) MarshalJSON() ([]byte, error) {
if internal.UseOptimizedJSONMarshaling {
return internal.DeterministicMarshal(s)
}
if s.Schema != nil {
return json.Marshal(s.Schema)
}
@ -136,6 +145,18 @@ func (s SchemaOrBool) MarshalJSON() ([]byte, error) {
return jsTrue, nil
}
// MarshalJSON convert this object to JSON
func (s SchemaOrBool) MarshalNextJSON(opts jsonv2.MarshalOptions, enc *jsonv2.Encoder) error {
if s.Schema != nil {
return opts.MarshalNext(enc, s.Schema)
}
if s.Schema == nil && !s.Allows {
return enc.WriteToken(jsonv2.False)
}
return enc.WriteToken(jsonv2.True)
}
// UnmarshalJSON converts this bool or schema object from a JSON structure
func (s *SchemaOrBool) UnmarshalJSON(data []byte) error {
if internal.UseOptimizedJSONUnmarshaling {
@ -143,15 +164,15 @@ func (s *SchemaOrBool) UnmarshalJSON(data []byte) error {
}
var nw SchemaOrBool
if len(data) >= 4 {
if data[0] == '{' {
var sch Schema
if err := json.Unmarshal(data, &sch); err != nil {
return err
}
nw.Schema = &sch
if len(data) > 0 && data[0] == '{' {
var sch Schema
if err := json.Unmarshal(data, &sch); err != nil {
return err
}
nw.Allows = !(data[0] == 'f' && data[1] == 'a' && data[2] == 'l' && data[3] == 's' && data[4] == 'e')
nw.Schema = &sch
nw.Allows = true
} else {
json.Unmarshal(data, &nw.Allows)
}
*s = nw
return nil
@ -185,6 +206,9 @@ type SchemaOrStringArray struct {
// MarshalJSON converts this schema object or array into JSON structure
func (s SchemaOrStringArray) MarshalJSON() ([]byte, error) {
if internal.UseOptimizedJSONMarshaling {
return internal.DeterministicMarshal(s)
}
if len(s.Property) > 0 {
return json.Marshal(s.Property)
}
@ -194,6 +218,17 @@ func (s SchemaOrStringArray) MarshalJSON() ([]byte, error) {
return []byte("null"), nil
}
// MarshalJSON converts this schema object or array into JSON structure
func (s SchemaOrStringArray) MarshalNextJSON(opts jsonv2.MarshalOptions, enc *jsonv2.Encoder) error {
if len(s.Property) > 0 {
return opts.MarshalNext(enc, s.Property)
}
if s.Schema != nil {
return opts.MarshalNext(enc, s.Schema)
}
return enc.WriteToken(jsonv2.Null)
}
// UnmarshalJSON converts this schema object or array from a JSON structure
func (s *SchemaOrStringArray) UnmarshalJSON(data []byte) error {
if internal.UseOptimizedJSONUnmarshaling {
@ -347,12 +382,23 @@ func (s *SchemaOrArray) ContainsType(name string) bool {
// MarshalJSON converts this schema object or array into JSON structure
func (s SchemaOrArray) MarshalJSON() ([]byte, error) {
if len(s.Schemas) > 0 {
if internal.UseOptimizedJSONMarshaling {
return internal.DeterministicMarshal(s)
}
if s.Schemas != nil {
return json.Marshal(s.Schemas)
}
return json.Marshal(s.Schema)
}
// MarshalJSON converts this schema object or array into JSON structure
func (s SchemaOrArray) MarshalNextJSON(opts jsonv2.MarshalOptions, enc *jsonv2.Encoder) error {
if s.Schemas != nil {
return opts.MarshalNext(enc, s.Schemas)
}
return opts.MarshalNext(enc, s.Schema)
}
// UnmarshalJSON converts this schema object or array from a JSON structure
func (s *SchemaOrArray) UnmarshalJSON(data []byte) error {
if internal.UseOptimizedJSONUnmarshaling {

View File

@ -41,6 +41,9 @@ type Tag struct {
// MarshalJSON marshal this to JSON
func (t Tag) MarshalJSON() ([]byte, error) {
if internal.UseOptimizedJSONMarshaling {
return internal.DeterministicMarshal(t)
}
b1, err := json.Marshal(t.TagProps)
if err != nil {
return nil, err
@ -52,6 +55,16 @@ func (t Tag) MarshalJSON() ([]byte, error) {
return swag.ConcatJSON(b1, b2), nil
}
func (t Tag) MarshalNextJSON(opts jsonv2.MarshalOptions, enc *jsonv2.Encoder) error {
var x struct {
Extensions
TagProps
}
x.Extensions = internal.SanitizeExtensions(t.Extensions)
x.TagProps = t.TagProps
return opts.MarshalNext(enc, x)
}
// UnmarshalJSON marshal this from JSON
func (t *Tag) UnmarshalJSON(data []byte) error {
if internal.UseOptimizedJSONUnmarshaling {
@ -72,11 +85,7 @@ func (t *Tag) UnmarshalNextJSON(opts jsonv2.UnmarshalOptions, dec *jsonv2.Decode
if err := opts.UnmarshalNext(dec, &x); err != nil {
return err
}
x.Extensions.sanitize()
if len(x.Extensions) == 0 {
x.Extensions = nil
}
t.VendorExtensible.Extensions = x.Extensions
t.Extensions = internal.SanitizeExtensions(x.Extensions)
t.TagProps = x.TagProps
return nil
}

201
vendor/k8s.io/metrics/LICENSE generated vendored Normal file
View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
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.

View File

@ -0,0 +1,46 @@
/*
Copyright 2020 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 custom_metrics
import (
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/conversion"
)
func Convert_v1_ObjectReference_To_custom_metrics_ObjectReference(in *v1.ObjectReference, out *ObjectReference, s conversion.Scope) error {
out.APIVersion = in.APIVersion
out.Kind = in.Kind
out.Namespace = in.Namespace
out.Name = in.Name
out.UID = in.UID
out.ResourceVersion = in.ResourceVersion
out.FieldPath = in.FieldPath
return nil
}
func Convert_custom_metrics_ObjectReference_To_v1_ObjectReference(in *ObjectReference, out *v1.ObjectReference, s conversion.Scope) error {
out.APIVersion = in.APIVersion
out.Kind = in.Kind
out.Namespace = in.Namespace
out.Name = in.Name
out.UID = in.UID
out.ResourceVersion = in.ResourceVersion
out.FieldPath = in.FieldPath
return nil
}

21
vendor/k8s.io/metrics/pkg/apis/custom_metrics/doc.go generated vendored Normal file
View File

@ -0,0 +1,21 @@
/*
Copyright 2017 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.
*/
// +k8s:deepcopy-gen=package
// +groupName=custom.metrics.k8s.io
// Package custom_metrics defines an API for using custom metrics.
package custom_metrics // import "k8s.io/metrics/pkg/apis/custom_metrics"

View File

@ -0,0 +1,35 @@
/*
Copyright 2017 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 install installs the experimental API group, making it available as
// an option to all of the API encoding/decoding machinery.
package install
import (
"k8s.io/apimachinery/pkg/runtime"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/metrics/pkg/apis/custom_metrics"
"k8s.io/metrics/pkg/apis/custom_metrics/v1beta1"
"k8s.io/metrics/pkg/apis/custom_metrics/v1beta2"
)
// Install registers the API group and adds types to a scheme
func Install(scheme *runtime.Scheme) {
utilruntime.Must(custom_metrics.AddToScheme(scheme))
utilruntime.Must(v1beta2.AddToScheme(scheme))
utilruntime.Must(v1beta1.AddToScheme(scheme))
utilruntime.Must(scheme.SetVersionPriority(v1beta1.SchemeGroupVersion, v1beta2.SchemeGroupVersion))
}

View File

@ -0,0 +1,52 @@
/*
Copyright 2017 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 custom_metrics
import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
// GroupName is the group name use in this package
const GroupName = "custom.metrics.k8s.io"
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal}
// Kind takes an unqualified kind and returns back a Group qualified GroupKind
func Kind(kind string) schema.GroupKind {
return SchemeGroupVersion.WithKind(kind).GroupKind()
}
// Resource takes an unqualified resource and returns back a Group qualified GroupResource
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
var (
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
AddToScheme = SchemeBuilder.AddToScheme
)
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&MetricValue{},
&MetricValueList{},
&MetricListOptions{},
)
return nil
}

106
vendor/k8s.io/metrics/pkg/apis/custom_metrics/types.go generated vendored Normal file
View File

@ -0,0 +1,106 @@
/*
Copyright 2017 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 custom_metrics
import (
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
)
type MetricIdentifier struct {
// name is the name of the given metric
Name string
// selector represents the label selector that could be used to select
// this metric, and will generally just be the selector passed in to
// the query used to fetch this metric.
// +optional
Selector *metav1.LabelSelector
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// a list of values for a given metric for some set of objects
type MetricValueList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
// the value of the metric across the described objects
Items []MetricValue `json:"items"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// a metric value for some object
type MetricValue struct {
metav1.TypeMeta
// a reference to the described object
DescribedObject ObjectReference
Metric MetricIdentifier
// indicates the time at which the metrics were produced
Timestamp metav1.Time
// indicates the window ([Timestamp-Window, Timestamp]) from
// which these metrics were calculated, when returning rate
// metrics calculated from cumulative metrics (or zero for
// non-calculated instantaneous metrics).
WindowSeconds *int64
// the value of the metric for this
Value resource.Quantity
}
// allObjects is a wildcard used to select metrics
// for all objects matching the given label selector
const AllObjects = "*"
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// MetricListOptions is used to select metrics by their label selectors
type MetricListOptions struct {
metav1.TypeMeta
// A selector to restrict the list of returned objects by their labels.
// Defaults to everything.
// +optional
LabelSelector string
// A selector to restrict the list of returned metrics by their labels
// +optional
MetricLabelSelector string
}
// NOTE: ObjectReference is copied from k8s.io/kubernetes/pkg/api/types.go. We
// cannot depend on k8s.io/kubernetes/pkg/api because that creates cyclic
// dependency between k8s.io/metrics and k8s.io/kubernetes. We cannot depend on
// k8s.io/client-go/pkg/api because the package is going to be deprecated soon.
// There is no need to keep it an exact copy. Each repo can define its own
// internal objects.
// ObjectReference contains enough information to let you inspect or modify the referred object.
type ObjectReference struct {
Kind string
Namespace string
Name string
UID types.UID
APIVersion string
ResourceVersion string
FieldPath string
}

View File

@ -0,0 +1,40 @@
/*
Copyright 2018 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/apimachinery/pkg/conversion"
"k8s.io/metrics/pkg/apis/custom_metrics"
)
func Convert_v1beta1_MetricValue_To_custom_metrics_MetricValue(in *MetricValue, out *custom_metrics.MetricValue, s conversion.Scope) error {
if err := autoConvert_v1beta1_MetricValue_To_custom_metrics_MetricValue(in, out, s); err != nil {
return err
}
out.Metric.Name = in.MetricName
out.Metric.Selector = in.Selector
return nil
}
func Convert_custom_metrics_MetricValue_To_v1beta1_MetricValue(in *custom_metrics.MetricValue, out *MetricValue, s conversion.Scope) error {
if err := autoConvert_custom_metrics_MetricValue_To_v1beta1_MetricValue(in, out, s); err != nil {
return err
}
out.MetricName = in.Metric.Name
out.Selector = in.Metric.Selector
return nil
}

View File

@ -0,0 +1,23 @@
/*
Copyright 2017 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.
*/
// +k8s:deepcopy-gen=package
// +k8s:protobuf-gen=package
// +k8s:conversion-gen=k8s.io/metrics/pkg/apis/custom_metrics
// +k8s:openapi-gen=true
// Package v1beta1 is the v1beta1 version of the custom_metrics API.
package v1beta1 // import "k8s.io/metrics/pkg/apis/custom_metrics/v1beta1"

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,80 @@
/*
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.
*/
// This file was autogenerated by go-to-protobuf. Do not edit it manually!
syntax = "proto2";
package k8s.io.metrics.pkg.apis.custom_metrics.v1beta1;
import "k8s.io/api/core/v1/generated.proto";
import "k8s.io/apimachinery/pkg/api/resource/generated.proto";
import "k8s.io/apimachinery/pkg/apis/meta/v1/generated.proto";
import "k8s.io/apimachinery/pkg/runtime/generated.proto";
import "k8s.io/apimachinery/pkg/runtime/schema/generated.proto";
// Package-wide variables from generator "generated".
option go_package = "k8s.io/metrics/pkg/apis/custom_metrics/v1beta1";
// MetricListOptions is used to select metrics by their label selectors
message MetricListOptions {
// A selector to restrict the list of returned objects by their labels.
// Defaults to everything.
// +optional
optional string labelSelector = 1;
// A selector to restrict the list of returned metrics by their labels
// +optional
optional string metricLabelSelector = 2;
}
// MetricValue is a metric value for some object
message MetricValue {
// a reference to the described object
optional k8s.io.api.core.v1.ObjectReference describedObject = 1;
// the name of the metric
optional string metricName = 2;
// indicates the time at which the metrics were produced
optional k8s.io.apimachinery.pkg.apis.meta.v1.Time timestamp = 3;
// indicates the window ([Timestamp-Window, Timestamp]) from
// which these metrics were calculated, when returning rate
// metrics calculated from cumulative metrics (or zero for
// non-calculated instantaneous metrics).
optional int64 window = 4;
// the value of the metric for this
optional k8s.io.apimachinery.pkg.api.resource.Quantity value = 5;
// selector represents the label selector that could be used to select
// this metric, and will generally just be the selector passed in to
// the query used to fetch this metric.
// When left blank, only the metric's Name will be used to gather metrics.
// +optional
optional k8s.io.apimachinery.pkg.apis.meta.v1.LabelSelector selector = 6;
}
// MetricValueList is a list of values for a given metric for some set of objects
message MetricValueList {
optional k8s.io.apimachinery.pkg.apis.meta.v1.ListMeta metadata = 1;
// the value of the metric across the described objects
repeated MetricValue items = 2;
}

View File

@ -0,0 +1,52 @@
/*
Copyright 2017 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 (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
// GroupName is the group name use in this package
const GroupName = "custom.metrics.k8s.io"
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1beta1"}
// Resource takes an unqualified resource and returns a Group qualified GroupResource
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
var (
// SchemeBuilder points to a list of functions added to Scheme.
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
localSchemeBuilder = &SchemeBuilder
// AddToScheme applies all the stored functions to the scheme.
AddToScheme = localSchemeBuilder.AddToScheme
)
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&MetricValue{},
&MetricValueList{},
&MetricListOptions{},
)
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil
}

View File

@ -0,0 +1,86 @@
/*
Copyright 2017 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/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// MetricValueList is a list of values for a given metric for some set of objects
type MetricValueList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
// the value of the metric across the described objects
Items []MetricValue `json:"items" protobuf:"bytes,2,rep,name=items"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// MetricValue is a metric value for some object
type MetricValue struct {
metav1.TypeMeta `json:",inline"`
// a reference to the described object
DescribedObject v1.ObjectReference `json:"describedObject" protobuf:"bytes,1,name=describedObject"`
// the name of the metric
MetricName string `json:"metricName" protobuf:"bytes,2,name=metricName"`
// indicates the time at which the metrics were produced
Timestamp metav1.Time `json:"timestamp" protobuf:"bytes,3,name=timestamp"`
// indicates the window ([Timestamp-Window, Timestamp]) from
// which these metrics were calculated, when returning rate
// metrics calculated from cumulative metrics (or zero for
// non-calculated instantaneous metrics).
WindowSeconds *int64 `json:"window,omitempty" protobuf:"bytes,4,opt,name=window"`
// the value of the metric for this
Value resource.Quantity `json:"value" protobuf:"bytes,5,name=value"`
// selector represents the label selector that could be used to select
// this metric, and will generally just be the selector passed in to
// the query used to fetch this metric.
// When left blank, only the metric's Name will be used to gather metrics.
// +optional
Selector *metav1.LabelSelector `json:"selector" protobuf:"bytes,6,opt,name=selector"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// MetricListOptions is used to select metrics by their label selectors
type MetricListOptions struct {
metav1.TypeMeta `json:",inline"`
// A selector to restrict the list of returned objects by their labels.
// Defaults to everything.
// +optional
LabelSelector string `json:"labelSelector,omitempty" protobuf:"bytes,1,opt,name=labelSelector"`
// A selector to restrict the list of returned metrics by their labels
// +optional
MetricLabelSelector string `json:"metricLabelSelector,omitempty" protobuf:"bytes,2,opt,name=metricLabelSelector"`
}
// AllObjects is a wildcard used to select metrics
// for all objects matching the given label selector
const AllObjects = "*"

View File

@ -0,0 +1,157 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
/*
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 conversion-gen. DO NOT EDIT.
package v1beta1
import (
unsafe "unsafe"
conversion "k8s.io/apimachinery/pkg/conversion"
runtime "k8s.io/apimachinery/pkg/runtime"
custommetrics "k8s.io/metrics/pkg/apis/custom_metrics"
)
func init() {
localSchemeBuilder.Register(RegisterConversions)
}
// RegisterConversions adds conversion functions to the given scheme.
// Public to allow building arbitrary schemes.
func RegisterConversions(s *runtime.Scheme) error {
if err := s.AddGeneratedConversionFunc((*MetricListOptions)(nil), (*custommetrics.MetricListOptions)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1beta1_MetricListOptions_To_custom_metrics_MetricListOptions(a.(*MetricListOptions), b.(*custommetrics.MetricListOptions), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*custommetrics.MetricListOptions)(nil), (*MetricListOptions)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_custom_metrics_MetricListOptions_To_v1beta1_MetricListOptions(a.(*custommetrics.MetricListOptions), b.(*MetricListOptions), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*MetricValueList)(nil), (*custommetrics.MetricValueList)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1beta1_MetricValueList_To_custom_metrics_MetricValueList(a.(*MetricValueList), b.(*custommetrics.MetricValueList), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*custommetrics.MetricValueList)(nil), (*MetricValueList)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_custom_metrics_MetricValueList_To_v1beta1_MetricValueList(a.(*custommetrics.MetricValueList), b.(*MetricValueList), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*custommetrics.MetricValue)(nil), (*MetricValue)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_custom_metrics_MetricValue_To_v1beta1_MetricValue(a.(*custommetrics.MetricValue), b.(*MetricValue), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*MetricValue)(nil), (*custommetrics.MetricValue)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1beta1_MetricValue_To_custom_metrics_MetricValue(a.(*MetricValue), b.(*custommetrics.MetricValue), scope)
}); err != nil {
return err
}
return nil
}
func autoConvert_v1beta1_MetricListOptions_To_custom_metrics_MetricListOptions(in *MetricListOptions, out *custommetrics.MetricListOptions, s conversion.Scope) error {
out.LabelSelector = in.LabelSelector
out.MetricLabelSelector = in.MetricLabelSelector
return nil
}
// Convert_v1beta1_MetricListOptions_To_custom_metrics_MetricListOptions is an autogenerated conversion function.
func Convert_v1beta1_MetricListOptions_To_custom_metrics_MetricListOptions(in *MetricListOptions, out *custommetrics.MetricListOptions, s conversion.Scope) error {
return autoConvert_v1beta1_MetricListOptions_To_custom_metrics_MetricListOptions(in, out, s)
}
func autoConvert_custom_metrics_MetricListOptions_To_v1beta1_MetricListOptions(in *custommetrics.MetricListOptions, out *MetricListOptions, s conversion.Scope) error {
out.LabelSelector = in.LabelSelector
out.MetricLabelSelector = in.MetricLabelSelector
return nil
}
// Convert_custom_metrics_MetricListOptions_To_v1beta1_MetricListOptions is an autogenerated conversion function.
func Convert_custom_metrics_MetricListOptions_To_v1beta1_MetricListOptions(in *custommetrics.MetricListOptions, out *MetricListOptions, s conversion.Scope) error {
return autoConvert_custom_metrics_MetricListOptions_To_v1beta1_MetricListOptions(in, out, s)
}
func autoConvert_v1beta1_MetricValue_To_custom_metrics_MetricValue(in *MetricValue, out *custommetrics.MetricValue, s conversion.Scope) error {
if err := custommetrics.Convert_v1_ObjectReference_To_custom_metrics_ObjectReference(&in.DescribedObject, &out.DescribedObject, s); err != nil {
return err
}
// WARNING: in.MetricName requires manual conversion: does not exist in peer-type
out.Timestamp = in.Timestamp
out.WindowSeconds = (*int64)(unsafe.Pointer(in.WindowSeconds))
out.Value = in.Value
// WARNING: in.Selector requires manual conversion: does not exist in peer-type
return nil
}
func autoConvert_custom_metrics_MetricValue_To_v1beta1_MetricValue(in *custommetrics.MetricValue, out *MetricValue, s conversion.Scope) error {
if err := custommetrics.Convert_custom_metrics_ObjectReference_To_v1_ObjectReference(&in.DescribedObject, &out.DescribedObject, s); err != nil {
return err
}
// WARNING: in.Metric requires manual conversion: does not exist in peer-type
out.Timestamp = in.Timestamp
out.WindowSeconds = (*int64)(unsafe.Pointer(in.WindowSeconds))
out.Value = in.Value
return nil
}
func autoConvert_v1beta1_MetricValueList_To_custom_metrics_MetricValueList(in *MetricValueList, out *custommetrics.MetricValueList, s conversion.Scope) error {
out.ListMeta = in.ListMeta
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]custommetrics.MetricValue, len(*in))
for i := range *in {
if err := Convert_v1beta1_MetricValue_To_custom_metrics_MetricValue(&(*in)[i], &(*out)[i], s); err != nil {
return err
}
}
} else {
out.Items = nil
}
return nil
}
// Convert_v1beta1_MetricValueList_To_custom_metrics_MetricValueList is an autogenerated conversion function.
func Convert_v1beta1_MetricValueList_To_custom_metrics_MetricValueList(in *MetricValueList, out *custommetrics.MetricValueList, s conversion.Scope) error {
return autoConvert_v1beta1_MetricValueList_To_custom_metrics_MetricValueList(in, out, s)
}
func autoConvert_custom_metrics_MetricValueList_To_v1beta1_MetricValueList(in *custommetrics.MetricValueList, out *MetricValueList, s conversion.Scope) error {
out.ListMeta = in.ListMeta
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]MetricValue, len(*in))
for i := range *in {
if err := Convert_custom_metrics_MetricValue_To_v1beta1_MetricValue(&(*in)[i], &(*out)[i], s); err != nil {
return err
}
}
} else {
out.Items = nil
}
return nil
}
// Convert_custom_metrics_MetricValueList_To_v1beta1_MetricValueList is an autogenerated conversion function.
func Convert_custom_metrics_MetricValueList_To_v1beta1_MetricValueList(in *custommetrics.MetricValueList, out *MetricValueList, s conversion.Scope) error {
return autoConvert_custom_metrics_MetricValueList_To_v1beta1_MetricValueList(in, out, s)
}

View File

@ -0,0 +1,123 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
/*
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 deepcopy-gen. DO NOT EDIT.
package v1beta1
import (
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *MetricListOptions) DeepCopyInto(out *MetricListOptions) {
*out = *in
out.TypeMeta = in.TypeMeta
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MetricListOptions.
func (in *MetricListOptions) DeepCopy() *MetricListOptions {
if in == nil {
return nil
}
out := new(MetricListOptions)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *MetricListOptions) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *MetricValue) DeepCopyInto(out *MetricValue) {
*out = *in
out.TypeMeta = in.TypeMeta
out.DescribedObject = in.DescribedObject
in.Timestamp.DeepCopyInto(&out.Timestamp)
if in.WindowSeconds != nil {
in, out := &in.WindowSeconds, &out.WindowSeconds
*out = new(int64)
**out = **in
}
out.Value = in.Value.DeepCopy()
if in.Selector != nil {
in, out := &in.Selector, &out.Selector
*out = new(v1.LabelSelector)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MetricValue.
func (in *MetricValue) DeepCopy() *MetricValue {
if in == nil {
return nil
}
out := new(MetricValue)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *MetricValue) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *MetricValueList) DeepCopyInto(out *MetricValueList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]MetricValue, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MetricValueList.
func (in *MetricValueList) DeepCopy() *MetricValueList {
if in == nil {
return nil
}
out := new(MetricValueList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *MetricValueList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}

View File

@ -0,0 +1,23 @@
/*
Copyright 2018 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.
*/
// +k8s:deepcopy-gen=package
// +k8s:protobuf-gen=package
// +k8s:conversion-gen=k8s.io/metrics/pkg/apis/custom_metrics
// +k8s:openapi-gen=true
// Package v1beta2 is the v1beta2 version of the custom_metrics API.
package v1beta2 // import "k8s.io/metrics/pkg/apis/custom_metrics/v1beta2"

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,85 @@
/*
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.
*/
// This file was autogenerated by go-to-protobuf. Do not edit it manually!
syntax = "proto2";
package k8s.io.metrics.pkg.apis.custom_metrics.v1beta2;
import "k8s.io/api/core/v1/generated.proto";
import "k8s.io/apimachinery/pkg/api/resource/generated.proto";
import "k8s.io/apimachinery/pkg/apis/meta/v1/generated.proto";
import "k8s.io/apimachinery/pkg/runtime/generated.proto";
import "k8s.io/apimachinery/pkg/runtime/schema/generated.proto";
// Package-wide variables from generator "generated".
option go_package = "k8s.io/metrics/pkg/apis/custom_metrics/v1beta2";
// MetricIdentifier identifies a metric by name and, optionally, selector
message MetricIdentifier {
// name is the name of the given metric
optional string name = 1;
// selector represents the label selector that could be used to select
// this metric, and will generally just be the selector passed in to
// the query used to fetch this metric.
// When left blank, only the metric's Name will be used to gather metrics.
// +optional
optional k8s.io.apimachinery.pkg.apis.meta.v1.LabelSelector selector = 2;
}
// MetricListOptions is used to select metrics by their label selectors
message MetricListOptions {
// A selector to restrict the list of returned objects by their labels.
// Defaults to everything.
// +optional
optional string labelSelector = 1;
// A selector to restrict the list of returned metrics by their labels
// +optional
optional string metricLabelSelector = 2;
}
// MetricValue is the metric value for some object
message MetricValue {
// a reference to the described object
optional k8s.io.api.core.v1.ObjectReference describedObject = 1;
optional MetricIdentifier metric = 2;
// indicates the time at which the metrics were produced
optional k8s.io.apimachinery.pkg.apis.meta.v1.Time timestamp = 3;
// indicates the window ([Timestamp-Window, Timestamp]) from
// which these metrics were calculated, when returning rate
// metrics calculated from cumulative metrics (or zero for
// non-calculated instantaneous metrics).
optional int64 windowSeconds = 4;
// the value of the metric for this
optional k8s.io.apimachinery.pkg.api.resource.Quantity value = 5;
}
// MetricValueList is a list of values for a given metric for some set of objects
message MetricValueList {
optional k8s.io.apimachinery.pkg.apis.meta.v1.ListMeta metadata = 1;
// the value of the metric across the described objects
repeated MetricValue items = 2;
}

View File

@ -0,0 +1,52 @@
/*
Copyright 2018 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 v1beta2
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
// GroupName is the group name use in this package
const GroupName = "custom.metrics.k8s.io"
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1beta2"}
// Resource takes an unqualified resource and returns a Group qualified GroupResource
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
var (
// SchemeBuilder points to a list of functions added to Scheme.
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
localSchemeBuilder = &SchemeBuilder
// AddToScheme applies all the stored functions to the scheme.
AddToScheme = localSchemeBuilder.AddToScheme
)
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&MetricValue{},
&MetricValueList{},
&MetricListOptions{},
)
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil
}

View File

@ -0,0 +1,90 @@
/*
Copyright 2018 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 v1beta2
import (
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// MetricIdentifier identifies a metric by name and, optionally, selector
type MetricIdentifier struct {
// name is the name of the given metric
Name string `json:"name" protobuf:"bytes,1,opt,name=name"`
// selector represents the label selector that could be used to select
// this metric, and will generally just be the selector passed in to
// the query used to fetch this metric.
// When left blank, only the metric's Name will be used to gather metrics.
// +optional
Selector *metav1.LabelSelector `json:"selector" protobuf:"bytes,2,opt,name=selector"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// MetricValueList is a list of values for a given metric for some set of objects
type MetricValueList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
// the value of the metric across the described objects
Items []MetricValue `json:"items" protobuf:"bytes,2,rep,name=items"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// MetricValue is the metric value for some object
type MetricValue struct {
metav1.TypeMeta `json:",inline"`
// a reference to the described object
DescribedObject v1.ObjectReference `json:"describedObject" protobuf:"bytes,1,name=describedObject"`
Metric MetricIdentifier `json:"metric" protobuf:"bytes,2,name=metric"`
// indicates the time at which the metrics were produced
Timestamp metav1.Time `json:"timestamp" protobuf:"bytes,3,name=timestamp"`
// indicates the window ([Timestamp-Window, Timestamp]) from
// which these metrics were calculated, when returning rate
// metrics calculated from cumulative metrics (or zero for
// non-calculated instantaneous metrics).
WindowSeconds *int64 `json:"windowSeconds,omitempty" protobuf:"bytes,4,opt,name=windowSeconds"`
// the value of the metric for this
Value resource.Quantity `json:"value" protobuf:"bytes,5,name=value"`
}
// AllObjects is a wildcard used to select metrics
// for all objects matching the given label selector
const AllObjects = "*"
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// MetricListOptions is used to select metrics by their label selectors
type MetricListOptions struct {
metav1.TypeMeta `json:",inline"`
// A selector to restrict the list of returned objects by their labels.
// Defaults to everything.
// +optional
LabelSelector string `json:"labelSelector,omitempty" protobuf:"bytes,1,opt,name=labelSelector"`
// A selector to restrict the list of returned metrics by their labels
// +optional
MetricLabelSelector string `json:"metricLabelSelector,omitempty" protobuf:"bytes,2,opt,name=metricLabelSelector"`
}

View File

@ -0,0 +1,203 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
/*
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 conversion-gen. DO NOT EDIT.
package v1beta2
import (
unsafe "unsafe"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
conversion "k8s.io/apimachinery/pkg/conversion"
runtime "k8s.io/apimachinery/pkg/runtime"
custommetrics "k8s.io/metrics/pkg/apis/custom_metrics"
)
func init() {
localSchemeBuilder.Register(RegisterConversions)
}
// RegisterConversions adds conversion functions to the given scheme.
// Public to allow building arbitrary schemes.
func RegisterConversions(s *runtime.Scheme) error {
if err := s.AddGeneratedConversionFunc((*MetricIdentifier)(nil), (*custommetrics.MetricIdentifier)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1beta2_MetricIdentifier_To_custom_metrics_MetricIdentifier(a.(*MetricIdentifier), b.(*custommetrics.MetricIdentifier), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*custommetrics.MetricIdentifier)(nil), (*MetricIdentifier)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_custom_metrics_MetricIdentifier_To_v1beta2_MetricIdentifier(a.(*custommetrics.MetricIdentifier), b.(*MetricIdentifier), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*MetricListOptions)(nil), (*custommetrics.MetricListOptions)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1beta2_MetricListOptions_To_custom_metrics_MetricListOptions(a.(*MetricListOptions), b.(*custommetrics.MetricListOptions), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*custommetrics.MetricListOptions)(nil), (*MetricListOptions)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_custom_metrics_MetricListOptions_To_v1beta2_MetricListOptions(a.(*custommetrics.MetricListOptions), b.(*MetricListOptions), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*MetricValue)(nil), (*custommetrics.MetricValue)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1beta2_MetricValue_To_custom_metrics_MetricValue(a.(*MetricValue), b.(*custommetrics.MetricValue), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*custommetrics.MetricValue)(nil), (*MetricValue)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_custom_metrics_MetricValue_To_v1beta2_MetricValue(a.(*custommetrics.MetricValue), b.(*MetricValue), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*MetricValueList)(nil), (*custommetrics.MetricValueList)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1beta2_MetricValueList_To_custom_metrics_MetricValueList(a.(*MetricValueList), b.(*custommetrics.MetricValueList), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*custommetrics.MetricValueList)(nil), (*MetricValueList)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_custom_metrics_MetricValueList_To_v1beta2_MetricValueList(a.(*custommetrics.MetricValueList), b.(*MetricValueList), scope)
}); err != nil {
return err
}
return nil
}
func autoConvert_v1beta2_MetricIdentifier_To_custom_metrics_MetricIdentifier(in *MetricIdentifier, out *custommetrics.MetricIdentifier, s conversion.Scope) error {
out.Name = in.Name
out.Selector = (*v1.LabelSelector)(unsafe.Pointer(in.Selector))
return nil
}
// Convert_v1beta2_MetricIdentifier_To_custom_metrics_MetricIdentifier is an autogenerated conversion function.
func Convert_v1beta2_MetricIdentifier_To_custom_metrics_MetricIdentifier(in *MetricIdentifier, out *custommetrics.MetricIdentifier, s conversion.Scope) error {
return autoConvert_v1beta2_MetricIdentifier_To_custom_metrics_MetricIdentifier(in, out, s)
}
func autoConvert_custom_metrics_MetricIdentifier_To_v1beta2_MetricIdentifier(in *custommetrics.MetricIdentifier, out *MetricIdentifier, s conversion.Scope) error {
out.Name = in.Name
out.Selector = (*v1.LabelSelector)(unsafe.Pointer(in.Selector))
return nil
}
// Convert_custom_metrics_MetricIdentifier_To_v1beta2_MetricIdentifier is an autogenerated conversion function.
func Convert_custom_metrics_MetricIdentifier_To_v1beta2_MetricIdentifier(in *custommetrics.MetricIdentifier, out *MetricIdentifier, s conversion.Scope) error {
return autoConvert_custom_metrics_MetricIdentifier_To_v1beta2_MetricIdentifier(in, out, s)
}
func autoConvert_v1beta2_MetricListOptions_To_custom_metrics_MetricListOptions(in *MetricListOptions, out *custommetrics.MetricListOptions, s conversion.Scope) error {
out.LabelSelector = in.LabelSelector
out.MetricLabelSelector = in.MetricLabelSelector
return nil
}
// Convert_v1beta2_MetricListOptions_To_custom_metrics_MetricListOptions is an autogenerated conversion function.
func Convert_v1beta2_MetricListOptions_To_custom_metrics_MetricListOptions(in *MetricListOptions, out *custommetrics.MetricListOptions, s conversion.Scope) error {
return autoConvert_v1beta2_MetricListOptions_To_custom_metrics_MetricListOptions(in, out, s)
}
func autoConvert_custom_metrics_MetricListOptions_To_v1beta2_MetricListOptions(in *custommetrics.MetricListOptions, out *MetricListOptions, s conversion.Scope) error {
out.LabelSelector = in.LabelSelector
out.MetricLabelSelector = in.MetricLabelSelector
return nil
}
// Convert_custom_metrics_MetricListOptions_To_v1beta2_MetricListOptions is an autogenerated conversion function.
func Convert_custom_metrics_MetricListOptions_To_v1beta2_MetricListOptions(in *custommetrics.MetricListOptions, out *MetricListOptions, s conversion.Scope) error {
return autoConvert_custom_metrics_MetricListOptions_To_v1beta2_MetricListOptions(in, out, s)
}
func autoConvert_v1beta2_MetricValue_To_custom_metrics_MetricValue(in *MetricValue, out *custommetrics.MetricValue, s conversion.Scope) error {
if err := custommetrics.Convert_v1_ObjectReference_To_custom_metrics_ObjectReference(&in.DescribedObject, &out.DescribedObject, s); err != nil {
return err
}
if err := Convert_v1beta2_MetricIdentifier_To_custom_metrics_MetricIdentifier(&in.Metric, &out.Metric, s); err != nil {
return err
}
out.Timestamp = in.Timestamp
out.WindowSeconds = (*int64)(unsafe.Pointer(in.WindowSeconds))
out.Value = in.Value
return nil
}
// Convert_v1beta2_MetricValue_To_custom_metrics_MetricValue is an autogenerated conversion function.
func Convert_v1beta2_MetricValue_To_custom_metrics_MetricValue(in *MetricValue, out *custommetrics.MetricValue, s conversion.Scope) error {
return autoConvert_v1beta2_MetricValue_To_custom_metrics_MetricValue(in, out, s)
}
func autoConvert_custom_metrics_MetricValue_To_v1beta2_MetricValue(in *custommetrics.MetricValue, out *MetricValue, s conversion.Scope) error {
if err := custommetrics.Convert_custom_metrics_ObjectReference_To_v1_ObjectReference(&in.DescribedObject, &out.DescribedObject, s); err != nil {
return err
}
if err := Convert_custom_metrics_MetricIdentifier_To_v1beta2_MetricIdentifier(&in.Metric, &out.Metric, s); err != nil {
return err
}
out.Timestamp = in.Timestamp
out.WindowSeconds = (*int64)(unsafe.Pointer(in.WindowSeconds))
out.Value = in.Value
return nil
}
// Convert_custom_metrics_MetricValue_To_v1beta2_MetricValue is an autogenerated conversion function.
func Convert_custom_metrics_MetricValue_To_v1beta2_MetricValue(in *custommetrics.MetricValue, out *MetricValue, s conversion.Scope) error {
return autoConvert_custom_metrics_MetricValue_To_v1beta2_MetricValue(in, out, s)
}
func autoConvert_v1beta2_MetricValueList_To_custom_metrics_MetricValueList(in *MetricValueList, out *custommetrics.MetricValueList, s conversion.Scope) error {
out.ListMeta = in.ListMeta
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]custommetrics.MetricValue, len(*in))
for i := range *in {
if err := Convert_v1beta2_MetricValue_To_custom_metrics_MetricValue(&(*in)[i], &(*out)[i], s); err != nil {
return err
}
}
} else {
out.Items = nil
}
return nil
}
// Convert_v1beta2_MetricValueList_To_custom_metrics_MetricValueList is an autogenerated conversion function.
func Convert_v1beta2_MetricValueList_To_custom_metrics_MetricValueList(in *MetricValueList, out *custommetrics.MetricValueList, s conversion.Scope) error {
return autoConvert_v1beta2_MetricValueList_To_custom_metrics_MetricValueList(in, out, s)
}
func autoConvert_custom_metrics_MetricValueList_To_v1beta2_MetricValueList(in *custommetrics.MetricValueList, out *MetricValueList, s conversion.Scope) error {
out.ListMeta = in.ListMeta
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]MetricValue, len(*in))
for i := range *in {
if err := Convert_custom_metrics_MetricValue_To_v1beta2_MetricValue(&(*in)[i], &(*out)[i], s); err != nil {
return err
}
}
} else {
out.Items = nil
}
return nil
}
// Convert_custom_metrics_MetricValueList_To_v1beta2_MetricValueList is an autogenerated conversion function.
func Convert_custom_metrics_MetricValueList_To_v1beta2_MetricValueList(in *custommetrics.MetricValueList, out *MetricValueList, s conversion.Scope) error {
return autoConvert_custom_metrics_MetricValueList_To_v1beta2_MetricValueList(in, out, s)
}

View File

@ -0,0 +1,140 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
/*
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 deepcopy-gen. DO NOT EDIT.
package v1beta2
import (
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *MetricIdentifier) DeepCopyInto(out *MetricIdentifier) {
*out = *in
if in.Selector != nil {
in, out := &in.Selector, &out.Selector
*out = new(v1.LabelSelector)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MetricIdentifier.
func (in *MetricIdentifier) DeepCopy() *MetricIdentifier {
if in == nil {
return nil
}
out := new(MetricIdentifier)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *MetricListOptions) DeepCopyInto(out *MetricListOptions) {
*out = *in
out.TypeMeta = in.TypeMeta
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MetricListOptions.
func (in *MetricListOptions) DeepCopy() *MetricListOptions {
if in == nil {
return nil
}
out := new(MetricListOptions)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *MetricListOptions) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *MetricValue) DeepCopyInto(out *MetricValue) {
*out = *in
out.TypeMeta = in.TypeMeta
out.DescribedObject = in.DescribedObject
in.Metric.DeepCopyInto(&out.Metric)
in.Timestamp.DeepCopyInto(&out.Timestamp)
if in.WindowSeconds != nil {
in, out := &in.WindowSeconds, &out.WindowSeconds
*out = new(int64)
**out = **in
}
out.Value = in.Value.DeepCopy()
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MetricValue.
func (in *MetricValue) DeepCopy() *MetricValue {
if in == nil {
return nil
}
out := new(MetricValue)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *MetricValue) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *MetricValueList) DeepCopyInto(out *MetricValueList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]MetricValue, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MetricValueList.
func (in *MetricValueList) DeepCopy() *MetricValueList {
if in == nil {
return nil
}
out := new(MetricValueList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *MetricValueList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}

View File

@ -0,0 +1,156 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
/*
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 deepcopy-gen. DO NOT EDIT.
package custom_metrics
import (
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *MetricIdentifier) DeepCopyInto(out *MetricIdentifier) {
*out = *in
if in.Selector != nil {
in, out := &in.Selector, &out.Selector
*out = new(v1.LabelSelector)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MetricIdentifier.
func (in *MetricIdentifier) DeepCopy() *MetricIdentifier {
if in == nil {
return nil
}
out := new(MetricIdentifier)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *MetricListOptions) DeepCopyInto(out *MetricListOptions) {
*out = *in
out.TypeMeta = in.TypeMeta
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MetricListOptions.
func (in *MetricListOptions) DeepCopy() *MetricListOptions {
if in == nil {
return nil
}
out := new(MetricListOptions)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *MetricListOptions) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *MetricValue) DeepCopyInto(out *MetricValue) {
*out = *in
out.TypeMeta = in.TypeMeta
out.DescribedObject = in.DescribedObject
in.Metric.DeepCopyInto(&out.Metric)
in.Timestamp.DeepCopyInto(&out.Timestamp)
if in.WindowSeconds != nil {
in, out := &in.WindowSeconds, &out.WindowSeconds
*out = new(int64)
**out = **in
}
out.Value = in.Value.DeepCopy()
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MetricValue.
func (in *MetricValue) DeepCopy() *MetricValue {
if in == nil {
return nil
}
out := new(MetricValue)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *MetricValue) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *MetricValueList) DeepCopyInto(out *MetricValueList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]MetricValue, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MetricValueList.
func (in *MetricValueList) DeepCopy() *MetricValueList {
if in == nil {
return nil
}
out := new(MetricValueList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *MetricValueList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ObjectReference) DeepCopyInto(out *ObjectReference) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ObjectReference.
func (in *ObjectReference) DeepCopy() *ObjectReference {
if in == nil {
return nil
}
out := new(ObjectReference)
in.DeepCopyInto(out)
return out
}

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