manifest.json: stick to k8s v1.6.1

Signed-off-by: Antonio Murdaca <runcom@redhat.com>
This commit is contained in:
Antonio Murdaca 2017-05-02 11:11:51 +02:00
parent f648cd6e60
commit 860a38335d
No known key found for this signature in database
GPG key ID: B2BEAD150DE936B9
6640 changed files with 417408 additions and 541133 deletions

View file

@ -1,5 +1,5 @@
{
"memo": "5791d48b7e77e9f18a26535dfb184838f1d863f5d364fc9907cf16b6013e9846",
"memo": "348395ad5cf0e6c04bd9b6bd3265db1c2c3c0d2f42710bf3f0cc64e58f1aa269",
"projects": [
{
"name": "cloud.google.com/go",
@ -745,11 +745,11 @@
},
{
"name": "k8s.io/apimachinery",
"branch": "master",
"revision": "21807b270ec15d19215659a5caa08b17f66d6f44",
"revision": "ae33df8bd0294deb6f1853cc107816dd181e0146",
"packages": [
"pkg/api/errors",
"pkg/api/meta",
"pkg/api/resource",
"pkg/apimachinery",
"pkg/apimachinery/announced",
"pkg/apimachinery/registered",
@ -772,17 +772,24 @@
"pkg/types",
"pkg/util/errors",
"pkg/util/framer",
"pkg/util/httpstream",
"pkg/util/httpstream/spdy",
"pkg/util/intstr",
"pkg/util/json",
"pkg/util/mergepatch",
"pkg/util/net",
"pkg/util/rand",
"pkg/util/runtime",
"pkg/util/sets",
"pkg/util/strategicpatch",
"pkg/util/validation",
"pkg/util/validation/field",
"pkg/util/wait",
"pkg/util/yaml",
"pkg/version",
"pkg/watch",
"third_party/forked/golang/json",
"third_party/forked/golang/netutil",
"third_party/forked/golang/reflect"
]
},
@ -800,6 +807,7 @@
"branch": "master",
"revision": "b766ef93a46ce6dc863462254658ca2861a53314",
"packages": [
"discovery",
"pkg/api",
"pkg/api/resource",
"pkg/api/v1",
@ -818,6 +826,7 @@
"third_party/forked/golang/template",
"tools/clientcmd/api",
"tools/metrics",
"tools/record",
"transport",
"util/cert",
"util/clock",
@ -829,25 +838,27 @@
},
{
"name": "k8s.io/kubernetes",
"branch": "master",
"revision": "760d8e98e8f6ad27aaf50b1a030cb9e7b6859aab",
"version": "v1.6.1",
"revision": "b0b7a323cc5a4a2019b2e9520c21c7830b7f708e",
"packages": [
"pkg/api",
"pkg/api/install",
"pkg/api/resource",
"pkg/api/v1",
"pkg/apis/apps",
"pkg/apis/apps/install",
"pkg/apis/apps/v1beta1",
"pkg/apis/authentication",
"pkg/apis/authentication/install",
"pkg/apis/authentication/v1",
"pkg/apis/authentication/v1beta1",
"pkg/apis/authorization",
"pkg/apis/authorization/install",
"pkg/apis/authorization/v1",
"pkg/apis/authorization/v1beta1",
"pkg/apis/autoscaling",
"pkg/apis/autoscaling/install",
"pkg/apis/autoscaling/v1",
"pkg/apis/autoscaling/v2alpha1",
"pkg/apis/batch",
"pkg/apis/batch/install",
"pkg/apis/batch/v1",
@ -865,14 +876,22 @@
"pkg/apis/rbac/install",
"pkg/apis/rbac/v1alpha1",
"pkg/apis/rbac/v1beta1",
"pkg/apis/settings",
"pkg/apis/settings/install",
"pkg/apis/settings/v1alpha1",
"pkg/apis/storage",
"pkg/apis/storage/install",
"pkg/apis/storage/v1",
"pkg/apis/storage/v1beta1",
"pkg/client/clientset_generated/clientset",
"pkg/client/clientset_generated/clientset/scheme",
"pkg/client/clientset_generated/clientset/typed/apps/v1beta1",
"pkg/client/clientset_generated/clientset/typed/authentication/v1",
"pkg/client/clientset_generated/clientset/typed/authentication/v1beta1",
"pkg/client/clientset_generated/clientset/typed/authorization/v1",
"pkg/client/clientset_generated/clientset/typed/authorization/v1beta1",
"pkg/client/clientset_generated/clientset/typed/autoscaling/v1",
"pkg/client/clientset_generated/clientset/typed/autoscaling/v2alpha1",
"pkg/client/clientset_generated/clientset/typed/batch/v1",
"pkg/client/clientset_generated/clientset/typed/batch/v2alpha1",
"pkg/client/clientset_generated/clientset/typed/certificates/v1beta1",
@ -881,13 +900,14 @@
"pkg/client/clientset_generated/clientset/typed/policy/v1beta1",
"pkg/client/clientset_generated/clientset/typed/rbac/v1alpha1",
"pkg/client/clientset_generated/clientset/typed/rbac/v1beta1",
"pkg/client/clientset_generated/clientset/typed/settings/v1alpha1",
"pkg/client/clientset_generated/clientset/typed/storage/v1",
"pkg/client/clientset_generated/clientset/typed/storage/v1beta1",
"pkg/client/record",
"pkg/client/typed/discovery",
"pkg/client/unversioned/remotecommand",
"pkg/cloudprovider",
"pkg/kubelet/api/v1alpha1/runtime",
"pkg/kubelet/container",
"pkg/kubelet/events",
"pkg/kubelet/server/portforward",
"pkg/kubelet/server/remotecommand",
"pkg/kubelet/server/streaming",
@ -898,22 +918,14 @@
"pkg/util/chown",
"pkg/util/exec",
"pkg/util/hash",
"pkg/util/httpstream",
"pkg/util/httpstream/spdy",
"pkg/util/interrupt",
"pkg/util/intstr",
"pkg/util/io",
"pkg/util/labels",
"pkg/util/mount",
"pkg/util/parsers",
"pkg/util/strategicpatch",
"pkg/util/term",
"pkg/version",
"pkg/volume",
"pkg/volume/util",
"third_party/forked/golang/expansion",
"third_party/forked/golang/json",
"third_party/forked/golang/netutil"
"third_party/forked/golang/expansion"
]
}
]

View file

@ -34,7 +34,10 @@
"version": "v1.0.1-GA"
},
"k8s.io/kubernetes": {
"branch": "master"
"version": "v1.6.1"
},
"k8s.io/apimachinery": {
"revision": "ae33df8bd0294deb6f1853cc107816dd181e0146"
}
}
}

181
vendor/k8s.io/apimachinery/Godeps/Godeps.json generated vendored Normal file
View file

@ -0,0 +1,181 @@
{
"ImportPath": "k8s.io/apimachinery",
"GoVersion": "go1.7",
"GodepVersion": "v74",
"Packages": [
"./..."
],
"Deps": [
{
"ImportPath": "github.com/PuerkitoBio/purell",
"Comment": "v1.0.0",
"Rev": "8a290539e2e8629dbc4e6bad948158f790ec31f4"
},
{
"ImportPath": "github.com/PuerkitoBio/urlesc",
"Rev": "5bd2802263f21d8788851d5305584c82a5c75d7e"
},
{
"ImportPath": "github.com/davecgh/go-spew/spew",
"Rev": "5215b55f46b2b919f50a1df0eaa5886afe4e3b3d"
},
{
"ImportPath": "github.com/docker/spdystream",
"Rev": "449fdfce4d962303d702fec724ef0ad181c92528"
},
{
"ImportPath": "github.com/docker/spdystream/spdy",
"Rev": "449fdfce4d962303d702fec724ef0ad181c92528"
},
{
"ImportPath": "github.com/elazarl/goproxy",
"Comment": "v1.0-66-g07b16b6",
"Rev": "07b16b6e30fcac0ad8c0435548e743bcf2ca7e92"
},
{
"ImportPath": "github.com/emicklei/go-restful",
"Comment": "v1.2-96-g09691a3",
"Rev": "09691a3b6378b740595c1002f40c34dd5f218a22"
},
{
"ImportPath": "github.com/emicklei/go-restful/log",
"Comment": "v1.2-96-g09691a3",
"Rev": "09691a3b6378b740595c1002f40c34dd5f218a22"
},
{
"ImportPath": "github.com/evanphx/json-patch",
"Rev": "465937c80b3c07a7c7ad20cc934898646a91c1de"
},
{
"ImportPath": "github.com/ghodss/yaml",
"Rev": "73d445a93680fa1a78ae23a5839bad48f32ba1ee"
},
{
"ImportPath": "github.com/go-openapi/jsonpointer",
"Rev": "46af16f9f7b149af66e5d1bd010e3574dc06de98"
},
{
"ImportPath": "github.com/go-openapi/jsonreference",
"Rev": "13c6e3589ad90f49bd3e3bbe2c2cb3d7a4142272"
},
{
"ImportPath": "github.com/go-openapi/spec",
"Rev": "6aced65f8501fe1217321abf0749d354824ba2ff"
},
{
"ImportPath": "github.com/go-openapi/swag",
"Rev": "1d0bd113de87027671077d3c71eb3ac5d7dbba72"
},
{
"ImportPath": "github.com/gogo/protobuf/proto",
"Comment": "v0.2-33-ge18d7aa8",
"Rev": "e18d7aa8f8c624c915db340349aad4c49b10d173"
},
{
"ImportPath": "github.com/gogo/protobuf/sortkeys",
"Comment": "v0.2-33-ge18d7aa8",
"Rev": "e18d7aa8f8c624c915db340349aad4c49b10d173"
},
{
"ImportPath": "github.com/golang/glog",
"Rev": "44145f04b68cf362d9c4df2182967c2275eaefed"
},
{
"ImportPath": "github.com/golang/protobuf/proto",
"Rev": "8616e8ee5e20a1704615e6c8d7afcdac06087a67"
},
{
"ImportPath": "github.com/google/gofuzz",
"Rev": "44d81051d367757e1c7c6a5a86423ece9afcf63c"
},
{
"ImportPath": "github.com/mailru/easyjson/buffer",
"Rev": "d5b7844b561a7bc640052f1b935f7b800330d7e0"
},
{
"ImportPath": "github.com/mailru/easyjson/jlexer",
"Rev": "d5b7844b561a7bc640052f1b935f7b800330d7e0"
},
{
"ImportPath": "github.com/mailru/easyjson/jwriter",
"Rev": "d5b7844b561a7bc640052f1b935f7b800330d7e0"
},
{
"ImportPath": "github.com/pborman/uuid",
"Rev": "ca53cad383cad2479bbba7f7a1a05797ec1386e4"
},
{
"ImportPath": "github.com/spf13/pflag",
"Rev": "9ff6c6923cfffbcd502984b8e0c80539a94968b7"
},
{
"ImportPath": "github.com/ugorji/go/codec",
"Rev": "ded73eae5db7e7a0ef6f55aace87a2873c5d2b74"
},
{
"ImportPath": "golang.org/x/net/http2",
"Rev": "e90d6d0afc4c315a0d87a568ae68577cc15149a0"
},
{
"ImportPath": "golang.org/x/net/http2/hpack",
"Rev": "e90d6d0afc4c315a0d87a568ae68577cc15149a0"
},
{
"ImportPath": "golang.org/x/net/idna",
"Rev": "e90d6d0afc4c315a0d87a568ae68577cc15149a0"
},
{
"ImportPath": "golang.org/x/net/lex/httplex",
"Rev": "e90d6d0afc4c315a0d87a568ae68577cc15149a0"
},
{
"ImportPath": "golang.org/x/text/cases",
"Rev": "2910a502d2bf9e43193af9d68ca516529614eed3"
},
{
"ImportPath": "golang.org/x/text/internal/tag",
"Rev": "2910a502d2bf9e43193af9d68ca516529614eed3"
},
{
"ImportPath": "golang.org/x/text/language",
"Rev": "2910a502d2bf9e43193af9d68ca516529614eed3"
},
{
"ImportPath": "golang.org/x/text/runes",
"Rev": "2910a502d2bf9e43193af9d68ca516529614eed3"
},
{
"ImportPath": "golang.org/x/text/secure/bidirule",
"Rev": "2910a502d2bf9e43193af9d68ca516529614eed3"
},
{
"ImportPath": "golang.org/x/text/secure/precis",
"Rev": "2910a502d2bf9e43193af9d68ca516529614eed3"
},
{
"ImportPath": "golang.org/x/text/transform",
"Rev": "2910a502d2bf9e43193af9d68ca516529614eed3"
},
{
"ImportPath": "golang.org/x/text/unicode/bidi",
"Rev": "2910a502d2bf9e43193af9d68ca516529614eed3"
},
{
"ImportPath": "golang.org/x/text/unicode/norm",
"Rev": "2910a502d2bf9e43193af9d68ca516529614eed3"
},
{
"ImportPath": "golang.org/x/text/width",
"Rev": "2910a502d2bf9e43193af9d68ca516529614eed3"
},
{
"ImportPath": "gopkg.in/inf.v0",
"Comment": "v0.9.0",
"Rev": "3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4"
},
{
"ImportPath": "gopkg.in/yaml.v2",
"Rev": "53feefa2559fb8dfa8d81baad31be332c97d6c77"
}
]
}

5
vendor/k8s.io/apimachinery/Godeps/Readme generated vendored Normal file
View file

@ -0,0 +1,5 @@
This directory tree is generated automatically by godep.
Please do not edit.
See https://github.com/tools/godep for more information.

View file

@ -1 +0,0 @@
acda1e7401fbf79d89ba23df5e11d81f4a9d4e0d

66
vendor/k8s.io/apimachinery/hack/godep-deps.sh generated vendored Executable file
View file

@ -0,0 +1,66 @@
#!/bin/bash
# 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.
# overall flow
# 1. make a clean gopath
# 2. godep restore based on k8s.io/kuberentes provided manifest
# 3. go get anything unlisted. This handles deps from k8s.io/*
# 4. remove old vendoring data
# 5. vendor packages we need
# 6. remove anything vendored from k8s.io/* from vendor, but not manifest.
# This allows go get to work and still be able to flatten dependencies.
# 6. copy new vendored packages and save them
set -o errexit
set -o nounset
set -o pipefail
goPath=$(mktemp -d "${TMPDIR:-/tmp/}$(basename 0).XXXXXXXXXXXX")
echo ${goPath}
export GOPATH=${goPath}
mkdir -p ${goPath}/src/k8s.io/apimachinery
cp -R . ${goPath}/src/k8s.io/apimachinery
pushd ${goPath}/src/k8s.io/apimachinery
rm -rf vendor || true
# restore what we have in our new manifest that we've sync
godep restore
# the manifest doesn't include any levels of k8s.io dependencies so load them using the go get
# assume you sync all the repos at the same time, the leves you get will be correct
go get -d ./... || true
# save the new levels of dependencies
rm -rf vendor || true
rm -rf Godeps || true
godep save ./...
popd
# remove the vendor dir we have and move the one we just made
rm -rf vendor || true
rm -rf Godeps || true
git rm -rf vendor || true
git rm -rf Godeps || true
mv ${goPath}/src/k8s.io/apimachinery/vendor .
mv ${goPath}/src/k8s.io/apimachinery/Godeps .
git add vendor
git add Godeps
git commit -m "sync: resync vendor folder"

View file

@ -26,6 +26,7 @@
set -e
ROOT=$(dirname "${BASH_SOURCE}")/..
dir=$(mktemp -d "${TMPDIR:-/tmp/}$(basename 0).XXXXXXXXXXXX")
git remote add upstream-kube git@github.com:kubernetes/kubernetes.git || true
@ -53,6 +54,9 @@ while read commitSHA; do
git cherry-pick ${commitSHA}
done <${dir}/commits
# update the vendored godeps
${ROOT}/hack/godep-deps.sh
# track the k8s.io/kubernetes commit SHA so we can always determine which level of kube this repo matches
# track the filtered branch commit SHA so that we can determine which commits need to be picked
echo ${newKubeSHA} > kubernetes-sha

View file

@ -1 +1 @@
424a0e9e827a39c714ee03e1891c43635adabcdc
082cfac70759233e47004c11c97651ccc5aa1fdb

View file

@ -3,3 +3,4 @@ approvers:
- deads2k
- lavalamp
- smarterclayton
- liggitt

View file

@ -334,6 +334,14 @@ func extractFromOwnerReference(v reflect.Value, o *metav1.OwnerReference) error
controller := *controllerPtr
o.Controller = &controller
}
var blockOwnerDeletionPtr *bool
if err := runtime.Field(v, "BlockOwnerDeletion", &blockOwnerDeletionPtr); err != nil {
return err
}
if blockOwnerDeletionPtr != nil {
block := *blockOwnerDeletionPtr
o.BlockOwnerDeletion = &block
}
return nil
}
@ -357,6 +365,12 @@ func setOwnerReference(v reflect.Value, o *metav1.OwnerReference) error {
return err
}
}
if o.BlockOwnerDeletion != nil {
block := *(o.BlockOwnerDeletion)
if err := runtime.SetField(&block, v, "BlockOwnerDeletion"); err != nil {
return err
}
}
return nil
}

86
vendor/k8s.io/apimachinery/pkg/api/testing/codec.go generated vendored Normal file
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 testing
import (
"fmt"
"mime"
"os"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/apimachinery/pkg/runtime/serializer/recognizer"
)
var (
testCodecMediaType string
testStorageCodecMediaType string
)
// TestCodec returns the codec for the API version to test against, as set by the
// KUBE_TEST_API_TYPE env var.
func TestCodec(codecs runtimeserializer.CodecFactory, gvs ...schema.GroupVersion) runtime.Codec {
if len(testCodecMediaType) != 0 {
serializerInfo, ok := runtime.SerializerInfoForMediaType(codecs.SupportedMediaTypes(), testCodecMediaType)
if !ok {
panic(fmt.Sprintf("no serializer for %s", testCodecMediaType))
}
return codecs.CodecForVersions(serializerInfo.Serializer, codecs.UniversalDeserializer(), schema.GroupVersions(gvs), nil)
}
return codecs.LegacyCodec(gvs...)
}
// TestStorageCodec returns the codec for the API version to test against used in storage, as set by the
// KUBE_TEST_API_STORAGE_TYPE env var.
func TestStorageCodec(codecs runtimeserializer.CodecFactory, gvs ...schema.GroupVersion) runtime.Codec {
if len(testStorageCodecMediaType) != 0 {
serializerInfo, ok := runtime.SerializerInfoForMediaType(codecs.SupportedMediaTypes(), testStorageCodecMediaType)
if !ok {
panic(fmt.Sprintf("no serializer for %s", testStorageCodecMediaType))
}
// etcd2 only supports string data - we must wrap any result before returning
// TODO: remove for etcd3 / make parameterizable
serializer := serializerInfo.Serializer
if !serializerInfo.EncodesAsText {
serializer = runtime.NewBase64Serializer(serializer)
}
decoder := recognizer.NewDecoder(serializer, codecs.UniversalDeserializer())
return codecs.CodecForVersions(serializer, decoder, schema.GroupVersions(gvs), nil)
}
return codecs.LegacyCodec(gvs...)
}
func init() {
var err error
if apiMediaType := os.Getenv("KUBE_TEST_API_TYPE"); len(apiMediaType) > 0 {
testCodecMediaType, _, err = mime.ParseMediaType(apiMediaType)
if err != nil {
panic(err)
}
}
if storageMediaType := os.Getenv("KUBE_TEST_API_STORAGE_TYPE"); len(storageMediaType) > 0 {
testStorageCodecMediaType, _, err = mime.ParseMediaType(storageMediaType)
if err != nil {
panic(err)
}
}
}

View file

@ -17,19 +17,25 @@ limitations under the License.
package testing
import (
"strconv"
"math/rand"
"reflect"
"strconv"
"testing"
"github.com/google/gofuzz"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/apimachinery/pkg/types"
)
func GenericFuzzerFuncs(t TestingCommon) []interface{} {
func GenericFuzzerFuncs(t TestingCommon, codecs runtimeserializer.CodecFactory) []interface{} {
return []interface{}{
func(q *resource.Quantity, c fuzz.Continue) {
*q = *resource.NewQuantity(c.Int63n(1000), resource.DecimalExponent)
},
func(j *int, c fuzz.Continue) {
*j = int(c.Int31())
},
@ -92,7 +98,7 @@ func GenericFuzzerFuncs(t TestingCommon) []interface{} {
// Find a codec for converting the object to raw bytes. This is necessary for the
// api version and kind to be correctly set be serialization.
var codec = Codec(metav1.SchemeGroupVersion)
var codec = TestCodec(codecs, metav1.SchemeGroupVersion)
// Convert the object to raw bytes
bytes, err := runtime.Encode(codec, obj)
@ -131,3 +137,38 @@ func FuzzerFor(funcs []interface{}, src rand.Source) *fuzz.Fuzzer {
f.Funcs(funcs...)
return f
}
// MergeFuzzerFuncs will merge the given funcLists, overriding early funcs with later ones if there first
// argument has the same type.
func MergeFuzzerFuncs(t TestingCommon, funcLists ...[]interface{}) []interface{} {
funcMap := map[string]interface{}{}
for _, list := range funcLists {
for _, f := range list {
fT := reflect.TypeOf(f)
if fT.Kind() != reflect.Func || fT.NumIn() != 2 {
t.Errorf("Fuzzer func with invalid type: %v", fT)
continue
}
funcMap[fT.In(0).String()] = f
}
}
result := []interface{}{}
for _, f := range funcMap {
result = append(result, f)
}
return result
}
func DefaultFuzzers(t TestingCommon, codecFactory runtimeserializer.CodecFactory, fuzzerFuncs []interface{}) *fuzz.Fuzzer {
seed := rand.Int63()
return FuzzerFor(
MergeFuzzerFuncs(t,
GenericFuzzerFuncs(t, codecFactory),
fuzzerFuncs,
),
rand.NewSource(seed),
)
}

View file

@ -1,57 +0,0 @@
/*
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 testing
import (
"os"
"mime"
"fmt"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/apimachinery/pkg/runtime/schema"
)
var scheme = runtime.NewScheme()
var codecs = runtimeserializer.NewCodecFactory(scheme)
var serializer runtime.SerializerInfo
// Codec returns the codec for the API version to test against, as set by the
// KUBE_TEST_API_TYPE env var.
func Codec(gvs ...schema.GroupVersion) runtime.Codec {
if serializer.Serializer == nil {
return codecs.LegacyCodec(gvs...)
}
return codecs.CodecForVersions(serializer.Serializer, codecs.UniversalDeserializer(), schema.GroupVersions(gvs), nil)
}
func init() {
metav1.AddToGroupVersion(scheme, metav1.SchemeGroupVersion)
if apiMediaType := os.Getenv("KUBE_TEST_API_TYPE"); len(apiMediaType) > 0 {
var ok bool
mediaType, _, err := mime.ParseMediaType(apiMediaType)
if err != nil {
panic(err)
}
serializer, ok = runtime.SerializerInfoForMediaType(codecs.SupportedMediaTypes(), mediaType)
if !ok {
panic(fmt.Sprintf("no serializer for %s", apiMediaType))
}
}
}

295
vendor/k8s.io/apimachinery/pkg/api/testing/roundtrip.go generated vendored Normal file
View file

@ -0,0 +1,295 @@
/*
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 testing
import (
"encoding/hex"
"fmt"
"reflect"
"strings"
"testing"
"github.com/davecgh/go-spew/spew"
"github.com/golang/protobuf/proto"
"github.com/google/gofuzz"
flag "github.com/spf13/pflag"
"k8s.io/apimachinery/pkg/runtime/serializer/protobuf"
apiequality "k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/apimachinery/announced"
"k8s.io/apimachinery/pkg/apimachinery/registered"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/apimachinery/pkg/util/diff"
"k8s.io/apimachinery/pkg/util/sets"
)
type InstallFunc func(groupFactoryRegistry announced.APIGroupFactoryRegistry, registry *registered.APIRegistrationManager, scheme *runtime.Scheme)
// RoundTripTestForAPIGroup is convenient to call from your install package to make sure that a "bare" install of your group provides
// enough information to round trip
func RoundTripTestForAPIGroup(t *testing.T, installFn InstallFunc, fuzzingFuncs []interface{}) {
groupFactoryRegistry := make(announced.APIGroupFactoryRegistry)
registry := registered.NewOrDie("")
scheme := runtime.NewScheme()
installFn(groupFactoryRegistry, registry, scheme)
RoundTripTestForScheme(t, scheme, fuzzingFuncs)
}
// RoundTripTestForScheme is convenient to call if you already have a scheme and want to make sure that its well-formed
func RoundTripTestForScheme(t *testing.T, scheme *runtime.Scheme, fuzzingFuncs []interface{}) {
codecFactory := runtimeserializer.NewCodecFactory(scheme)
fuzzer := DefaultFuzzers(t, codecFactory, fuzzingFuncs)
RoundTripTypesWithoutProtobuf(t, scheme, codecFactory, fuzzer, nil)
}
// RoundTripProtobufTestForAPIGroup is convenient to call from your install package to make sure that a "bare" install of your group provides
// enough information to round trip
func RoundTripProtobufTestForAPIGroup(t *testing.T, installFn InstallFunc, fuzzingFuncs []interface{}) {
groupFactoryRegistry := make(announced.APIGroupFactoryRegistry)
registry := registered.NewOrDie("")
scheme := runtime.NewScheme()
installFn(groupFactoryRegistry, registry, scheme)
RoundTripProtobufTestForScheme(t, scheme, fuzzingFuncs)
}
// RoundTripProtobufTestForScheme is convenient to call if you already have a scheme and want to make sure that its well-formed
func RoundTripProtobufTestForScheme(t *testing.T, scheme *runtime.Scheme, fuzzingFuncs []interface{}) {
codecFactory := runtimeserializer.NewCodecFactory(scheme)
fuzzer := DefaultFuzzers(t, codecFactory, fuzzingFuncs)
RoundTripTypes(t, scheme, codecFactory, fuzzer, nil)
}
var FuzzIters = flag.Int("fuzz-iters", 20, "How many fuzzing iterations to do.")
// globalNonRoundTrippableTypes are kinds that are effectively reserved across all GroupVersions
// They don't roundtrip
var globalNonRoundTrippableTypes = sets.NewString(
"ExportOptions",
"GetOptions",
// WatchEvent does not include kind and version and can only be deserialized
// implicitly (if the caller expects the specific object). The watch call defines
// the schema by content type, rather than via kind/version included in each
// object.
"WatchEvent",
// ListOptions is now part of the meta group
"ListOptions",
// Delete options is only read in metav1
"DeleteOptions",
)
// RoundTripTypesWithoutProtobuf applies the round-trip test to all round-trippable Kinds
// in the scheme. It will skip all the GroupVersionKinds in the skip list.
func RoundTripTypesWithoutProtobuf(t *testing.T, scheme *runtime.Scheme, codecFactory runtimeserializer.CodecFactory, fuzzer *fuzz.Fuzzer, nonRoundTrippableTypes map[schema.GroupVersionKind]bool) {
roundTripTypes(t, scheme, codecFactory, fuzzer, nonRoundTrippableTypes, true)
}
func RoundTripTypes(t *testing.T, scheme *runtime.Scheme, codecFactory runtimeserializer.CodecFactory, fuzzer *fuzz.Fuzzer, nonRoundTrippableTypes map[schema.GroupVersionKind]bool) {
roundTripTypes(t, scheme, codecFactory, fuzzer, nonRoundTrippableTypes, false)
}
func roundTripTypes(t *testing.T, scheme *runtime.Scheme, codecFactory runtimeserializer.CodecFactory, fuzzer *fuzz.Fuzzer, nonRoundTrippableTypes map[schema.GroupVersionKind]bool, skipProtobuf bool) {
for _, group := range groupsFromScheme(scheme) {
t.Logf("starting group %q", group)
internalVersion := schema.GroupVersion{Group: group, Version: runtime.APIVersionInternal}
internalKindToGoType := scheme.KnownTypes(internalVersion)
for kind := range internalKindToGoType {
if globalNonRoundTrippableTypes.Has(kind) {
continue
}
internalGVK := internalVersion.WithKind(kind)
roundTripSpecificKind(t, internalGVK, scheme, codecFactory, fuzzer, nonRoundTrippableTypes, skipProtobuf)
}
t.Logf("finished group %q", group)
}
}
func RoundTripSpecificKindWithoutProtobuf(t *testing.T, internalGVK schema.GroupVersionKind, scheme *runtime.Scheme, codecFactory runtimeserializer.CodecFactory, fuzzer *fuzz.Fuzzer, nonRoundTrippableTypes map[schema.GroupVersionKind]bool) {
roundTripSpecificKind(t, internalGVK, scheme, codecFactory, fuzzer, nonRoundTrippableTypes, true)
}
func RoundTripSpecificKind(t *testing.T, internalGVK schema.GroupVersionKind, scheme *runtime.Scheme, codecFactory runtimeserializer.CodecFactory, fuzzer *fuzz.Fuzzer, nonRoundTrippableTypes map[schema.GroupVersionKind]bool) {
roundTripSpecificKind(t, internalGVK, scheme, codecFactory, fuzzer, nonRoundTrippableTypes, false)
}
func roundTripSpecificKind(t *testing.T, internalGVK schema.GroupVersionKind, scheme *runtime.Scheme, codecFactory runtimeserializer.CodecFactory, fuzzer *fuzz.Fuzzer, nonRoundTrippableTypes map[schema.GroupVersionKind]bool, skipProtobuf bool) {
if nonRoundTrippableTypes[internalGVK] {
t.Logf("skipping %v", internalGVK)
return
}
t.Logf("round tripping %v", internalGVK)
// Try a few times, since runTest uses random values.
for i := 0; i < *FuzzIters; i++ {
roundTripToAllExternalVersions(t, scheme, codecFactory, fuzzer, internalGVK, nonRoundTrippableTypes, skipProtobuf)
if t.Failed() {
break
}
}
}
// fuzzInternalObject fuzzes an arbitrary runtime object using the appropriate
// fuzzer registered with the apitesting package.
func fuzzInternalObject(t *testing.T, fuzzer *fuzz.Fuzzer, object runtime.Object) runtime.Object {
fuzzer.Fuzz(object)
j, err := meta.TypeAccessor(object)
if err != nil {
t.Fatalf("Unexpected error %v for %#v", err, object)
}
j.SetKind("")
j.SetAPIVersion("")
return object
}
func groupsFromScheme(scheme *runtime.Scheme) []string {
ret := sets.String{}
for gvk := range scheme.AllKnownTypes() {
ret.Insert(gvk.Group)
}
return ret.List()
}
func roundTripToAllExternalVersions(t *testing.T, scheme *runtime.Scheme, codecFactory runtimeserializer.CodecFactory, fuzzer *fuzz.Fuzzer, internalGVK schema.GroupVersionKind, nonRoundTrippableTypes map[schema.GroupVersionKind]bool, skipProtobuf bool) {
object, err := scheme.New(internalGVK)
if err != nil {
t.Fatalf("Couldn't make a %v? %v", internalGVK, err)
}
if _, err := meta.TypeAccessor(object); err != nil {
t.Fatalf("%q is not a TypeMeta and cannot be tested - add it to nonRoundTrippableInternalTypes: %v", internalGVK, err)
}
fuzzInternalObject(t, fuzzer, object)
// find all potential serializations in the scheme.
// TODO fix this up to handle kinds that cross registered with different names.
for externalGVK, externalGoType := range scheme.AllKnownTypes() {
if externalGVK.Version == runtime.APIVersionInternal {
continue
}
if externalGVK.GroupKind() != internalGVK.GroupKind() {
continue
}
if nonRoundTrippableTypes[externalGVK] {
t.Logf("\tskipping %v %v", externalGVK, externalGoType)
continue
}
t.Logf("\tround tripping to %v %v", externalGVK, externalGoType)
roundTrip(t, scheme, TestCodec(codecFactory, externalGVK.GroupVersion()), object)
// TODO remove this hack after we're past the intermediate steps
if !skipProtobuf && externalGVK.Group != "kubeadm.k8s.io" {
s := protobuf.NewSerializer(scheme, scheme, "application/arbitrary.content.type")
protobufCodec := codecFactory.CodecForVersions(s, s, externalGVK.GroupVersion(), nil)
roundTrip(t, scheme, protobufCodec, object)
}
}
}
// roundTrip applies a single round-trip test to the given runtime object
// using the given codec. The round-trip test ensures that an object can be
// deep-copied and converted from internal -> versioned -> internal without
// loss of data.
func roundTrip(t *testing.T, scheme *runtime.Scheme, codec runtime.Codec, object runtime.Object) {
printer := spew.ConfigState{DisableMethods: true}
original := object
// deep copy the original object
copied, err := scheme.DeepCopy(object)
if err != nil {
panic(fmt.Sprintf("unable to copy: %v", err))
}
object = copied.(runtime.Object)
name := reflect.TypeOf(object).Elem().Name()
// encode (serialize) the deep copy using the provided codec
data, err := runtime.Encode(codec, object)
if err != nil {
if runtime.IsNotRegisteredError(err) {
t.Logf("%v: not registered: %v (%s)", name, err, printer.Sprintf("%#v", object))
} else {
t.Errorf("%v: %v (%s)", name, err, printer.Sprintf("%#v", object))
}
return
}
// ensure that the deep copy is equal to the original; neither the deep
// copy or conversion should alter the object
// TODO eliminate this global
if !apiequality.Semantic.DeepEqual(original, object) {
t.Errorf("0: %v: encode altered the object, diff: %v", name, diff.ObjectReflectDiff(original, object))
return
}
// decode (deserialize) the encoded data back into an object
obj2, err := runtime.Decode(codec, data)
if err != nil {
t.Errorf("0: %v: %v\nCodec: %#v\nData: %s\nSource: %#v", name, err, codec, dataAsString(data), printer.Sprintf("%#v", object))
panic("failed")
}
// ensure that the object produced from decoding the encoded data is equal
// to the original object
if !apiequality.Semantic.DeepEqual(original, obj2) {
t.Errorf("1: %v: diff: %v\nCodec: %#v\nSource:\n\n%#v\n\nEncoded:\n\n%s\n\nFinal:\n\n%#v", name, diff.ObjectReflectDiff(object, obj2), codec, printer.Sprintf("%#v", object), dataAsString(data), printer.Sprintf("%#v", obj2))
return
}
// decode the encoded data into a new object (instead of letting the codec
// create a new object)
obj3 := reflect.New(reflect.TypeOf(object).Elem()).Interface().(runtime.Object)
if err := runtime.DecodeInto(codec, data, obj3); err != nil {
t.Errorf("2: %v: %v", name, err)
return
}
// ensure that the new runtime object is equal to the original after being
// decoded into
if !apiequality.Semantic.DeepEqual(object, obj3) {
t.Errorf("3: %v: diff: %v\nCodec: %#v", name, diff.ObjectReflectDiff(object, obj3), codec)
return
}
// do structure-preserving fuzzing of the deep-copied object. If it shares anything with the original,
// the deep-copy was actually only a shallow copy. Then original and obj3 will be different after fuzzing.
// NOTE: we use the encoding+decoding here as an alternative, guaranteed deep-copy to compare against.
ValueFuzz(object)
if !apiequality.Semantic.DeepEqual(original, obj3) {
t.Errorf("0: %v: fuzzing a copy altered the original, diff: %v", name, diff.ObjectReflectDiff(original, object))
return
}
}
// dataAsString returns the given byte array as a string; handles detecting
// protocol buffers.
func dataAsString(data []byte) string {
dataString := string(data)
if !strings.HasPrefix(dataString, "{") {
dataString = "\n" + hex.Dump(data)
proto.NewBuffer(make([]byte, 0, 1024)).DebugPrint("decoded object", data)
}
return dataString
}

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 testing
import (
"reflect"
)
// ValueFuzz recursively changes all basic type values in an object. Any kind of references will not
// be touch, i.e. the addresses of slices, maps, pointers will stay unchanged.
func ValueFuzz(obj interface{}) {
valueFuzz(reflect.ValueOf(obj))
}
func valueFuzz(obj reflect.Value) {
switch obj.Kind() {
case reflect.Array:
for i := 0; i < obj.Len(); i++ {
valueFuzz(obj.Index(i))
}
case reflect.Slice:
if obj.IsNil() {
// TODO: set non-nil value
} else {
for i := 0; i < obj.Len(); i++ {
valueFuzz(obj.Index(i))
}
}
case reflect.Interface, reflect.Ptr:
if obj.IsNil() {
// TODO: set non-nil value
} else {
valueFuzz(obj.Elem())
}
case reflect.Struct:
for i, n := 0, obj.NumField(); i < n; i++ {
valueFuzz(obj.Field(i))
}
case reflect.Map:
if obj.IsNil() {
// TODO: set non-nil value
} else {
for _, k := range obj.MapKeys() {
// map values are not addressable. We need a copy.
v := obj.MapIndex(k)
copy := reflect.New(v.Type())
copy.Elem().Set(v)
valueFuzz(copy.Elem())
obj.SetMapIndex(k, copy.Elem())
}
// TODO: set some new value
}
case reflect.Func: // ignore, we don't have function types in our API
default:
if !obj.CanSet() {
return
}
switch obj.Kind() {
case reflect.String:
obj.SetString(obj.String() + "x")
case reflect.Bool:
obj.SetBool(!obj.Bool())
case reflect.Float32, reflect.Float64:
obj.SetFloat(obj.Float()*2.0 + 1.0)
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
obj.SetInt(obj.Int() + 1)
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
obj.SetUint(obj.Uint() + 1)
default:
}
}
}

View file

@ -0,0 +1,73 @@
/*
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 testing
import "testing"
func TestValueFuzz(t *testing.T) {
type (
Y struct {
I int
B bool
F float32
U uint
}
X struct {
Ptr *X
Y Y
Map map[string]int
Slice []int
}
)
x := X{
Ptr: &X{},
Map: map[string]int{"foo": 42},
Slice: []int{1, 2, 3},
}
p := x.Ptr
m := x.Map
s := x.Slice
ValueFuzz(x)
if x.Ptr.Y.I == 0 {
t.Errorf("x.Ptr.Y.I should have changed")
}
if x.Map["foo"] == 42 {
t.Errorf("x.Map[foo] should have changed")
}
if x.Slice[0] == 1 {
t.Errorf("x.Slice[0] should have changed")
}
if x.Ptr != p {
t.Errorf("x.Ptr changed")
}
m["foo"] = 7
if x.Map["foo"] != m["foo"] {
t.Errorf("x.Map changed")
}
s[0] = 7
if x.Slice[0] != s[0] {
t.Errorf("x.Slice changed")
}
}

View file

@ -0,0 +1,261 @@
/*
Copyright 2014 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 validation
import (
"fmt"
"strings"
apiequality "k8s.io/apimachinery/pkg/api/equality"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
v1validation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/validation"
"k8s.io/apimachinery/pkg/util/validation/field"
)
// TODO: delete this global variable when we enable the validation of common
// fields by default.
var RepairMalformedUpdates bool = true
const FieldImmutableErrorMsg string = `field is immutable`
const totalAnnotationSizeLimitB int = 256 * (1 << 10) // 256 kB
// BannedOwners is a black list of object that are not allowed to be owners.
var BannedOwners = map[schema.GroupVersionKind]struct{}{
schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Event"}: {},
}
// ValidateClusterName can be used to check whether the given cluster name is valid.
var ValidateClusterName = NameIsDNS1035Label
// ValidateAnnotations validates that a set of annotations are correctly defined.
func ValidateAnnotations(annotations map[string]string, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
var totalSize int64
for k, v := range annotations {
for _, msg := range validation.IsQualifiedName(strings.ToLower(k)) {
allErrs = append(allErrs, field.Invalid(fldPath, k, msg))
}
totalSize += (int64)(len(k)) + (int64)(len(v))
}
if totalSize > (int64)(totalAnnotationSizeLimitB) {
allErrs = append(allErrs, field.TooLong(fldPath, "", totalAnnotationSizeLimitB))
}
return allErrs
}
func validateOwnerReference(ownerReference metav1.OwnerReference, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
gvk := schema.FromAPIVersionAndKind(ownerReference.APIVersion, ownerReference.Kind)
// gvk.Group is empty for the legacy group.
if len(gvk.Version) == 0 {
allErrs = append(allErrs, field.Invalid(fldPath.Child("apiVersion"), ownerReference.APIVersion, "version must not be empty"))
}
if len(gvk.Kind) == 0 {
allErrs = append(allErrs, field.Invalid(fldPath.Child("kind"), ownerReference.Kind, "kind must not be empty"))
}
if len(ownerReference.Name) == 0 {
allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), ownerReference.Name, "name must not be empty"))
}
if len(ownerReference.UID) == 0 {
allErrs = append(allErrs, field.Invalid(fldPath.Child("uid"), ownerReference.UID, "uid must not be empty"))
}
if _, ok := BannedOwners[gvk]; ok {
allErrs = append(allErrs, field.Invalid(fldPath, ownerReference, fmt.Sprintf("%s is disallowed from being an owner", gvk)))
}
return allErrs
}
func ValidateOwnerReferences(ownerReferences []metav1.OwnerReference, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
controllerName := ""
for _, ref := range ownerReferences {
allErrs = append(allErrs, validateOwnerReference(ref, fldPath)...)
if ref.Controller != nil && *ref.Controller {
if controllerName != "" {
allErrs = append(allErrs, field.Invalid(fldPath, ownerReferences,
fmt.Sprintf("Only one reference can have Controller set to true. Found \"true\" in references for %v and %v", controllerName, ref.Name)))
} else {
controllerName = ref.Name
}
}
}
return allErrs
}
// Validate finalizer names
func ValidateFinalizerName(stringValue string, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
for _, msg := range validation.IsQualifiedName(stringValue) {
allErrs = append(allErrs, field.Invalid(fldPath, stringValue, msg))
}
return allErrs
}
func ValidateNoNewFinalizers(newFinalizers []string, oldFinalizers []string, fldPath *field.Path) field.ErrorList {
const newFinalizersErrorMsg string = `no new finalizers can be added if the object is being deleted`
allErrs := field.ErrorList{}
extra := sets.NewString(newFinalizers...).Difference(sets.NewString(oldFinalizers...))
if len(extra) != 0 {
allErrs = append(allErrs, field.Forbidden(fldPath, fmt.Sprintf("no new finalizers can be added if the object is being deleted, found new finalizers %#v", extra.List())))
}
return allErrs
}
func ValidateImmutableField(newVal, oldVal interface{}, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if !apiequality.Semantic.DeepEqual(oldVal, newVal) {
allErrs = append(allErrs, field.Invalid(fldPath, newVal, FieldImmutableErrorMsg))
}
return allErrs
}
// ValidateObjectMeta validates an object's metadata on creation. It expects that name generation has already
// been performed.
// It doesn't return an error for rootscoped resources with namespace, because namespace should already be cleared before.
func ValidateObjectMeta(meta *metav1.ObjectMeta, requiresNamespace bool, nameFn ValidateNameFunc, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if len(meta.GenerateName) != 0 {
for _, msg := range nameFn(meta.GenerateName, true) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("generateName"), meta.GenerateName, msg))
}
}
// If the generated name validates, but the calculated value does not, it's a problem with generation, and we
// report it here. This may confuse users, but indicates a programming bug and still must be validated.
// If there are multiple fields out of which one is required then add an or as a separator
if len(meta.Name) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("name"), "name or generateName is required"))
} else {
for _, msg := range nameFn(meta.Name, false) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), meta.Name, msg))
}
}
if requiresNamespace {
if len(meta.Namespace) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("namespace"), ""))
} else {
for _, msg := range ValidateNamespaceName(meta.Namespace, false) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("namespace"), meta.Namespace, msg))
}
}
} else {
if len(meta.Namespace) != 0 {
allErrs = append(allErrs, field.Forbidden(fldPath.Child("namespace"), "not allowed on this type"))
}
}
if len(meta.ClusterName) != 0 {
for _, msg := range ValidateClusterName(meta.ClusterName, false) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("clusterName"), meta.ClusterName, msg))
}
}
allErrs = append(allErrs, ValidateNonnegativeField(meta.Generation, fldPath.Child("generation"))...)
allErrs = append(allErrs, v1validation.ValidateLabels(meta.Labels, fldPath.Child("labels"))...)
allErrs = append(allErrs, ValidateAnnotations(meta.Annotations, fldPath.Child("annotations"))...)
allErrs = append(allErrs, ValidateOwnerReferences(meta.OwnerReferences, fldPath.Child("ownerReferences"))...)
allErrs = append(allErrs, ValidateFinalizers(meta.Finalizers, fldPath.Child("finalizers"))...)
return allErrs
}
// ValidateFinalizers tests if the finalizers name are valid, and if there are conflicting finalizers.
func ValidateFinalizers(finalizers []string, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
hasFinalizerOrphanDependents := false
hasFinalizerDeleteDependents := false
for _, finalizer := range finalizers {
allErrs = append(allErrs, ValidateFinalizerName(finalizer, fldPath)...)
if finalizer == metav1.FinalizerOrphanDependents {
hasFinalizerOrphanDependents = true
}
if finalizer == metav1.FinalizerDeleteDependents {
hasFinalizerDeleteDependents = true
}
}
if hasFinalizerDeleteDependents && hasFinalizerOrphanDependents {
allErrs = append(allErrs, field.Invalid(fldPath, finalizers, fmt.Sprintf("finalizer %s and %s cannot be both set", metav1.FinalizerOrphanDependents, metav1.FinalizerDeleteDependents)))
}
return allErrs
}
// ValidateObjectMetaUpdate validates an object's metadata when updated
func ValidateObjectMetaUpdate(newMeta, oldMeta *metav1.ObjectMeta, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if !RepairMalformedUpdates && newMeta.UID != oldMeta.UID {
allErrs = append(allErrs, field.Invalid(fldPath.Child("uid"), newMeta.UID, "field is immutable"))
}
// in the event it is left empty, set it, to allow clients more flexibility
// TODO: remove the following code that repairs the update request when we retire the clients that modify the immutable fields.
// Please do not copy this pattern elsewhere; validation functions should not be modifying the objects they are passed!
if RepairMalformedUpdates {
if len(newMeta.UID) == 0 {
newMeta.UID = oldMeta.UID
}
// ignore changes to timestamp
if oldMeta.CreationTimestamp.IsZero() {
oldMeta.CreationTimestamp = newMeta.CreationTimestamp
} else {
newMeta.CreationTimestamp = oldMeta.CreationTimestamp
}
// an object can never remove a deletion timestamp or clear/change grace period seconds
if !oldMeta.DeletionTimestamp.IsZero() {
newMeta.DeletionTimestamp = oldMeta.DeletionTimestamp
}
if oldMeta.DeletionGracePeriodSeconds != nil && newMeta.DeletionGracePeriodSeconds == nil {
newMeta.DeletionGracePeriodSeconds = oldMeta.DeletionGracePeriodSeconds
}
}
// TODO: needs to check if newMeta==nil && oldMeta !=nil after the repair logic is removed.
if newMeta.DeletionGracePeriodSeconds != nil && (oldMeta.DeletionGracePeriodSeconds == nil || *newMeta.DeletionGracePeriodSeconds != *oldMeta.DeletionGracePeriodSeconds) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("deletionGracePeriodSeconds"), newMeta.DeletionGracePeriodSeconds, "field is immutable; may only be changed via deletion"))
}
if newMeta.DeletionTimestamp != nil && (oldMeta.DeletionTimestamp == nil || !newMeta.DeletionTimestamp.Equal(*oldMeta.DeletionTimestamp)) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("deletionTimestamp"), newMeta.DeletionTimestamp, "field is immutable; may only be changed via deletion"))
}
// Finalizers cannot be added if the object is already being deleted.
if oldMeta.DeletionTimestamp != nil {
allErrs = append(allErrs, ValidateNoNewFinalizers(newMeta.Finalizers, oldMeta.Finalizers, fldPath.Child("finalizers"))...)
}
// Reject updates that don't specify a resource version
if len(newMeta.ResourceVersion) == 0 {
allErrs = append(allErrs, field.Invalid(fldPath.Child("resourceVersion"), newMeta.ResourceVersion, "must be specified for an update"))
}
// Generation shouldn't be decremented
if newMeta.Generation < oldMeta.Generation {
allErrs = append(allErrs, field.Invalid(fldPath.Child("generation"), newMeta.Generation, "must not be decremented"))
}
allErrs = append(allErrs, ValidateImmutableField(newMeta.Name, oldMeta.Name, fldPath.Child("name"))...)
allErrs = append(allErrs, ValidateImmutableField(newMeta.Namespace, oldMeta.Namespace, fldPath.Child("namespace"))...)
allErrs = append(allErrs, ValidateImmutableField(newMeta.UID, oldMeta.UID, fldPath.Child("uid"))...)
allErrs = append(allErrs, ValidateImmutableField(newMeta.CreationTimestamp, oldMeta.CreationTimestamp, fldPath.Child("creationTimestamp"))...)
allErrs = append(allErrs, ValidateImmutableField(newMeta.ClusterName, oldMeta.ClusterName, fldPath.Child("clusterName"))...)
allErrs = append(allErrs, v1validation.ValidateLabels(newMeta.Labels, fldPath.Child("labels"))...)
allErrs = append(allErrs, ValidateAnnotations(newMeta.Annotations, fldPath.Child("annotations"))...)
allErrs = append(allErrs, ValidateOwnerReferences(newMeta.OwnerReferences, fldPath.Child("ownerReferences"))...)
return allErrs
}

View file

@ -0,0 +1,500 @@
/*
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 validation
import (
"math/rand"
"reflect"
"strings"
"testing"
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/validation/field"
)
const (
maxLengthErrMsg = "must be no more than"
namePartErrMsg = "name part must consist of"
nameErrMsg = "a qualified name must consist of"
)
// Ensure custom name functions are allowed
func TestValidateObjectMetaCustomName(t *testing.T) {
errs := ValidateObjectMeta(
&metav1.ObjectMeta{Name: "test", GenerateName: "foo"},
false,
func(s string, prefix bool) []string {
if s == "test" {
return nil
}
return []string{"name-gen"}
},
field.NewPath("field"))
if len(errs) != 1 {
t.Fatalf("unexpected errors: %v", errs)
}
if !strings.Contains(errs[0].Error(), "name-gen") {
t.Errorf("unexpected error message: %v", errs)
}
}
// Ensure namespace names follow dns label format
func TestValidateObjectMetaNamespaces(t *testing.T) {
errs := ValidateObjectMeta(
&metav1.ObjectMeta{Name: "test", Namespace: "foo.bar"},
true,
func(s string, prefix bool) []string {
return nil
},
field.NewPath("field"))
if len(errs) != 1 {
t.Fatalf("unexpected errors: %v", errs)
}
if !strings.Contains(errs[0].Error(), `Invalid value: "foo.bar"`) {
t.Errorf("unexpected error message: %v", errs)
}
maxLength := 63
letters := []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
b := make([]rune, maxLength+1)
for i := range b {
b[i] = letters[rand.Intn(len(letters))]
}
errs = ValidateObjectMeta(
&metav1.ObjectMeta{Name: "test", Namespace: string(b)},
true,
func(s string, prefix bool) []string {
return nil
},
field.NewPath("field"))
if len(errs) != 2 {
t.Fatalf("unexpected errors: %v", errs)
}
if !strings.Contains(errs[0].Error(), "Invalid value") || !strings.Contains(errs[1].Error(), "Invalid value") {
t.Errorf("unexpected error message: %v", errs)
}
}
func TestValidateObjectMetaOwnerReferences(t *testing.T) {
trueVar := true
falseVar := false
testCases := []struct {
description string
ownerReferences []metav1.OwnerReference
expectError bool
expectedErrorMessage string
}{
{
description: "simple success - third party extension.",
ownerReferences: []metav1.OwnerReference{
{
APIVersion: "thirdpartyVersion",
Kind: "thirdpartyKind",
Name: "name",
UID: "1",
},
},
expectError: false,
expectedErrorMessage: "",
},
{
description: "simple failures - event shouldn't be set as an owner",
ownerReferences: []metav1.OwnerReference{
{
APIVersion: "v1",
Kind: "Event",
Name: "name",
UID: "1",
},
},
expectError: true,
expectedErrorMessage: "is disallowed from being an owner",
},
{
description: "simple controller ref success - one reference with Controller set",
ownerReferences: []metav1.OwnerReference{
{
APIVersion: "thirdpartyVersion",
Kind: "thirdpartyKind",
Name: "name",
UID: "1",
Controller: &falseVar,
},
{
APIVersion: "thirdpartyVersion",
Kind: "thirdpartyKind",
Name: "name",
UID: "2",
Controller: &trueVar,
},
{
APIVersion: "thirdpartyVersion",
Kind: "thirdpartyKind",
Name: "name",
UID: "3",
Controller: &falseVar,
},
{
APIVersion: "thirdpartyVersion",
Kind: "thirdpartyKind",
Name: "name",
UID: "4",
},
},
expectError: false,
expectedErrorMessage: "",
},
{
description: "simple controller ref failure - two references with Controller set",
ownerReferences: []metav1.OwnerReference{
{
APIVersion: "thirdpartyVersion",
Kind: "thirdpartyKind",
Name: "name",
UID: "1",
Controller: &falseVar,
},
{
APIVersion: "thirdpartyVersion",
Kind: "thirdpartyKind",
Name: "name",
UID: "2",
Controller: &trueVar,
},
{
APIVersion: "thirdpartyVersion",
Kind: "thirdpartyKind",
Name: "name",
UID: "3",
Controller: &trueVar,
},
{
APIVersion: "thirdpartyVersion",
Kind: "thirdpartyKind",
Name: "name",
UID: "4",
},
},
expectError: true,
expectedErrorMessage: "Only one reference can have Controller set to true",
},
}
for _, tc := range testCases {
errs := ValidateObjectMeta(
&metav1.ObjectMeta{Name: "test", Namespace: "test", OwnerReferences: tc.ownerReferences},
true,
func(s string, prefix bool) []string {
return nil
},
field.NewPath("field"))
if len(errs) != 0 && !tc.expectError {
t.Errorf("unexpected error: %v in test case %v", errs, tc.description)
}
if len(errs) == 0 && tc.expectError {
t.Errorf("expect error in test case %v", tc.description)
}
if len(errs) != 0 && !strings.Contains(errs[0].Error(), tc.expectedErrorMessage) {
t.Errorf("unexpected error message: %v in test case %v", errs, tc.description)
}
}
}
func TestValidateObjectMetaUpdateIgnoresCreationTimestamp(t *testing.T) {
if errs := ValidateObjectMetaUpdate(
&metav1.ObjectMeta{Name: "test", ResourceVersion: "1"},
&metav1.ObjectMeta{Name: "test", ResourceVersion: "1", CreationTimestamp: metav1.NewTime(time.Unix(10, 0))},
field.NewPath("field"),
); len(errs) != 0 {
t.Fatalf("unexpected errors: %v", errs)
}
if errs := ValidateObjectMetaUpdate(
&metav1.ObjectMeta{Name: "test", ResourceVersion: "1", CreationTimestamp: metav1.NewTime(time.Unix(10, 0))},
&metav1.ObjectMeta{Name: "test", ResourceVersion: "1"},
field.NewPath("field"),
); len(errs) != 0 {
t.Fatalf("unexpected errors: %v", errs)
}
if errs := ValidateObjectMetaUpdate(
&metav1.ObjectMeta{Name: "test", ResourceVersion: "1", CreationTimestamp: metav1.NewTime(time.Unix(10, 0))},
&metav1.ObjectMeta{Name: "test", ResourceVersion: "1", CreationTimestamp: metav1.NewTime(time.Unix(11, 0))},
field.NewPath("field"),
); len(errs) != 0 {
t.Fatalf("unexpected errors: %v", errs)
}
}
func TestValidateFinalizersUpdate(t *testing.T) {
testcases := map[string]struct {
Old metav1.ObjectMeta
New metav1.ObjectMeta
ExpectedErr string
}{
"invalid adding finalizers": {
Old: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &metav1.Time{}, Finalizers: []string{"x/a"}},
New: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &metav1.Time{}, Finalizers: []string{"x/a", "y/b"}},
ExpectedErr: "y/b",
},
"invalid changing finalizers": {
Old: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &metav1.Time{}, Finalizers: []string{"x/a"}},
New: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &metav1.Time{}, Finalizers: []string{"x/b"}},
ExpectedErr: "x/b",
},
"valid removing finalizers": {
Old: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &metav1.Time{}, Finalizers: []string{"x/a", "y/b"}},
New: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &metav1.Time{}, Finalizers: []string{"x/a"}},
ExpectedErr: "",
},
"valid adding finalizers for objects not being deleted": {
Old: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", Finalizers: []string{"x/a"}},
New: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", Finalizers: []string{"x/a", "y/b"}},
ExpectedErr: "",
},
}
for name, tc := range testcases {
errs := ValidateObjectMetaUpdate(&tc.New, &tc.Old, field.NewPath("field"))
if len(errs) == 0 {
if len(tc.ExpectedErr) != 0 {
t.Errorf("case: %q, expected error to contain %q", name, tc.ExpectedErr)
}
} else if e, a := tc.ExpectedErr, errs.ToAggregate().Error(); !strings.Contains(a, e) {
t.Errorf("case: %q, expected error to contain %q, got error %q", name, e, a)
}
}
}
func TestValidateFinalizersPreventConflictingFinalizers(t *testing.T) {
testcases := map[string]struct {
ObjectMeta metav1.ObjectMeta
ExpectedErr string
}{
"conflicting finalizers": {
ObjectMeta: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", Finalizers: []string{metav1.FinalizerOrphanDependents, metav1.FinalizerDeleteDependents}},
ExpectedErr: "cannot be both set",
},
}
for name, tc := range testcases {
errs := ValidateObjectMeta(&tc.ObjectMeta, false, NameIsDNSSubdomain, field.NewPath("field"))
if len(errs) == 0 {
if len(tc.ExpectedErr) != 0 {
t.Errorf("case: %q, expected error to contain %q", name, tc.ExpectedErr)
}
} else if e, a := tc.ExpectedErr, errs.ToAggregate().Error(); !strings.Contains(a, e) {
t.Errorf("case: %q, expected error to contain %q, got error %q", name, e, a)
}
}
}
func TestValidateObjectMetaUpdatePreventsDeletionFieldMutation(t *testing.T) {
now := metav1.NewTime(time.Unix(1000, 0).UTC())
later := metav1.NewTime(time.Unix(2000, 0).UTC())
gracePeriodShort := int64(30)
gracePeriodLong := int64(40)
testcases := map[string]struct {
Old metav1.ObjectMeta
New metav1.ObjectMeta
ExpectedNew metav1.ObjectMeta
ExpectedErrs []string
}{
"valid without deletion fields": {
Old: metav1.ObjectMeta{Name: "test", ResourceVersion: "1"},
New: metav1.ObjectMeta{Name: "test", ResourceVersion: "1"},
ExpectedNew: metav1.ObjectMeta{Name: "test", ResourceVersion: "1"},
ExpectedErrs: []string{},
},
"valid with deletion fields": {
Old: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &now, DeletionGracePeriodSeconds: &gracePeriodShort},
New: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &now, DeletionGracePeriodSeconds: &gracePeriodShort},
ExpectedNew: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &now, DeletionGracePeriodSeconds: &gracePeriodShort},
ExpectedErrs: []string{},
},
"invalid set deletionTimestamp": {
Old: metav1.ObjectMeta{Name: "test", ResourceVersion: "1"},
New: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &now},
ExpectedNew: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &now},
ExpectedErrs: []string{"field.deletionTimestamp: Invalid value: 1970-01-01 00:16:40 +0000 UTC: field is immutable; may only be changed via deletion"},
},
"invalid clear deletionTimestamp": {
Old: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &now},
New: metav1.ObjectMeta{Name: "test", ResourceVersion: "1"},
ExpectedNew: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &now},
ExpectedErrs: []string{}, // no errors, validation copies the old value
},
"invalid change deletionTimestamp": {
Old: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &now},
New: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &later},
ExpectedNew: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &now},
ExpectedErrs: []string{}, // no errors, validation copies the old value
},
"invalid set deletionGracePeriodSeconds": {
Old: metav1.ObjectMeta{Name: "test", ResourceVersion: "1"},
New: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionGracePeriodSeconds: &gracePeriodShort},
ExpectedNew: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionGracePeriodSeconds: &gracePeriodShort},
ExpectedErrs: []string{"field.deletionGracePeriodSeconds: Invalid value: 30: field is immutable; may only be changed via deletion"},
},
"invalid clear deletionGracePeriodSeconds": {
Old: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionGracePeriodSeconds: &gracePeriodShort},
New: metav1.ObjectMeta{Name: "test", ResourceVersion: "1"},
ExpectedNew: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionGracePeriodSeconds: &gracePeriodShort},
ExpectedErrs: []string{}, // no errors, validation copies the old value
},
"invalid change deletionGracePeriodSeconds": {
Old: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionGracePeriodSeconds: &gracePeriodShort},
New: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionGracePeriodSeconds: &gracePeriodLong},
ExpectedNew: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionGracePeriodSeconds: &gracePeriodLong},
ExpectedErrs: []string{"field.deletionGracePeriodSeconds: Invalid value: 40: field is immutable; may only be changed via deletion"},
},
}
for k, tc := range testcases {
errs := ValidateObjectMetaUpdate(&tc.New, &tc.Old, field.NewPath("field"))
if len(errs) != len(tc.ExpectedErrs) {
t.Logf("%s: Expected: %#v", k, tc.ExpectedErrs)
t.Logf("%s: Got: %#v", k, errs)
t.Errorf("%s: expected %d errors, got %d", k, len(tc.ExpectedErrs), len(errs))
continue
}
for i := range errs {
if errs[i].Error() != tc.ExpectedErrs[i] {
t.Errorf("%s: error #%d: expected %q, got %q", k, i, tc.ExpectedErrs[i], errs[i].Error())
}
}
if !reflect.DeepEqual(tc.New, tc.ExpectedNew) {
t.Errorf("%s: Expected after validation:\n%#v\ngot\n%#v", k, tc.ExpectedNew, tc.New)
}
}
}
func TestObjectMetaGenerationUpdate(t *testing.T) {
testcases := map[string]struct {
Old metav1.ObjectMeta
New metav1.ObjectMeta
ExpectedErrs []string
}{
"invalid generation change - decremented": {
Old: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", Generation: 5},
New: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", Generation: 4},
ExpectedErrs: []string{"field.generation: Invalid value: 4: must not be decremented"},
},
"valid generation change - incremented by one": {
Old: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", Generation: 1},
New: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", Generation: 2},
ExpectedErrs: []string{},
},
"valid generation field - not updated": {
Old: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", Generation: 5},
New: metav1.ObjectMeta{Name: "test", ResourceVersion: "1", Generation: 5},
ExpectedErrs: []string{},
},
}
for k, tc := range testcases {
errList := []string{}
errs := ValidateObjectMetaUpdate(&tc.New, &tc.Old, field.NewPath("field"))
if len(errs) != len(tc.ExpectedErrs) {
t.Logf("%s: Expected: %#v", k, tc.ExpectedErrs)
for _, err := range errs {
errList = append(errList, err.Error())
}
t.Logf("%s: Got: %#v", k, errList)
t.Errorf("%s: expected %d errors, got %d", k, len(tc.ExpectedErrs), len(errs))
continue
}
for i := range errList {
if errList[i] != tc.ExpectedErrs[i] {
t.Errorf("%s: error #%d: expected %q, got %q", k, i, tc.ExpectedErrs[i], errList[i])
}
}
}
}
// Ensure trailing slash is allowed in generate name
func TestValidateObjectMetaTrimsTrailingSlash(t *testing.T) {
errs := ValidateObjectMeta(
&metav1.ObjectMeta{Name: "test", GenerateName: "foo-"},
false,
NameIsDNSSubdomain,
field.NewPath("field"))
if len(errs) != 0 {
t.Fatalf("unexpected errors: %v", errs)
}
}
func TestValidateAnnotations(t *testing.T) {
successCases := []map[string]string{
{"simple": "bar"},
{"now-with-dashes": "bar"},
{"1-starts-with-num": "bar"},
{"1234": "bar"},
{"simple/simple": "bar"},
{"now-with-dashes/simple": "bar"},
{"now-with-dashes/now-with-dashes": "bar"},
{"now.with.dots/simple": "bar"},
{"now-with.dashes-and.dots/simple": "bar"},
{"1-num.2-num/3-num": "bar"},
{"1234/5678": "bar"},
{"1.2.3.4/5678": "bar"},
{"UpperCase123": "bar"},
{"a": strings.Repeat("b", totalAnnotationSizeLimitB-1)},
{
"a": strings.Repeat("b", totalAnnotationSizeLimitB/2-1),
"c": strings.Repeat("d", totalAnnotationSizeLimitB/2-1),
},
}
for i := range successCases {
errs := ValidateAnnotations(successCases[i], field.NewPath("field"))
if len(errs) != 0 {
t.Errorf("case[%d] expected success, got %#v", i, errs)
}
}
nameErrorCases := []struct {
annotations map[string]string
expect string
}{
{map[string]string{"nospecialchars^=@": "bar"}, namePartErrMsg},
{map[string]string{"cantendwithadash-": "bar"}, namePartErrMsg},
{map[string]string{"only/one/slash": "bar"}, nameErrMsg},
{map[string]string{strings.Repeat("a", 254): "bar"}, maxLengthErrMsg},
}
for i := range nameErrorCases {
errs := ValidateAnnotations(nameErrorCases[i].annotations, field.NewPath("field"))
if len(errs) != 1 {
t.Errorf("case[%d]: expected failure", i)
} else {
if !strings.Contains(errs[0].Detail, nameErrorCases[i].expect) {
t.Errorf("case[%d]: error details do not include %q: %q", i, nameErrorCases[i].expect, errs[0].Detail)
}
}
}
totalSizeErrorCases := []map[string]string{
{"a": strings.Repeat("b", totalAnnotationSizeLimitB)},
{
"a": strings.Repeat("b", totalAnnotationSizeLimitB/2),
"c": strings.Repeat("d", totalAnnotationSizeLimitB/2),
},
}
for i := range totalSizeErrorCases {
errs := ValidateAnnotations(totalSizeErrorCases[i], field.NewPath("field"))
if len(errs) != 1 {
t.Errorf("case[%d] expected failure", i)
}
}
}

View file

@ -42,10 +42,16 @@ type GroupVersionFactoryArgs struct {
// GroupMetaFactoryArgs contains the group-level args of a GroupMetaFactory.
type GroupMetaFactoryArgs struct {
// GroupName is the name of the API-Group
//
// example: 'servicecatalog.k8s.io'
GroupName string
VersionPreferenceOrder []string
// ImportPrefix is the base go package of the API-Group
//
// example: 'k8s.io/kubernetes/pkg/apis/autoscaling'
ImportPrefix string
// RootScopedKinds are resources that are not namespaced.
RootScopedKinds sets.String // nil is allowed
IgnoredKinds sets.String // nil is allowed

View file

@ -49,8 +49,8 @@ func TestListOptions(t *testing.T) {
// verify failing conversion
for i, failingObject := range []*metav1.ListOptions{
&metav1.ListOptions{LabelSelector: "a!!!"},
&metav1.ListOptions{FieldSelector: "a!!!"},
{LabelSelector: "a!!!"},
{FieldSelector: "a!!!"},
} {
out = &ListOptions{}
if err := scheme.Convert(failingObject, out, nil); err == nil {

View file

@ -0,0 +1,264 @@
/*
Copyright 2014 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 v1
import (
"fmt"
"strconv"
"strings"
"k8s.io/apimachinery/pkg/api/resource"
"k8s.io/apimachinery/pkg/conversion"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/intstr"
)
func AddConversionFuncs(scheme *runtime.Scheme) error {
return scheme.AddConversionFuncs(
Convert_v1_TypeMeta_To_v1_TypeMeta,
Convert_unversioned_ListMeta_To_unversioned_ListMeta,
Convert_intstr_IntOrString_To_intstr_IntOrString,
Convert_unversioned_Time_To_unversioned_Time,
Convert_Slice_string_To_unversioned_Time,
Convert_resource_Quantity_To_resource_Quantity,
Convert_string_To_labels_Selector,
Convert_labels_Selector_To_string,
Convert_string_To_fields_Selector,
Convert_fields_Selector_To_string,
Convert_Pointer_bool_To_bool,
Convert_bool_To_Pointer_bool,
Convert_Pointer_string_To_string,
Convert_string_To_Pointer_string,
Convert_Pointer_int64_To_int,
Convert_int_To_Pointer_int64,
Convert_Pointer_int32_To_int32,
Convert_int32_To_Pointer_int32,
Convert_Pointer_float64_To_float64,
Convert_float64_To_Pointer_float64,
Convert_map_to_unversioned_LabelSelector,
Convert_unversioned_LabelSelector_to_map,
Convert_Slice_string_To_Slice_int32,
)
}
func Convert_Pointer_float64_To_float64(in **float64, out *float64, s conversion.Scope) error {
if *in == nil {
*out = 0
return nil
}
*out = float64(**in)
return nil
}
func Convert_float64_To_Pointer_float64(in *float64, out **float64, s conversion.Scope) error {
temp := float64(*in)
*out = &temp
return nil
}
func Convert_Pointer_int32_To_int32(in **int32, out *int32, s conversion.Scope) error {
if *in == nil {
*out = 0
return nil
}
*out = int32(**in)
return nil
}
func Convert_int32_To_Pointer_int32(in *int32, out **int32, s conversion.Scope) error {
temp := int32(*in)
*out = &temp
return nil
}
func Convert_Pointer_int64_To_int(in **int64, out *int, s conversion.Scope) error {
if *in == nil {
*out = 0
return nil
}
*out = int(**in)
return nil
}
func Convert_int_To_Pointer_int64(in *int, out **int64, s conversion.Scope) error {
temp := int64(*in)
*out = &temp
return nil
}
func Convert_Pointer_string_To_string(in **string, out *string, s conversion.Scope) error {
if *in == nil {
*out = ""
return nil
}
*out = **in
return nil
}
func Convert_string_To_Pointer_string(in *string, out **string, s conversion.Scope) error {
if in == nil {
stringVar := ""
*out = &stringVar
return nil
}
*out = in
return nil
}
func Convert_Pointer_bool_To_bool(in **bool, out *bool, s conversion.Scope) error {
if *in == nil {
*out = false
return nil
}
*out = **in
return nil
}
func Convert_bool_To_Pointer_bool(in *bool, out **bool, s conversion.Scope) error {
if in == nil {
boolVar := false
*out = &boolVar
return nil
}
*out = in
return nil
}
// +k8s:conversion-fn=drop
func Convert_v1_TypeMeta_To_v1_TypeMeta(in, out *TypeMeta, s conversion.Scope) error {
// These values are explicitly not copied
//out.APIVersion = in.APIVersion
//out.Kind = in.Kind
return nil
}
// +k8s:conversion-fn=copy-only
func Convert_unversioned_ListMeta_To_unversioned_ListMeta(in, out *ListMeta, s conversion.Scope) error {
*out = *in
return nil
}
// +k8s:conversion-fn=copy-only
func Convert_intstr_IntOrString_To_intstr_IntOrString(in, out *intstr.IntOrString, s conversion.Scope) error {
*out = *in
return nil
}
// +k8s:conversion-fn=copy-only
func Convert_unversioned_Time_To_unversioned_Time(in *Time, out *Time, s conversion.Scope) error {
// Cannot deep copy these, because time.Time has unexported fields.
*out = *in
return nil
}
// Convert_Slice_string_To_unversioned_Time allows converting a URL query parameter value
func Convert_Slice_string_To_unversioned_Time(input *[]string, out *Time, s conversion.Scope) error {
str := ""
if len(*input) > 0 {
str = (*input)[0]
}
return out.UnmarshalQueryParameter(str)
}
func Convert_string_To_labels_Selector(in *string, out *labels.Selector, s conversion.Scope) error {
selector, err := labels.Parse(*in)
if err != nil {
return err
}
*out = selector
return nil
}
func Convert_string_To_fields_Selector(in *string, out *fields.Selector, s conversion.Scope) error {
selector, err := fields.ParseSelector(*in)
if err != nil {
return err
}
*out = selector
return nil
}
func Convert_labels_Selector_To_string(in *labels.Selector, out *string, s conversion.Scope) error {
if *in == nil {
return nil
}
*out = (*in).String()
return nil
}
func Convert_fields_Selector_To_string(in *fields.Selector, out *string, s conversion.Scope) error {
if *in == nil {
return nil
}
*out = (*in).String()
return nil
}
// +k8s:conversion-fn=copy-only
func Convert_resource_Quantity_To_resource_Quantity(in *resource.Quantity, out *resource.Quantity, s conversion.Scope) error {
*out = *in
return nil
}
func Convert_map_to_unversioned_LabelSelector(in *map[string]string, out *LabelSelector, s conversion.Scope) error {
if in == nil {
return nil
}
out = new(LabelSelector)
for labelKey, labelValue := range *in {
AddLabelToSelector(out, labelKey, labelValue)
}
return nil
}
func Convert_unversioned_LabelSelector_to_map(in *LabelSelector, out *map[string]string, s conversion.Scope) error {
var err error
*out, err = LabelSelectorAsMap(in)
return err
}
// Convert_Slice_string_To_Slice_int32 converts multiple query parameters or
// a single query parameter with a comma delimited value to multiple int32.
// This is used for port forwarding which needs the ports as int32.
func Convert_Slice_string_To_Slice_int32(in *[]string, out *[]int32, s conversion.Scope) error {
for _, s := range *in {
for _, v := range strings.Split(s, ",") {
x, err := strconv.ParseUint(v, 10, 16)
if err != nil {
return fmt.Errorf("cannot convert to []int32: %v", err)
}
*out = append(*out, int32(x))
}
}
return nil
}

View file

@ -512,6 +512,12 @@ func (m *DeleteOptions) MarshalTo(data []byte) (int, error) {
}
i++
}
if m.PropagationPolicy != nil {
data[i] = 0x22
i++
i = encodeVarintGenerated(data, i, uint64(len(*m.PropagationPolicy)))
i += copy(data[i:], *m.PropagationPolicy)
}
return i, nil
}
@ -1087,6 +1093,16 @@ func (m *OwnerReference) MarshalTo(data []byte) (int, error) {
}
i++
}
if m.BlockOwnerDeletion != nil {
data[i] = 0x38
i++
if *m.BlockOwnerDeletion {
data[i] = 1
} else {
data[i] = 0
}
i++
}
return i, nil
}
@ -1539,6 +1555,10 @@ func (m *DeleteOptions) Size() (n int) {
if m.OrphanDependents != nil {
n += 2
}
if m.PropagationPolicy != nil {
l = len(*m.PropagationPolicy)
n += 1 + l + sovGenerated(uint64(l))
}
return n
}
@ -1763,6 +1783,9 @@ func (m *OwnerReference) Size() (n int) {
if m.Controller != nil {
n += 2
}
if m.BlockOwnerDeletion != nil {
n += 2
}
return n
}
@ -1957,6 +1980,7 @@ func (this *DeleteOptions) String() string {
`GracePeriodSeconds:` + valueToStringGenerated(this.GracePeriodSeconds) + `,`,
`Preconditions:` + strings.Replace(fmt.Sprintf("%v", this.Preconditions), "Preconditions", "Preconditions", 1) + `,`,
`OrphanDependents:` + valueToStringGenerated(this.OrphanDependents) + `,`,
`PropagationPolicy:` + valueToStringGenerated(this.PropagationPolicy) + `,`,
`}`,
}, "")
return s
@ -2115,6 +2139,7 @@ func (this *OwnerReference) String() string {
`UID:` + fmt.Sprintf("%v", this.UID) + `,`,
`APIVersion:` + fmt.Sprintf("%v", this.APIVersion) + `,`,
`Controller:` + valueToStringGenerated(this.Controller) + `,`,
`BlockOwnerDeletion:` + valueToStringGenerated(this.BlockOwnerDeletion) + `,`,
`}`,
}, "")
return s
@ -2997,6 +3022,36 @@ func (m *DeleteOptions) Unmarshal(data []byte) error {
}
b := bool(v != 0)
m.OrphanDependents = &b
case 4:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field PropagationPolicy", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := data[iNdEx]
iNdEx++
stringLen |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthGenerated
}
postIndex := iNdEx + intStringLen
if postIndex > l {
return io.ErrUnexpectedEOF
}
s := DeletionPropagation(data[iNdEx:postIndex])
m.PropagationPolicy = &s
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipGenerated(data[iNdEx:])
@ -5379,6 +5434,27 @@ func (m *OwnerReference) Unmarshal(data []byte) error {
}
b := bool(v != 0)
m.Controller = &b
case 7:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field BlockOwnerDeletion", wireType)
}
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := data[iNdEx]
iNdEx++
v |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
b := bool(v != 0)
m.BlockOwnerDeletion = &b
default:
iNdEx = preIndex
skippy, err := skipGenerated(data[iNdEx:])
@ -6700,137 +6776,140 @@ var (
)
var fileDescriptorGenerated = []byte{
// 2098 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xcc, 0x19, 0x4b, 0x6f, 0x23, 0x49,
0x39, 0x6d, 0xc7, 0x1e, 0xfb, 0x73, 0x9c, 0x47, 0x91, 0x01, 0x6f, 0x24, 0xec, 0x6c, 0xef, 0x0a,
0x65, 0x61, 0xd6, 0x26, 0x59, 0x58, 0x0d, 0x03, 0x0c, 0xc4, 0x71, 0x26, 0x8a, 0x76, 0x32, 0x89,
0x2a, 0x3b, 0x83, 0x58, 0x46, 0x88, 0x8e, 0xbb, 0xe2, 0x34, 0x69, 0x77, 0x37, 0x55, 0x65, 0x4f,
0xc2, 0x1e, 0x58, 0x69, 0x41, 0x70, 0x40, 0x68, 0x8e, 0x1c, 0x10, 0xda, 0x11, 0xdc, 0xb8, 0xf1,
0x27, 0x98, 0xe3, 0x4a, 0x5c, 0x38, 0x20, 0x8b, 0x09, 0x07, 0x8e, 0xdc, 0x23, 0x0e, 0xa8, 0xaa,
0xab, 0xfa, 0xe1, 0x8c, 0x37, 0x6d, 0x76, 0x0f, 0x9c, 0xe2, 0xfa, 0xde, 0xf5, 0xd5, 0xf7, 0xec,
0xc0, 0xde, 0xe9, 0x6d, 0xd6, 0x74, 0xfc, 0xd6, 0xe9, 0xe0, 0x88, 0x50, 0x8f, 0x70, 0xc2, 0x5a,
0x43, 0xe2, 0xd9, 0x3e, 0x6d, 0x29, 0x84, 0x15, 0x38, 0x7d, 0xab, 0x7b, 0xe2, 0x78, 0x84, 0x9e,
0xb7, 0x82, 0xd3, 0x9e, 0x00, 0xb0, 0x56, 0x9f, 0x70, 0xab, 0x35, 0x5c, 0x6f, 0xf5, 0x88, 0x47,
0xa8, 0xc5, 0x89, 0xdd, 0x0c, 0xa8, 0xcf, 0x7d, 0xf4, 0x7a, 0xc8, 0xd5, 0x4c, 0x72, 0x35, 0x83,
0xd3, 0x9e, 0x00, 0xb0, 0xa6, 0xe0, 0x6a, 0x0e, 0xd7, 0x57, 0xde, 0xec, 0x39, 0xfc, 0x64, 0x70,
0xd4, 0xec, 0xfa, 0xfd, 0x56, 0xcf, 0xef, 0xf9, 0x2d, 0xc9, 0x7c, 0x34, 0x38, 0x96, 0x27, 0x79,
0x90, 0xbf, 0x42, 0xa1, 0x2b, 0x13, 0x4d, 0xa1, 0x03, 0x8f, 0x3b, 0x7d, 0x32, 0x6e, 0xc5, 0xca,
0xdb, 0xd7, 0x31, 0xb0, 0xee, 0x09, 0xe9, 0x5b, 0x57, 0xf8, 0xde, 0x9a, 0xc4, 0x37, 0xe0, 0x8e,
0xdb, 0x72, 0x3c, 0xce, 0x38, 0x1d, 0x67, 0x32, 0xff, 0x92, 0x87, 0xd2, 0xe6, 0xc1, 0xee, 0x0e,
0xf5, 0x07, 0x01, 0x5a, 0x85, 0x59, 0xcf, 0xea, 0x93, 0x9a, 0xb1, 0x6a, 0xac, 0x95, 0xdb, 0x73,
0xcf, 0x47, 0x8d, 0x99, 0x8b, 0x51, 0x63, 0xf6, 0x81, 0xd5, 0x27, 0x58, 0x62, 0x90, 0x0b, 0xa5,
0x21, 0xa1, 0xcc, 0xf1, 0x3d, 0x56, 0xcb, 0xad, 0xe6, 0xd7, 0x2a, 0x1b, 0x77, 0x9b, 0x59, 0x9c,
0xd6, 0x94, 0x0a, 0x1e, 0x85, 0xac, 0xf7, 0x7c, 0xda, 0x71, 0x58, 0xd7, 0x1f, 0x12, 0x7a, 0xde,
0x5e, 0x54, 0x5a, 0x4a, 0x0a, 0xc9, 0x70, 0xa4, 0x01, 0xfd, 0xdc, 0x80, 0xc5, 0x80, 0x92, 0x63,
0x42, 0x29, 0xb1, 0x15, 0xbe, 0x96, 0x5f, 0x35, 0x3e, 0x03, 0xb5, 0x35, 0xa5, 0x76, 0xf1, 0x60,
0x4c, 0x3e, 0xbe, 0xa2, 0x11, 0xfd, 0xc1, 0x80, 0x15, 0x46, 0xe8, 0x90, 0xd0, 0x4d, 0xdb, 0xa6,
0x84, 0xb1, 0xf6, 0xf9, 0x96, 0xeb, 0x10, 0x8f, 0x6f, 0xed, 0x76, 0x30, 0xab, 0xcd, 0x4a, 0x3f,
0x7c, 0x27, 0x9b, 0x41, 0x87, 0x93, 0xe4, 0xb4, 0x4d, 0x65, 0xd1, 0xca, 0x44, 0x12, 0x86, 0x3f,
0xc1, 0x0c, 0xf3, 0x18, 0xe6, 0xf4, 0x43, 0xde, 0x77, 0x18, 0x47, 0x8f, 0xa0, 0xd8, 0x13, 0x07,
0x56, 0x33, 0xa4, 0x81, 0xcd, 0x6c, 0x06, 0x6a, 0x19, 0xed, 0x79, 0x65, 0x4f, 0x51, 0x1e, 0x19,
0x56, 0xd2, 0xcc, 0x0f, 0x73, 0x50, 0xd9, 0x3c, 0xd8, 0xc5, 0x84, 0xf9, 0x03, 0xda, 0x25, 0x19,
0x82, 0x66, 0x03, 0x40, 0xfc, 0x65, 0x81, 0xd5, 0x25, 0x76, 0x2d, 0xb7, 0x6a, 0xac, 0x95, 0xda,
0x48, 0xd1, 0xc1, 0x83, 0x08, 0x83, 0x13, 0x54, 0x42, 0xea, 0xa9, 0xe3, 0xd9, 0xf2, 0xb5, 0x13,
0x52, 0xdf, 0x71, 0x3c, 0x1b, 0x4b, 0x0c, 0xba, 0x0f, 0x85, 0x21, 0xa1, 0x47, 0xc2, 0xff, 0x22,
0x20, 0xbe, 0x92, 0xed, 0x7a, 0x8f, 0x04, 0x4b, 0xbb, 0x7c, 0x31, 0x6a, 0x14, 0xe4, 0x4f, 0x1c,
0x0a, 0x41, 0x4d, 0x00, 0x76, 0xe2, 0x53, 0x2e, 0xcd, 0xa9, 0x15, 0x56, 0xf3, 0x6b, 0xe5, 0xf6,
0xbc, 0xb0, 0xef, 0x30, 0x82, 0xe2, 0x04, 0x85, 0xf9, 0x67, 0x03, 0x16, 0x12, 0x5e, 0x90, 0x1e,
0xbf, 0x0d, 0x73, 0xbd, 0x44, 0xbc, 0x29, 0x8f, 0x2c, 0x2b, 0xdb, 0xe7, 0x92, 0xb1, 0x88, 0x53,
0x94, 0x88, 0x40, 0x99, 0x2a, 0x49, 0x3a, 0xaf, 0xd6, 0x33, 0x3f, 0x97, 0xb6, 0x21, 0xd6, 0x94,
0x00, 0x32, 0x1c, 0x4b, 0x36, 0xff, 0x65, 0xc8, 0xa7, 0xd3, 0x99, 0x86, 0xd6, 0x12, 0xd9, 0x6c,
0xc8, 0x2b, 0xcf, 0x4d, 0xc8, 0xc4, 0x6b, 0x52, 0x20, 0xf7, 0x7f, 0x91, 0x02, 0x77, 0x4a, 0xbf,
0xfd, 0xa8, 0x31, 0xf3, 0xc1, 0xdf, 0x57, 0x67, 0xcc, 0x5f, 0xe6, 0xa0, 0xda, 0x21, 0x2e, 0xe1,
0x64, 0x3f, 0xe0, 0xf2, 0x06, 0xf7, 0x00, 0xf5, 0xa8, 0xd5, 0x25, 0x07, 0x84, 0x3a, 0xbe, 0x7d,
0x48, 0xba, 0xbe, 0x67, 0x33, 0xf9, 0x44, 0xf9, 0xf6, 0xe7, 0x2f, 0x46, 0x0d, 0xb4, 0x73, 0x05,
0x8b, 0x5f, 0xc2, 0x81, 0x5c, 0xa8, 0x06, 0x54, 0xfe, 0x76, 0xb8, 0x2a, 0x83, 0x22, 0xfc, 0xde,
0xca, 0x76, 0xf7, 0x83, 0x24, 0x6b, 0x7b, 0xe9, 0x62, 0xd4, 0xa8, 0xa6, 0x40, 0x38, 0x2d, 0x1c,
0x7d, 0x17, 0x16, 0x7d, 0x1a, 0x9c, 0x58, 0x5e, 0x87, 0x04, 0xc4, 0xb3, 0x89, 0xc7, 0x99, 0x4c,
0x89, 0x52, 0x7b, 0x59, 0x14, 0xaf, 0xfd, 0x31, 0x1c, 0xbe, 0x42, 0x6d, 0xee, 0x42, 0xa9, 0x33,
0xa0, 0x96, 0x10, 0x87, 0xbe, 0x0d, 0x25, 0x5b, 0xfd, 0x56, 0x37, 0x7f, 0x55, 0x57, 0x5f, 0x4d,
0x73, 0x39, 0x6a, 0x54, 0x45, 0x93, 0x69, 0x6a, 0x00, 0x8e, 0x58, 0xcc, 0xc7, 0x50, 0xdd, 0x3e,
0x0b, 0x7c, 0xca, 0xb5, 0x4f, 0xbf, 0x04, 0x45, 0x22, 0x01, 0x52, 0x5a, 0x29, 0x2e, 0x19, 0x21,
0x19, 0x56, 0x58, 0xf4, 0x1a, 0x14, 0xc8, 0x99, 0xd5, 0xe5, 0x2a, 0xf7, 0xab, 0x8a, 0xac, 0xb0,
0x2d, 0x80, 0x38, 0xc4, 0x99, 0xfb, 0x00, 0x3b, 0x24, 0x12, 0xbd, 0x09, 0x0b, 0x3a, 0x6e, 0xd3,
0xe9, 0xf4, 0x05, 0xc5, 0xbc, 0x80, 0xd3, 0x68, 0x3c, 0x4e, 0x6f, 0x3e, 0x86, 0xb2, 0x4c, 0x39,
0x51, 0x33, 0x84, 0x09, 0x32, 0xe3, 0x94, 0x94, 0xc8, 0x04, 0x49, 0x81, 0x43, 0x5c, 0x54, 0x74,
0x72, 0x93, 0x8a, 0x4e, 0x22, 0xc2, 0x5c, 0xa8, 0x86, 0xbc, 0xba, 0x0e, 0x66, 0xd2, 0x70, 0x0b,
0x4a, 0xda, 0x4c, 0xa5, 0x25, 0xea, 0x7f, 0x5a, 0x10, 0x8e, 0x28, 0x12, 0xda, 0x4e, 0x20, 0x55,
0x3e, 0xb2, 0x29, 0x7b, 0x03, 0x6e, 0xa8, 0x04, 0x56, 0xba, 0x16, 0x14, 0xd9, 0x0d, 0xed, 0x33,
0x8d, 0x4f, 0x68, 0xfa, 0x19, 0xd4, 0x26, 0x35, 0xcd, 0x4f, 0x51, 0xe0, 0xb2, 0x9b, 0x62, 0xfe,
0xc6, 0x80, 0xc5, 0xa4, 0xa4, 0xec, 0xcf, 0x97, 0x5d, 0xc9, 0xf5, 0xed, 0x25, 0xe1, 0x91, 0xdf,
0x1b, 0xb0, 0x9c, 0xba, 0xda, 0x54, 0x2f, 0x3e, 0x85, 0x51, 0xc9, 0xe0, 0xc8, 0x4f, 0x11, 0x1c,
0x7f, 0xcd, 0x41, 0xf5, 0xbe, 0x75, 0x44, 0xdc, 0x43, 0xe2, 0x92, 0x2e, 0xf7, 0x29, 0x7a, 0x1f,
0x2a, 0x7d, 0x8b, 0x77, 0x4f, 0x24, 0x54, 0x0f, 0x00, 0x9d, 0x6c, 0x25, 0x2a, 0x25, 0xa9, 0xb9,
0x17, 0x8b, 0xd9, 0xf6, 0x38, 0x3d, 0x6f, 0x7f, 0x4e, 0x99, 0x54, 0x49, 0x60, 0x70, 0x52, 0x9b,
0x9c, 0xda, 0xe4, 0x79, 0xfb, 0x2c, 0x10, 0x35, 0x7a, 0xfa, 0x61, 0x31, 0x65, 0x02, 0x26, 0x3f,
0x19, 0x38, 0x94, 0xf4, 0x89, 0xc7, 0xe3, 0xa9, 0x6d, 0x6f, 0x4c, 0x3e, 0xbe, 0xa2, 0x71, 0xe5,
0x2e, 0x2c, 0x8e, 0x1b, 0x8f, 0x16, 0x21, 0x7f, 0x4a, 0xce, 0xc3, 0xf7, 0xc2, 0xe2, 0x27, 0x5a,
0x86, 0xc2, 0xd0, 0x72, 0x07, 0x2a, 0x1b, 0x71, 0x78, 0xb8, 0x93, 0xbb, 0x6d, 0x98, 0x7f, 0x34,
0xa0, 0x36, 0xc9, 0x10, 0xf4, 0xc5, 0x84, 0xa0, 0x76, 0x45, 0x59, 0x95, 0x7f, 0x87, 0x9c, 0x87,
0x52, 0xb7, 0xa1, 0xe4, 0x07, 0x62, 0xce, 0xf6, 0xa9, 0x7a, 0xf5, 0x37, 0xf4, 0x4b, 0xee, 0x2b,
0xf8, 0xe5, 0xa8, 0x71, 0x33, 0x25, 0x5e, 0x23, 0x70, 0xc4, 0x8a, 0x4c, 0x28, 0x4a, 0x7b, 0x44,
0xcd, 0x17, 0xdd, 0x19, 0x44, 0x6d, 0x7d, 0x24, 0x21, 0x58, 0x61, 0xcc, 0xf7, 0xa1, 0x24, 0x86,
0x8f, 0x3d, 0xc2, 0x2d, 0x11, 0x40, 0x8c, 0xb8, 0xc7, 0xf7, 0x1d, 0xef, 0x54, 0x99, 0x16, 0x05,
0xd0, 0xa1, 0x82, 0xe3, 0x88, 0xe2, 0x65, 0x25, 0x36, 0x37, 0x65, 0x89, 0xfd, 0x53, 0x0e, 0x2a,
0x42, 0xbb, 0xae, 0xda, 0xdf, 0x84, 0xaa, 0x9b, 0xbc, 0x93, 0xb2, 0xe2, 0xa6, 0x12, 0x98, 0x8e,
0x52, 0x9c, 0xa6, 0x15, 0xcc, 0xc7, 0x0e, 0x71, 0xed, 0x88, 0x39, 0x97, 0x66, 0xbe, 0x97, 0x44,
0xe2, 0x34, 0xad, 0xc8, 0xc5, 0x27, 0xe2, 0xb5, 0x55, 0x77, 0x8c, 0x72, 0xf1, 0x7b, 0x02, 0x88,
0x43, 0xdc, 0xcb, 0x6e, 0x3c, 0x3b, 0xdd, 0x8d, 0xd1, 0x1d, 0x98, 0x17, 0xed, 0xd1, 0x1f, 0x70,
0x3d, 0x42, 0x14, 0x64, 0x23, 0x45, 0x17, 0xa3, 0xc6, 0xfc, 0xbb, 0x29, 0x0c, 0x1e, 0xa3, 0x34,
0x3f, 0x04, 0x80, 0xfd, 0xa3, 0x1f, 0x93, 0x6e, 0xf8, 0x5a, 0xd7, 0x0f, 0xce, 0xa2, 0xde, 0xaa,
0x7d, 0x4d, 0x40, 0x95, 0x43, 0xe2, 0x7a, 0x9b, 0xc0, 0xe1, 0x14, 0x25, 0x6a, 0x41, 0x39, 0x1a,
0xa6, 0x55, 0x2d, 0x59, 0x52, 0x6c, 0xe5, 0x68, 0xe2, 0xc6, 0x31, 0x4d, 0x2a, 0x74, 0x66, 0xaf,
0x0d, 0x9d, 0x36, 0xe4, 0x07, 0x8e, 0x2d, 0xaf, 0x5e, 0x6e, 0x7f, 0x55, 0x87, 0xff, 0xc3, 0xdd,
0xce, 0xe5, 0xa8, 0xf1, 0xea, 0xa4, 0x35, 0x94, 0x9f, 0x07, 0x84, 0x35, 0x1f, 0xee, 0x76, 0xb0,
0x60, 0x7e, 0xd9, 0x63, 0x14, 0xa7, 0x7c, 0x8c, 0x0d, 0x00, 0x75, 0x6b, 0xc1, 0x7d, 0x23, 0x7c,
0x08, 0xbd, 0x58, 0xec, 0x44, 0x18, 0x9c, 0xa0, 0x42, 0x0c, 0x96, 0xba, 0x94, 0xc8, 0xdf, 0xe2,
0xb9, 0x18, 0xb7, 0xfa, 0x41, 0xad, 0x24, 0x67, 0xb8, 0x2f, 0x67, 0xab, 0x4e, 0x82, 0xad, 0xfd,
0x8a, 0x52, 0xb3, 0xb4, 0x35, 0x2e, 0x0c, 0x5f, 0x95, 0x8f, 0x7c, 0x58, 0xb2, 0xc5, 0x34, 0x9a,
0x52, 0x5a, 0x9e, 0x5a, 0xe9, 0x4d, 0xa1, 0xb0, 0x33, 0x2e, 0x08, 0x5f, 0x95, 0x8d, 0x7e, 0x08,
0x2b, 0x1a, 0x78, 0x75, 0xae, 0xad, 0x81, 0xf4, 0x54, 0x5d, 0x4c, 0xda, 0x9d, 0x89, 0x54, 0xf8,
0x13, 0x24, 0x20, 0x1b, 0x8a, 0x6e, 0xd8, 0x5b, 0x2a, 0xb2, 0xb0, 0x7f, 0x2b, 0xdb, 0x2d, 0xe2,
0xe8, 0x6f, 0x26, 0x7b, 0x4a, 0x34, 0x37, 0xaa, 0x76, 0xa2, 0x64, 0xa3, 0x33, 0xa8, 0x58, 0x9e,
0xe7, 0x73, 0x2b, 0x9c, 0xb4, 0xe7, 0xa4, 0xaa, 0xcd, 0xa9, 0x55, 0x6d, 0xc6, 0x32, 0xc6, 0x7a,
0x58, 0x02, 0x83, 0x93, 0xaa, 0xd0, 0x13, 0x58, 0xf0, 0x9f, 0x78, 0x84, 0x62, 0x72, 0x4c, 0x28,
0xf1, 0xc4, 0x5a, 0x56, 0x95, 0xda, 0xbf, 0x96, 0x51, 0x7b, 0x8a, 0x39, 0x0e, 0xe9, 0x34, 0x9c,
0xe1, 0x71, 0x2d, 0x62, 0x0f, 0x3d, 0x76, 0x3c, 0xcb, 0x75, 0x7e, 0x4a, 0x28, 0xab, 0xcd, 0xc7,
0x7b, 0xe8, 0xbd, 0x08, 0x8a, 0x13, 0x14, 0xe8, 0xeb, 0x50, 0xe9, 0xba, 0x03, 0xc6, 0x09, 0x95,
0x15, 0x62, 0x41, 0x66, 0x50, 0x74, 0xbf, 0xad, 0x18, 0x85, 0x93, 0x74, 0x2b, 0xdf, 0x80, 0xca,
0xff, 0xd8, 0x17, 0x45, 0x5f, 0x1d, 0x77, 0xe8, 0x54, 0x7d, 0xf5, 0x3f, 0x06, 0xcc, 0xa7, 0xdd,
0x10, 0x4d, 0x63, 0xc6, 0xc4, 0x65, 0x5f, 0xd7, 0xca, 0xfc, 0xc4, 0x5a, 0xa9, 0x4a, 0xd2, 0xec,
0xa7, 0x29, 0x49, 0x1b, 0x00, 0x56, 0xe0, 0xe8, 0x6a, 0x14, 0x56, 0xb7, 0xa8, 0x9e, 0xc4, 0x8b,
0x33, 0x4e, 0x50, 0x89, 0x07, 0xeb, 0xfa, 0x1e, 0xa7, 0xbe, 0xeb, 0x12, 0x2a, 0x2b, 0x58, 0x29,
0x7c, 0xb0, 0xad, 0x08, 0x8a, 0x13, 0x14, 0xe6, 0x3e, 0xa4, 0x37, 0x3e, 0x74, 0x37, 0x34, 0x3c,
0xbc, 0xfb, 0xad, 0xa9, 0x8d, 0x36, 0x6f, 0x41, 0x19, 0xfb, 0x3e, 0x3f, 0xb0, 0xf8, 0x09, 0x43,
0x0d, 0x28, 0x04, 0xe2, 0x87, 0x5a, 0xe7, 0xe5, 0x77, 0x0e, 0x89, 0xc1, 0x21, 0xdc, 0xfc, 0xb5,
0x01, 0xaf, 0x4c, 0xdc, 0xae, 0x85, 0x03, 0xba, 0xd1, 0x49, 0x99, 0x14, 0x39, 0x20, 0xa6, 0xc3,
0x09, 0x2a, 0xd1, 0xb6, 0x53, 0x2b, 0xf9, 0x78, 0xdb, 0x4e, 0x69, 0xc3, 0x69, 0x5a, 0xf3, 0xdf,
0x39, 0x28, 0x1e, 0x72, 0x8b, 0x0f, 0x18, 0x7a, 0x0c, 0x25, 0x91, 0x3d, 0xb6, 0xc5, 0x2d, 0xa9,
0x39, 0xf3, 0x17, 0x2b, 0x3d, 0xfe, 0xc4, 0x1d, 0x4b, 0x43, 0x70, 0x24, 0x51, 0xac, 0xaa, 0x4c,
0xea, 0x51, 0xe6, 0x45, 0x25, 0x27, 0xd4, 0x8e, 0x15, 0x56, 0x8c, 0xeb, 0x7d, 0xc2, 0x98, 0xd5,
0xd3, 0xb1, 0x16, 0x8d, 0xeb, 0x7b, 0x21, 0x18, 0x6b, 0x3c, 0x7a, 0x1b, 0x8a, 0x94, 0x58, 0x2c,
0x1a, 0x22, 0xea, 0x5a, 0x24, 0x96, 0xd0, 0xcb, 0x51, 0x63, 0x4e, 0x09, 0x97, 0x67, 0xac, 0xa8,
0xd1, 0x7b, 0x70, 0xc3, 0x26, 0xdc, 0x72, 0xdc, 0x70, 0x76, 0xc8, 0xfc, 0xed, 0x20, 0x14, 0xd6,
0x09, 0x59, 0xdb, 0x15, 0x61, 0x93, 0x3a, 0x60, 0x2d, 0x50, 0xe4, 0x49, 0xd7, 0xb7, 0x89, 0x8c,
0xc3, 0x42, 0x9c, 0x27, 0x5b, 0xbe, 0x4d, 0xb0, 0xc4, 0x98, 0x4f, 0x0d, 0xa8, 0x84, 0x92, 0xb6,
0xac, 0x01, 0x23, 0x68, 0x3d, 0xba, 0x45, 0xf8, 0xdc, 0xba, 0xb1, 0xcd, 0xbe, 0x7b, 0x1e, 0x90,
0xcb, 0x51, 0xa3, 0x2c, 0xc9, 0xc4, 0x21, 0xba, 0x40, 0xc2, 0x47, 0xb9, 0x6b, 0x7c, 0xf4, 0x1a,
0x14, 0xe4, 0x9c, 0xa6, 0x9c, 0x19, 0x8d, 0x65, 0x72, 0x96, 0xc3, 0x21, 0xce, 0xfc, 0x5d, 0x0e,
0xaa, 0xa9, 0xcb, 0x65, 0x18, 0x8d, 0xa2, 0xdd, 0x2b, 0x97, 0x61, 0x9f, 0x9f, 0xfc, 0x11, 0xf1,
0xfb, 0x50, 0xec, 0x8a, 0xfb, 0xe9, 0xaf, 0xb8, 0xeb, 0xd3, 0x3c, 0x85, 0xf4, 0x4c, 0x1c, 0x49,
0xf2, 0xc8, 0xb0, 0x12, 0x88, 0x76, 0x60, 0x89, 0x12, 0x4e, 0xcf, 0x37, 0x8f, 0x39, 0xa1, 0xc9,
0x61, 0xb1, 0x10, 0x0f, 0x0f, 0x78, 0x9c, 0x00, 0x5f, 0xe5, 0x31, 0x5d, 0x98, 0x15, 0x8d, 0x5d,
0xb8, 0x9d, 0xa5, 0x3e, 0x5b, 0x45, 0x6e, 0xd7, 0xcc, 0x1a, 0x2f, 0xbc, 0xe3, 0x59, 0x9e, 0x1f,
0x06, 0x7b, 0x21, 0xf6, 0xce, 0x03, 0x01, 0xc4, 0x21, 0xee, 0xce, 0xb2, 0x58, 0x20, 0x7f, 0xf5,
0xac, 0x31, 0xf3, 0xf4, 0x59, 0x63, 0xe6, 0xa3, 0x67, 0x6a, 0x99, 0xfc, 0x01, 0x94, 0xe3, 0x31,
0xe2, 0x33, 0x56, 0x69, 0xfe, 0x08, 0x4a, 0x22, 0x92, 0xf4, 0xf8, 0x7b, 0x4d, 0xd1, 0x4f, 0x97,
0xe3, 0x5c, 0x96, 0x72, 0x6c, 0x6e, 0x40, 0xf8, 0x5d, 0x57, 0x54, 0x42, 0x87, 0x93, 0x7e, 0xaa,
0x12, 0xee, 0x0a, 0x00, 0x0e, 0xe1, 0x89, 0xfd, 0xf9, 0x17, 0x06, 0x80, 0xdc, 0x13, 0xb6, 0x87,
0x62, 0xb7, 0x5b, 0x85, 0x59, 0x51, 0x62, 0xc7, 0x0d, 0x93, 0x29, 0x20, 0x31, 0xe8, 0x21, 0x14,
0x7d, 0x39, 0x5e, 0xa8, 0x8f, 0x7f, 0x6f, 0x4e, 0x8c, 0x1a, 0xf5, 0x2f, 0x9b, 0x26, 0xb6, 0x9e,
0x6c, 0x9f, 0x71, 0xe2, 0x09, 0x1b, 0xe3, 0x88, 0x09, 0x67, 0x14, 0xac, 0x84, 0xb5, 0x5f, 0x7f,
0xfe, 0xa2, 0x3e, 0xf3, 0xf1, 0x8b, 0xfa, 0xcc, 0xdf, 0x5e, 0xd4, 0x67, 0x3e, 0xb8, 0xa8, 0x1b,
0xcf, 0x2f, 0xea, 0xc6, 0xc7, 0x17, 0x75, 0xe3, 0x1f, 0x17, 0x75, 0xe3, 0xe9, 0x3f, 0xeb, 0x33,
0xef, 0xe5, 0x86, 0xeb, 0xff, 0x0d, 0x00, 0x00, 0xff, 0xff, 0x99, 0xeb, 0x4a, 0xdd, 0xf4, 0x1a,
0x00, 0x00,
// 2160 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xcc, 0x19, 0xcd, 0x6f, 0x23, 0x57,
0x3d, 0x63, 0xc7, 0x5e, 0xfb, 0xe7, 0x38, 0x1f, 0xaf, 0x59, 0x70, 0x23, 0x61, 0xa7, 0xd3, 0x0a,
0xa5, 0xb0, 0xb5, 0x49, 0x0a, 0xd5, 0xb2, 0xc0, 0x42, 0x26, 0xce, 0x46, 0x51, 0x37, 0x9b, 0xe8,
0xa5, 0xbb, 0x88, 0x65, 0x85, 0x98, 0xcc, 0xbc, 0x38, 0x43, 0xc6, 0x33, 0xc3, 0x7b, 0x63, 0x6f,
0x4c, 0x0f, 0x54, 0x2a, 0x48, 0x1c, 0x10, 0xda, 0x23, 0x07, 0x84, 0xba, 0x82, 0x1b, 0x37, 0xfe,
0x06, 0x24, 0xf6, 0x58, 0x89, 0x0b, 0x07, 0x64, 0xb1, 0xee, 0x81, 0x23, 0xf7, 0x9c, 0xd0, 0x7b,
0xf3, 0xe6, 0xcb, 0x8e, 0x9b, 0x31, 0xed, 0xa1, 0xa7, 0x78, 0x7e, 0xdf, 0xef, 0xf7, 0xfd, 0x5e,
0xe0, 0xe0, 0xfc, 0x36, 0x6b, 0x5a, 0x6e, 0xeb, 0xbc, 0x77, 0x42, 0xa8, 0x43, 0x7c, 0xc2, 0x5a,
0x7d, 0xe2, 0x98, 0x2e, 0x6d, 0x49, 0x84, 0xee, 0x59, 0x5d, 0xdd, 0x38, 0xb3, 0x1c, 0x42, 0x07,
0x2d, 0xef, 0xbc, 0xc3, 0x01, 0xac, 0xd5, 0x25, 0xbe, 0xde, 0xea, 0x6f, 0xb6, 0x3a, 0xc4, 0x21,
0x54, 0xf7, 0x89, 0xd9, 0xf4, 0xa8, 0xeb, 0xbb, 0xe8, 0x8d, 0x80, 0xab, 0x99, 0xe4, 0x6a, 0x7a,
0xe7, 0x1d, 0x0e, 0x60, 0x4d, 0xce, 0xd5, 0xec, 0x6f, 0xae, 0xbd, 0xd5, 0xb1, 0xfc, 0xb3, 0xde,
0x49, 0xd3, 0x70, 0xbb, 0xad, 0x8e, 0xdb, 0x71, 0x5b, 0x82, 0xf9, 0xa4, 0x77, 0x2a, 0xbe, 0xc4,
0x87, 0xf8, 0x15, 0x08, 0x5d, 0x9b, 0x6a, 0x0a, 0xed, 0x39, 0xbe, 0xd5, 0x25, 0xe3, 0x56, 0xac,
0xbd, 0x73, 0x1d, 0x03, 0x33, 0xce, 0x48, 0x57, 0x9f, 0xe0, 0x7b, 0x7b, 0x1a, 0x5f, 0xcf, 0xb7,
0xec, 0x96, 0xe5, 0xf8, 0xcc, 0xa7, 0xe3, 0x4c, 0xea, 0xdf, 0xf3, 0x50, 0xda, 0x3e, 0xda, 0xdf,
0xa3, 0x6e, 0xcf, 0x43, 0xeb, 0x30, 0xef, 0xe8, 0x5d, 0x52, 0x53, 0xd6, 0x95, 0x8d, 0xb2, 0xb6,
0xf0, 0x62, 0xd8, 0x98, 0x1b, 0x0d, 0x1b, 0xf3, 0x0f, 0xf4, 0x2e, 0xc1, 0x02, 0x83, 0x6c, 0x28,
0xf5, 0x09, 0x65, 0x96, 0xeb, 0xb0, 0x5a, 0x6e, 0x3d, 0xbf, 0x51, 0xd9, 0xba, 0xdb, 0xcc, 0xe2,
0xb4, 0xa6, 0x50, 0xf0, 0x28, 0x60, 0xbd, 0xe7, 0xd2, 0xb6, 0xc5, 0x0c, 0xb7, 0x4f, 0xe8, 0x40,
0x5b, 0x96, 0x5a, 0x4a, 0x12, 0xc9, 0x70, 0xa4, 0x01, 0xfd, 0x4a, 0x81, 0x65, 0x8f, 0x92, 0x53,
0x42, 0x29, 0x31, 0x25, 0xbe, 0x96, 0x5f, 0x57, 0x3e, 0x07, 0xb5, 0x35, 0xa9, 0x76, 0xf9, 0x68,
0x4c, 0x3e, 0x9e, 0xd0, 0x88, 0xfe, 0xa4, 0xc0, 0x1a, 0x23, 0xb4, 0x4f, 0xe8, 0xb6, 0x69, 0x52,
0xc2, 0x98, 0x36, 0xd8, 0xb1, 0x2d, 0xe2, 0xf8, 0x3b, 0xfb, 0x6d, 0xcc, 0x6a, 0xf3, 0xc2, 0x0f,
0xdf, 0xcf, 0x66, 0xd0, 0xf1, 0x34, 0x39, 0x9a, 0x2a, 0x2d, 0x5a, 0x9b, 0x4a, 0xc2, 0xf0, 0xa7,
0x98, 0xa1, 0x9e, 0xc2, 0x42, 0x18, 0xc8, 0xfb, 0x16, 0xf3, 0xd1, 0x23, 0x28, 0x76, 0xf8, 0x07,
0xab, 0x29, 0xc2, 0xc0, 0x66, 0x36, 0x03, 0x43, 0x19, 0xda, 0xa2, 0xb4, 0xa7, 0x28, 0x3e, 0x19,
0x96, 0xd2, 0xd4, 0x0f, 0x73, 0x50, 0xd9, 0x3e, 0xda, 0xc7, 0x84, 0xb9, 0x3d, 0x6a, 0x90, 0x0c,
0x49, 0xb3, 0x05, 0xc0, 0xff, 0x32, 0x4f, 0x37, 0x88, 0x59, 0xcb, 0xad, 0x2b, 0x1b, 0x25, 0x0d,
0x49, 0x3a, 0x78, 0x10, 0x61, 0x70, 0x82, 0x8a, 0x4b, 0x3d, 0xb7, 0x1c, 0x53, 0x44, 0x3b, 0x21,
0xf5, 0x5d, 0xcb, 0x31, 0xb1, 0xc0, 0xa0, 0xfb, 0x50, 0xe8, 0x13, 0x7a, 0xc2, 0xfd, 0xcf, 0x13,
0xe2, 0xeb, 0xd9, 0x8e, 0xf7, 0x88, 0xb3, 0x68, 0xe5, 0xd1, 0xb0, 0x51, 0x10, 0x3f, 0x71, 0x20,
0x04, 0x35, 0x01, 0xd8, 0x99, 0x4b, 0x7d, 0x61, 0x4e, 0xad, 0xb0, 0x9e, 0xdf, 0x28, 0x6b, 0x8b,
0xdc, 0xbe, 0xe3, 0x08, 0x8a, 0x13, 0x14, 0xea, 0x5f, 0x15, 0x58, 0x4a, 0x78, 0x41, 0x78, 0xfc,
0x36, 0x2c, 0x74, 0x12, 0xf9, 0x26, 0x3d, 0xb2, 0x2a, 0x6d, 0x5f, 0x48, 0xe6, 0x22, 0x4e, 0x51,
0x22, 0x02, 0x65, 0x2a, 0x25, 0x85, 0x75, 0xb5, 0x99, 0x39, 0x5c, 0xa1, 0x0d, 0xb1, 0xa6, 0x04,
0x90, 0xe1, 0x58, 0xb2, 0xfa, 0x1f, 0x45, 0x84, 0x2e, 0xac, 0x34, 0xb4, 0x91, 0xa8, 0x66, 0x45,
0x1c, 0x79, 0x61, 0x4a, 0x25, 0x5e, 0x53, 0x02, 0xb9, 0x2f, 0x44, 0x09, 0xdc, 0x29, 0xfd, 0xfe,
0xa3, 0xc6, 0xdc, 0x07, 0xff, 0x5a, 0x9f, 0x53, 0x3f, 0xc9, 0x41, 0xb5, 0x4d, 0x6c, 0xe2, 0x93,
0x43, 0xcf, 0x17, 0x27, 0xb8, 0x07, 0xa8, 0x43, 0x75, 0x83, 0x1c, 0x11, 0x6a, 0xb9, 0xe6, 0x31,
0x31, 0x5c, 0xc7, 0x64, 0x22, 0x44, 0x79, 0xed, 0x4b, 0xa3, 0x61, 0x03, 0xed, 0x4d, 0x60, 0xf1,
0x15, 0x1c, 0xc8, 0x86, 0xaa, 0x47, 0xc5, 0x6f, 0xcb, 0x97, 0x6d, 0x90, 0xa7, 0xdf, 0xdb, 0xd9,
0xce, 0x7e, 0x94, 0x64, 0xd5, 0x56, 0x46, 0xc3, 0x46, 0x35, 0x05, 0xc2, 0x69, 0xe1, 0xe8, 0x07,
0xb0, 0xec, 0x52, 0xef, 0x4c, 0x77, 0xda, 0xc4, 0x23, 0x8e, 0x49, 0x1c, 0x9f, 0x89, 0x92, 0x28,
0x69, 0xab, 0xbc, 0x79, 0x1d, 0x8e, 0xe1, 0xf0, 0x04, 0x35, 0x7a, 0x0c, 0x2b, 0x1e, 0x75, 0x3d,
0xbd, 0xa3, 0x73, 0x89, 0x47, 0xae, 0x6d, 0x19, 0x03, 0x51, 0x32, 0x65, 0xed, 0xd6, 0x68, 0xd8,
0x58, 0x39, 0x1a, 0x47, 0x5e, 0x0e, 0x1b, 0xaf, 0x08, 0xd7, 0x71, 0x48, 0x8c, 0xc4, 0x93, 0x62,
0xd4, 0x7d, 0x28, 0xb5, 0x7b, 0x54, 0x40, 0xd0, 0xf7, 0xa0, 0x64, 0xca, 0xdf, 0xd2, 0xab, 0xaf,
0x85, 0x9d, 0x3d, 0xa4, 0xb9, 0x1c, 0x36, 0xaa, 0x7c, 0x80, 0x35, 0x43, 0x00, 0x8e, 0x58, 0xd4,
0x27, 0x50, 0xdd, 0xbd, 0xf0, 0x5c, 0xea, 0x87, 0xf1, 0xfa, 0x2a, 0x14, 0x89, 0x00, 0x08, 0x69,
0xa5, 0xb8, 0x1d, 0x05, 0x64, 0x58, 0x62, 0xd1, 0xeb, 0x50, 0x20, 0x17, 0xba, 0xe1, 0xcb, 0xbe,
0x52, 0x95, 0x64, 0x85, 0x5d, 0x0e, 0xc4, 0x01, 0x4e, 0x3d, 0x04, 0xd8, 0x23, 0x91, 0xe8, 0x6d,
0x58, 0x0a, 0x6b, 0x22, 0x5d, 0xaa, 0x5f, 0x96, 0xcc, 0x4b, 0x38, 0x8d, 0xc6, 0xe3, 0xf4, 0xea,
0x13, 0x28, 0x8b, 0x72, 0xe6, 0xfd, 0x88, 0x9b, 0x20, 0xaa, 0x59, 0x4a, 0x89, 0x4c, 0x10, 0x14,
0x38, 0xc0, 0x45, 0x0d, 0x2d, 0x37, 0xad, 0xa1, 0x25, 0xb2, 0xd7, 0x86, 0x6a, 0xc0, 0x1b, 0xf6,
0xd8, 0x4c, 0x1a, 0x6e, 0x41, 0x29, 0x34, 0x53, 0x6a, 0x89, 0x66, 0x6b, 0x28, 0x08, 0x47, 0x14,
0x09, 0x6d, 0x67, 0x90, 0x6a, 0x4d, 0xd9, 0x94, 0xbd, 0x09, 0x37, 0x64, 0x73, 0x90, 0xba, 0x96,
0x24, 0xd9, 0x8d, 0xd0, 0x67, 0x21, 0x3e, 0xa1, 0xe9, 0x97, 0x50, 0x9b, 0x36, 0x90, 0x3f, 0x43,
0xf3, 0xcc, 0x6e, 0x8a, 0xfa, 0x3b, 0x05, 0x96, 0x93, 0x92, 0xb2, 0x87, 0x2f, 0xbb, 0x92, 0xeb,
0x47, 0x57, 0xc2, 0x23, 0x7f, 0x54, 0x60, 0x35, 0x75, 0xb4, 0x99, 0x22, 0x3e, 0x83, 0x51, 0xc9,
0xe4, 0xc8, 0xcf, 0x90, 0x1c, 0xff, 0xc8, 0x41, 0xf5, 0xbe, 0x7e, 0x42, 0xec, 0x63, 0x62, 0x13,
0xc3, 0x77, 0x29, 0x7a, 0x1f, 0x2a, 0x5d, 0xdd, 0x37, 0xce, 0x04, 0x34, 0x5c, 0x2e, 0xda, 0xd9,
0xda, 0x5f, 0x4a, 0x52, 0xf3, 0x20, 0x16, 0xb3, 0xeb, 0xf8, 0x74, 0xa0, 0xbd, 0x22, 0x4d, 0xaa,
0x24, 0x30, 0x38, 0xa9, 0x4d, 0x6c, 0x84, 0xe2, 0x7b, 0xf7, 0xc2, 0xe3, 0xfd, 0x7f, 0xf6, 0x45,
0x34, 0x65, 0x02, 0x26, 0x3f, 0xef, 0x59, 0x94, 0x74, 0x89, 0xe3, 0xc7, 0x1b, 0xe1, 0xc1, 0x98,
0x7c, 0x3c, 0xa1, 0x71, 0xed, 0x2e, 0x2c, 0x8f, 0x1b, 0x8f, 0x96, 0x21, 0x7f, 0x4e, 0x06, 0x41,
0xbc, 0x30, 0xff, 0x89, 0x56, 0xa1, 0xd0, 0xd7, 0xed, 0x9e, 0xac, 0x46, 0x1c, 0x7c, 0xdc, 0xc9,
0xdd, 0x56, 0xd4, 0x3f, 0x2b, 0x50, 0x9b, 0x66, 0x08, 0xfa, 0x4a, 0x42, 0x90, 0x56, 0x91, 0x56,
0xe5, 0xdf, 0x25, 0x83, 0x40, 0xea, 0x2e, 0x94, 0x5c, 0x8f, 0xef, 0xf0, 0x2e, 0x95, 0x51, 0x7f,
0x33, 0x8c, 0xe4, 0xa1, 0x84, 0x5f, 0x0e, 0x1b, 0x37, 0x53, 0xe2, 0x43, 0x04, 0x8e, 0x58, 0x91,
0x0a, 0x45, 0x61, 0x0f, 0x9f, 0x27, 0x7c, 0xf2, 0x03, 0xef, 0xad, 0x8f, 0x04, 0x04, 0x4b, 0x8c,
0xfa, 0x3e, 0x94, 0xf8, 0x62, 0x73, 0x40, 0x7c, 0x9d, 0x27, 0x10, 0x23, 0xf6, 0xe9, 0x7d, 0xcb,
0x39, 0x97, 0xa6, 0x45, 0x09, 0x74, 0x2c, 0xe1, 0x38, 0xa2, 0xb8, 0xaa, 0xc5, 0xe6, 0x66, 0x6c,
0xb1, 0x7f, 0xc9, 0x41, 0x85, 0x6b, 0x0f, 0xbb, 0xf6, 0x77, 0xa0, 0x6a, 0x27, 0xcf, 0x24, 0xad,
0xb8, 0x29, 0x05, 0xa6, 0xb3, 0x14, 0xa7, 0x69, 0x39, 0xf3, 0xa9, 0x45, 0x6c, 0x33, 0x62, 0xce,
0xa5, 0x99, 0xef, 0x25, 0x91, 0x38, 0x4d, 0xcb, 0x6b, 0xf1, 0x29, 0x8f, 0xb6, 0x9c, 0xbc, 0x51,
0x2d, 0xfe, 0x90, 0x03, 0x71, 0x80, 0xbb, 0xea, 0xc4, 0xf3, 0xb3, 0x9d, 0x18, 0xdd, 0x81, 0x45,
0x3e, 0x1e, 0xdd, 0x9e, 0x1f, 0xae, 0x27, 0x05, 0x31, 0x48, 0xd1, 0x68, 0xd8, 0x58, 0x7c, 0x2f,
0x85, 0xc1, 0x63, 0x94, 0xea, 0x87, 0x00, 0x70, 0x78, 0xf2, 0x33, 0x62, 0x04, 0xd1, 0xba, 0x7e,
0x29, 0xe7, 0xfd, 0x56, 0xde, 0x05, 0x39, 0x54, 0x3a, 0x24, 0xee, 0xb7, 0x09, 0x1c, 0x4e, 0x51,
0xa2, 0x16, 0x94, 0xa3, 0x45, 0x5d, 0xf6, 0x92, 0x15, 0xc9, 0x56, 0x8e, 0xb6, 0x79, 0x1c, 0xd3,
0xa4, 0x52, 0x67, 0xfe, 0xda, 0xd4, 0xd1, 0x20, 0xdf, 0xb3, 0x4c, 0x71, 0xf4, 0xb2, 0xf6, 0x8d,
0x30, 0xfd, 0x1f, 0xee, 0xb7, 0x2f, 0x87, 0x8d, 0xd7, 0xa6, 0x5d, 0x71, 0xfd, 0x81, 0x47, 0x58,
0xf3, 0xe1, 0x7e, 0x1b, 0x73, 0xe6, 0xab, 0x82, 0x51, 0x9c, 0x31, 0x18, 0x5b, 0x00, 0xf2, 0xd4,
0x9c, 0xfb, 0x46, 0x10, 0x88, 0xf0, 0xd2, 0xb2, 0x17, 0x61, 0x70, 0x82, 0x0a, 0x31, 0x58, 0x31,
0x28, 0x11, 0xbf, 0x79, 0xb8, 0x98, 0xaf, 0x77, 0xbd, 0x5a, 0x49, 0xec, 0x87, 0x5f, 0xcb, 0xd6,
0x9d, 0x38, 0x9b, 0xf6, 0xaa, 0x54, 0xb3, 0xb2, 0x33, 0x2e, 0x0c, 0x4f, 0xca, 0x47, 0x2e, 0xac,
0x98, 0x72, 0x5d, 0x8b, 0x95, 0x96, 0x67, 0x56, 0x7a, 0x93, 0x2b, 0x6c, 0x8f, 0x0b, 0xc2, 0x93,
0xb2, 0xd1, 0x4f, 0x60, 0x2d, 0x04, 0x4e, 0xee, 0xcc, 0x35, 0x10, 0x9e, 0xaa, 0xf3, 0x2d, 0xbe,
0x3d, 0x95, 0x0a, 0x7f, 0x8a, 0x04, 0x64, 0x42, 0xd1, 0x0e, 0x66, 0x4b, 0x45, 0x34, 0xf6, 0xef,
0x66, 0x3b, 0x45, 0x9c, 0xfd, 0xcd, 0xe4, 0x4c, 0x89, 0xf6, 0x46, 0x39, 0x4e, 0xa4, 0x6c, 0x74,
0x01, 0x15, 0xdd, 0x71, 0x5c, 0x5f, 0x0f, 0xb6, 0xf8, 0x05, 0xa1, 0x6a, 0x7b, 0x66, 0x55, 0xdb,
0xb1, 0x8c, 0xb1, 0x19, 0x96, 0xc0, 0xe0, 0xa4, 0x2a, 0xf4, 0x14, 0x96, 0xdc, 0xa7, 0x0e, 0xa1,
0x98, 0x9c, 0x12, 0x4a, 0x1c, 0x7e, 0xe5, 0xab, 0x0a, 0xed, 0xdf, 0xcc, 0xa8, 0x3d, 0xc5, 0x1c,
0xa7, 0x74, 0x1a, 0xce, 0xf0, 0xb8, 0x16, 0x7e, 0xc7, 0x3d, 0xb5, 0x1c, 0xdd, 0xb6, 0x7e, 0x41,
0x28, 0xab, 0x2d, 0xc6, 0x77, 0xdc, 0x7b, 0x11, 0x14, 0x27, 0x28, 0xd0, 0xb7, 0xa0, 0x62, 0xd8,
0x3d, 0xe6, 0x13, 0x2a, 0x3a, 0xc4, 0x92, 0xa8, 0xa0, 0xe8, 0x7c, 0x3b, 0x31, 0x0a, 0x27, 0xe9,
0xd6, 0xbe, 0x0d, 0x95, 0xff, 0x73, 0x2e, 0xf2, 0xb9, 0x3a, 0xee, 0xd0, 0x99, 0xe6, 0xea, 0xdf,
0x72, 0xb0, 0x98, 0x76, 0x43, 0xb4, 0x8d, 0x29, 0x53, 0x1f, 0x12, 0xc2, 0x5e, 0x99, 0x9f, 0xda,
0x2b, 0x65, 0x4b, 0x9a, 0xff, 0x2c, 0x2d, 0x69, 0x0b, 0x40, 0xf7, 0xac, 0xb0, 0x1b, 0x05, 0xdd,
0x2d, 0xea, 0x27, 0xf1, 0xa5, 0x1c, 0x27, 0xa8, 0x78, 0xc0, 0x0c, 0xd7, 0xf1, 0xa9, 0x6b, 0xdb,
0x84, 0x8a, 0x0e, 0x56, 0x0a, 0x02, 0xb6, 0x13, 0x41, 0x71, 0x82, 0x82, 0xdf, 0x71, 0x4f, 0x6c,
0xd7, 0x38, 0x17, 0x2e, 0x08, 0xab, 0x4f, 0xf4, 0xae, 0x52, 0x70, 0xc7, 0xd5, 0x26, 0xb0, 0xf8,
0x0a, 0x0e, 0xf5, 0x10, 0xd2, 0xb7, 0x52, 0x74, 0x37, 0x70, 0x80, 0x12, 0x5d, 0x1b, 0x67, 0x3b,
0xbc, 0x7a, 0x0b, 0xca, 0xd8, 0x75, 0xfd, 0x23, 0xdd, 0x3f, 0x63, 0xa8, 0x01, 0x05, 0x8f, 0xff,
0x90, 0x4f, 0x0e, 0xe2, 0x2d, 0x46, 0x60, 0x70, 0x00, 0x57, 0x7f, 0xab, 0xc0, 0xab, 0x53, 0x5f,
0x00, 0xb8, 0x23, 0x8d, 0xe8, 0x4b, 0x9a, 0x14, 0x39, 0x32, 0xa6, 0xc3, 0x09, 0x2a, 0x3e, 0xfe,
0x53, 0xcf, 0x06, 0xe3, 0xe3, 0x3f, 0xa5, 0x0d, 0xa7, 0x69, 0xd5, 0xff, 0xe6, 0xa0, 0x78, 0xec,
0xeb, 0x7e, 0x8f, 0xa1, 0x27, 0x50, 0xe2, 0x55, 0x68, 0xea, 0xbe, 0x2e, 0x34, 0x67, 0x7e, 0x55,
0x0b, 0xd7, 0xa8, 0x78, 0xf2, 0x85, 0x10, 0x1c, 0x49, 0xe4, 0x57, 0x5e, 0x26, 0xf4, 0x48, 0xf3,
0xa2, 0xd6, 0x15, 0x68, 0xc7, 0x12, 0xcb, 0xd7, 0xfe, 0x2e, 0x61, 0x4c, 0xef, 0x84, 0x39, 0x1b,
0xad, 0xfd, 0x07, 0x01, 0x18, 0x87, 0x78, 0xf4, 0x0e, 0x14, 0x29, 0xd1, 0x59, 0xb4, 0x8c, 0xd4,
0x43, 0x91, 0x58, 0x40, 0x2f, 0x87, 0x8d, 0x05, 0x29, 0x5c, 0x7c, 0x63, 0x49, 0x8d, 0x1e, 0xc3,
0x0d, 0x93, 0xf8, 0xba, 0x65, 0x07, 0x3b, 0x48, 0xe6, 0xf7, 0x8d, 0x40, 0x58, 0x3b, 0x60, 0xd5,
0x2a, 0xdc, 0x26, 0xf9, 0x81, 0x43, 0x81, 0xbc, 0xde, 0x0c, 0xd7, 0x24, 0x22, 0x9f, 0x0b, 0x71,
0xbd, 0xed, 0xb8, 0x26, 0xc1, 0x02, 0xa3, 0x3e, 0x53, 0xa0, 0x12, 0x48, 0xda, 0xd1, 0x7b, 0x8c,
0xa0, 0xcd, 0xe8, 0x14, 0x41, 0xb8, 0xc3, 0x01, 0x39, 0xff, 0xde, 0xc0, 0x23, 0x97, 0xc3, 0x46,
0x59, 0x90, 0xf1, 0x8f, 0xe8, 0x00, 0x09, 0x1f, 0xe5, 0xae, 0xf1, 0xd1, 0xeb, 0x50, 0x10, 0xfb,
0x9e, 0x74, 0x66, 0xb4, 0xde, 0x89, 0x9d, 0x10, 0x07, 0x38, 0xf5, 0x0f, 0x39, 0xa8, 0xa6, 0x0e,
0x97, 0x61, 0xc5, 0x8a, 0xee, 0x70, 0xb9, 0x0c, 0xef, 0x02, 0xd3, 0x1f, 0x3a, 0x7f, 0x04, 0x45,
0x83, 0x9f, 0x2f, 0x7c, 0x69, 0xde, 0x9c, 0x25, 0x14, 0xc2, 0x33, 0x71, 0x26, 0x89, 0x4f, 0x86,
0xa5, 0x40, 0xb4, 0x07, 0x2b, 0x94, 0xf8, 0x74, 0xb0, 0x7d, 0xea, 0x13, 0x9a, 0x5c, 0x3a, 0x0b,
0xf1, 0x12, 0x82, 0xc7, 0x09, 0xf0, 0x24, 0x8f, 0x6a, 0xc3, 0x3c, 0x5f, 0x10, 0xb8, 0xdb, 0x59,
0xea, 0x69, 0x2d, 0x72, 0x7b, 0xc8, 0x1c, 0xe2, 0xb9, 0x77, 0x1c, 0xdd, 0x71, 0x83, 0x64, 0x2f,
0xc4, 0xde, 0x79, 0xc0, 0x81, 0x38, 0xc0, 0xdd, 0x59, 0xe5, 0x17, 0xd1, 0xdf, 0x3c, 0x6f, 0xcc,
0x3d, 0x7b, 0xde, 0x98, 0xfb, 0xe8, 0xb9, 0xbc, 0x94, 0xfe, 0x18, 0xca, 0xf1, 0x3a, 0xf2, 0x39,
0xab, 0x54, 0x7f, 0x0a, 0x25, 0x9e, 0x49, 0xe1, 0x1a, 0x7d, 0xcd, 0xf0, 0x48, 0xb7, 0xf5, 0x5c,
0x96, 0xb6, 0xae, 0x6e, 0x41, 0xf0, 0xf6, 0xcc, 0x3b, 0xa1, 0xe5, 0x93, 0x6e, 0xaa, 0x13, 0xee,
0x73, 0x00, 0x0e, 0xe0, 0x89, 0x7b, 0xf8, 0xaf, 0x15, 0x00, 0x71, 0xdf, 0xd8, 0xed, 0xf3, 0x3b,
0xe2, 0x3a, 0xcc, 0xf3, 0x16, 0x3b, 0x6e, 0x98, 0x28, 0x01, 0x81, 0x41, 0x0f, 0xa1, 0xe8, 0x8a,
0x35, 0x45, 0x3e, 0x50, 0xbe, 0x35, 0x35, 0x6b, 0xe4, 0xbf, 0x95, 0x9a, 0x58, 0x7f, 0xba, 0x7b,
0xe1, 0x13, 0x87, 0xdb, 0x18, 0x67, 0x4c, 0xb0, 0xeb, 0x60, 0x29, 0x4c, 0x7b, 0xe3, 0xc5, 0xcb,
0xfa, 0xdc, 0xc7, 0x2f, 0xeb, 0x73, 0xff, 0x7c, 0x59, 0x9f, 0xfb, 0x60, 0x54, 0x57, 0x5e, 0x8c,
0xea, 0xca, 0xc7, 0xa3, 0xba, 0xf2, 0xef, 0x51, 0x5d, 0x79, 0xf6, 0x49, 0x7d, 0xee, 0x71, 0xae,
0xbf, 0xf9, 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xb1, 0xe8, 0xc1, 0x2f, 0x98, 0x1b, 0x00, 0x00,
}

View file

@ -121,10 +121,18 @@ message DeleteOptions {
// +optional
optional Preconditions preconditions = 2;
// Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7.
// Should the dependent objects be orphaned. If true/false, the "orphan"
// finalizer will be added to/removed from the object's finalizers list.
// Either this field or PropagationPolicy may be set, but not both.
// +optional
optional bool orphanDependents = 3;
// Whether and how garbage collection will be performed.
// Defaults to Default.
// Either this field or OrphanDependents may be set, but not both.
// +optional
optional string propagationPolicy = 4;
}
// Duration is a wrapper around time.Duration which supports correct
@ -469,6 +477,15 @@ message OwnerReference {
// If true, this reference points to the managing controller.
// +optional
optional bool controller = 6;
// If true, AND if the owner has the "foregroundDeletion" finalizer, then
// the owner cannot be deleted from the key-value store until this
// reference is removed.
// Defaults to false.
// To set this field, a user needs "delete" permission of the owner,
// otherwise 422 (Unprocessable Entity) will be returned.
// +optional
optional bool blockOwnerDeletion = 7;
}
// Preconditions must be fulfilled before an operation (update, delete, etc.) is carried out.

View file

@ -226,3 +226,9 @@ func NewUIDPreconditions(uid string) *Preconditions {
u := types.UID(uid)
return &Preconditions{UID: &u}
}
// HasObjectMetaSystemFieldValues returns true if fields that are managed by the system on ObjectMeta have values.
func HasObjectMetaSystemFieldValues(meta *ObjectMeta) bool {
return !meta.CreationTimestamp.Time.IsZero() ||
len(meta.UID) != 0
}

View file

@ -174,6 +174,10 @@ func (meta *ObjectMeta) GetOwnerReferences() []OwnerReference {
value := *meta.OwnerReferences[i].Controller
ret[i].Controller = &value
}
if meta.OwnerReferences[i].BlockOwnerDeletion != nil {
value := *meta.OwnerReferences[i].BlockOwnerDeletion
ret[i].BlockOwnerDeletion = &value
}
}
return ret
}
@ -189,6 +193,10 @@ func (meta *ObjectMeta) SetOwnerReferences(references []OwnerReference) {
value := *references[i].Controller
newReferences[i].Controller = &value
}
if references[i].BlockOwnerDeletion != nil {
value := *references[i].BlockOwnerDeletion
newReferences[i].BlockOwnerDeletion = &value
}
}
meta.OwnerReferences = newReferences
}

View file

@ -55,6 +55,11 @@ func AddToGroupVersion(scheme *runtime.Scheme, groupVersion schema.GroupVersion)
Convert_watch_Event_to_versioned_Event,
Convert_versioned_Event_to_versioned_InternalEvent,
)
// register manually. This usually goes through the SchemeBuilder, which we cannot use here.
scheme.AddGeneratedDeepCopyFuncs(GetGeneratedDeepCopyFuncs()...)
AddConversionFuncs(scheme)
RegisterDefaults(scheme)
}
// scheme is the registry for the common types that adhere to the meta v1 API spec.
@ -70,4 +75,8 @@ func init() {
&GetOptions{},
&DeleteOptions{},
)
// register manually. This usually goes through the SchemeBuilder, which we cannot use here.
scheme.AddGeneratedDeepCopyFuncs(GetGeneratedDeepCopyFuncs()...)
RegisterDefaults(scheme)
}

View file

@ -71,6 +71,12 @@ type ListMeta struct {
ResourceVersion string `json:"resourceVersion,omitempty" protobuf:"bytes,2,opt,name=resourceVersion"`
}
// These are internal finalizer values for Kubernetes-like APIs, must be qualified name unless defined here
const (
FinalizerOrphanDependents string = "orphan"
FinalizerDeleteDependents string = "foregroundDeletion"
)
// ObjectMeta is metadata that all persisted resources must have, which includes all objects
// users must create.
type ObjectMeta struct {
@ -228,6 +234,8 @@ const (
NamespaceNone string = ""
// NamespaceSystem is the system namespace where we place system components.
NamespaceSystem string = "kube-system"
// NamespacePublic is the namespace where we place public info (ConfigMaps)
NamespacePublic string = "kube-public"
)
// OwnerReference contains enough information to let you identify an owning
@ -248,6 +256,14 @@ type OwnerReference struct {
// If true, this reference points to the managing controller.
// +optional
Controller *bool `json:"controller,omitempty" protobuf:"varint,6,opt,name=controller"`
// If true, AND if the owner has the "foregroundDeletion" finalizer, then
// the owner cannot be deleted from the key-value store until this
// reference is removed.
// Defaults to false.
// To set this field, a user needs "delete" permission of the owner,
// otherwise 422 (Unprocessable Entity) will be returned.
// +optional
BlockOwnerDeletion *bool `json:"blockOwnerDeletion,omitempty" protobuf:"varint,7,opt,name=blockOwnerDeletion"`
}
// ListOptions is the query options to a standard REST list call.
@ -298,6 +314,24 @@ type GetOptions struct {
ResourceVersion string `json:"resourceVersion,omitempty" protobuf:"bytes,1,opt,name=resourceVersion"`
}
// DeletionPropagation decides if a deletion will propagate to the dependents of
// the object, and how the garbage collector will handle the propagation.
type DeletionPropagation string
const (
// Orphans the dependents.
DeletePropagationOrphan DeletionPropagation = "Orphan"
// Deletes the object from the key-value store, the garbage collector will
// delete the dependents in the background.
DeletePropagationBackground DeletionPropagation = "Background"
// The object exists in the key-value store until the garbage collector
// deletes all the dependents whose ownerReference.blockOwnerDeletion=true
// from the key-value store. API sever will put the "foregroundDeletion"
// finalizer on the object, and sets its deletionTimestamp. This policy is
// cascading, i.e., the dependents will be deleted with Foreground.
DeletePropagationForeground DeletionPropagation = "Foreground"
)
// DeleteOptions may be provided when deleting an API object.
type DeleteOptions struct {
TypeMeta `json:",inline"`
@ -314,10 +348,18 @@ type DeleteOptions struct {
// +optional
Preconditions *Preconditions `json:"preconditions,omitempty" protobuf:"bytes,2,opt,name=preconditions"`
// Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7.
// Should the dependent objects be orphaned. If true/false, the "orphan"
// finalizer will be added to/removed from the object's finalizers list.
// Either this field or PropagationPolicy may be set, but not both.
// +optional
OrphanDependents *bool `json:"orphanDependents,omitempty" protobuf:"varint,3,opt,name=orphanDependents"`
// Whether and how garbage collection will be performed.
// Defaults to Default.
// Either this field or OrphanDependents may be set, but not both.
// +optional
PropagationPolicy *DeletionPropagation `json:"propagationPolicy,omitempty" protobuf:"varint,4,opt,name=propagationPolicy"`
}
// Preconditions must be fulfilled before an operation (update, delete, etc.) is carried out.

View file

@ -85,7 +85,8 @@ var map_DeleteOptions = map[string]string{
"": "DeleteOptions may be provided when deleting an API object.",
"gracePeriodSeconds": "The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately.",
"preconditions": "Must be fulfilled before a deletion is carried out. If not possible, a 409 Conflict status will be returned.",
"orphanDependents": "Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list.",
"orphanDependents": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.",
"propagationPolicy": "Whether and how garbage collection will be performed. Defaults to Default. Either this field or OrphanDependents may be set, but not both.",
}
func (DeleteOptions) SwaggerDoc() map[string]string {
@ -195,6 +196,7 @@ var map_OwnerReference = map[string]string{
"name": "Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names",
"uid": "UID of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#uids",
"controller": "If true, this reference points to the managing controller.",
"blockOwnerDeletion": "If true, AND if the owner has the \"foregroundDeletion\" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. Defaults to false. To set this field, a user needs \"delete\" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned.",
}
func (OwnerReference) SwaggerDoc() map[string]string {

View file

@ -73,11 +73,24 @@ func (obj *Unstructured) UnstructuredContent() map[string]interface{} {
}
return obj.Object
}
// UnstructuredContent returns a map contain an overlay of the Items field onto
// the Object field. Items always overwrites overlay. Changing "items" in the
// returned object will affect items in the underlying Items field, but changing
// the "items" slice itself will have no effect.
// TODO: expose SetUnstructuredContent on runtime.Unstructured that allows
// items to be changed.
func (obj *UnstructuredList) UnstructuredContent() map[string]interface{} {
if obj.Object == nil {
obj.Object = make(map[string]interface{})
out := obj.Object
if out == nil {
out = make(map[string]interface{})
}
return obj.Object
items := make([]interface{}, len(obj.Items))
for i, item := range obj.Items {
items[i] = item.Object
}
out["items"] = items
return out
}
// MarshalJSON ensures that the unstructured object produces proper
@ -191,14 +204,23 @@ func (u *Unstructured) setNestedMap(value map[string]string, fields ...string) {
func extractOwnerReference(src interface{}) metav1.OwnerReference {
v := src.(map[string]interface{})
controllerPtr, ok := (getNestedField(v, "controller")).(*bool)
// though this field is a *bool, but when decoded from JSON, it's
// unmarshalled as bool.
var controllerPtr *bool
controller, ok := (getNestedField(v, "controller")).(bool)
if !ok {
controllerPtr = nil
} else {
if controllerPtr != nil {
controller := *controllerPtr
controllerPtr = &controller
controllerCopy := controller
controllerPtr = &controllerCopy
}
var blockOwnerDeletionPtr *bool
blockOwnerDeletion, ok := (getNestedField(v, "blockOwnerDeletion")).(bool)
if !ok {
blockOwnerDeletionPtr = nil
} else {
blockOwnerDeletionCopy := blockOwnerDeletion
blockOwnerDeletionPtr = &blockOwnerDeletionCopy
}
return metav1.OwnerReference{
Kind: getNestedString(v, "kind"),
@ -206,6 +228,7 @@ func extractOwnerReference(src interface{}) metav1.OwnerReference {
APIVersion: getNestedString(v, "apiVersion"),
UID: (types.UID)(getNestedString(v, "uid")),
Controller: controllerPtr,
BlockOwnerDeletion: blockOwnerDeletionPtr,
}
}
@ -216,11 +239,17 @@ func setOwnerReference(src metav1.OwnerReference) map[string]interface{} {
controller := *controllerPtr
controllerPtr = &controller
}
blockOwnerDeletionPtr := src.BlockOwnerDeletion
if blockOwnerDeletionPtr != nil {
blockOwnerDeletion := *blockOwnerDeletionPtr
blockOwnerDeletionPtr = &blockOwnerDeletion
}
setNestedField(ret, src.Kind, "kind")
setNestedField(ret, src.Name, "name")
setNestedField(ret, src.APIVersion, "apiVersion")
setNestedField(ret, string(src.UID), "uid")
setNestedField(ret, controllerPtr, "controller")
setNestedField(ret, blockOwnerDeletionPtr, "blockOwnerDeletion")
return ret
}

View file

@ -0,0 +1,36 @@
/*
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 unstructured
import "testing"
func TestUnstructuredList(t *testing.T) {
list := &UnstructuredList{
Object: map[string]interface{}{"kind": "List", "apiVersion": "v1"},
Items: []*Unstructured{
{Object: map[string]interface{}{"kind": "Pod", "apiVersion": "v1", "metadata": map[string]interface{}{"name": "test"}}},
},
}
content := list.UnstructuredContent()
items := content["items"].([]interface{})
if len(items) != 1 {
t.Fatalf("unexpected items: %#v", items)
}
if getNestedField(items[0].(map[string]interface{}), "metadata", "name") != "test" {
t.Fatalf("unexpected fields: %#v", items[0])
}
}

View file

@ -17,6 +17,8 @@ limitations under the License.
package validation
import (
"fmt"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/validation"
"k8s.io/apimachinery/pkg/util/validation/field"
@ -72,3 +74,17 @@ func ValidateLabels(labels map[string]string, fldPath *field.Path) field.ErrorLi
}
return allErrs
}
func ValidateDeleteOptions(options *metav1.DeleteOptions) field.ErrorList {
allErrs := field.ErrorList{}
if options.OrphanDependents != nil && options.PropagationPolicy != nil {
allErrs = append(allErrs, field.Invalid(field.NewPath(""), options, "OrphanDependents and DeletionPropagation cannot be both set"))
}
if options.PropagationPolicy != nil &&
*options.PropagationPolicy != metav1.DeletePropagationForeground &&
*options.PropagationPolicy != metav1.DeletePropagationBackground &&
*options.PropagationPolicy != metav1.DeletePropagationOrphan {
allErrs = append(allErrs, field.Invalid(field.NewPath(""), options, fmt.Sprintf("DeletionPropagation need to be one of %q, %q, %q or nil", metav1.DeletePropagationForeground, metav1.DeletePropagationBackground, metav1.DeletePropagationOrphan)))
}
return allErrs
}

View file

@ -186,6 +186,11 @@ func DeepCopy_v1_DeleteOptions(in interface{}, out interface{}, c *conversion.Cl
*out = new(bool)
**out = **in
}
if in.PropagationPolicy != nil {
in, out := &in.PropagationPolicy, &out.PropagationPolicy
*out = new(DeletionPropagation)
**out = **in
}
return nil
}
}
@ -412,6 +417,11 @@ func DeepCopy_v1_OwnerReference(in interface{}, out interface{}, c *conversion.C
*out = new(bool)
**out = **in
}
if in.BlockOwnerDeletion != nil {
in, out := &in.BlockOwnerDeletion, &out.BlockOwnerDeletion
*out = new(bool)
**out = **in
}
return nil
}
}

View file

@ -0,0 +1,681 @@
/*
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 unstructured
import (
"bytes"
encodingjson "encoding/json"
"fmt"
"math"
"os"
"reflect"
"strconv"
"strings"
"sync"
"sync/atomic"
apiequality "k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/diff"
"k8s.io/apimachinery/pkg/util/json"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"github.com/golang/glog"
)
// Converter is an interface for converting between runtime.Object
// and map[string]interface representation.
type Converter interface {
ToUnstructured(obj runtime.Object, u *map[string]interface{}) error
FromUnstructured(u map[string]interface{}, obj runtime.Object) error
}
type structField struct {
structType reflect.Type
field int
}
type fieldInfo struct {
name string
nameValue reflect.Value
omitempty bool
}
type fieldsCacheMap map[structField]*fieldInfo
type fieldsCache struct {
sync.Mutex
value atomic.Value
}
func newFieldsCache() *fieldsCache {
cache := &fieldsCache{}
cache.value.Store(make(fieldsCacheMap))
return cache
}
var (
marshalerType = reflect.TypeOf(new(encodingjson.Marshaler)).Elem()
unmarshalerType = reflect.TypeOf(new(encodingjson.Unmarshaler)).Elem()
mapStringInterfaceType = reflect.TypeOf(map[string]interface{}{})
stringType = reflect.TypeOf(string(""))
int64Type = reflect.TypeOf(int64(0))
uint64Type = reflect.TypeOf(uint64(0))
float64Type = reflect.TypeOf(float64(0))
boolType = reflect.TypeOf(bool(false))
fieldCache = newFieldsCache()
DefaultConverter = NewConverter(parseBool(os.Getenv("KUBE_PATCH_CONVERSION_DETECTOR")))
)
func parseBool(key string) bool {
if len(key) == 0 {
return false
}
value, err := strconv.ParseBool(key)
if err != nil {
utilruntime.HandleError(fmt.Errorf("Couldn't parse '%s' as bool for unstructured mismatch detection", key))
}
return value
}
// ConverterImpl knows how to convert betweek runtime.Object and
// Unstructured in both ways.
type converterImpl struct {
// If true, we will be additionally running conversion via json
// to ensure that the result is true.
// This is supposed to be set only in tests.
mismatchDetection bool
}
func NewConverter(mismatchDetection bool) Converter {
return &converterImpl{
mismatchDetection: mismatchDetection,
}
}
func (c *converterImpl) FromUnstructured(u map[string]interface{}, obj runtime.Object) error {
err := fromUnstructured(reflect.ValueOf(u), reflect.ValueOf(obj).Elem())
if c.mismatchDetection {
newObj := reflect.New(reflect.TypeOf(obj).Elem()).Interface().(runtime.Object)
newErr := fromUnstructuredViaJSON(u, newObj)
if (err != nil) != (newErr != nil) {
glog.Fatalf("FromUnstructured unexpected error for %v: error: %v", u, err)
}
if err == nil && !apiequality.Semantic.DeepEqual(obj, newObj) {
glog.Fatalf("FromUnstructured mismatch for %#v, diff: %v", obj, diff.ObjectReflectDiff(obj, newObj))
}
}
return err
}
func fromUnstructuredViaJSON(u map[string]interface{}, obj runtime.Object) error {
data, err := json.Marshal(u)
if err != nil {
return err
}
return json.Unmarshal(data, obj)
}
func fromUnstructured(sv, dv reflect.Value) error {
sv = unwrapInterface(sv)
if !sv.IsValid() {
dv.Set(reflect.Zero(dv.Type()))
return nil
}
st, dt := sv.Type(), dv.Type()
switch dt.Kind() {
case reflect.Map, reflect.Slice, reflect.Ptr, reflect.Struct, reflect.Interface:
// Those require non-trivial conversion.
default:
// This should handle all simple types.
if st.AssignableTo(dt) {
dv.Set(sv)
return nil
}
// We cannot simply use "ConvertibleTo", as JSON doesn't support conversions
// between those four groups: bools, integers, floats and string. We need to
// do the same.
if st.ConvertibleTo(dt) {
switch st.Kind() {
case reflect.String:
switch dt.Kind() {
case reflect.String:
dv.Set(sv.Convert(dt))
return nil
}
case reflect.Bool:
switch dt.Kind() {
case reflect.Bool:
dv.Set(sv.Convert(dt))
return nil
}
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
switch dt.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
dv.Set(sv.Convert(dt))
return nil
}
case reflect.Float32, reflect.Float64:
switch dt.Kind() {
case reflect.Float32, reflect.Float64:
dv.Set(sv.Convert(dt))
return nil
}
if sv.Float() == math.Trunc(sv.Float()) {
dv.Set(sv.Convert(dt))
return nil
}
}
return fmt.Errorf("cannot convert %s to %s", st.String(), dt.String())
}
}
// Check if the object has a custom JSON marshaller/unmarshaller.
if reflect.PtrTo(dt).Implements(unmarshalerType) {
data, err := json.Marshal(sv.Interface())
if err != nil {
return fmt.Errorf("error encoding %s to json: %v", st.String(), err)
}
unmarshaler := dv.Addr().Interface().(encodingjson.Unmarshaler)
return unmarshaler.UnmarshalJSON(data)
}
switch dt.Kind() {
case reflect.Map:
return mapFromUnstructured(sv, dv)
case reflect.Slice:
return sliceFromUnstructured(sv, dv)
case reflect.Ptr:
return pointerFromUnstructured(sv, dv)
case reflect.Struct:
return structFromUnstructured(sv, dv)
case reflect.Interface:
return interfaceFromUnstructured(sv, dv)
default:
return fmt.Errorf("unrecognized type: %v", dt.Kind())
}
}
func fieldInfoFromField(structType reflect.Type, field int) *fieldInfo {
fieldCacheMap := fieldCache.value.Load().(fieldsCacheMap)
if info, ok := fieldCacheMap[structField{structType, field}]; ok {
return info
}
// Cache miss - we need to compute the field name.
info := &fieldInfo{}
typeField := structType.Field(field)
jsonTag := typeField.Tag.Get("json")
if len(jsonTag) == 0 {
// Make the first character lowercase.
if typeField.Name == "" {
info.name = typeField.Name
} else {
info.name = strings.ToLower(typeField.Name[:1]) + typeField.Name[1:]
}
} else {
items := strings.Split(jsonTag, ",")
info.name = items[0]
for i := range items {
if items[i] == "omitempty" {
info.omitempty = true
}
}
}
info.nameValue = reflect.ValueOf(info.name)
fieldCache.Lock()
defer fieldCache.Unlock()
fieldCacheMap = fieldCache.value.Load().(fieldsCacheMap)
newFieldCacheMap := make(fieldsCacheMap)
for k, v := range fieldCacheMap {
newFieldCacheMap[k] = v
}
newFieldCacheMap[structField{structType, field}] = info
fieldCache.value.Store(newFieldCacheMap)
return info
}
func unwrapInterface(v reflect.Value) reflect.Value {
for v.Kind() == reflect.Interface {
v = v.Elem()
}
return v
}
func mapFromUnstructured(sv, dv reflect.Value) error {
st, dt := sv.Type(), dv.Type()
if st.Kind() != reflect.Map {
return fmt.Errorf("cannot restore map from %v", st.Kind())
}
if !st.Key().AssignableTo(dt.Key()) && !st.Key().ConvertibleTo(dt.Key()) {
return fmt.Errorf("cannot copy map with non-assignable keys: %v %v", st.Key(), dt.Key())
}
if sv.IsNil() {
dv.Set(reflect.Zero(dt))
return nil
}
dv.Set(reflect.MakeMap(dt))
for _, key := range sv.MapKeys() {
value := reflect.New(dt.Elem()).Elem()
if val := unwrapInterface(sv.MapIndex(key)); val.IsValid() {
if err := fromUnstructured(val, value); err != nil {
return err
}
} else {
value.Set(reflect.Zero(dt.Elem()))
}
if st.Key().AssignableTo(dt.Key()) {
dv.SetMapIndex(key, value)
} else {
dv.SetMapIndex(key.Convert(dt.Key()), value)
}
}
return nil
}
func sliceFromUnstructured(sv, dv reflect.Value) error {
st, dt := sv.Type(), dv.Type()
if st.Kind() == reflect.String && dt.Elem().Kind() == reflect.Uint8 {
// We store original []byte representation as string.
// This conversion is allowed, but we need to be careful about
// marshaling data appropriately.
if len(sv.Interface().(string)) > 0 {
marshalled, err := json.Marshal(sv.Interface())
if err != nil {
return fmt.Errorf("error encoding %s to json: %v", st, err)
}
// TODO: Is this Unmarshal needed?
var data []byte
err = json.Unmarshal(marshalled, &data)
if err != nil {
return fmt.Errorf("error decoding from json: %v", err)
}
dv.SetBytes(data)
} else {
dv.Set(reflect.Zero(dt))
}
return nil
}
if st.Kind() != reflect.Slice {
return fmt.Errorf("cannot restore slice from %v", st.Kind())
}
if sv.IsNil() {
dv.Set(reflect.Zero(dt))
return nil
}
dv.Set(reflect.MakeSlice(dt, sv.Len(), sv.Cap()))
for i := 0; i < sv.Len(); i++ {
if err := fromUnstructured(sv.Index(i), dv.Index(i)); err != nil {
return err
}
}
return nil
}
func pointerFromUnstructured(sv, dv reflect.Value) error {
st, dt := sv.Type(), dv.Type()
if st.Kind() == reflect.Ptr && sv.IsNil() {
dv.Set(reflect.Zero(dt))
return nil
}
dv.Set(reflect.New(dt.Elem()))
switch st.Kind() {
case reflect.Ptr, reflect.Interface:
return fromUnstructured(sv.Elem(), dv.Elem())
default:
return fromUnstructured(sv, dv.Elem())
}
}
func structFromUnstructured(sv, dv reflect.Value) error {
st, dt := sv.Type(), dv.Type()
if st.Kind() != reflect.Map {
return fmt.Errorf("cannot restore struct from: %v", st.Kind())
}
for i := 0; i < dt.NumField(); i++ {
fieldInfo := fieldInfoFromField(dt, i)
fv := dv.Field(i)
if len(fieldInfo.name) == 0 {
// This field is inlined.
if err := fromUnstructured(sv, fv); err != nil {
return err
}
} else {
value := unwrapInterface(sv.MapIndex(fieldInfo.nameValue))
if value.IsValid() {
if err := fromUnstructured(value, fv); err != nil {
return err
}
} else {
fv.Set(reflect.Zero(fv.Type()))
}
}
}
return nil
}
func interfaceFromUnstructured(sv, dv reflect.Value) error {
// TODO: Is this conversion safe?
dv.Set(sv)
return nil
}
func (c *converterImpl) ToUnstructured(obj runtime.Object, u *map[string]interface{}) error {
err := toUnstructured(reflect.ValueOf(obj).Elem(), reflect.ValueOf(u).Elem())
if c.mismatchDetection {
newUnstr := &map[string]interface{}{}
newErr := toUnstructuredViaJSON(obj, newUnstr)
if (err != nil) != (newErr != nil) {
glog.Fatalf("ToUnstructured unexpected error for %v: error: %v", obj, err)
}
if err == nil && !apiequality.Semantic.DeepEqual(u, newUnstr) {
glog.Fatalf("ToUnstructured mismatch for %#v, diff: %v", u, diff.ObjectReflectDiff(u, newUnstr))
}
}
return err
}
func toUnstructuredViaJSON(obj runtime.Object, u *map[string]interface{}) error {
data, err := json.Marshal(obj)
if err != nil {
return err
}
return json.Unmarshal(data, u)
}
func toUnstructured(sv, dv reflect.Value) error {
st, dt := sv.Type(), dv.Type()
// Check if the object has a custom JSON marshaller/unmarshaller.
if st.Implements(marshalerType) {
if sv.Kind() == reflect.Ptr && sv.IsNil() {
// We're done - we don't need to store anything.
return nil
}
marshaler := sv.Interface().(encodingjson.Marshaler)
data, err := marshaler.MarshalJSON()
if err != nil {
return err
}
if bytes.Equal(data, []byte("null")) {
// We're done - we don't need to store anything.
} else {
switch {
case len(data) > 0 && data[0] == '"':
var result string
err := json.Unmarshal(data, &result)
if err != nil {
return fmt.Errorf("error decoding from json: %v", err)
}
dv.Set(reflect.ValueOf(result))
case len(data) > 0 && data[0] == '{':
result := make(map[string]interface{})
err := json.Unmarshal(data, &result)
if err != nil {
return fmt.Errorf("error decoding from json: %v", err)
}
dv.Set(reflect.ValueOf(result))
default:
var result int64
err := json.Unmarshal(data, &result)
if err != nil {
return fmt.Errorf("error decoding from json: %v", err)
}
dv.Set(reflect.ValueOf(result))
}
}
return nil
}
switch st.Kind() {
case reflect.String:
if dt.Kind() == reflect.Interface && dv.NumMethod() == 0 {
dv.Set(reflect.New(stringType))
}
dv.Set(reflect.ValueOf(sv.String()))
return nil
case reflect.Bool:
if dt.Kind() == reflect.Interface && dv.NumMethod() == 0 {
dv.Set(reflect.New(boolType))
}
dv.Set(reflect.ValueOf(sv.Bool()))
return nil
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if dt.Kind() == reflect.Interface && dv.NumMethod() == 0 {
dv.Set(reflect.New(int64Type))
}
dv.Set(reflect.ValueOf(sv.Int()))
return nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
if dt.Kind() == reflect.Interface && dv.NumMethod() == 0 {
dv.Set(reflect.New(uint64Type))
}
dv.Set(reflect.ValueOf(sv.Uint()))
return nil
case reflect.Float32, reflect.Float64:
if dt.Kind() == reflect.Interface && dv.NumMethod() == 0 {
dv.Set(reflect.New(float64Type))
}
dv.Set(reflect.ValueOf(sv.Float()))
return nil
case reflect.Map:
return mapToUnstructured(sv, dv)
case reflect.Slice:
return sliceToUnstructured(sv, dv)
case reflect.Ptr:
return pointerToUnstructured(sv, dv)
case reflect.Struct:
return structToUnstructured(sv, dv)
case reflect.Interface:
return interfaceToUnstructured(sv, dv)
default:
return fmt.Errorf("unrecognized type: %v", st.Kind())
}
}
func mapToUnstructured(sv, dv reflect.Value) error {
st, dt := sv.Type(), dv.Type()
if sv.IsNil() {
dv.Set(reflect.Zero(dt))
return nil
}
if dt.Kind() == reflect.Interface && dv.NumMethod() == 0 {
if st.Key().Kind() == reflect.String {
switch st.Elem().Kind() {
// TODO It should be possible to reuse the slice for primitive types.
// However, it is panicing in the following form.
// case reflect.String, reflect.Bool,
// reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
// reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
// sv.Set(sv)
// return nil
default:
// We need to do a proper conversion.
}
}
dv.Set(reflect.MakeMap(mapStringInterfaceType))
dv = dv.Elem()
dt = dv.Type()
}
if dt.Kind() != reflect.Map {
return fmt.Errorf("cannot convert struct to: %v", dt.Kind())
}
if !st.Key().AssignableTo(dt.Key()) && !st.Key().ConvertibleTo(dt.Key()) {
return fmt.Errorf("cannot copy map with non-assignable keys: %v %v", st.Key(), dt.Key())
}
for _, key := range sv.MapKeys() {
value := reflect.New(dt.Elem()).Elem()
if err := toUnstructured(sv.MapIndex(key), value); err != nil {
return err
}
if st.Key().AssignableTo(dt.Key()) {
dv.SetMapIndex(key, value)
} else {
dv.SetMapIndex(key.Convert(dt.Key()), value)
}
}
return nil
}
func sliceToUnstructured(sv, dv reflect.Value) error {
st, dt := sv.Type(), dv.Type()
if sv.IsNil() {
dv.Set(reflect.Zero(dt))
return nil
}
if st.Elem().Kind() == reflect.Uint8 {
dv.Set(reflect.New(stringType))
data, err := json.Marshal(sv.Bytes())
if err != nil {
return err
}
var result string
if err = json.Unmarshal(data, &result); err != nil {
return err
}
dv.Set(reflect.ValueOf(result))
return nil
}
if dt.Kind() == reflect.Interface && dv.NumMethod() == 0 {
switch st.Elem().Kind() {
// TODO It should be possible to reuse the slice for primitive types.
// However, it is panicing in the following form.
// case reflect.String, reflect.Bool,
// reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
// reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
// sv.Set(sv)
// return nil
default:
// We need to do a proper conversion.
dv.Set(reflect.MakeSlice(reflect.SliceOf(dt), sv.Len(), sv.Cap()))
dv = dv.Elem()
dt = dv.Type()
}
}
if dt.Kind() != reflect.Slice {
return fmt.Errorf("cannot convert slice to: %v", dt.Kind())
}
for i := 0; i < sv.Len(); i++ {
if err := toUnstructured(sv.Index(i), dv.Index(i)); err != nil {
return err
}
}
return nil
}
func pointerToUnstructured(sv, dv reflect.Value) error {
if sv.IsNil() {
// We're done - we don't need to store anything.
return nil
}
return toUnstructured(sv.Elem(), dv)
}
func isZero(v reflect.Value) bool {
switch v.Kind() {
case reflect.Array, reflect.String:
return v.Len() == 0
case reflect.Bool:
return !v.Bool()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return v.Int() == 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return v.Uint() == 0
case reflect.Float32, reflect.Float64:
return v.Float() == 0
case reflect.Map, reflect.Slice:
// TODO: It seems that 0-len maps are ignored in it.
return v.IsNil() || v.Len() == 0
case reflect.Ptr, reflect.Interface:
return v.IsNil()
}
return false
}
func structToUnstructured(sv, dv reflect.Value) error {
st, dt := sv.Type(), dv.Type()
if dt.Kind() == reflect.Interface && dv.NumMethod() == 0 {
dv.Set(reflect.MakeMap(mapStringInterfaceType))
dv = dv.Elem()
dt = dv.Type()
}
if dt.Kind() != reflect.Map {
return fmt.Errorf("cannot convert struct to: %v", dt.Kind())
}
realMap := dv.Interface().(map[string]interface{})
for i := 0; i < st.NumField(); i++ {
fieldInfo := fieldInfoFromField(st, i)
fv := sv.Field(i)
if fieldInfo.name == "-" {
// This field should be skipped.
continue
}
if fieldInfo.omitempty && isZero(fv) {
// omitempty fields should be ignored.
continue
}
if len(fieldInfo.name) == 0 {
// This field is inlined.
if err := toUnstructured(fv, dv); err != nil {
return err
}
continue
}
switch fv.Type().Kind() {
case reflect.String:
realMap[fieldInfo.name] = fv.String()
case reflect.Bool:
realMap[fieldInfo.name] = fv.Bool()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
realMap[fieldInfo.name] = fv.Int()
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
realMap[fieldInfo.name] = fv.Uint()
case reflect.Float32, reflect.Float64:
realMap[fieldInfo.name] = fv.Float()
default:
subv := reflect.New(dt.Elem()).Elem()
if err := toUnstructured(fv, subv); err != nil {
return err
}
dv.SetMapIndex(fieldInfo.nameValue, subv)
}
}
return nil
}
func interfaceToUnstructured(sv, dv reflect.Value) error {
if !sv.IsValid() || sv.IsNil() {
dv.Set(reflect.Zero(dv.Type()))
return nil
}
return toUnstructured(sv.Elem(), dv)
}

View file

@ -0,0 +1,462 @@
/*
Copyright 2015 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package unstructured
import (
"fmt"
"reflect"
"testing"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/diff"
"k8s.io/apimachinery/pkg/util/json"
)
// Definte a number of test types.
type A struct {
A int `json:"aa,omitempty"`
B string `json:"ab,omitempty"`
C bool `json:"ac,omitempty"`
}
type B struct {
A A `json:"ba"`
B string `json:"bb"`
C map[string]string `json:"bc"`
D []string `json:"bd"`
}
type C struct {
A []A `json:"ca"`
B `json:",inline"`
C string `json:"cc"`
D *int64 `json:"cd"`
E map[string]int `json:"ce"`
F []bool `json:"cf"`
G []int `json:"cg"`
H float32 `json:"ch"`
I []interface{} `json:"ci"`
}
type D struct {
A []interface{} `json:"da"`
}
type E struct {
A interface{} `json:"ea"`
}
type F struct {
A string `json:"fa"`
B map[string]string `json:"fb"`
C []A `json:"fc"`
D int `json:"fd"`
E float32 `json:"fe"`
F []string `json:"ff"`
G []int `json:"fg"`
H []bool `json:"fh"`
I []float32 `json:"fi"`
}
// Implement runtime.Object to make types usable for tests.
func (c *C) GetObjectKind() schema.ObjectKind {
return schema.EmptyObjectKind
}
func (d *D) GetObjectKind() schema.ObjectKind {
return schema.EmptyObjectKind
}
func (e *E) GetObjectKind() schema.ObjectKind {
return schema.EmptyObjectKind
}
func (f *F) GetObjectKind() schema.ObjectKind {
return schema.EmptyObjectKind
}
func doRoundTrip(t *testing.T, item runtime.Object) {
data, err := json.Marshal(item)
if err != nil {
t.Errorf("Error when marshaling object: %v", err)
return
}
unstr := make(map[string]interface{})
err = json.Unmarshal(data, &unstr)
if err != nil {
t.Errorf("Error when unmarshaling to unstructured: %v", err)
return
}
data, err = json.Marshal(unstr)
if err != nil {
t.Errorf("Error when marshaling unstructured: %v", err)
return
}
unmarshalledObj := reflect.New(reflect.TypeOf(item).Elem()).Interface()
err = json.Unmarshal(data, &unmarshalledObj)
if err != nil {
t.Errorf("Error when unmarshaling to object: %v", err)
return
}
if !reflect.DeepEqual(item, unmarshalledObj) {
t.Errorf("Object changed during JSON operations, diff: %v", diff.ObjectReflectDiff(item, unmarshalledObj))
return
}
newUnstr := make(map[string]interface{})
err = DefaultConverter.ToUnstructured(item, &newUnstr)
if err != nil {
t.Errorf("ToUnstructured failed: %v", err)
return
}
newObj := reflect.New(reflect.TypeOf(item).Elem()).Interface().(runtime.Object)
err = DefaultConverter.FromUnstructured(newUnstr, newObj)
if err != nil {
t.Errorf("FromUnstructured failed: %v", err)
return
}
if !reflect.DeepEqual(item, newObj) {
t.Errorf("Object changed, diff: %v", diff.ObjectReflectDiff(item, newObj))
}
}
func TestRoundTrip(t *testing.T) {
intVal := int64(42)
testCases := []struct {
obj runtime.Object
}{
{
// This (among others) tests nil map, slice and pointer.
obj: &C{
C: "ccc",
},
},
{
// This (among others) tests empty map and slice.
obj: &C{
A: []A{},
C: "ccc",
E: map[string]int{},
I: []interface{}{},
},
},
{
obj: &C{
A: []A{
{
A: 1,
B: "11",
C: true,
},
{
A: 2,
B: "22",
C: false,
},
},
B: B{
A: A{
A: 3,
B: "33",
},
B: "bbb",
C: map[string]string{
"k1": "v1",
"k2": "v2",
},
D: []string{"s1", "s2"},
},
C: "ccc",
D: &intVal,
E: map[string]int{
"k1": 1,
"k2": 2,
},
F: []bool{true, false, false},
G: []int{1, 2, 5},
H: 3.3,
I: []interface{}{nil, nil, nil},
},
},
{
// Test slice of interface{} with empty slices.
obj: &D{
A: []interface{}{[]interface{}{}, []interface{}{}},
},
},
{
// Test slice of interface{} with different values.
obj: &D{
A: []interface{}{3.0, "3.0", nil},
},
},
}
for i := range testCases {
doRoundTrip(t, testCases[i].obj)
if t.Failed() {
break
}
}
}
// Verifies that:
// 1) serialized json -> object
// 2) serialized json -> map[string]interface{} -> object
// produces the same object.
func doUnrecognized(t *testing.T, jsonData string, item runtime.Object, expectedErr error) {
unmarshalledObj := reflect.New(reflect.TypeOf(item).Elem()).Interface()
err := json.Unmarshal([]byte(jsonData), &unmarshalledObj)
if (err != nil) != (expectedErr != nil) {
t.Errorf("Unexpected error when unmarshaling to object: %v, expected: %v", err, expectedErr)
return
}
unstr := make(map[string]interface{})
err = json.Unmarshal([]byte(jsonData), &unstr)
if err != nil {
t.Errorf("Error when unmarshaling to unstructured: %v", err)
return
}
newObj := reflect.New(reflect.TypeOf(item).Elem()).Interface().(runtime.Object)
err = DefaultConverter.FromUnstructured(unstr, newObj)
if (err != nil) != (expectedErr != nil) {
t.Errorf("Unexpected error in FromUnstructured: %v, expected: %v", err, expectedErr)
}
if expectedErr == nil && !reflect.DeepEqual(unmarshalledObj, newObj) {
t.Errorf("Object changed, diff: %v", diff.ObjectReflectDiff(unmarshalledObj, newObj))
}
}
func TestUnrecognized(t *testing.T) {
testCases := []struct {
data string
obj runtime.Object
err error
}{
{
data: "{\"da\":[3.0,\"3.0\",null]}",
obj: &D{},
},
{
data: "{\"ea\":[3.0,\"3.0\",null]}",
obj: &E{},
},
{
data: "{\"ea\":[null,null,null]}",
obj: &E{},
},
{
data: "{\"ea\":[[],[null]]}",
obj: &E{},
},
{
data: "{\"ea\":{\"a\":[],\"b\":null}}",
obj: &E{},
},
{
data: "{\"fa\":\"fa\",\"fb\":{\"a\":\"a\"}}",
obj: &F{},
},
{
data: "{\"fa\":\"fa\",\"fb\":{\"a\":null}}",
obj: &F{},
},
{
data: "{\"fc\":[null]}",
obj: &F{},
},
{
data: "{\"fc\":[{\"aa\":123,\"ab\":\"bbb\"}]}",
obj: &F{},
},
{
// Only unknown fields
data: "{\"fx\":[{\"aa\":123,\"ab\":\"bbb\"}],\"fz\":123}",
obj: &F{},
},
{
data: "{\"fc\":[{\"aa\":\"aaa\",\"ab\":\"bbb\"}]}",
obj: &F{},
err: fmt.Errorf("json: cannot unmarshal string into Go value of type int"),
},
{
data: "{\"fd\":123,\"fe\":3.5}",
obj: &F{},
},
{
data: "{\"ff\":[\"abc\"],\"fg\":[123],\"fh\":[true,false]}",
obj: &F{},
},
{
// Invalid string data
data: "{\"fa\":123}",
obj: &F{},
err: fmt.Errorf("json: cannot unmarshal number into Go value of type string"),
},
{
// Invalid string data
data: "{\"fa\":13.5}",
obj: &F{},
err: fmt.Errorf("json: cannot unmarshal number into Go value of type string"),
},
{
// Invalid string data
data: "{\"fa\":true}",
obj: &F{},
err: fmt.Errorf("json: cannot unmarshal bool into Go value of type string"),
},
{
// Invalid []string data
data: "{\"ff\":123}",
obj: &F{},
err: fmt.Errorf("json: cannot unmarshal number into Go value of type []string"),
},
{
// Invalid []string data
data: "{\"ff\":3.5}",
obj: &F{},
err: fmt.Errorf("json: cannot unmarshal number into Go value of type []string"),
},
{
// Invalid []string data
data: "{\"ff\":[123,345]}",
obj: &F{},
err: fmt.Errorf("json: cannot unmarshal number into Go value of type string"),
},
{
// Invalid []int data
data: "{\"fg\":123}",
obj: &F{},
err: fmt.Errorf("json: cannot unmarshal number into Go value of type []int"),
},
{
// Invalid []int data
data: "{\"fg\":\"abc\"}",
obj: &F{},
err: fmt.Errorf("json: cannot unmarshal string into Go value of type []int"),
},
{
// Invalid []int data
data: "{\"fg\":[\"abc\"]}",
obj: &F{},
err: fmt.Errorf("json: cannot unmarshal string into Go value of type int"),
},
{
// Invalid []int data
data: "{\"fg\":[3.5]}",
obj: &F{},
err: fmt.Errorf("json: cannot unmarshal number 3.5 into Go value of type int"),
},
{
// Invalid []int data
data: "{\"fg\":[true,false]}",
obj: &F{},
err: fmt.Errorf("json: cannot unmarshal number 3.5 into Go value of type int"),
},
{
// Invalid []bool data
data: "{\"fh\":123}",
obj: &F{},
err: fmt.Errorf("json: cannot unmarshal number into Go value of type []bool"),
},
{
// Invalid []bool data
data: "{\"fh\":\"abc\"}",
obj: &F{},
err: fmt.Errorf("json: cannot unmarshal string into Go value of type []bool"),
},
{
// Invalid []bool data
data: "{\"fh\":[\"abc\"]}",
obj: &F{},
err: fmt.Errorf("json: cannot unmarshal string into Go value of type bool"),
},
{
// Invalid []bool data
data: "{\"fh\":[3.5]}",
obj: &F{},
err: fmt.Errorf("json: cannot unmarshal number into Go value of type bool"),
},
{
// Invalid []bool data
data: "{\"fh\":[123]}",
obj: &F{},
err: fmt.Errorf("json: cannot unmarshal number into Go value of type bool"),
},
{
// Invalid []float data
data: "{\"fi\":123}",
obj: &F{},
err: fmt.Errorf("json: cannot unmarshal number into Go value of type []float32"),
},
{
// Invalid []float data
data: "{\"fi\":\"abc\"}",
obj: &F{},
err: fmt.Errorf("json: cannot unmarshal string into Go value of type []float32"),
},
{
// Invalid []float data
data: "{\"fi\":[\"abc\"]}",
obj: &F{},
err: fmt.Errorf("json: cannot unmarshal string into Go value of type float32"),
},
{
// Invalid []float data
data: "{\"fi\":[true]}",
obj: &F{},
err: fmt.Errorf("json: cannot unmarshal bool into Go value of type float32"),
},
}
for i := range testCases {
doUnrecognized(t, testCases[i].data, testCases[i].obj, testCases[i].err)
if t.Failed() {
break
}
}
}
func TestFloatIntConversion(t *testing.T) {
unstr := map[string]interface{}{"fd": float64(3)}
var obj F
if err := DefaultConverter.FromUnstructured(unstr, &obj); err != nil {
t.Errorf("Unexpected error in FromUnstructured: %v", err)
}
data, err := json.Marshal(unstr)
if err != nil {
t.Fatalf("Error when marshaling unstructured: %v", err)
}
var unmarshalled F
if err := json.Unmarshal(data, &unmarshalled); err != nil {
t.Fatalf("Error when unmarshaling to object: %v", err)
}
if !reflect.DeepEqual(obj, unmarshalled) {
t.Errorf("Incorrect conversion, diff: %v", diff.ObjectReflectDiff(obj, unmarshalled))
}
}

View file

@ -0,0 +1,19 @@
/*
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 unstructured provides conversion from runtime objects
// to map[string]interface{} representation.
package unstructured // import "k8s.io/apimachinery/pkg/conversion/unstructured"

View file

@ -406,3 +406,8 @@ func parseSelector(selector string, fn TransformFunc) (Selector, error) {
func OneTermEqualSelector(k, v string) Selector {
return &hasTerm{field: k, value: v}
}
// AndSelectors creates a selector that is the logical AND of all the given selectors
func AndSelectors(selectors ...Selector) Selector {
return andTerm(selectors)
}

View file

@ -19,6 +19,7 @@ package openapi
import (
"github.com/emicklei/go-restful"
"github.com/go-openapi/spec"
"strings"
)
// OpenAPIDefinition describes single type. Normally these definitions are auto-generated using gen-openapi.
@ -27,8 +28,10 @@ type OpenAPIDefinition struct {
Dependencies []string
}
type ReferenceCallback func(path string) spec.Ref
// OpenAPIDefinitions is collection of all definitions.
type OpenAPIDefinitions map[string]OpenAPIDefinition
type GetOpenAPIDefinitions func(ReferenceCallback) map[string]OpenAPIDefinition
// OpenAPIDefinitionGetter gets openAPI definitions for a given type. If a type implements this interface,
// the definition returned by it will be used, otherwise the auto-generated definitions will be used. See
@ -59,11 +62,18 @@ type Config struct {
// OpenAPIDefinitions should provide definition for all models used by routes. Failure to provide this map
// or any of the models will result in spec generation failure.
Definitions *OpenAPIDefinitions
GetDefinitions GetOpenAPIDefinitions
// GetOperationIDAndTags returns operation id and tags for a restful route. It is an optional function to customize operation IDs.
GetOperationIDAndTags func(servePath string, r *restful.Route) (string, []string, error)
// GetDefinitionName returns a friendly name for a definition base on the serving path. parameter `name` is the full name of the definition.
// It is an optional function to customize model names.
GetDefinitionName func(servePath string, name string) (string, spec.Extensions)
// PostProcessSpec runs after the spec is ready to serve. It allows a final modification to the spec before serving.
PostProcessSpec func(*spec.Swagger) (*spec.Swagger, error)
// SecurityDefinitions is list of all security definitions for OpenAPI service. If this is not nil, the user of config
// is responsible to provide DefaultSecurity and (maybe) add unauthorized response to CommonResponses.
SecurityDefinitions *spec.SecurityDefinitions
@ -141,3 +151,10 @@ func GetOpenAPITypeFormat(typeName string) (string, string) {
}
return mapped[0], mapped[1]
}
func EscapeJsonPointer(p string) string {
// Escaping reference name using rfc6901
p = strings.Replace(p, "~", "~0", -1)
p = strings.Replace(p, "/", "~1", -1)
return p
}

View file

@ -161,11 +161,12 @@ func (c *parameterCodec) DecodeParameters(parameters url.Values, from schema.Gro
if err != nil {
return err
}
targetGVK := targetGVKs[0]
if targetGVK.GroupVersion() == from {
for i := range targetGVKs {
if targetGVKs[i].GroupVersion() == from {
return c.convertor.Convert(&parameters, into, nil)
}
input, err := c.creator.New(from.WithKind(targetGVK.Kind))
}
input, err := c.creator.New(from.WithKind(targetGVKs[0].Kind))
if err != nil {
return err
}

View file

@ -163,22 +163,13 @@ func (s *Scheme) AddUnversionedTypes(version schema.GroupVersion, types ...Objec
// the struct becomes the "kind" field when encoding. Version may not be empty - use the
// APIVersionInternal constant if you have a type that does not have a formal version.
func (s *Scheme) AddKnownTypes(gv schema.GroupVersion, types ...Object) {
if len(gv.Version) == 0 {
panic(fmt.Sprintf("version is required on all types: %s %v", gv, types[0]))
}
for _, obj := range types {
t := reflect.TypeOf(obj)
if t.Kind() != reflect.Ptr {
panic("All types must be pointers to structs.")
}
t = t.Elem()
if t.Kind() != reflect.Struct {
panic("All types must be pointers to structs.")
}
gvk := gv.WithKind(t.Name())
s.gvkToType[gvk] = t
s.typeToGVK[t] = append(s.typeToGVK[t], gvk)
s.AddKnownTypeWithName(gv.WithKind(t.Name()), obj)
}
}
@ -199,7 +190,17 @@ func (s *Scheme) AddKnownTypeWithName(gvk schema.GroupVersionKind, obj Object) {
panic("All types must be pointers to structs.")
}
if oldT, found := s.gvkToType[gvk]; found && oldT != t {
panic(fmt.Sprintf("Double registration of different types for %v: old=%v.%v, new=%v.%v", gvk, oldT.PkgPath(), oldT.Name(), t.PkgPath(), t.Name()))
}
s.gvkToType[gvk] = t
for _, existingGvk := range s.typeToGVK[t] {
if existingGvk == gvk {
return
}
}
s.typeToGVK[t] = append(s.typeToGVK[t], gvk)
}

View file

@ -548,6 +548,79 @@ func TestKnownTypes(t *testing.T) {
}
}
func TestAddKnownTypesIdemPotent(t *testing.T) {
s := runtime.NewScheme()
gv := schema.GroupVersion{Group: "foo", Version: "v1"}
s.AddKnownTypes(gv, &InternalSimple{})
s.AddKnownTypes(gv, &InternalSimple{})
if len(s.KnownTypes(gv)) != 1 {
t.Errorf("expected only one %v type after double registration", gv)
}
if len(s.AllKnownTypes()) != 1 {
t.Errorf("expected only one type after double registration")
}
s.AddKnownTypeWithName(gv.WithKind("InternalSimple"), &InternalSimple{})
s.AddKnownTypeWithName(gv.WithKind("InternalSimple"), &InternalSimple{})
if len(s.KnownTypes(gv)) != 1 {
t.Errorf("expected only one %v type after double registration with custom name", gv)
}
if len(s.AllKnownTypes()) != 1 {
t.Errorf("expected only one type after double registration with custom name")
}
s.AddUnversionedTypes(gv, &InternalSimple{})
if len(s.KnownTypes(gv)) != 1 {
t.Errorf("expected only one %v type after double registration with custom name", gv)
}
if len(s.AllKnownTypes()) != 1 {
t.Errorf("expected only one type after double registration with custom name")
}
kinds, _, err := s.ObjectKinds(&InternalSimple{})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if len(kinds) != 1 {
t.Errorf("expected only one kind for InternalSimple after double registration", gv)
}
}
func TestConflictingAddKnownTypes(t *testing.T) {
s := runtime.NewScheme()
gv := schema.GroupVersion{Group: "foo", Version: "v1"}
panicked := make(chan bool)
go func() {
defer func() {
if recover() != nil {
panicked <- true
}
}()
s.AddKnownTypeWithName(gv.WithKind("InternalSimple"), &InternalSimple{})
s.AddKnownTypeWithName(gv.WithKind("InternalSimple"), &ExternalSimple{})
panicked <- false
}()
if !<-panicked {
t.Errorf("Expected AddKnownTypesWithName to panic with conflicting type registrations")
}
go func() {
defer func() {
if recover() != nil {
panicked <- true
}
}()
s.AddUnversionedTypes(gv, &InternalSimple{})
s.AddUnversionedTypes(gv, &InternalSimple{})
panicked <- false
}()
if !<-panicked {
t.Errorf("Expected AddUnversionedTypes to panic with conflicting type registrations")
}
}
func TestConvertToVersionBasic(t *testing.T) {
s := GetTestScheme()
tt := &TestType1{A: "I'm not a pointer object"}

View file

@ -21,8 +21,8 @@ import (
"net/http"
"strings"
"k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/httpstream"
"k8s.io/apimachinery/pkg/util/runtime"
)
const HeaderSpdy31 = "SPDY/3.1"

View file

@ -0,0 +1,144 @@
/*
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 jsonmergepatch
import (
"fmt"
"reflect"
"github.com/evanphx/json-patch"
"k8s.io/apimachinery/pkg/util/json"
"k8s.io/apimachinery/pkg/util/mergepatch"
)
// Create a 3-way merge patch based-on JSON merge patch.
// Calculate addition-and-change patch between current and modified.
// Calculate deletion patch between original and modified.
func CreateThreeWayJSONMergePatch(original, modified, current []byte, fns ...mergepatch.PreconditionFunc) ([]byte, error) {
if len(original) == 0 {
original = []byte(`{}`)
}
if len(modified) == 0 {
modified = []byte(`{}`)
}
if len(current) == 0 {
current = []byte(`{}`)
}
addAndChangePatch, err := jsonpatch.CreateMergePatch(current, modified)
if err != nil {
return nil, err
}
// Only keep addition and changes
addAndChangePatch, addAndChangePatchObj, err := keepOrDeleteNullInJsonPatch(addAndChangePatch, false)
if err != nil {
return nil, err
}
deletePatch, err := jsonpatch.CreateMergePatch(original, modified)
if err != nil {
return nil, err
}
// Only keep deletion
deletePatch, deletePatchObj, err := keepOrDeleteNullInJsonPatch(deletePatch, true)
if err != nil {
return nil, err
}
hasConflicts, err := mergepatch.HasConflicts(addAndChangePatchObj, deletePatchObj)
if err != nil {
return nil, err
}
if hasConflicts {
return nil, mergepatch.NewErrConflict(mergepatch.ToYAMLOrError(addAndChangePatchObj), mergepatch.ToYAMLOrError(deletePatchObj))
}
patch, err := jsonpatch.MergePatch(deletePatch, addAndChangePatch)
if err != nil {
return nil, err
}
var patchMap map[string]interface{}
err = json.Unmarshal(patch, &patchMap)
if err != nil {
return nil, fmt.Errorf("Failed to unmarshal patch for precondition check: %s", patch)
}
meetPreconditions, err := meetPreconditions(patchMap, fns...)
if err != nil {
return nil, err
}
if !meetPreconditions {
return nil, mergepatch.NewErrPreconditionFailed(patchMap)
}
return patch, nil
}
// keepOrDeleteNullInJsonPatch takes a json-encoded byte array and a boolean.
// It returns a filtered object and its corresponding json-encoded byte array.
// It is a wrapper of func keepOrDeleteNullInObj
func keepOrDeleteNullInJsonPatch(patch []byte, keepNull bool) ([]byte, map[string]interface{}, error) {
var patchMap map[string]interface{}
err := json.Unmarshal(patch, &patchMap)
if err != nil {
return nil, nil, err
}
filteredMap, err := keepOrDeleteNullInObj(patchMap, keepNull)
if err != nil {
return nil, nil, err
}
o, err := json.Marshal(filteredMap)
return o, filteredMap, err
}
// keepOrDeleteNullInObj will keep only the null value and delete all the others,
// if keepNull is true. Otherwise, it will delete all the null value and keep the others.
func keepOrDeleteNullInObj(m map[string]interface{}, keepNull bool) (map[string]interface{}, error) {
filteredMap := make(map[string]interface{})
var err error
for key, val := range m {
switch {
case keepNull && val == nil:
filteredMap[key] = nil
case val != nil:
switch typedVal := val.(type) {
case map[string]interface{}:
filteredMap[key], err = keepOrDeleteNullInObj(typedVal, keepNull)
if err != nil {
return nil, err
}
case []interface{}, string, float64, bool, int, int64, nil:
// Lists are always replaced in Json, no need to check each entry in the list.
if !keepNull {
filteredMap[key] = val
}
default:
return nil, fmt.Errorf("unknown type: %v", reflect.TypeOf(typedVal))
}
}
}
return filteredMap, nil
}
func meetPreconditions(patchObj map[string]interface{}, fns ...mergepatch.PreconditionFunc) (bool, error) {
// Apply the preconditions to the patch, and return an error if any of them fail.
for _, fn := range fns {
if !fn(patchObj) {
return false, fmt.Errorf("precondition failed for: %v", patchObj)
}
}
return true, nil
}

View file

@ -0,0 +1,663 @@
/*
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 jsonmergepatch
import (
"fmt"
"reflect"
"testing"
"github.com/davecgh/go-spew/spew"
"github.com/evanphx/json-patch"
"github.com/ghodss/yaml"
"k8s.io/apimachinery/pkg/util/json"
)
type FilterNullTestCases struct {
TestCases []FilterNullTestCase
}
type FilterNullTestCase struct {
Description string
OriginalObj map[string]interface{}
ExpectedWithNull map[string]interface{}
ExpectedWithoutNull map[string]interface{}
}
var filterNullTestCaseData = []byte(`
testCases:
- description: nil original
originalObj: {}
expectedWithNull: {}
expectedWithoutNull: {}
- description: simple map
originalObj:
nilKey: null
nonNilKey: foo
expectedWithNull:
nilKey: null
expectedWithoutNull:
nonNilKey: foo
- description: simple map with all nil values
originalObj:
nilKey1: null
nilKey2: null
expectedWithNull:
nilKey1: null
nilKey2: null
expectedWithoutNull: {}
- description: simple map with all non-nil values
originalObj:
nonNilKey: foo
nonNilKey: bar
expectedWithNull: {}
expectedWithoutNull:
nonNilKey: foo
nonNilKey: bar
- description: nested map
originalObj:
mapKey:
nilKey: null
nonNilKey: foo
expectedWithNull:
mapKey:
nilKey: null
expectedWithoutNull:
mapKey:
nonNilKey: foo
- description: nested map that all subkeys are nil
originalObj:
mapKey:
nilKey1: null
nilKey2: null
expectedWithNull:
mapKey:
nilKey1: null
nilKey2: null
expectedWithoutNull:
mapKey: {}
- description: nested map that all subkeys are non-nil
originalObj:
mapKey:
nonNilKey: foo
nonNilKey: bar
expectedWithNull:
mapKey: {}
expectedWithoutNull:
mapKey:
nonNilKey: foo
nonNilKey: bar
- description: empty list
originalObj:
listKey: []
expectedWithNull: {}
expectedWithoutNull:
listKey: []
- description: list of primitives
originalObj:
listKey:
- 1
- 2
expectedWithNull: {}
expectedWithoutNull:
listKey:
- 1
- 2
- description: list of maps
originalObj:
listKey:
- k1: v1
- k2: null
- k3: v3
k4: null
expectedWithNull: {}
expectedWithoutNull:
listKey:
- k1: v1
- k2: null
- k3: v3
k4: null
- description: list of different types
originalObj:
listKey:
- k1: v1
- k2: null
- v3
expectedWithNull: {}
expectedWithoutNull:
listKey:
- k1: v1
- k2: null
- v3
`)
func TestKeepOrDeleteNullInObj(t *testing.T) {
tc := FilterNullTestCases{}
err := yaml.Unmarshal(filterNullTestCaseData, &tc)
if err != nil {
t.Fatalf("can't unmarshal test cases: %s\n", err)
}
for _, test := range tc.TestCases {
resultWithNull, err := keepOrDeleteNullInObj(test.OriginalObj, true)
if err != nil {
t.Errorf("Failed in test case %q when trying to keep null values: %s", test.Description, err)
}
if !reflect.DeepEqual(test.ExpectedWithNull, resultWithNull) {
t.Errorf("Failed in test case %q when trying to keep null values:\nexpected expectedWithNull:\n%+v\nbut got:\n%+v\n", test.Description, test.ExpectedWithNull, resultWithNull)
}
resultWithoutNull, err := keepOrDeleteNullInObj(test.OriginalObj, false)
if err != nil {
t.Errorf("Failed in test case %q when trying to keep non-null values: %s", test.Description, err)
}
if !reflect.DeepEqual(test.ExpectedWithoutNull, resultWithoutNull) {
t.Errorf("Failed in test case %q when trying to keep non-null values:\n expected expectedWithoutNull:\n%+v\nbut got:\n%+v\n", test.Description, test.ExpectedWithoutNull, resultWithoutNull)
}
}
}
type JSONMergePatchTestCases struct {
TestCases []JSONMergePatchTestCase
}
type JSONMergePatchTestCase struct {
Description string
JSONMergePatchTestCaseData
}
type JSONMergePatchTestCaseData struct {
// Original is the original object (last-applied config in annotation)
Original map[string]interface{}
// Modified is the modified object (new config we want)
Modified map[string]interface{}
// Current is the current object (live config in the server)
Current map[string]interface{}
// ThreeWay is the expected three-way merge patch
ThreeWay map[string]interface{}
// Result is the expected object after applying the three-way patch on current object.
Result map[string]interface{}
}
var createJSONMergePatchTestCaseData = []byte(`
testCases:
- description: nil original
modified:
name: 1
value: 1
current:
name: 1
other: a
threeWay:
value: 1
result:
name: 1
value: 1
other: a
- description: nil patch
original:
name: 1
modified:
name: 1
current:
name: 1
threeWay:
{}
result:
name: 1
- description: add field to map
original:
name: 1
modified:
name: 1
value: 1
current:
name: 1
other: a
threeWay:
value: 1
result:
name: 1
value: 1
other: a
- description: add field to map with conflict
original:
name: 1
modified:
name: 1
value: 1
current:
name: a
other: a
threeWay:
name: 1
value: 1
result:
name: 1
value: 1
other: a
- description: add field and delete field from map
original:
name: 1
modified:
value: 1
current:
name: 1
other: a
threeWay:
name: null
value: 1
result:
value: 1
other: a
- description: add field and delete field from map with conflict
original:
name: 1
modified:
value: 1
current:
name: a
other: a
threeWay:
name: null
value: 1
result:
value: 1
other: a
- description: delete field from nested map
original:
simpleMap:
key1: 1
key2: 1
modified:
simpleMap:
key1: 1
current:
simpleMap:
key1: 1
key2: 1
other: a
threeWay:
simpleMap:
key2: null
result:
simpleMap:
key1: 1
other: a
- description: delete field from nested map with conflict
original:
simpleMap:
key1: 1
key2: 1
modified:
simpleMap:
key1: 1
current:
simpleMap:
key1: a
key2: 1
other: a
threeWay:
simpleMap:
key1: 1
key2: null
result:
simpleMap:
key1: 1
other: a
- description: delete all fields from map
original:
name: 1
value: 1
modified: {}
current:
name: 1
value: 1
other: a
threeWay:
name: null
value: null
result:
other: a
- description: delete all fields from map with conflict
original:
name: 1
value: 1
modified: {}
current:
name: 1
value: a
other: a
threeWay:
name: null
value: null
result:
other: a
- description: add field and delete all fields from map
original:
name: 1
value: 1
modified:
other: a
current:
name: 1
value: 1
other: a
threeWay:
name: null
value: null
result:
other: a
- description: add field and delete all fields from map with conflict
original:
name: 1
value: 1
modified:
other: a
current:
name: 1
value: 1
other: b
threeWay:
name: null
value: null
other: a
result:
other: a
- description: replace list of scalars
original:
intList:
- 1
- 2
modified:
intList:
- 2
- 3
current:
intList:
- 1
- 2
threeWay:
intList:
- 2
- 3
result:
intList:
- 2
- 3
- description: replace list of scalars with conflict
original:
intList:
- 1
- 2
modified:
intList:
- 2
- 3
current:
intList:
- 1
- 4
threeWay:
intList:
- 2
- 3
result:
intList:
- 2
- 3
- description: patch with different scalar type
original:
foo: 1
modified:
foo: true
current:
foo: 1
bar: 2
threeWay:
foo: true
result:
foo: true
bar: 2
- description: patch from scalar to list
original:
foo: 0
modified:
foo:
- 1
- 2
current:
foo: 0
bar: 2
threeWay:
foo:
- 1
- 2
result:
foo:
- 1
- 2
bar: 2
- description: patch from list to scalar
original:
foo:
- 1
- 2
modified:
foo: 0
current:
foo:
- 1
- 2
bar: 2
threeWay:
foo: 0
result:
foo: 0
bar: 2
- description: patch from scalar to map
original:
foo: 0
modified:
foo:
baz: 1
current:
foo: 0
bar: 2
threeWay:
foo:
baz: 1
result:
foo:
baz: 1
bar: 2
- description: patch from map to scalar
original:
foo:
baz: 1
modified:
foo: 0
current:
foo:
baz: 1
bar: 2
threeWay:
foo: 0
result:
foo: 0
bar: 2
- description: patch from map to list
original:
foo:
baz: 1
modified:
foo:
- 1
- 2
current:
foo:
baz: 1
bar: 2
threeWay:
foo:
- 1
- 2
result:
foo:
- 1
- 2
bar: 2
- description: patch from list to map
original:
foo:
- 1
- 2
modified:
foo:
baz: 0
current:
foo:
- 1
- 2
bar: 2
threeWay:
foo:
baz: 0
result:
foo:
baz: 0
bar: 2
- description: patch with different nested types
original:
foo:
- a: true
- 2
- false
modified:
foo:
- 1
- false
- b: 1
current:
foo:
- a: true
- 2
- false
bar: 0
threeWay:
foo:
- 1
- false
- b: 1
result:
foo:
- 1
- false
- b: 1
bar: 0
`)
func TestCreateThreeWayJSONMergePatch(t *testing.T) {
tc := JSONMergePatchTestCases{}
err := yaml.Unmarshal(createJSONMergePatchTestCaseData, &tc)
if err != nil {
t.Errorf("can't unmarshal test cases: %s\n", err)
return
}
for _, c := range tc.TestCases {
testThreeWayPatch(t, c)
}
}
func testThreeWayPatch(t *testing.T, c JSONMergePatchTestCase) {
original, modified, current, expected, result := threeWayTestCaseToJSONOrFail(t, c)
actual, err := CreateThreeWayJSONMergePatch(original, modified, current)
if err != nil {
t.Fatalf("error: %s", err)
}
testPatchCreation(t, expected, actual, c.Description)
testPatchApplication(t, current, actual, result, c.Description)
}
func testPatchCreation(t *testing.T, expected, actual []byte, description string) {
if !reflect.DeepEqual(actual, expected) {
t.Errorf("error in test case: %s\nexpected patch:\n%s\ngot:\n%s\n",
description, jsonToYAMLOrError(expected), jsonToYAMLOrError(actual))
return
}
}
func testPatchApplication(t *testing.T, original, patch, expected []byte, description string) {
result, err := jsonpatch.MergePatch(original, patch)
if err != nil {
t.Errorf("error: %s\nin test case: %s\ncannot apply patch:\n%s\nto original:\n%s\n",
err, description, jsonToYAMLOrError(patch), jsonToYAMLOrError(original))
return
}
if !reflect.DeepEqual(result, expected) {
format := "error in test case: %s\npatch application failed:\noriginal:\n%s\npatch:\n%s\nexpected:\n%s\ngot:\n%s\n"
t.Errorf(format, description,
jsonToYAMLOrError(original), jsonToYAMLOrError(patch),
jsonToYAMLOrError(expected), jsonToYAMLOrError(result))
return
}
}
func threeWayTestCaseToJSONOrFail(t *testing.T, c JSONMergePatchTestCase) ([]byte, []byte, []byte, []byte, []byte) {
return testObjectToJSONOrFail(t, c.Original),
testObjectToJSONOrFail(t, c.Modified),
testObjectToJSONOrFail(t, c.Current),
testObjectToJSONOrFail(t, c.ThreeWay),
testObjectToJSONOrFail(t, c.Result)
}
func testObjectToJSONOrFail(t *testing.T, o map[string]interface{}) []byte {
if o == nil {
return nil
}
j, err := toJSON(o)
if err != nil {
t.Error(err)
}
return j
}
func jsonToYAMLOrError(j []byte) string {
y, err := jsonToYAML(j)
if err != nil {
return err.Error()
}
return string(y)
}
func toJSON(v interface{}) ([]byte, error) {
j, err := json.Marshal(v)
if err != nil {
return nil, fmt.Errorf("json marshal failed: %v\n%v\n", err, spew.Sdump(v))
}
return j, nil
}
func jsonToYAML(j []byte) ([]byte, error) {
y, err := yaml.JSONToYAML(j)
if err != nil {
return nil, fmt.Errorf("json to yaml failed: %v\n%v\n", err, j)
}
return y, nil
}

View file

@ -0,0 +1,66 @@
/*
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 mergepatch
import (
"errors"
"fmt"
)
var ErrBadJSONDoc = errors.New("Invalid JSON document")
var ErrNoListOfLists = errors.New("Lists of lists are not supported")
var ErrBadPatchFormatForPrimitiveList = errors.New("Invalid patch format of primitive list")
// IsPreconditionFailed returns true if the provided error indicates
// a precondition failed.
func IsPreconditionFailed(err error) bool {
_, ok := err.(ErrPreconditionFailed)
return ok
}
type ErrPreconditionFailed struct {
message string
}
func NewErrPreconditionFailed(target map[string]interface{}) ErrPreconditionFailed {
s := fmt.Sprintf("precondition failed for: %v", target)
return ErrPreconditionFailed{s}
}
func (err ErrPreconditionFailed) Error() string {
return err.message
}
type ErrConflict struct {
message string
}
func NewErrConflict(patch, current string) ErrConflict {
s := fmt.Sprintf("patch:\n%s\nconflicts with changes made from original to current:\n%s\n", patch, current)
return ErrConflict{s}
}
func (err ErrConflict) Error() string {
return err.message
}
// IsConflict returns true if the provided error indicates
// a conflict between the patch and the current configuration.
func IsConflict(err error) bool {
_, ok := err.(ErrConflict)
return ok
}

126
vendor/k8s.io/apimachinery/pkg/util/mergepatch/util.go generated vendored Normal file
View file

@ -0,0 +1,126 @@
/*
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 mergepatch
import (
"fmt"
"reflect"
"github.com/davecgh/go-spew/spew"
"github.com/ghodss/yaml"
)
// PreconditionFunc asserts that an incompatible change is not present within a patch.
type PreconditionFunc func(interface{}) bool
// RequireKeyUnchanged returns a precondition function that fails if the provided key
// is present in the patch (indicating that its value has changed).
func RequireKeyUnchanged(key string) PreconditionFunc {
return func(patch interface{}) bool {
patchMap, ok := patch.(map[string]interface{})
if !ok {
return true
}
// The presence of key means that its value has been changed, so the test fails.
_, ok = patchMap[key]
return !ok
}
}
// RequireMetadataKeyUnchanged creates a precondition function that fails
// if the metadata.key is present in the patch (indicating its value
// has changed).
func RequireMetadataKeyUnchanged(key string) PreconditionFunc {
return func(patch interface{}) bool {
patchMap, ok := patch.(map[string]interface{})
if !ok {
return true
}
patchMap1, ok := patchMap["metadata"]
if !ok {
return true
}
patchMap2, ok := patchMap1.(map[string]interface{})
if !ok {
return true
}
_, ok = patchMap2[key]
return !ok
}
}
func ToYAMLOrError(v interface{}) string {
y, err := toYAML(v)
if err != nil {
return err.Error()
}
return y
}
func toYAML(v interface{}) (string, error) {
y, err := yaml.Marshal(v)
if err != nil {
return "", fmt.Errorf("yaml marshal failed:%v\n%v\n", err, spew.Sdump(v))
}
return string(y), nil
}
// HasConflicts returns true if the left and right JSON interface objects overlap with
// different values in any key. All keys are required to be strings. Since patches of the
// same Type have congruent keys, this is valid for multiple patch types. This method
// supports JSON merge patch semantics.
func HasConflicts(left, right interface{}) (bool, error) {
switch typedLeft := left.(type) {
case map[string]interface{}:
switch typedRight := right.(type) {
case map[string]interface{}:
for key, leftValue := range typedLeft {
rightValue, ok := typedRight[key]
if !ok {
return false, nil
}
return HasConflicts(leftValue, rightValue)
}
return false, nil
default:
return true, nil
}
case []interface{}:
switch typedRight := right.(type) {
case []interface{}:
if len(typedLeft) != len(typedRight) {
return true, nil
}
for i := range typedLeft {
return HasConflicts(typedLeft[i], typedRight[i])
}
return false, nil
default:
return true, nil
}
case string, float64, bool, int, int64, nil:
return !reflect.DeepEqual(left, right), nil
default:
return true, fmt.Errorf("unknown type: %v", reflect.TypeOf(left))
}
}

View file

@ -0,0 +1,84 @@
/*
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 mergepatch
import (
"testing"
)
func TestHasConflicts(t *testing.T) {
testCases := []struct {
A interface{}
B interface{}
Ret bool
}{
{A: "hello", B: "hello", Ret: false}, // 0
{A: "hello", B: "hell", Ret: true},
{A: "hello", B: nil, Ret: true},
{A: "hello", B: 1, Ret: true},
{A: "hello", B: float64(1.0), Ret: true},
{A: "hello", B: false, Ret: true},
{A: 1, B: 1, Ret: false},
{A: false, B: false, Ret: false},
{A: float64(3), B: float64(3), Ret: false},
{A: "hello", B: []interface{}{}, Ret: true}, // 6
{A: []interface{}{1}, B: []interface{}{}, Ret: true},
{A: []interface{}{}, B: []interface{}{}, Ret: false},
{A: []interface{}{1}, B: []interface{}{1}, Ret: false},
{A: map[string]interface{}{}, B: []interface{}{1}, Ret: true},
{A: map[string]interface{}{}, B: map[string]interface{}{"a": 1}, Ret: false}, // 11
{A: map[string]interface{}{"a": 1}, B: map[string]interface{}{"a": 1}, Ret: false},
{A: map[string]interface{}{"a": 1}, B: map[string]interface{}{"a": 2}, Ret: true},
{A: map[string]interface{}{"a": 1}, B: map[string]interface{}{"b": 2}, Ret: false},
{ // 15
A: map[string]interface{}{"a": []interface{}{1}},
B: map[string]interface{}{"a": []interface{}{1}},
Ret: false,
},
{
A: map[string]interface{}{"a": []interface{}{1}},
B: map[string]interface{}{"a": []interface{}{}},
Ret: true,
},
{
A: map[string]interface{}{"a": []interface{}{1}},
B: map[string]interface{}{"a": 1},
Ret: true,
},
}
for i, testCase := range testCases {
out, err := HasConflicts(testCase.A, testCase.B)
if err != nil {
t.Errorf("%d: unexpected error: %v", i, err)
}
if out != testCase.Ret {
t.Errorf("%d: expected %t got %t", i, testCase.Ret, out)
continue
}
out, err = HasConflicts(testCase.B, testCase.A)
if err != nil {
t.Errorf("%d: unexpected error: %v", i, err)
}
if out != testCase.Ret {
t.Errorf("%d: expected reversed %t got %t", i, testCase.Ret, out)
}
}
}

View file

@ -23,10 +23,8 @@ import (
"strings"
"k8s.io/apimachinery/pkg/util/json"
"k8s.io/apimachinery/pkg/util/mergepatch"
forkedjson "k8s.io/apimachinery/third_party/forked/golang/json"
"github.com/davecgh/go-spew/spew"
"github.com/ghodss/yaml"
)
// An alternate implementation of JSON Merge Patch
@ -55,110 +53,26 @@ const (
// json marshaling and/or unmarshaling operations.
type JSONMap map[string]interface{}
// IsPreconditionFailed returns true if the provided error indicates
// a precondition failed.
func IsPreconditionFailed(err error) bool {
_, ok := err.(errPreconditionFailed)
return ok
}
type errPreconditionFailed struct {
message string
}
func newErrPreconditionFailed(target map[string]interface{}) errPreconditionFailed {
s := fmt.Sprintf("precondition failed for: %v", target)
return errPreconditionFailed{s}
}
func (err errPreconditionFailed) Error() string {
return err.message
}
type errConflict struct {
message string
}
func newErrConflict(patch, current string) errConflict {
s := fmt.Sprintf("patch:\n%s\nconflicts with changes made from original to current:\n%s\n", patch, current)
return errConflict{s}
}
func (err errConflict) Error() string {
return err.message
}
// IsConflict returns true if the provided error indicates
// a conflict between the patch and the current configuration.
func IsConflict(err error) bool {
_, ok := err.(errConflict)
return ok
}
var errBadJSONDoc = fmt.Errorf("Invalid JSON document")
var errNoListOfLists = fmt.Errorf("Lists of lists are not supported")
var errBadPatchFormatForPrimitiveList = fmt.Errorf("Invalid patch format of primitive list")
// The following code is adapted from github.com/openshift/origin/pkg/util/jsonmerge.
// Instead of defining a Delta that holds an original, a patch and a set of preconditions,
// the reconcile method accepts a set of preconditions as an argument.
// PreconditionFunc asserts that an incompatible change is not present within a patch.
type PreconditionFunc func(interface{}) bool
// RequireKeyUnchanged returns a precondition function that fails if the provided key
// is present in the patch (indicating that its value has changed).
func RequireKeyUnchanged(key string) PreconditionFunc {
return func(patch interface{}) bool {
patchMap, ok := patch.(map[string]interface{})
if !ok {
return true
}
// The presence of key means that its value has been changed, so the test fails.
_, ok = patchMap[key]
return !ok
}
}
// RequireMetadataKeyUnchanged creates a precondition function that fails
// if the metadata.key is present in the patch (indicating its value
// has changed).
func RequireMetadataKeyUnchanged(key string) PreconditionFunc {
return func(patch interface{}) bool {
patchMap, ok := patch.(map[string]interface{})
if !ok {
return true
}
patchMap1, ok := patchMap["metadata"]
if !ok {
return true
}
patchMap2, ok := patchMap1.(map[string]interface{})
if !ok {
return true
}
_, ok = patchMap2[key]
return !ok
}
}
// CreateTwoWayMergePatch creates a patch that can be passed to StrategicMergePatch from an original
// document and a modified document, which are passed to the method as json encoded content. It will
// return a patch that yields the modified document when applied to the original document, or an error
// if either of the two documents is invalid.
func CreateTwoWayMergePatch(original, modified []byte, dataStruct interface{}, fns ...PreconditionFunc) ([]byte, error) {
func CreateTwoWayMergePatch(original, modified []byte, dataStruct interface{}, fns ...mergepatch.PreconditionFunc) ([]byte, error) {
originalMap := map[string]interface{}{}
if len(original) > 0 {
if err := json.Unmarshal(original, &originalMap); err != nil {
return nil, errBadJSONDoc
return nil, mergepatch.ErrBadJSONDoc
}
}
modifiedMap := map[string]interface{}{}
if len(modified) > 0 {
if err := json.Unmarshal(modified, &modifiedMap); err != nil {
return nil, errBadJSONDoc
return nil, mergepatch.ErrBadJSONDoc
}
}
@ -173,7 +87,7 @@ func CreateTwoWayMergePatch(original, modified []byte, dataStruct interface{}, f
// CreateTwoWayMergeMapPatch creates a patch from an original and modified JSON objects,
// encoded JSONMap.
// The serialized version of the map can then be passed to StrategicMergeMapPatch.
func CreateTwoWayMergeMapPatch(original, modified JSONMap, dataStruct interface{}, fns ...PreconditionFunc) (JSONMap, error) {
func CreateTwoWayMergeMapPatch(original, modified JSONMap, dataStruct interface{}, fns ...mergepatch.PreconditionFunc) (JSONMap, error) {
t, err := getTagStructType(dataStruct)
if err != nil {
return nil, err
@ -187,7 +101,7 @@ func CreateTwoWayMergeMapPatch(original, modified JSONMap, dataStruct interface{
// Apply the preconditions to the patch, and return an error if any of them fail.
for _, fn := range fns {
if !fn(patchMap) {
return nil, newErrPreconditionFailed(patchMap)
return nil, mergepatch.NewErrPreconditionFailed(patchMap)
}
}
@ -244,11 +158,24 @@ func diffMaps(original, modified map[string]interface{}, t reflect.Type, ignoreC
switch originalValueTyped := originalValue.(type) {
case map[string]interface{}:
modifiedValueTyped := modifiedValue.(map[string]interface{})
fieldType, _, _, err := forkedjson.LookupPatchMetadata(t, key)
fieldType, fieldPatchStrategy, _, err := forkedjson.LookupPatchMetadata(t, key)
if err != nil {
// We couldn't look up metadata for the field
// If the values are identical, this doesn't matter, no patch is needed
if reflect.DeepEqual(originalValue, modifiedValue) {
continue
}
// Otherwise, return the error
return nil, err
}
if fieldPatchStrategy == replaceDirective {
if !ignoreChangesAndAdditions {
patch[key] = modifiedValue
}
continue
}
patchValue, err := diffMaps(originalValueTyped, modifiedValueTyped, fieldType, ignoreChangesAndAdditions, ignoreDeletions)
if err != nil {
return nil, err
@ -263,6 +190,12 @@ func diffMaps(original, modified map[string]interface{}, t reflect.Type, ignoreC
modifiedValueTyped := modifiedValue.([]interface{})
fieldType, fieldPatchStrategy, fieldPatchMergeKey, err := forkedjson.LookupPatchMetadata(t, key)
if err != nil {
// We couldn't look up metadata for the field
// If the values are identical, this doesn't matter, no patch is needed
if reflect.DeepEqual(originalValue, modifiedValue) {
continue
}
// Otherwise, return the error
return nil, err
}
@ -332,7 +265,7 @@ func diffLists(original, modified []interface{}, t reflect.Type, mergeKey string
return patchList, nil, err
case reflect.Slice:
// Lists of Lists are not permitted by the api
return nil, nil, errNoListOfLists
return nil, nil, mergepatch.ErrNoListOfLists
default:
return diffListsOfScalars(original, modified, ignoreChangesAndAdditions, ignoreDeletions)
}
@ -523,13 +456,13 @@ func StrategicMergePatch(original, patch []byte, dataStruct interface{}) ([]byte
originalMap := map[string]interface{}{}
err := json.Unmarshal(original, &originalMap)
if err != nil {
return nil, errBadJSONDoc
return nil, mergepatch.ErrBadJSONDoc
}
patchMap := map[string]interface{}{}
err = json.Unmarshal(patch, &patchMap)
if err != nil {
return nil, errBadJSONDoc
return nil, mergepatch.ErrBadJSONDoc
}
result, err := StrategicMergeMapPatch(originalMap, patchMap, dataStruct)
@ -548,7 +481,7 @@ func StrategicMergeMapPatch(original, patch JSONMap, dataStruct interface{}) (JS
if err != nil {
return nil, err
}
return mergeMap(original, patch, t, true)
return mergeMap(original, patch, t, true, true)
}
func getTagStructType(dataStruct interface{}) (reflect.Type, error) {
@ -574,7 +507,10 @@ var errBadPatchTypeFmt = "unknown patch type: %s in map: %v"
// both the original map and the patch because getting a deep copy of a map in
// golang is highly non-trivial.
// flag mergeDeleteList controls if using the parallel list to delete or keeping the list.
func mergeMap(original, patch map[string]interface{}, t reflect.Type, mergeDeleteList bool) (map[string]interface{}, error) {
// If patch contains any null field (e.g. field_1: null) that is not
// present in original, then to propagate it to the end result use
// ignoreUnmatchedNulls == false.
func mergeMap(original, patch map[string]interface{}, t reflect.Type, mergeDeleteList, ignoreUnmatchedNulls bool) (map[string]interface{}, error) {
if v, ok := patch[directiveMarker]; ok {
if v == replaceDirective {
// If the patch contains "$patch: replace", don't merge it, just use the
@ -612,21 +548,25 @@ func mergeMap(original, patch map[string]interface{}, t reflect.Type, mergeDelet
}
substrings := strings.SplitN(k, "/", 2)
if len(substrings) <= 1 {
return nil, errBadPatchFormatForPrimitiveList
return nil, mergepatch.ErrBadPatchFormatForPrimitiveList
}
isDeleteList = true
k = substrings[1]
}
// If the value of this key is null, delete the key if it exists in the
// original. Otherwise, skip it.
// original. Otherwise, check if we want to preserve it or skip it.
// Preserving the null value is useful when we want to send an explicit
// delete to the API server.
if patchV == nil {
if _, ok := original[k]; ok {
delete(original, k)
}
if ignoreUnmatchedNulls {
continue
}
}
_, ok := original[k]
if !ok {
@ -654,7 +594,7 @@ func mergeMap(original, patch map[string]interface{}, t reflect.Type, mergeDelet
typedOriginal := original[k].(map[string]interface{})
typedPatch := patchV.(map[string]interface{})
var err error
original[k], err = mergeMap(typedOriginal, typedPatch, fieldType, mergeDeleteList)
original[k], err = mergeMap(typedOriginal, typedPatch, fieldType, mergeDeleteList, ignoreUnmatchedNulls)
if err != nil {
return nil, err
}
@ -667,7 +607,7 @@ func mergeMap(original, patch map[string]interface{}, t reflect.Type, mergeDelet
typedOriginal := original[k].([]interface{})
typedPatch := patchV.([]interface{})
var err error
original[k], err = mergeSlice(typedOriginal, typedPatch, elemType, fieldPatchMergeKey, mergeDeleteList, isDeleteList)
original[k], err = mergeSlice(typedOriginal, typedPatch, elemType, fieldPatchMergeKey, mergeDeleteList, isDeleteList, ignoreUnmatchedNulls)
if err != nil {
return nil, err
}
@ -688,7 +628,7 @@ func mergeMap(original, patch map[string]interface{}, t reflect.Type, mergeDelet
// Merge two slices together. Note: This may modify both the original slice and
// the patch because getting a deep copy of a slice in golang is highly
// non-trivial.
func mergeSlice(original, patch []interface{}, elemType reflect.Type, mergeKey string, mergeDeleteList, isDeleteList bool) ([]interface{}, error) {
func mergeSlice(original, patch []interface{}, elemType reflect.Type, mergeKey string, mergeDeleteList, isDeleteList, ignoreUnmatchedNulls bool) ([]interface{}, error) {
if len(original) == 0 && len(patch) == 0 {
return original, nil
}
@ -779,7 +719,7 @@ func mergeSlice(original, patch []interface{}, elemType reflect.Type, mergeKey s
var mergedMaps interface{}
var err error
// Merge into original.
mergedMaps, err = mergeMap(originalMap, typedV, elemType, mergeDeleteList)
mergedMaps, err = mergeMap(originalMap, typedV, elemType, mergeDeleteList, ignoreUnmatchedNulls)
if err != nil {
return nil, err
}
@ -864,7 +804,7 @@ func sortMergeListsByNameMap(s map[string]interface{}, t reflect.Type) (map[stri
if strings.HasPrefix(k, deleteFromPrimitiveListDirectivePrefix) {
typedV, ok := v.([]interface{})
if !ok {
return nil, errBadPatchFormatForPrimitiveList
return nil, mergepatch.ErrBadPatchFormatForPrimitiveList
}
v = uniqifyAndSortScalars(typedV)
} else if k != directiveMarker {
@ -1045,7 +985,7 @@ func sliceElementType(slices ...[]interface{}) (reflect.Type, error) {
prevType = currentType
// We don't support lists of lists yet.
if prevType.Kind() == reflect.Slice {
return nil, errNoListOfLists
return nil, mergepatch.ErrNoListOfLists
}
} else {
if prevType != currentType {
@ -1063,49 +1003,6 @@ func sliceElementType(slices ...[]interface{}) (reflect.Type, error) {
return prevType, nil
}
// HasConflicts returns true if the left and right JSON interface objects overlap with
// different values in any key. All keys are required to be strings. Since patches of the
// same Type have congruent keys, this is valid for multiple patch types. This method
// supports JSON merge patch semantics.
func HasConflicts(left, right interface{}) (bool, error) {
switch typedLeft := left.(type) {
case map[string]interface{}:
switch typedRight := right.(type) {
case map[string]interface{}:
for key, leftValue := range typedLeft {
rightValue, ok := typedRight[key]
if !ok {
return false, nil
}
return HasConflicts(leftValue, rightValue)
}
return false, nil
default:
return true, nil
}
case []interface{}:
switch typedRight := right.(type) {
case []interface{}:
if len(typedLeft) != len(typedRight) {
return true, nil
}
for i := range typedLeft {
return HasConflicts(typedLeft[i], typedRight[i])
}
return false, nil
default:
return true, nil
}
case string, float64, bool, int, int64, nil:
return !reflect.DeepEqual(left, right), nil
default:
return true, fmt.Errorf("unknown type: %v", reflect.TypeOf(left))
}
}
// MergingMapsHaveConflicts returns true if the left and right JSON interface
// objects overlap with different values in any key. All keys are required to be
// strings. Since patches of the same Type have congruent keys, this is valid
@ -1148,6 +1045,10 @@ func mergingMapFieldsHaveConflicts(
}
}
if fieldPatchStrategy == replaceDirective {
return false, nil
}
// Check the individual keys.
return mapsHaveConflicts(leftType, rightType, fieldType)
default:
@ -1282,26 +1183,27 @@ func mapsOfMapsHaveConflicts(typedLeft, typedRight map[string]interface{}, struc
// configurations. Conflicts are defined as keys changed differently from original to modified
// than from original to current. In other words, a conflict occurs if modified changes any key
// in a way that is different from how it is changed in current (e.g., deleting it, changing its
// value).
func CreateThreeWayMergePatch(original, modified, current []byte, dataStruct interface{}, overwrite bool, fns ...PreconditionFunc) ([]byte, error) {
// value). We also propagate values fields that do not exist in original but are explicitly
// defined in modified.
func CreateThreeWayMergePatch(original, modified, current []byte, dataStruct interface{}, overwrite bool, fns ...mergepatch.PreconditionFunc) ([]byte, error) {
originalMap := map[string]interface{}{}
if len(original) > 0 {
if err := json.Unmarshal(original, &originalMap); err != nil {
return nil, errBadJSONDoc
return nil, mergepatch.ErrBadJSONDoc
}
}
modifiedMap := map[string]interface{}{}
if len(modified) > 0 {
if err := json.Unmarshal(modified, &modifiedMap); err != nil {
return nil, errBadJSONDoc
return nil, mergepatch.ErrBadJSONDoc
}
}
currentMap := map[string]interface{}{}
if len(current) > 0 {
if err := json.Unmarshal(current, &currentMap); err != nil {
return nil, errBadJSONDoc
return nil, mergepatch.ErrBadJSONDoc
}
}
@ -1324,7 +1226,7 @@ func CreateThreeWayMergePatch(original, modified, current []byte, dataStruct int
return nil, err
}
patchMap, err := mergeMap(deletionsMap, deltaMap, t, false)
patchMap, err := mergeMap(deletionsMap, deltaMap, t, false, false)
if err != nil {
return nil, err
}
@ -1332,7 +1234,7 @@ func CreateThreeWayMergePatch(original, modified, current []byte, dataStruct int
// Apply the preconditions to the patch, and return an error if any of them fail.
for _, fn := range fns {
if !fn(patchMap) {
return nil, newErrPreconditionFailed(patchMap)
return nil, mergepatch.NewErrPreconditionFailed(patchMap)
}
}
@ -1350,27 +1252,9 @@ func CreateThreeWayMergePatch(original, modified, current []byte, dataStruct int
}
if hasConflicts {
return nil, newErrConflict(toYAMLOrError(patchMap), toYAMLOrError(changedMap))
return nil, mergepatch.NewErrConflict(mergepatch.ToYAMLOrError(patchMap), mergepatch.ToYAMLOrError(changedMap))
}
}
return json.Marshal(patchMap)
}
func toYAMLOrError(v interface{}) string {
y, err := toYAML(v)
if err != nil {
return err.Error()
}
return y
}
func toYAML(v interface{}) (string, error) {
y, err := yaml.Marshal(v)
if err != nil {
return "", fmt.Errorf("yaml marshal failed:%v\n%v\n", err, spew.Sdump(v))
}
return string(y), nil
}

View file

@ -25,6 +25,9 @@ import (
"github.com/davecgh/go-spew/spew"
"github.com/ghodss/yaml"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/mergepatch"
"k8s.io/apimachinery/pkg/util/sets"
)
type SortMergeListTestCases struct {
@ -64,6 +67,9 @@ type StrategicMergePatchTestCaseData struct {
ThreeWay map[string]interface{}
// Result is the expected object after applying the three-way patch on current object.
Result map[string]interface{}
// TwoWayResult is the expected object after applying the two-way patch on current object.
// If nil, Modified is used.
TwoWayResult map[string]interface{}
}
// The meaning of each field is the same as StrategicMergePatchTestCaseData's.
@ -75,6 +81,7 @@ type StrategicMergePatchRawTestCaseData struct {
TwoWay []byte
ThreeWay []byte
Result []byte
TwoWayResult []byte
}
type MergeItem struct {
@ -87,6 +94,7 @@ type MergeItem struct {
NonMergingIntList []int
MergeItemPtr *MergeItem `patchStrategy:"merge" patchMergeKey:"name"`
SimpleMap map[string]string
ReplacingItem runtime.RawExtension `patchStrategy:"replace"`
}
var mergeItem MergeItem
@ -262,7 +270,7 @@ func TestSortMergeLists(t *testing.T) {
sorted := testObjectToJSONOrFail(t, c.Sorted, c.Description)
if !reflect.DeepEqual(original, sorted) {
t.Errorf("error in test case: %s\ncannot sort object:\n%s\nexpected:\n%s\ngot:\n%s\n",
c.Description, toYAMLOrError(c.Original), toYAMLOrError(c.Sorted), jsonToYAMLOrError(original))
c.Description, mergepatch.ToYAMLOrError(c.Original), mergepatch.ToYAMLOrError(c.Sorted), jsonToYAMLOrError(original))
}
}
}
@ -360,8 +368,8 @@ func TestCustomStrategicMergePatch(t *testing.T) {
}
for _, c := range tc.TestCases {
original, twoWay, modified := twoWayTestCaseToJSONOrFail(t, c)
testPatchApplication(t, original, twoWay, modified, c.Description)
original, expectedTwoWayPatch, _, expectedResult := twoWayTestCaseToJSONOrFail(t, c)
testPatchApplication(t, original, expectedTwoWayPatch, expectedResult, c.Description)
}
}
@ -1817,6 +1825,52 @@ testCases:
other: b
- name: 2
other: b
- description: defined null values should propagate overwrite current fields (with conflict)
original:
name: 2
twoWay:
name: 1
value: 1
other: null
twoWayResult:
name: 1
value: 1
modified:
name: 1
value: 1
other: null
current:
name: a
other: a
threeWay:
name: 1
value: 1
other: null
result:
name: 1
value: 1
- description: defined null values should propagate removing original fields
original:
name: original-name
value: original-value
current:
name: original-name
value: original-value
other: current-other
modified:
name: modified-name
value: null
twoWay:
name: modified-name
value: null
twoWayResult:
name: modified-name
threeWay:
name: modified-name
value: null
result:
name: modified-name
other: current-other
`)
var strategicMergePatchRawTestCases = []StrategicMergePatchRawTestCase{
@ -1940,9 +1994,9 @@ mergingIntList:
func TestStrategicMergePatch(t *testing.T) {
testStrategicMergePatchWithCustomArguments(t, "bad original",
"<THIS IS NOT JSON>", "{}", mergeItem, errBadJSONDoc)
"<THIS IS NOT JSON>", "{}", mergeItem, mergepatch.ErrBadJSONDoc)
testStrategicMergePatchWithCustomArguments(t, "bad patch",
"{}", "<THIS IS NOT JSON>", mergeItem, errBadJSONDoc)
"{}", "<THIS IS NOT JSON>", mergeItem, mergepatch.ErrBadJSONDoc)
testStrategicMergePatchWithCustomArguments(t, "bad struct",
"{}", "{}", []byte("<THIS IS NOT A STRUCT>"), fmt.Errorf(errBadArgTypeFmt, "struct", "slice"))
testStrategicMergePatchWithCustomArguments(t, "nil struct",
@ -1982,58 +2036,68 @@ func testStrategicMergePatchWithCustomArguments(t *testing.T, description, origi
}
func testTwoWayPatch(t *testing.T, c StrategicMergePatchTestCase) {
original, expected, modified := twoWayTestCaseToJSONOrFail(t, c)
original, expectedPatch, modified, expectedResult := twoWayTestCaseToJSONOrFail(t, c)
actual, err := CreateTwoWayMergePatch(original, modified, mergeItem)
actualPatch, err := CreateTwoWayMergePatch(original, modified, mergeItem)
if err != nil {
t.Errorf("error: %s\nin test case: %s\ncannot create two way patch: %s:\n%s\n",
err, c.Description, original, toYAMLOrError(c.StrategicMergePatchTestCaseData))
err, c.Description, original, mergepatch.ToYAMLOrError(c.StrategicMergePatchTestCaseData))
return
}
testPatchCreation(t, expected, actual, c.Description)
testPatchApplication(t, original, actual, modified, c.Description)
testPatchCreation(t, expectedPatch, actualPatch, c.Description)
testPatchApplication(t, original, actualPatch, expectedResult, c.Description)
}
func testTwoWayPatchForRawTestCase(t *testing.T, c StrategicMergePatchRawTestCase) {
original, expected, modified := twoWayRawTestCaseToJSONOrFail(t, c)
original, expectedPatch, modified, expectedResult := twoWayRawTestCaseToJSONOrFail(t, c)
actual, err := CreateTwoWayMergePatch(original, modified, mergeItem)
actualPatch, err := CreateTwoWayMergePatch(original, modified, mergeItem)
if err != nil {
t.Errorf("error: %s\nin test case: %s\ncannot create two way patch:\noriginal:%s\ntwoWay:%s\nmodified:%s\ncurrent:%s\nthreeWay:%s\nresult:%s\n",
err, c.Description, c.Original, c.TwoWay, c.Modified, c.Current, c.ThreeWay, c.Result)
return
}
testPatchCreation(t, expected, actual, c.Description)
testPatchApplication(t, original, actual, modified, c.Description)
testPatchCreation(t, expectedPatch, actualPatch, c.Description)
testPatchApplication(t, original, actualPatch, expectedResult, c.Description)
}
func twoWayTestCaseToJSONOrFail(t *testing.T, c StrategicMergePatchTestCase) ([]byte, []byte, []byte) {
func twoWayTestCaseToJSONOrFail(t *testing.T, c StrategicMergePatchTestCase) ([]byte, []byte, []byte, []byte) {
expectedResult := c.TwoWayResult
if expectedResult == nil {
expectedResult = c.Modified
}
return testObjectToJSONOrFail(t, c.Original, c.Description),
testObjectToJSONOrFail(t, c.TwoWay, c.Description),
testObjectToJSONOrFail(t, c.Modified, c.Description)
testObjectToJSONOrFail(t, c.Modified, c.Description),
testObjectToJSONOrFail(t, expectedResult, c.Description)
}
func twoWayRawTestCaseToJSONOrFail(t *testing.T, c StrategicMergePatchRawTestCase) ([]byte, []byte, []byte) {
func twoWayRawTestCaseToJSONOrFail(t *testing.T, c StrategicMergePatchRawTestCase) ([]byte, []byte, []byte, []byte) {
expectedResult := c.TwoWayResult
if expectedResult == nil {
expectedResult = c.Modified
}
return yamlToJSONOrError(t, c.Original),
yamlToJSONOrError(t, c.TwoWay),
yamlToJSONOrError(t, c.Modified)
yamlToJSONOrError(t, c.Modified),
yamlToJSONOrError(t, expectedResult)
}
func testThreeWayPatch(t *testing.T, c StrategicMergePatchTestCase) {
original, modified, current, expected, result := threeWayTestCaseToJSONOrFail(t, c)
actual, err := CreateThreeWayMergePatch(original, modified, current, mergeItem, false)
if err != nil {
if !IsConflict(err) {
if !mergepatch.IsConflict(err) {
t.Errorf("error: %s\nin test case: %s\ncannot create three way patch:\n%s\n",
err, c.Description, toYAMLOrError(c.StrategicMergePatchTestCaseData))
err, c.Description, mergepatch.ToYAMLOrError(c.StrategicMergePatchTestCaseData))
return
}
if !strings.Contains(c.Description, "conflict") {
t.Errorf("unexpected conflict: %s\nin test case: %s\ncannot create three way patch:\n%s\n",
err, c.Description, toYAMLOrError(c.StrategicMergePatchTestCaseData))
err, c.Description, mergepatch.ToYAMLOrError(c.StrategicMergePatchTestCaseData))
return
}
@ -2041,7 +2105,7 @@ func testThreeWayPatch(t *testing.T, c StrategicMergePatchTestCase) {
actual, err := CreateThreeWayMergePatch(original, modified, current, mergeItem, true)
if err != nil {
t.Errorf("error: %s\nin test case: %s\ncannot force three way patch application:\n%s\n",
err, c.Description, toYAMLOrError(c.StrategicMergePatchTestCaseData))
err, c.Description, mergepatch.ToYAMLOrError(c.StrategicMergePatchTestCaseData))
return
}
@ -2054,7 +2118,7 @@ func testThreeWayPatch(t *testing.T, c StrategicMergePatchTestCase) {
if strings.Contains(c.Description, "conflict") || len(c.Result) < 1 {
t.Errorf("error in test case: %s\nexpected conflict did not occur:\n%s\n",
c.Description, toYAMLOrError(c.StrategicMergePatchTestCaseData))
c.Description, mergepatch.ToYAMLOrError(c.StrategicMergePatchTestCaseData))
return
}
@ -2066,7 +2130,7 @@ func testThreeWayPatchForRawTestCase(t *testing.T, c StrategicMergePatchRawTestC
original, modified, current, expected, result := threeWayRawTestCaseToJSONOrFail(t, c)
actual, err := CreateThreeWayMergePatch(original, modified, current, mergeItem, false)
if err != nil {
if !IsConflict(err) {
if !mergepatch.IsConflict(err) {
t.Errorf("error: %s\nin test case: %s\ncannot create three way patch:\noriginal:%s\ntwoWay:%s\nmodified:%s\ncurrent:%s\nthreeWay:%s\nresult:%s\n",
err, c.Description, c.Original, c.TwoWay, c.Modified, c.Current, c.ThreeWay, c.Result)
return
@ -2222,69 +2286,6 @@ func yamlToJSONOrError(t *testing.T, y []byte) []byte {
return j
}
func TestHasConflicts(t *testing.T) {
testCases := []struct {
A interface{}
B interface{}
Ret bool
}{
{A: "hello", B: "hello", Ret: false}, // 0
{A: "hello", B: "hell", Ret: true},
{A: "hello", B: nil, Ret: true},
{A: "hello", B: 1, Ret: true},
{A: "hello", B: float64(1.0), Ret: true},
{A: "hello", B: false, Ret: true},
{A: 1, B: 1, Ret: false},
{A: false, B: false, Ret: false},
{A: float64(3), B: float64(3), Ret: false},
{A: "hello", B: []interface{}{}, Ret: true}, // 6
{A: []interface{}{1}, B: []interface{}{}, Ret: true},
{A: []interface{}{}, B: []interface{}{}, Ret: false},
{A: []interface{}{1}, B: []interface{}{1}, Ret: false},
{A: map[string]interface{}{}, B: []interface{}{1}, Ret: true},
{A: map[string]interface{}{}, B: map[string]interface{}{"a": 1}, Ret: false}, // 11
{A: map[string]interface{}{"a": 1}, B: map[string]interface{}{"a": 1}, Ret: false},
{A: map[string]interface{}{"a": 1}, B: map[string]interface{}{"a": 2}, Ret: true},
{A: map[string]interface{}{"a": 1}, B: map[string]interface{}{"b": 2}, Ret: false},
{ // 15
A: map[string]interface{}{"a": []interface{}{1}},
B: map[string]interface{}{"a": []interface{}{1}},
Ret: false,
},
{
A: map[string]interface{}{"a": []interface{}{1}},
B: map[string]interface{}{"a": []interface{}{}},
Ret: true,
},
{
A: map[string]interface{}{"a": []interface{}{1}},
B: map[string]interface{}{"a": 1},
Ret: true,
},
}
for i, testCase := range testCases {
out, err := HasConflicts(testCase.A, testCase.B)
if err != nil {
t.Errorf("%d: unexpected error: %v", i, err)
}
if out != testCase.Ret {
t.Errorf("%d: expected %t got %t", i, testCase.Ret, out)
continue
}
out, err = HasConflicts(testCase.B, testCase.A)
if err != nil {
t.Errorf("%d: unexpected error: %v", i, err)
}
if out != testCase.Ret {
t.Errorf("%d: expected reversed %t got %t", i, testCase.Ret, out)
}
}
}
type PrecisionItem struct {
Name string
Int32 int32
@ -2368,3 +2369,374 @@ func TestNumberConversion(t *testing.T) {
}
}
}
var replaceRawExtensionPatchTestCases = []StrategicMergePatchRawTestCase{
{
Description: "replace RawExtension field, rest unchanched",
StrategicMergePatchRawTestCaseData: StrategicMergePatchRawTestCaseData{
Original: []byte(`
name: my-object
value: some-value
other: current-other
replacingItem:
Some: Generic
Yaml: Inside
The: RawExtension
Field: Period
`),
Current: []byte(`
name: my-object
value: some-value
other: current-other
merginglist:
- name: 1
- name: 2
- name: 3
replacingItem:
Some: Generic
Yaml: Inside
The: RawExtension
Field: Period
`),
Modified: []byte(`
name: my-object
value: some-value
other: current-other
merginglist:
- name: 1
- name: 2
- name: 3
replacingItem:
Newly: Modified
Yaml: Inside
The: RawExtension
`),
TwoWay: []byte(`
merginglist:
- name: 1
- name: 2
- name: 3
replacingItem:
Newly: Modified
Yaml: Inside
The: RawExtension
`),
TwoWayResult: []byte(`
name: my-object
value: some-value
other: current-other
merginglist:
- name: 1
- name: 2
- name: 3
replacingItem:
Newly: Modified
Yaml: Inside
The: RawExtension
`),
ThreeWay: []byte(`
replacingItem:
Newly: Modified
Yaml: Inside
The: RawExtension
`),
Result: []byte(`
name: my-object
value: some-value
other: current-other
merginglist:
- name: 1
- name: 2
- name: 3
replacingItem:
Newly: Modified
Yaml: Inside
The: RawExtension
`),
},
},
{
Description: "replace RawExtension field and merge list",
StrategicMergePatchRawTestCaseData: StrategicMergePatchRawTestCaseData{
Original: []byte(`
name: my-object
value: some-value
other: current-other
merginglist:
- name: 1
replacingItem:
Some: Generic
Yaml: Inside
The: RawExtension
Field: Period
`),
Current: []byte(`
name: my-object
value: some-value
other: current-other
merginglist:
- name: 1
- name: 3
replacingItem:
Some: Generic
Yaml: Inside
The: RawExtension
Field: Period
`),
Modified: []byte(`
name: my-object
value: some-value
other: current-other
merginglist:
- name: 1
- name: 2
replacingItem:
Newly: Modified
Yaml: Inside
The: RawExtension
`),
TwoWay: []byte(`
merginglist:
- name: 2
replacingItem:
Newly: Modified
Yaml: Inside
The: RawExtension
`),
TwoWayResult: []byte(`
name: my-object
value: some-value
other: current-other
merginglist:
- name: 1
- name: 2
replacingItem:
Newly: Modified
Yaml: Inside
The: RawExtension
`),
ThreeWay: []byte(`
merginglist:
- name: 2
replacingItem:
Newly: Modified
Yaml: Inside
The: RawExtension
`),
Result: []byte(`
name: my-object
value: some-value
other: current-other
merginglist:
- name: 1
- name: 3
- name: 2
replacingItem:
Newly: Modified
Yaml: Inside
The: RawExtension
`),
},
},
}
func TestReplaceWithRawExtension(t *testing.T) {
for _, c := range replaceRawExtensionPatchTestCases {
testTwoWayPatchWithoutSorting(t, c)
testThreeWayPatchWithoutSorting(t, c)
}
}
func testTwoWayPatchWithoutSorting(t *testing.T, c StrategicMergePatchRawTestCase) {
original, expectedPatch, modified, expectedResult := twoWayRawTestCaseToJSONOrFail(t, c)
actualPatch, err := CreateTwoWayMergePatch(original, modified, mergeItem)
if err != nil {
t.Errorf("error: %s\nin test case: %s\ncannot create two way patch:\noriginal:%s\ntwoWay:%s\nmodified:%s\ncurrent:%s\nthreeWay:%s\nresult:%s\n",
err, c.Description, c.Original, c.TwoWay, c.Modified, c.Current, c.ThreeWay, c.Result)
return
}
testPatchCreationWithoutSorting(t, expectedPatch, actualPatch, c.Description)
testPatchApplicationWithoutSorting(t, original, actualPatch, expectedResult, c.Description)
}
func testThreeWayPatchWithoutSorting(t *testing.T, c StrategicMergePatchRawTestCase) {
original, modified, current, expected, result := threeWayRawTestCaseToJSONOrFail(t, c)
actual, err := CreateThreeWayMergePatch(original, modified, current, mergeItem, false)
if err != nil {
if !mergepatch.IsConflict(err) {
t.Errorf("error: %s\nin test case: %s\ncannot create three way patch:\noriginal:%s\ntwoWay:%s\nmodified:%s\ncurrent:%s\nthreeWay:%s\nresult:%s\n",
err, c.Description, c.Original, c.TwoWay, c.Modified, c.Current, c.ThreeWay, c.Result)
return
}
if !strings.Contains(c.Description, "conflict") {
t.Errorf("unexpected conflict: %s\nin test case: %s\ncannot create three way patch:\noriginal:%s\ntwoWay:%s\nmodified:%s\ncurrent:%s\nthreeWay:%s\nresult:%s\n",
err, c.Description, c.Original, c.TwoWay, c.Modified, c.Current, c.ThreeWay, c.Result)
return
}
if len(c.Result) > 0 {
actual, err := CreateThreeWayMergePatch(original, modified, current, mergeItem, true)
if err != nil {
t.Errorf("error: %s\nin test case: %s\ncannot force three way patch application:\noriginal:%s\ntwoWay:%s\nmodified:%s\ncurrent:%s\nthreeWay:%s\nresult:%s\n",
err, c.Description, c.Original, c.TwoWay, c.Modified, c.Current, c.ThreeWay, c.Result)
return
}
testPatchCreationWithoutSorting(t, expected, actual, c.Description)
testPatchApplicationWithoutSorting(t, current, actual, result, c.Description)
}
return
}
if strings.Contains(c.Description, "conflict") || len(c.Result) < 1 {
t.Errorf("error: %s\nin test case: %s\nexpected conflict did not occur:\noriginal:%s\ntwoWay:%s\nmodified:%s\ncurrent:%s\nthreeWay:%s\nresult:%s\n",
err, c.Description, c.Original, c.TwoWay, c.Modified, c.Current, c.ThreeWay, c.Result)
return
}
testPatchCreationWithoutSorting(t, expected, actual, c.Description)
testPatchApplicationWithoutSorting(t, current, actual, result, c.Description)
}
func testPatchCreationWithoutSorting(t *testing.T, expected, actual []byte, description string) {
if !reflect.DeepEqual(actual, expected) {
t.Errorf("error in test case: %s\nexpected patch:\n%s\ngot:\n%s\n",
description, jsonToYAMLOrError(expected), jsonToYAMLOrError(actual))
return
}
}
func testPatchApplicationWithoutSorting(t *testing.T, original, patch, expected []byte, description string) {
result, err := StrategicMergePatch(original, patch, mergeItem)
if err != nil {
t.Errorf("error: %s\nin test case: %s\ncannot apply patch:\n%s\nto original:\n%s\n",
err, description, jsonToYAMLOrError(patch), jsonToYAMLOrError(original))
return
}
if !reflect.DeepEqual(result, expected) {
format := "error in test case: %s\npatch application failed:\noriginal:\n%s\npatch:\n%s\nexpected:\n%s\ngot:\n%s\n"
t.Errorf(format, description,
jsonToYAMLOrError(original), jsonToYAMLOrError(patch),
jsonToYAMLOrError(expected), jsonToYAMLOrError(result))
return
}
}
func TestUnknownField(t *testing.T) {
testcases := map[string]struct {
Original string
Current string
Modified string
ExpectedTwoWay string
ExpectedTwoWayErr string
ExpectedTwoWayResult string
ExpectedThreeWay string
ExpectedThreeWayErr string
ExpectedThreeWayResult string
}{
// cases we can successfully strategically merge
"no diff": {
Original: `{"array":[1,2,3],"complex":{"nested":true},"name":"foo","scalar":true}`,
Current: `{"array":[1,2,3],"complex":{"nested":true},"name":"foo","scalar":true}`,
Modified: `{"array":[1,2,3],"complex":{"nested":true},"name":"foo","scalar":true}`,
ExpectedTwoWay: `{}`,
ExpectedTwoWayResult: `{"array":[1,2,3],"complex":{"nested":true},"name":"foo","scalar":true}`,
ExpectedThreeWay: `{}`,
ExpectedThreeWayResult: `{"array":[1,2,3],"complex":{"nested":true},"name":"foo","scalar":true}`,
},
"added only": {
Original: `{"name":"foo"}`,
Current: `{"name":"foo"}`,
Modified: `{"name":"foo","scalar":true,"complex":{"nested":true},"array":[1,2,3]}`,
ExpectedTwoWay: `{"array":[1,2,3],"complex":{"nested":true},"scalar":true}`,
ExpectedTwoWayResult: `{"array":[1,2,3],"complex":{"nested":true},"name":"foo","scalar":true}`,
ExpectedThreeWay: `{"array":[1,2,3],"complex":{"nested":true},"scalar":true}`,
ExpectedThreeWayResult: `{"array":[1,2,3],"complex":{"nested":true},"name":"foo","scalar":true}`,
},
"removed only": {
Original: `{"name":"foo","scalar":true,"complex":{"nested":true}}`,
Current: `{"name":"foo","scalar":true,"complex":{"nested":true},"array":[1,2,3]}`,
Modified: `{"name":"foo"}`,
ExpectedTwoWay: `{"complex":null,"scalar":null}`,
ExpectedTwoWayResult: `{"name":"foo"}`,
ExpectedThreeWay: `{"complex":null,"scalar":null}`,
ExpectedThreeWayResult: `{"array":[1,2,3],"name":"foo"}`,
},
// cases we cannot successfully strategically merge (expect errors)
"diff": {
Original: `{"array":[1,2,3],"complex":{"nested":true},"name":"foo","scalar":true}`,
Current: `{"array":[1,2,3],"complex":{"nested":true},"name":"foo","scalar":true}`,
Modified: `{"array":[1,2,3],"complex":{"nested":false},"name":"foo","scalar":true}`,
ExpectedTwoWayErr: `unable to find api field`,
ExpectedThreeWayErr: `unable to find api field`,
},
}
for _, k := range sets.StringKeySet(testcases).List() {
tc := testcases[k]
func() {
twoWay, err := CreateTwoWayMergePatch([]byte(tc.Original), []byte(tc.Modified), &MergeItem{})
if err != nil {
if len(tc.ExpectedTwoWayErr) == 0 {
t.Errorf("%s: error making two-way patch: %v", k, err)
}
if !strings.Contains(err.Error(), tc.ExpectedTwoWayErr) {
t.Errorf("%s: expected error making two-way patch to contain '%s', got %s", k, tc.ExpectedTwoWayErr, err)
}
return
}
if string(twoWay) != tc.ExpectedTwoWay {
t.Errorf("%s: expected two-way patch:\n\t%s\ngot\n\t%s", k, string(tc.ExpectedTwoWay), string(twoWay))
return
}
twoWayResult, err := StrategicMergePatch([]byte(tc.Original), twoWay, MergeItem{})
if err != nil {
t.Errorf("%s: error applying two-way patch: %v", k, err)
return
}
if string(twoWayResult) != tc.ExpectedTwoWayResult {
t.Errorf("%s: expected two-way result:\n\t%s\ngot\n\t%s", k, string(tc.ExpectedTwoWayResult), string(twoWayResult))
return
}
}()
func() {
threeWay, err := CreateThreeWayMergePatch([]byte(tc.Original), []byte(tc.Modified), []byte(tc.Current), &MergeItem{}, false)
if err != nil {
if len(tc.ExpectedThreeWayErr) == 0 {
t.Errorf("%s: error making three-way patch: %v", k, err)
} else if !strings.Contains(err.Error(), tc.ExpectedThreeWayErr) {
t.Errorf("%s: expected error making three-way patch to contain '%s', got %s", k, tc.ExpectedThreeWayErr, err)
}
return
}
if string(threeWay) != tc.ExpectedThreeWay {
t.Errorf("%s: expected three-way patch:\n\t%s\ngot\n\t%s", k, string(tc.ExpectedThreeWay), string(threeWay))
return
}
threeWayResult, err := StrategicMergePatch([]byte(tc.Current), threeWay, MergeItem{})
if err != nil {
t.Errorf("%s: error applying three-way patch: %v", k, err)
return
} else if string(threeWayResult) != tc.ExpectedThreeWayResult {
t.Errorf("%s: expected three-way result:\n\t%s\ngot\n\t%s", k, string(tc.ExpectedThreeWayResult), string(threeWayResult))
return
}
}()
}
}

View file

@ -17,8 +17,8 @@ limitations under the License.
package field
import (
"encoding/json"
"fmt"
"reflect"
"strings"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
@ -49,14 +49,33 @@ func (v *Error) ErrorBody() string {
case ErrorTypeRequired, ErrorTypeForbidden, ErrorTypeTooLong, ErrorTypeInternal:
s = fmt.Sprintf("%s", v.Type)
default:
var bad string
badBytes, err := json.Marshal(v.BadValue)
if err != nil {
bad = err.Error()
value := v.BadValue
valueType := reflect.TypeOf(value)
if value == nil || valueType == nil {
value = "null"
} else if valueType.Kind() == reflect.Ptr {
if reflectValue := reflect.ValueOf(value); reflectValue.IsNil() {
value = "null"
} else {
bad = string(badBytes)
value = reflectValue.Elem().Interface()
}
}
switch t := value.(type) {
case int64, int32, float64, float32, bool:
// use simple printer for simple types
s = fmt.Sprintf("%s: %v", v.Type, value)
case string:
s = fmt.Sprintf("%s: %q", v.Type, t)
case fmt.Stringer:
// anything that defines String() is better than raw struct
s = fmt.Sprintf("%s: %s", v.Type, t.String())
default:
// fallback to raw struct
// TODO: internal types have panic guards against json.Marshalling to prevent
// accidental use of internal types in external serialized form. For now, use
// %#v, although it would be better to show a more expressive output in the future
s = fmt.Sprintf("%s: %#v", v.Type, value)
}
s = fmt.Sprintf("%s: %s", v.Type, bad)
}
if len(v.Detail) != 0 {
s += fmt.Sprintf(": %s", v.Detail)

View file

@ -62,6 +62,14 @@ func TestMakeFuncs(t *testing.T) {
}
func TestErrorUsefulMessage(t *testing.T) {
{
s := Invalid(nil, nil, "").Error()
t.Logf("message: %v", s)
if !strings.Contains(s, "null") {
t.Errorf("error message did not contain 'null': %s", s)
}
}
s := Invalid(NewPath("foo"), "bar", "deet").Error()
t.Logf("message: %v", s)
for _, part := range []string{"foo", "bar", "deet", ErrorTypeInvalid.String()} {
@ -90,7 +98,8 @@ func TestErrorUsefulMessage(t *testing.T) {
for _, part := range []string{
"foo", ErrorTypeInvalid.String(),
"Baz", "Qux", "Inner", "KV", "detail",
"1", "aoeu", "asdf", "Billy", "2",
"1", "aoeu", "Billy", "2",
// "asdf", TODO: reenable once we have a better nested printer
} {
if !strings.Contains(s, part) {
t.Errorf("error message did not contain expected part '%v'", part)

View file

@ -17,6 +17,7 @@ limitations under the License.
package watch
import (
"errors"
"time"
"k8s.io/apimachinery/pkg/util/wait"
@ -28,6 +29,9 @@ import (
// from false to true).
type ConditionFunc func(event Event) (bool, error)
// errWatchClosed is returned when the watch channel is closed before timeout in Until.
var errWatchClosed = errors.New("watch closed before Until timeout")
// Until reads items from the watch until each provided condition succeeds, and then returns the last watch
// encountered. The first condition that returns an error terminates the watch (and the event is also returned).
// If no event has been received, the returned event will be nil.
@ -61,7 +65,7 @@ func Until(timeout time.Duration, watcher Interface, conditions ...ConditionFunc
select {
case event, ok := <-ch:
if !ok {
return lastEvent, wait.ErrWaitTimeout
return lastEvent, errWatchClosed
}
lastEvent = &event

4
vendor/k8s.io/kubernetes/.bazelrc generated vendored
View file

@ -2,5 +2,5 @@
build --verbose_failures
test --test_output=errors
# Retry tests up to 3 times if they fail.
test --flaky_test_attempts=3
# Include git version info
build --workspace_status_command hack/print-workspace-status.sh

View file

@ -27,4 +27,4 @@ path-prefix vendor/
path-prefix api/swagger-spec/
path-prefix pkg/generated/
paths-from-repo .generated_docs
paths-from-repo docs/.generated_docs

View file

@ -43,5 +43,5 @@ explain why.
**How to reproduce it** (as minimally and precisely as possible):
**Anything else do we need to know**:
**Anything else we need to know**:

View file

@ -1,7 +1,7 @@
<!-- Thanks for sending a pull request! Here are some tips for you:
1. If this is your first time, read our contributor guidelines https://github.com/kubernetes/kubernetes/blob/master/CONTRIBUTING.md and developer guide https://github.com/kubernetes/kubernetes/blob/master/docs/devel/development.md
2. If you want *faster* PR reviews, read how: https://github.com/kubernetes/kubernetes/blob/master/docs/devel/faster_reviews.md
3. Follow the instructions for writing a release note: https://github.com/kubernetes/kubernetes/blob/master/docs/devel/pull-requests.md#release-notes
1. If this is your first time, read our contributor guidelines https://github.com/kubernetes/community/blob/master/contributors/devel/pull-requests.md#pull-request-process and developer guide https://github.com/kubernetes/community/blob/master/contributors/devel/development.md#development-guide
2. If you want *faster* PR reviews, read how: https://github.com/kubernetes/community/blob/master/contributors/devel/faster_reviews.md
3. Follow the instructions for writing a release note: https://github.com/kubernetes/community/blob/master/contributors/devel/pull-requests.md#release-notes
-->
**What this PR does / why we need it**:

View file

@ -33,6 +33,9 @@
Session.vim
.netrwhist
# cscope-related files
cscope.*
# Go test binaries
*.test
/hack/.test-cmd-auth

View file

@ -52,3 +52,10 @@ filegroup(
],
tags = ["automanaged"],
)
genrule(
name = "save_git_version",
outs = ["version"],
cmd = "grep ^STABLE_BUILD_SCM_REVISION bazel-out/stable-status.txt | cut -d' ' -f2 >$@",
stamp = 1,
)

1478
vendor/k8s.io/kubernetes/CHANGELOG.md generated vendored

File diff suppressed because it is too large Load diff

View file

@ -1,132 +1,9 @@
# Contributing guidelines
Want to hack on Kubernetes? Yay!
## Developer Guide
We have a [Developer's Guide](docs/devel/README.md) that outlines everything
you need to know from setting up your dev environment to how to get faster Pull
Request reviews. If you find something undocumented or incorrect along the way,
please feel free to send a Pull Request.
## Filing issues
If you have a question about Kubernetes or have a problem using it, please
start with the [troubleshooting guide](http://kubernetes.io/docs/troubleshooting/). If that
doesn't answer your questions, or if you think you found a bug, please [file an
issue](https://github.com/kubernetes/kubernetes/issues/new).
## How to become a contributor and submit your own code
### Contributor License Agreements
We'd love to accept your patches! Before we can take them, we have to jump a
couple of legal hurdles.
The Cloud Native Computing Foundation (CNCF) CLA [must be signed](https://github.com/kubernetes/community/blob/master/CLA.md) by all contributors.
Please fill out either the individual or corporate Contributor License
Agreement (CLA).
Once you are CLA'ed, we'll be able to accept your pull requests. For any issues that you face during this process,
please add a comment [here](https://github.com/kubernetes/kubernetes/issues/27796) explaining the issue and we will help get it sorted out.
***NOTE***: Only original source code from you and other people that have
signed the CLA can be accepted into the repository. This policy does not
apply to [third_party](third_party/) and [vendor](vendor/).
### Finding Things That Need Help
If you're new to the project and want to help, but don't know where to start,
we have a semi-curated list of issues that have should not need deep knowledge
of the system. [Have a look and see if anything sounds
interesting](https://github.com/kubernetes/kubernetes/issues?q=is%3Aopen+is%3Aissue+label%3Ahelp-wanted).
Alternatively, read some of the many docs on the system, for example [the
architecture](docs/design/architecture.md), and pick a component that seems
interesting. Start with `main()` (look in the [cmd](cmd/) directory) and read
until you find something you want to fix. The best way to learn is to hack!
There's always code that can be clarified and variables or functions that can
be renamed or commented.
### Contributing A Patch
If you're working on an existing issue, such as one of the `help-wanted` ones
above, simply respond to the issue and express interest in working on it. This
helps other people know that the issue is active, and hopefully prevents
duplicated efforts.
If you want to work on a new idea of relatively small scope:
1. Submit an issue describing your proposed change to the repo in question.
1. The repo owners will respond to your issue promptly.
1. If your proposed change is accepted, and you haven't already done so, sign a
Contributor License Agreement (see details above).
1. Fork the repo, develop, and test your changes.
1. Submit a pull request.
If you want to work on a bigger idea, we STRONGLY recommend that you start with
some bugs or smaller features. We have a [feature development
process](https://github.com/kubernetes/features/blob/master/README.md), but
navigating the Kubernetes system as a newcomer can be very challenging.
### Downloading the project
There are a few ways you can download this code. You must download it into a
GOPATH - see [golang.org](https://golang.org/doc/code.html) for more info on
how Go works with code. This project expects to be found at the Go package
`k8s.io/kubernetes`.
1. You can `git clone` the repo. If you do this, you MUST make sure it is in
the GOPATH as `k8s.io/kubernetes` or it may not build. E.g.: `git clone
https://github.com/kubernetes/kubernetes $GOPATH/src/k8s.io/kubernetes`
1. You can use `go get` to fetch the repo. This will automatically put it into
your GOPATH in the right place. E.g.: `go get -d k8s.io/kubernetes`
1. You can download an archive of the source. If you do this, you MUST make
sure it is unpacked into the GOPATH as `k8s.io/kubernetes` or it may not
build. See [rel.k8s.io](http://rel.k8s.io) for a list of available releases.
### Building the project
There are a few things you need to build and test this project:
1. `make` - the human interface to the Kubernetes build is `make`, so you must
have this tool installed on your machine. We try not to use too many crazy
features of `Makefile`s and other tools, so most commonly available versions
should work.
1. `docker` - some parts of the build/test system depend on `docker`. You
need a relatively recent version of Docker installed, and available to you.
1. `go` - Kubernetes is written in Go (aka golang), so you need a relatively
recent version of the [Go toolchain](https://golang.org/dl/) installed.
While Linux is the primary platform for Kubernetes, it should compile on a
Mac, too. Windows is in progress.
To build Kubernetes, simply type `make`. This should figure out what it needs
to do and not need any input from you. If you want to just build a subset of
code, you can pass the `WHAT` variable to `make`: e.g. `make
WHAT="cmd/kubelet"`.
To run basic tests, simply type `make test`. This will run all of the unit
tests in the project. If you want to just test a subset of the project, you
can pass the `WHAT` variable to `make`: e.g. `make test WHAT=pkg/kubelet`.
### Protocols for Collaborative Development
Please read [this doc](docs/devel/collab.md) for information on how we're
running development for the project. Also take a look at the [development
guide](docs/devel/development.md) for information on how to set up your
environment, run tests, manage dependencies, etc.
### Adding dependencies
If your patch depends on new packages, add that package with
[`godep`](https://github.com/tools/godep). Follow the [instructions to add a
dependency](docs/devel/development.md#godep-and-dependency-management).
### Community Expectations
Please see our [expectations](docs/devel/community-expectations.md) for members
of the Kubernetes community.
# Contributing
Information about contributing to the
[kubernetes code repo](README.md) lives in the
[kubernetes community repo](https://github.com/kubernetes/community)
(it's a big topic).
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/CONTRIBUTING.md?pixel)]()

File diff suppressed because it is too large Load diff

13553
vendor/k8s.io/kubernetes/Godeps/LICENSES generated vendored

File diff suppressed because it is too large Load diff

25
vendor/k8s.io/kubernetes/Makefile generated vendored
View file

@ -53,6 +53,10 @@ KUBE_GOFLAGS := $(GOFLAGS)
KUBE_GOLDFLAGS := $(GOLDFLAGS)
KUBE_GOGCFLAGS = $(GOGCFLAGS)
# Extra options for the release or quick-release options:
KUBE_RELEASE_RUN_TESTS := $(KUBE_RELEASE_RUN_TESTS)
KUBE_FASTBUILD := $(KUBE_FASTBUILD)
# This controls the verbosity of the build. Higher numbers mean more output.
KUBE_VERBOSE ?= 1
@ -191,7 +195,7 @@ test-e2e:
@echo "$$TEST_E2E_HELP_INFO"
else
test-e2e: ginkgo generated_files
go run hack/e2e.go -v --build --up --test --down
go run hack/e2e.go -- -v --build --up --test --down
endif
define TEST_E2E_NODE_HELP_INFO
@ -227,11 +231,13 @@ define TEST_E2E_NODE_HELP_INFO
# GUBERNATOR: For REMOTE=true only. Produce link to Gubernator to view logs.
# Defaults to false.
# PARALLELISM: The number of gingko nodes to run. Defaults to 8.
# RUNTIME: Container runtime to use (eg. docker, rkt, remote).
# Defaults to "docker".
#
# Example:
# make test-e2e-node FOCUS=Kubelet SKIP=container
# make test-e2e-node REMOTE=true DELETE_INSTANCES=true
# make test-e2e-node TEST_ARGS="--experimental-cgroups-per-qos=true"
# make test-e2e-node TEST_ARGS="--cgroups-per-qos=true"
# Build and run tests.
endef
.PHONY: test-e2e-node
@ -349,16 +355,23 @@ endif
define RELEASE_SKIP_TESTS_HELP_INFO
# Build a release, but skip tests
#
# Args:
# KUBE_RELEASE_RUN_TESTS: Whether to run tests. Set to 'y' to run tests anyways.
# KUBE_FASTBUILD: Whether to cross-compile for other architectures. Set to 'true' to do so.
#
# Example:
# make release-skip-tests
# make quick-release
endef
.PHONY: release-skip-tests quick-release
ifeq ($(PRINT_HELP),y)
release-skip-tests quick-release:
@echo "$$RELEASE_SKIP_TESTS_HELP_INFO"
else
release-skip-tests quick-release: KUBE_RELEASE_RUN_TESTS = n
release-skip-tests quick-release: KUBE_FASTBUILD = true
release-skip-tests quick-release:
KUBE_RELEASE_RUN_TESTS=n KUBE_FASTBUILD=true build/release.sh
build/release.sh
endif
define CROSS_HELP_INFO
@ -483,22 +496,22 @@ bazel-build:
@echo "$$BAZEL_BUILD_HELP_INFO"
else
bazel-build:
bazel build //cmd/... //pkg/... //federation/... //plugin/... //build/... //examples/... //test/... //third_party/...
bazel build //cmd/... //pkg/... //federation/... //plugin/... //third_party/... //examples/... //test/...
endif
ifeq ($(PRINT_HELP),y)
bazel-test:
define BAZEL_TEST_HELP_INFO
# Test with bazel
#
# Example:
# make bazel-test
endef
bazel-test:
@echo "$$BAZEL_TEST_HELP_INFO"
else
bazel-test:
bazel test //cmd/... //pkg/... //federation/... //plugin/... //build/... //third_party/... //hack/... //hack:verify-all
bazel test --flaky_test_attempts=3 //cmd/... //pkg/... //federation/... //plugin/... //third_party/... //hack/... //hack:verify-all
endif
ifeq ($(PRINT_HELP),y)

1
vendor/k8s.io/kubernetes/OWNERS generated vendored
View file

@ -13,3 +13,4 @@ approvers:
- lavalamp
- smarterclayton
- thockin
- wojtek-t

View file

@ -1,10 +1,21 @@
aliases:
sig-scheduling-maintainers:
- davidopp
- timothysc
- wojtek-t
sig-scheduling:
- davidopp
- timothysc
- wojtek-t
- k82cn
- jayunit100
sig-cli-maintainers:
- adohe
- brendandburns
- deads2k
- fabianofranz
- janetkuo
- liggitt
- pwittrock
- smarterclayton
sig-cli:
@ -47,3 +58,11 @@ aliases:
- vishh
- yifan-gu
- yujuhong
sig-network-reviewers:
- thockin
- freehan
- bowei
- mrhohn
- dcbw
- danwinship
- caseydavenport

100
vendor/k8s.io/kubernetes/README.md generated vendored
View file

@ -1,68 +1,45 @@
# Kubernetes
[![Submit Queue Widget]][Submit Queue] [![GoDoc Widget]][GoDoc] [![Coverage Status Widget]][Coverage Status]
[![Submit Queue Widget]][Submit Queue] [![GoDoc Widget]][GoDoc]
<img src="https://github.com/kubernetes/kubernetes/raw/master/logo/logo.png" width="100">
[Submit Queue]: http://submit-queue.k8s.io/#/e2e
[Submit Queue Widget]: http://submit-queue.k8s.io/health.svg?v=1
[GoDoc]: https://godoc.org/k8s.io/kubernetes
[GoDoc Widget]: https://godoc.org/k8s.io/kubernetes?status.svg
[Coverage Status]: https://coveralls.io/r/kubernetes/kubernetes
[Coverage Status Widget]: https://coveralls.io/repos/kubernetes/kubernetes/badge.svg
----
## Introduction
Kubernetes is an open source system for managing [containerized applications]
across multiple hosts, providing basic mechanisms for deployment, maintenance,
and scaling of applications.
Kubernetes is an open source system for managing [containerized applications](http://kubernetes.io/docs/whatisk8s/) across multiple hosts,
providing basic mechanisms for deployment, maintenance, and scaling of applications. Kubernetes is hosted by the Cloud Native Computing Foundation ([CNCF](https://www.cncf.io))
Kubernetes builds upon a decade and a half of experience at Google running
production workloads at scale using a system called [Borg],
combined with best-of-breed ideas and practices from the community.
Kubernetes builds upon a decade and a half of experience at Google running production workloads at scale using a system called [Borg](https://research.google.com/pubs/pub43438.html), combined with best-of-breed ideas and practices from the community.
Kubernetes is hosted by the Cloud Native Computing Foundation ([CNCF]).
If you are a company that wants to help shape the evolution of
technologies that are container-packaged, dynamically-scheduled
and microservices-oriented, consider joining the CNCF.
For details about who's involved and how Kubernetes plays a role,
read the CNCF [announcement].
<hr>
----
### Are you ...
## To start using Kubernetes
* Interested in learning more about using Kubernetes?
- See our documentation on [kubernetes.io](http://kubernetes.io)
- Try our [interactive tutorial](http://kubernetes.io/docs/tutorials/kubernetes-basics/)
- Take a free course on [Scalable Microservices with Kubernetes](https://www.udacity.com/course/scalable-microservices-with-kubernetes--ud615).
* Interested in developing the core Kubernetes code base, developing tools using the Kubernetes API or helping in anyway possible ? Keep reading!
See our documentation on [kubernetes.io].
## Code of Conduct
Try our [interactive tutorial].
The Kubernetes community abides by the CNCF [code of conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md). Here is an excerpt:
Take a free course on [Scalable Microservices with Kubernetes].
_As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities._
## To start developing Kubernetes
## Community
Do you want to help "shape the evolution of technologies that are container-packaged, dynamically-scheduled and microservices-oriented? ". If you are a company, you should consider joining the [CNCF](https://cncf.io/about). For details about who's involved in CNCF and how Kubernetes plays a role, read [the announcement](https://cncf.io/news/announcement/2015/07/new-cloud-native-computing-foundation-drive-alignment-among-container). For general information about our community see the website [community](http://kubernetes.io/community/) page.
Join us on social media ([Twitter](https://twitter.com/kubernetesio), [Google+](https://plus.google.com/u/0/b/116512812300813784482/116512812300813784482)) and read our [blog](http://blog.kubernetes.io/)
Ask questions and help answer them on [Slack](http://slack.k8s.io/) or [Stack Overflow](http://stackoverflow.com/questions/tagged/kubernetes)
Attend our key events ([kubecon](http://events.linuxfoundation.org/events/kubecon), [cloudnativecon](http://events.linuxfoundation.org/events/cloudnativecon), weekly [community meeting](https://github.com/kubernetes/community/blob/master/community/README.md))
Join a Special Interest Group ([SIG](https://github.com/kubernetes/community))
## Contribute
If you're interested in being a contributor and want to get involved in developing Kubernetes, get started with this reading:
* The community [expectations](https://github.com/kubernetes/community/blob/master/contributors/devel/community-expectations.md)
* The [contributor guidelines](CONTRIBUTING.md)
* The [Kubernetes Developer Guide](https://github.com/kubernetes/community/tree/master/contributors/devel)
You will then most certainly gain a lot from joining a [SIG](https://github.com/kubernetes/community), attending the regular hangouts as well as the community [meeting](https://github.com/kubernetes/community/blob/master/community/README.md).
If you have an idea for a new feature, see the [Kubernetes Features](https://github.com/kubernetes/features) repository for a list of features that are coming in new releases as well as details on how to propose one.
### Building Kubernetes for the impatient
The [community repository] hosts all information about
building Kubernetes from source, how to contribute code
and documentation, who to contact about what, etc.
If you want to build Kubernetes right away there are two options:
* You have a working [Go environment](https://golang.org/doc/install).
##### You have a working [Go environment].
```
$ go get -d k8s.io/kubernetes
@ -70,7 +47,7 @@ $ cd $GOPATH/src/k8s.io/kubernetes
$ make
```
* You have a working [Docker environment](https://docs.docker.com/engine/).
##### You have a working [Docker environment].
```
$ git clone https://github.com/kubernetes/kubernetes
@ -78,15 +55,32 @@ $ cd kubernetes
$ make quick-release
```
If you are less impatient, head over to the [developer's documentation](https://github.com/kubernetes/community/tree/master/contributors/devel).
If you are less impatient, head over to the [developer's documentation].
## Support
While there are many different channels that you can use to get hold of us ([Slack](http://slack.k8s.io/), [Stack Overflow](http://stackoverflow.com/questions/tagged/kubernetes), [Issues](https://github.com/kubernetes/kubernetes/issues/new), [Forums/Mailing lists](https://groups.google.com/forum/#!forum/kubernetes-users)), you can help make sure that we are efficient in getting you the help that you need.
If you need support, start with the [troubleshooting guide]
and work your way through the process that we've outlined.
If you need support, start with the [troubleshooting guide](http://kubernetes.io/docs/troubleshooting/) and work your way through the process that we've outlined.
That said, if you have questions, reach out to us
[one way or another][communication].
That said, if you have questions, reach out to us one way or another. We don't bite!
[announcement]: https://cncf.io/news/announcement/2015/07/new-cloud-native-computing-foundation-drive-alignment-among-container
[Borg]: https://research.google.com/pubs/pub43438.html
[CNCF]: https://www.cncf.io/about
[communication]: https://github.com/kubernetes/community/blob/master/communication.md
[community repository]: https://github.com/kubernetes/community
[containerized applications]: http://kubernetes.io/docs/whatisk8s
[developer's documentation]: https://github.com/kubernetes/community/tree/master/contributors/devel
[Docker environment]: https://docs.docker.com/engine
[Go environment]: https://golang.org/doc/install
[GoDoc]: https://godoc.org/k8s.io/kubernetes
[GoDoc Widget]: https://godoc.org/k8s.io/kubernetes?status.svg
[interactive tutorial]: http://kubernetes.io/docs/tutorials/kubernetes-basics
[kubernetes.io]: http://kubernetes.io
[Scalable Microservices with Kubernetes]: https://www.udacity.com/course/scalable-microservices-with-kubernetes--ud615
[Submit Queue]: http://submit-queue.k8s.io/#/e2e
[Submit Queue Widget]: http://submit-queue.k8s.io/health.svg?v=1
[troubleshooting guide]: http://kubernetes.io/docs/troubleshooting
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/README.md?pixel)]()

14
vendor/k8s.io/kubernetes/WORKSPACE generated vendored
View file

@ -1,13 +1,13 @@
git_repository(
name = "io_bazel_rules_go",
commit = "d0142854a22a0dd98306280e897e64086289a0de",
commit = "7828452850597b52b49ec603b23f8ad2bcb22aed",
remote = "https://github.com/bazelbuild/rules_go.git",
)
git_repository(
name = "io_kubernetes_build",
commit = "418b8e976cb32d94fd765c80f2b04e660c5ec4ec",
remote = "https://github.com/kubernetes/release.git",
commit = "685f15b90b454af3086ab071fdea1b6db213d1fb",
remote = "https://github.com/kubernetes/repo-infra.git",
)
git_repository(
@ -24,8 +24,8 @@ go_repositories()
debs = (
(
"busybox_deb",
"f262cc9cf893740bb70c3dd01da9429b858c94be696badd4a702e0a8c7f6f80b",
"http://ftp.us.debian.org/debian/pool/main/b/busybox/busybox-static_1.22.0-19+b1_amd64.deb",
"7465567f5e5255188b1d004d7081066cd79f77a5c18a5d418d27966d698e0bef",
"http://ftp.us.debian.org/debian/pool/main/b/busybox/busybox-static_1.22.0-19+b2_amd64.deb",
),
(
"libc_deb",
@ -62,6 +62,6 @@ debs = (
http_file(
name = "kubernetes_cni",
sha256 = "ddcb7a429f82b284a13bdb36313eeffd997753b6fa5191205f1e978dcfeb0792",
url = " https://storage.googleapis.com/kubernetes-release/network-plugins/cni-amd64-07a8a28637e97b22eb8dfe710eeae1344f69d16e.tar.gz",
sha256 = "05ab3937bc68562e989dc143362ec4d4275262ba9f359338aed720fc914457a5",
url = "https://storage.googleapis.com/kubernetes-release/network-plugins/cni-amd64-0799f5732f2a11b329d9e3d51b9c8f2e3759f2ff.tar.gz",
)

File diff suppressed because it is too large Load diff

View file

@ -1303,7 +1303,7 @@
},
"dnsPolicy": {
"type": "string",
"description": "Set DNS policy for containers within the pod. One of 'ClusterFirst' or 'Default'. Defaults to \"ClusterFirst\"."
"description": "Set DNS policy for containers within the pod. One of 'ClusterFirstWithHostNet', 'ClusterFirst' or 'Default'. Defaults to \"ClusterFirst\". To have DNS options set along with hostNetwork, you have to specify DNS policy explicitly to 'ClusterFirstWithHostNet'."
},
"nodeSelector": {
"type": "object",

File diff suppressed because it is too large Load diff

View file

@ -1 +1,340 @@
{
"swaggerVersion": "1.2",
"apiVersion": "authentication.k8s.io/v1",
"basePath": "https://10.10.10.10:6443",
"resourcePath": "/apis/authentication.k8s.io/v1",
"info": {
"title": "",
"description": ""
},
"apis": [
{
"path": "/apis/authentication.k8s.io/v1/tokenreviews",
"description": "API at /apis/authentication.k8s.io/v1",
"operations": [
{
"type": "v1.TokenReview",
"method": "POST",
"summary": "create a TokenReview",
"nickname": "createTokenReview",
"parameters": [
{
"type": "string",
"paramType": "query",
"name": "pretty",
"description": "If 'true', then the output is pretty printed.",
"required": false,
"allowMultiple": false
},
{
"type": "v1.TokenReview",
"paramType": "body",
"name": "body",
"description": "",
"required": true,
"allowMultiple": false
}
],
"responseMessages": [
{
"code": 200,
"message": "OK",
"responseModel": "v1.TokenReview"
}
],
"produces": [
"application/json",
"application/yaml",
"application/vnd.kubernetes.protobuf"
],
"consumes": [
"*/*"
]
}
]
},
{
"path": "/apis/authentication.k8s.io/v1",
"description": "API at /apis/authentication.k8s.io/v1",
"operations": [
{
"type": "v1.APIResourceList",
"method": "GET",
"summary": "get available resources",
"nickname": "getAPIResources",
"parameters": [],
"produces": [
"application/json",
"application/yaml",
"application/vnd.kubernetes.protobuf"
],
"consumes": [
"application/json",
"application/yaml",
"application/vnd.kubernetes.protobuf"
]
}
]
}
],
"models": {
"v1.TokenReview": {
"id": "v1.TokenReview",
"description": "TokenReview attempts to authenticate a token to a known user. Note: TokenReview requests may be cached by the webhook token authenticator plugin in the kube-apiserver.",
"required": [
"spec"
],
"properties": {
"kind": {
"type": "string",
"description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds"
},
"apiVersion": {
"type": "string",
"description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#resources"
},
"metadata": {
"$ref": "v1.ObjectMeta"
},
"spec": {
"$ref": "v1.TokenReviewSpec",
"description": "Spec holds information about the request being evaluated"
},
"status": {
"$ref": "v1.TokenReviewStatus",
"description": "Status is filled in by the server and indicates whether the request can be authenticated."
}
}
},
"v1.ObjectMeta": {
"id": "v1.ObjectMeta",
"description": "ObjectMeta is metadata that all persisted resources must have, which includes all objects users must create.",
"properties": {
"name": {
"type": "string",
"description": "Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names"
},
"generateName": {
"type": "string",
"description": "GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.\n\nIf this field is specified and the generated name exists, the server will NOT return a 409 - instead, it will either return 201 Created or 500 with Reason ServerTimeout indicating a unique name could not be found in the time allotted, and the client should retry (optionally after the time indicated in the Retry-After header).\n\nApplied only if Name is not specified. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#idempotency"
},
"namespace": {
"type": "string",
"description": "Namespace defines the space within each name must be unique. An empty namespace is equivalent to the \"default\" namespace, but \"default\" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.\n\nMust be a DNS_LABEL. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/namespaces"
},
"selfLink": {
"type": "string",
"description": "SelfLink is a URL representing this object. Populated by the system. Read-only."
},
"uid": {
"type": "string",
"description": "UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations.\n\nPopulated by the system. Read-only. More info: http://kubernetes.io/docs/user-guide/identifiers#uids"
},
"resourceVersion": {
"type": "string",
"description": "An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources.\n\nPopulated by the system. Read-only. Value must be treated as opaque by clients and . More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#concurrency-control-and-consistency"
},
"generation": {
"type": "integer",
"format": "int64",
"description": "A sequence number representing a specific generation of the desired state. Populated by the system. Read-only."
},
"creationTimestamp": {
"type": "string",
"description": "CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC.\n\nPopulated by the system. Read-only. Null for lists. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata"
},
"deletionTimestamp": {
"type": "string",
"description": "DeletionTimestamp is RFC 3339 date and time at which this resource will be deleted. This field is set by the server when a graceful deletion is requested by the user, and is not directly settable by a client. The resource is expected to be deleted (no longer visible from resource lists, and not reachable by name) after the time in this field. Once set, this value may not be unset or be set further into the future, although it may be shortened or the resource may be deleted prior to this time. For example, a user may request that a pod is deleted in 30 seconds. The Kubelet will react by sending a graceful termination signal to the containers in the pod. After that 30 seconds, the Kubelet will send a hard termination signal (SIGKILL) to the container and after cleanup, remove the pod from the API. In the presence of network partitions, this object may still exist after this timestamp, until an administrator or automated process can determine the resource is fully terminated. If not set, graceful deletion of the object has not been requested.\n\nPopulated by the system when a graceful deletion is requested. Read-only. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata"
},
"deletionGracePeriodSeconds": {
"type": "integer",
"format": "int64",
"description": "Number of seconds allowed for this object to gracefully terminate before it will be removed from the system. Only set when deletionTimestamp is also set. May only be shortened. Read-only."
},
"labels": {
"type": "object",
"description": "Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels"
},
"annotations": {
"type": "object",
"description": "Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: http://kubernetes.io/docs/user-guide/annotations"
},
"ownerReferences": {
"type": "array",
"items": {
"$ref": "v1.OwnerReference"
},
"description": "List of objects depended by this object. If ALL objects in the list have been deleted, this object will be garbage collected. If this object is managed by a controller, then an entry in this list will point to this controller, with the controller field set to true. There cannot be more than one managing controller."
},
"finalizers": {
"type": "array",
"items": {
"type": "string"
},
"description": "Must be empty before the object is deleted from the registry. Each entry is an identifier for the responsible component that will remove the entry from the list. If the deletionTimestamp of the object is non-nil, entries in this list can only be removed."
},
"clusterName": {
"type": "string",
"description": "The name of the cluster which the object belongs to. This is used to distinguish resources with same name and namespace in different clusters. This field is not set anywhere right now and apiserver is going to ignore it if set in create or update request."
}
}
},
"v1.OwnerReference": {
"id": "v1.OwnerReference",
"description": "OwnerReference contains enough information to let you identify an owning object. Currently, an owning object must be in the same namespace, so there is no namespace field.",
"required": [
"apiVersion",
"kind",
"name",
"uid"
],
"properties": {
"apiVersion": {
"type": "string",
"description": "API version of the referent."
},
"kind": {
"type": "string",
"description": "Kind of the referent. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds"
},
"name": {
"type": "string",
"description": "Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names"
},
"uid": {
"type": "string",
"description": "UID of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#uids"
},
"controller": {
"type": "boolean",
"description": "If true, this reference points to the managing controller."
},
"blockOwnerDeletion": {
"type": "boolean",
"description": "If true, AND if the owner has the \"foregroundDeletion\" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. Defaults to false. To set this field, a user needs \"delete\" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned."
}
}
},
"v1.TokenReviewSpec": {
"id": "v1.TokenReviewSpec",
"description": "TokenReviewSpec is a description of the token authentication request.",
"properties": {
"token": {
"type": "string",
"description": "Token is the opaque bearer token."
}
}
},
"v1.TokenReviewStatus": {
"id": "v1.TokenReviewStatus",
"description": "TokenReviewStatus is the result of the token authentication request.",
"properties": {
"authenticated": {
"type": "boolean",
"description": "Authenticated indicates that the token was associated with a known user."
},
"user": {
"$ref": "v1.UserInfo",
"description": "User is the UserInfo associated with the provided token."
},
"error": {
"type": "string",
"description": "Error indicates that the token couldn't be checked"
}
}
},
"v1.UserInfo": {
"id": "v1.UserInfo",
"description": "UserInfo holds the information about the user needed to implement the user.Info interface.",
"properties": {
"username": {
"type": "string",
"description": "The name that uniquely identifies this user among all active users."
},
"uid": {
"type": "string",
"description": "A unique value that identifies this user across time. If this user is deleted and another user by the same name is added, they will have different UIDs."
},
"groups": {
"type": "array",
"items": {
"type": "string"
},
"description": "The names of groups this user is a part of."
},
"extra": {
"type": "object",
"description": "Any additional information provided by the authenticator."
}
}
},
"v1.APIResourceList": {
"id": "v1.APIResourceList",
"description": "APIResourceList is a list of APIResource, it is used to expose the name of the resources supported in a specific group and version, and if the resource is namespaced.",
"required": [
"groupVersion",
"resources"
],
"properties": {
"kind": {
"type": "string",
"description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds"
},
"apiVersion": {
"type": "string",
"description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#resources"
},
"groupVersion": {
"type": "string",
"description": "groupVersion is the group and version this APIResourceList is for."
},
"resources": {
"type": "array",
"items": {
"$ref": "v1.APIResource"
},
"description": "resources contains the name of the resources and if they are namespaced."
}
}
},
"v1.APIResource": {
"id": "v1.APIResource",
"description": "APIResource specifies the name of a resource and whether it is namespaced.",
"required": [
"name",
"namespaced",
"kind",
"verbs"
],
"properties": {
"name": {
"type": "string",
"description": "name is the name of the resource."
},
"namespaced": {
"type": "boolean",
"description": "namespaced indicates if a resource is namespaced or not."
},
"kind": {
"type": "string",
"description": "kind is the kind for the resource (e.g. 'Foo' is the kind for a resource 'foo')"
},
"verbs": {
"type": "array",
"items": {
"type": "string"
},
"description": "verbs is a list of supported kube verbs (this includes get, list, watch, create, update, patch, delete, deletecollection, and proxy)"
},
"shortNames": {
"type": "array",
"items": {
"type": "string"
},
"description": "shortNames is a list of suggested short names of the resource."
}
}
}
}
}

View file

@ -209,6 +209,10 @@
"controller": {
"type": "boolean",
"description": "If true, this reference points to the managing controller."
},
"blockOwnerDeletion": {
"type": "boolean",
"description": "If true, AND if the owner has the \"foregroundDeletion\" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. Defaults to false. To set this field, a user needs \"delete\" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned."
}
}
},
@ -322,6 +326,13 @@
"type": "string"
},
"description": "verbs is a list of supported kube verbs (this includes get, list, watch, create, update, patch, delete, deletecollection, and proxy)"
},
"shortNames": {
"type": "array",
"items": {
"type": "string"
},
"description": "shortNames is a list of suggested short names of the resource."
}
}
}

View file

@ -0,0 +1,553 @@
{
"swaggerVersion": "1.2",
"apiVersion": "authorization.k8s.io/v1",
"basePath": "https://10.10.10.10:6443",
"resourcePath": "/apis/authorization.k8s.io/v1",
"info": {
"title": "",
"description": ""
},
"apis": [
{
"path": "/apis/authorization.k8s.io/v1/namespaces/{namespace}/localsubjectaccessreviews",
"description": "API at /apis/authorization.k8s.io/v1",
"operations": [
{
"type": "v1.LocalSubjectAccessReview",
"method": "POST",
"summary": "create a LocalSubjectAccessReview",
"nickname": "createNamespacedLocalSubjectAccessReview",
"parameters": [
{
"type": "string",
"paramType": "query",
"name": "pretty",
"description": "If 'true', then the output is pretty printed.",
"required": false,
"allowMultiple": false
},
{
"type": "v1.LocalSubjectAccessReview",
"paramType": "body",
"name": "body",
"description": "",
"required": true,
"allowMultiple": false
},
{
"type": "string",
"paramType": "path",
"name": "namespace",
"description": "object name and auth scope, such as for teams and projects",
"required": true,
"allowMultiple": false
}
],
"responseMessages": [
{
"code": 200,
"message": "OK",
"responseModel": "v1.LocalSubjectAccessReview"
}
],
"produces": [
"application/json",
"application/yaml",
"application/vnd.kubernetes.protobuf"
],
"consumes": [
"*/*"
]
}
]
},
{
"path": "/apis/authorization.k8s.io/v1/selfsubjectaccessreviews",
"description": "API at /apis/authorization.k8s.io/v1",
"operations": [
{
"type": "v1.SelfSubjectAccessReview",
"method": "POST",
"summary": "create a SelfSubjectAccessReview",
"nickname": "createSelfSubjectAccessReview",
"parameters": [
{
"type": "string",
"paramType": "query",
"name": "pretty",
"description": "If 'true', then the output is pretty printed.",
"required": false,
"allowMultiple": false
},
{
"type": "v1.SelfSubjectAccessReview",
"paramType": "body",
"name": "body",
"description": "",
"required": true,
"allowMultiple": false
}
],
"responseMessages": [
{
"code": 200,
"message": "OK",
"responseModel": "v1.SelfSubjectAccessReview"
}
],
"produces": [
"application/json",
"application/yaml",
"application/vnd.kubernetes.protobuf"
],
"consumes": [
"*/*"
]
}
]
},
{
"path": "/apis/authorization.k8s.io/v1/subjectaccessreviews",
"description": "API at /apis/authorization.k8s.io/v1",
"operations": [
{
"type": "v1.SubjectAccessReview",
"method": "POST",
"summary": "create a SubjectAccessReview",
"nickname": "createSubjectAccessReview",
"parameters": [
{
"type": "string",
"paramType": "query",
"name": "pretty",
"description": "If 'true', then the output is pretty printed.",
"required": false,
"allowMultiple": false
},
{
"type": "v1.SubjectAccessReview",
"paramType": "body",
"name": "body",
"description": "",
"required": true,
"allowMultiple": false
}
],
"responseMessages": [
{
"code": 200,
"message": "OK",
"responseModel": "v1.SubjectAccessReview"
}
],
"produces": [
"application/json",
"application/yaml",
"application/vnd.kubernetes.protobuf"
],
"consumes": [
"*/*"
]
}
]
},
{
"path": "/apis/authorization.k8s.io/v1",
"description": "API at /apis/authorization.k8s.io/v1",
"operations": [
{
"type": "v1.APIResourceList",
"method": "GET",
"summary": "get available resources",
"nickname": "getAPIResources",
"parameters": [],
"produces": [
"application/json",
"application/yaml",
"application/vnd.kubernetes.protobuf"
],
"consumes": [
"application/json",
"application/yaml",
"application/vnd.kubernetes.protobuf"
]
}
]
}
],
"models": {
"v1.LocalSubjectAccessReview": {
"id": "v1.LocalSubjectAccessReview",
"description": "LocalSubjectAccessReview checks whether or not a user or group can perform an action in a given namespace. Having a namespace scoped resource makes it much easier to grant namespace scoped policy that includes permissions checking.",
"required": [
"spec"
],
"properties": {
"kind": {
"type": "string",
"description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds"
},
"apiVersion": {
"type": "string",
"description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#resources"
},
"metadata": {
"$ref": "v1.ObjectMeta"
},
"spec": {
"$ref": "v1.SubjectAccessReviewSpec",
"description": "Spec holds information about the request being evaluated. spec.namespace must be equal to the namespace you made the request against. If empty, it is defaulted."
},
"status": {
"$ref": "v1.SubjectAccessReviewStatus",
"description": "Status is filled in by the server and indicates whether the request is allowed or not"
}
}
},
"v1.ObjectMeta": {
"id": "v1.ObjectMeta",
"description": "ObjectMeta is metadata that all persisted resources must have, which includes all objects users must create.",
"properties": {
"name": {
"type": "string",
"description": "Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names"
},
"generateName": {
"type": "string",
"description": "GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.\n\nIf this field is specified and the generated name exists, the server will NOT return a 409 - instead, it will either return 201 Created or 500 with Reason ServerTimeout indicating a unique name could not be found in the time allotted, and the client should retry (optionally after the time indicated in the Retry-After header).\n\nApplied only if Name is not specified. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#idempotency"
},
"namespace": {
"type": "string",
"description": "Namespace defines the space within each name must be unique. An empty namespace is equivalent to the \"default\" namespace, but \"default\" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.\n\nMust be a DNS_LABEL. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/namespaces"
},
"selfLink": {
"type": "string",
"description": "SelfLink is a URL representing this object. Populated by the system. Read-only."
},
"uid": {
"type": "string",
"description": "UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations.\n\nPopulated by the system. Read-only. More info: http://kubernetes.io/docs/user-guide/identifiers#uids"
},
"resourceVersion": {
"type": "string",
"description": "An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources.\n\nPopulated by the system. Read-only. Value must be treated as opaque by clients and . More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#concurrency-control-and-consistency"
},
"generation": {
"type": "integer",
"format": "int64",
"description": "A sequence number representing a specific generation of the desired state. Populated by the system. Read-only."
},
"creationTimestamp": {
"type": "string",
"description": "CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC.\n\nPopulated by the system. Read-only. Null for lists. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata"
},
"deletionTimestamp": {
"type": "string",
"description": "DeletionTimestamp is RFC 3339 date and time at which this resource will be deleted. This field is set by the server when a graceful deletion is requested by the user, and is not directly settable by a client. The resource is expected to be deleted (no longer visible from resource lists, and not reachable by name) after the time in this field. Once set, this value may not be unset or be set further into the future, although it may be shortened or the resource may be deleted prior to this time. For example, a user may request that a pod is deleted in 30 seconds. The Kubelet will react by sending a graceful termination signal to the containers in the pod. After that 30 seconds, the Kubelet will send a hard termination signal (SIGKILL) to the container and after cleanup, remove the pod from the API. In the presence of network partitions, this object may still exist after this timestamp, until an administrator or automated process can determine the resource is fully terminated. If not set, graceful deletion of the object has not been requested.\n\nPopulated by the system when a graceful deletion is requested. Read-only. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata"
},
"deletionGracePeriodSeconds": {
"type": "integer",
"format": "int64",
"description": "Number of seconds allowed for this object to gracefully terminate before it will be removed from the system. Only set when deletionTimestamp is also set. May only be shortened. Read-only."
},
"labels": {
"type": "object",
"description": "Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels"
},
"annotations": {
"type": "object",
"description": "Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: http://kubernetes.io/docs/user-guide/annotations"
},
"ownerReferences": {
"type": "array",
"items": {
"$ref": "v1.OwnerReference"
},
"description": "List of objects depended by this object. If ALL objects in the list have been deleted, this object will be garbage collected. If this object is managed by a controller, then an entry in this list will point to this controller, with the controller field set to true. There cannot be more than one managing controller."
},
"finalizers": {
"type": "array",
"items": {
"type": "string"
},
"description": "Must be empty before the object is deleted from the registry. Each entry is an identifier for the responsible component that will remove the entry from the list. If the deletionTimestamp of the object is non-nil, entries in this list can only be removed."
},
"clusterName": {
"type": "string",
"description": "The name of the cluster which the object belongs to. This is used to distinguish resources with same name and namespace in different clusters. This field is not set anywhere right now and apiserver is going to ignore it if set in create or update request."
}
}
},
"v1.OwnerReference": {
"id": "v1.OwnerReference",
"description": "OwnerReference contains enough information to let you identify an owning object. Currently, an owning object must be in the same namespace, so there is no namespace field.",
"required": [
"apiVersion",
"kind",
"name",
"uid"
],
"properties": {
"apiVersion": {
"type": "string",
"description": "API version of the referent."
},
"kind": {
"type": "string",
"description": "Kind of the referent. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds"
},
"name": {
"type": "string",
"description": "Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names"
},
"uid": {
"type": "string",
"description": "UID of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#uids"
},
"controller": {
"type": "boolean",
"description": "If true, this reference points to the managing controller."
},
"blockOwnerDeletion": {
"type": "boolean",
"description": "If true, AND if the owner has the \"foregroundDeletion\" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. Defaults to false. To set this field, a user needs \"delete\" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned."
}
}
},
"v1.SubjectAccessReviewSpec": {
"id": "v1.SubjectAccessReviewSpec",
"description": "SubjectAccessReviewSpec is a description of the access request. Exactly one of ResourceAuthorizationAttributes and NonResourceAuthorizationAttributes must be set",
"properties": {
"resourceAttributes": {
"$ref": "v1.ResourceAttributes",
"description": "ResourceAuthorizationAttributes describes information for a resource access request"
},
"nonResourceAttributes": {
"$ref": "v1.NonResourceAttributes",
"description": "NonResourceAttributes describes information for a non-resource access request"
},
"user": {
"type": "string",
"description": "User is the user you're testing for. If you specify \"User\" but not \"Groups\", then is it interpreted as \"What if User were not a member of any groups"
},
"groups": {
"type": "array",
"items": {
"type": "string"
},
"description": "Groups is the groups you're testing for."
},
"extra": {
"type": "object",
"description": "Extra corresponds to the user.Info.GetExtra() method from the authenticator. Since that is input to the authorizer it needs a reflection here."
}
}
},
"v1.ResourceAttributes": {
"id": "v1.ResourceAttributes",
"description": "ResourceAttributes includes the authorization attributes available for resource requests to the Authorizer interface",
"properties": {
"namespace": {
"type": "string",
"description": "Namespace is the namespace of the action being requested. Currently, there is no distinction between no namespace and all namespaces \"\" (empty) is defaulted for LocalSubjectAccessReviews \"\" (empty) is empty for cluster-scoped resources \"\" (empty) means \"all\" for namespace scoped resources from a SubjectAccessReview or SelfSubjectAccessReview"
},
"verb": {
"type": "string",
"description": "Verb is a kubernetes resource API verb, like: get, list, watch, create, update, delete, proxy. \"*\" means all."
},
"group": {
"type": "string",
"description": "Group is the API Group of the Resource. \"*\" means all."
},
"version": {
"type": "string",
"description": "Version is the API Version of the Resource. \"*\" means all."
},
"resource": {
"type": "string",
"description": "Resource is one of the existing resource types. \"*\" means all."
},
"subresource": {
"type": "string",
"description": "Subresource is one of the existing resource types. \"\" means none."
},
"name": {
"type": "string",
"description": "Name is the name of the resource being requested for a \"get\" or deleted for a \"delete\". \"\" (empty) means all."
}
}
},
"v1.NonResourceAttributes": {
"id": "v1.NonResourceAttributes",
"description": "NonResourceAttributes includes the authorization attributes available for non-resource requests to the Authorizer interface",
"properties": {
"path": {
"type": "string",
"description": "Path is the URL path of the request"
},
"verb": {
"type": "string",
"description": "Verb is the standard HTTP verb"
}
}
},
"v1.SubjectAccessReviewStatus": {
"id": "v1.SubjectAccessReviewStatus",
"description": "SubjectAccessReviewStatus",
"required": [
"allowed"
],
"properties": {
"allowed": {
"type": "boolean",
"description": "Allowed is required. True if the action would be allowed, false otherwise."
},
"reason": {
"type": "string",
"description": "Reason is optional. It indicates why a request was allowed or denied."
},
"evaluationError": {
"type": "string",
"description": "EvaluationError is an indication that some error occurred during the authorization check. It is entirely possible to get an error and be able to continue determine authorization status in spite of it. For instance, RBAC can be missing a role, but enough roles are still present and bound to reason about the request."
}
}
},
"v1.SelfSubjectAccessReview": {
"id": "v1.SelfSubjectAccessReview",
"description": "SelfSubjectAccessReview checks whether or the current user can perform an action. Not filling in a spec.namespace means \"in all namespaces\". Self is a special case, because users should always be able to check whether they can perform an action",
"required": [
"spec"
],
"properties": {
"kind": {
"type": "string",
"description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds"
},
"apiVersion": {
"type": "string",
"description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#resources"
},
"metadata": {
"$ref": "v1.ObjectMeta"
},
"spec": {
"$ref": "v1.SelfSubjectAccessReviewSpec",
"description": "Spec holds information about the request being evaluated. user and groups must be empty"
},
"status": {
"$ref": "v1.SubjectAccessReviewStatus",
"description": "Status is filled in by the server and indicates whether the request is allowed or not"
}
}
},
"v1.SelfSubjectAccessReviewSpec": {
"id": "v1.SelfSubjectAccessReviewSpec",
"description": "SelfSubjectAccessReviewSpec is a description of the access request. Exactly one of ResourceAuthorizationAttributes and NonResourceAuthorizationAttributes must be set",
"properties": {
"resourceAttributes": {
"$ref": "v1.ResourceAttributes",
"description": "ResourceAuthorizationAttributes describes information for a resource access request"
},
"nonResourceAttributes": {
"$ref": "v1.NonResourceAttributes",
"description": "NonResourceAttributes describes information for a non-resource access request"
}
}
},
"v1.SubjectAccessReview": {
"id": "v1.SubjectAccessReview",
"description": "SubjectAccessReview checks whether or not a user or group can perform an action.",
"required": [
"spec"
],
"properties": {
"kind": {
"type": "string",
"description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds"
},
"apiVersion": {
"type": "string",
"description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#resources"
},
"metadata": {
"$ref": "v1.ObjectMeta"
},
"spec": {
"$ref": "v1.SubjectAccessReviewSpec",
"description": "Spec holds information about the request being evaluated"
},
"status": {
"$ref": "v1.SubjectAccessReviewStatus",
"description": "Status is filled in by the server and indicates whether the request is allowed or not"
}
}
},
"v1.APIResourceList": {
"id": "v1.APIResourceList",
"description": "APIResourceList is a list of APIResource, it is used to expose the name of the resources supported in a specific group and version, and if the resource is namespaced.",
"required": [
"groupVersion",
"resources"
],
"properties": {
"kind": {
"type": "string",
"description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds"
},
"apiVersion": {
"type": "string",
"description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#resources"
},
"groupVersion": {
"type": "string",
"description": "groupVersion is the group and version this APIResourceList is for."
},
"resources": {
"type": "array",
"items": {
"$ref": "v1.APIResource"
},
"description": "resources contains the name of the resources and if they are namespaced."
}
}
},
"v1.APIResource": {
"id": "v1.APIResource",
"description": "APIResource specifies the name of a resource and whether it is namespaced.",
"required": [
"name",
"namespaced",
"kind",
"verbs"
],
"properties": {
"name": {
"type": "string",
"description": "name is the name of the resource."
},
"namespaced": {
"type": "boolean",
"description": "namespaced indicates if a resource is namespaced or not."
},
"kind": {
"type": "string",
"description": "kind is the kind for the resource (e.g. 'Foo' is the kind for a resource 'foo')"
},
"verbs": {
"type": "array",
"items": {
"type": "string"
},
"description": "verbs is a list of supported kube verbs (this includes get, list, watch, create, update, patch, delete, deletecollection, and proxy)"
},
"shortNames": {
"type": "array",
"items": {
"type": "string"
},
"description": "shortNames is a list of suggested short names of the resource."
}
}
}
}
}

View file

@ -307,6 +307,10 @@
"controller": {
"type": "boolean",
"description": "If true, this reference points to the managing controller."
},
"blockOwnerDeletion": {
"type": "boolean",
"description": "If true, AND if the owner has the \"foregroundDeletion\" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. Defaults to false. To set this field, a user needs \"delete\" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned."
}
}
},
@ -535,6 +539,13 @@
"type": "string"
},
"description": "verbs is a list of supported kube verbs (this includes get, list, watch, create, update, patch, delete, deletecollection, and proxy)"
},
"shortNames": {
"type": "array",
"items": {
"type": "string"
},
"description": "shortNames is a list of suggested short names of the resource."
}
}
}

View file

@ -521,7 +521,15 @@
"type": "boolean",
"paramType": "query",
"name": "orphanDependents",
"description": "Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list.",
"description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.",
"required": false,
"allowMultiple": false
},
{
"type": "string",
"paramType": "query",
"name": "propagationPolicy",
"description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy.",
"required": false,
"allowMultiple": false
},
@ -1174,6 +1182,10 @@
"controller": {
"type": "boolean",
"description": "If true, this reference points to the managing controller."
},
"blockOwnerDeletion": {
"type": "boolean",
"description": "If true, AND if the owner has the \"foregroundDeletion\" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. Defaults to false. To set this field, a user needs \"delete\" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned."
}
}
},
@ -1392,7 +1404,11 @@
},
"orphanDependents": {
"type": "boolean",
"description": "Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list."
"description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both."
},
"propagationPolicy": {
"$ref": "v1.DeletionPropagation",
"description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy."
}
}
},
@ -1410,6 +1426,10 @@
"id": "types.UID",
"properties": {}
},
"v1.DeletionPropagation": {
"id": "v1.DeletionPropagation",
"properties": {}
},
"v1.APIResourceList": {
"id": "v1.APIResourceList",
"description": "APIResourceList is a list of APIResource, it is used to expose the name of the resources supported in a specific group and version, and if the resource is namespaced.",
@ -1467,6 +1487,13 @@
"type": "string"
},
"description": "verbs is a list of supported kube verbs (this includes get, list, watch, create, update, patch, delete, deletecollection, and proxy)"
},
"shortNames": {
"type": "array",
"items": {
"type": "string"
},
"description": "shortNames is a list of suggested short names of the resource."
}
}
}

View file

@ -0,0 +1,104 @@
{
"swaggerVersion": "1.2",
"apiVersion": "autoscaling/v2alpha1",
"basePath": "https://10.10.10.10:6443",
"resourcePath": "/apis/autoscaling/v2alpha1",
"info": {
"title": "",
"description": ""
},
"apis": [
{
"path": "/apis/autoscaling/v2alpha1",
"description": "API at /apis/autoscaling/v2alpha1",
"operations": [
{
"type": "v1.APIResourceList",
"method": "GET",
"summary": "get available resources",
"nickname": "getAPIResources",
"parameters": [],
"produces": [
"application/json",
"application/yaml",
"application/vnd.kubernetes.protobuf"
],
"consumes": [
"application/json",
"application/yaml",
"application/vnd.kubernetes.protobuf"
]
}
]
}
],
"models": {
"v1.APIResourceList": {
"id": "v1.APIResourceList",
"description": "APIResourceList is a list of APIResource, it is used to expose the name of the resources supported in a specific group and version, and if the resource is namespaced.",
"required": [
"groupVersion",
"resources"
],
"properties": {
"kind": {
"type": "string",
"description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds"
},
"apiVersion": {
"type": "string",
"description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#resources"
},
"groupVersion": {
"type": "string",
"description": "groupVersion is the group and version this APIResourceList is for."
},
"resources": {
"type": "array",
"items": {
"$ref": "v1.APIResource"
},
"description": "resources contains the name of the resources and if they are namespaced."
}
}
},
"v1.APIResource": {
"id": "v1.APIResource",
"description": "APIResource specifies the name of a resource and whether it is namespaced.",
"required": [
"name",
"namespaced",
"kind",
"verbs"
],
"properties": {
"name": {
"type": "string",
"description": "name is the name of the resource."
},
"namespaced": {
"type": "boolean",
"description": "namespaced indicates if a resource is namespaced or not."
},
"kind": {
"type": "string",
"description": "kind is the kind for the resource (e.g. 'Foo' is the kind for a resource 'foo')"
},
"verbs": {
"type": "array",
"items": {
"type": "string"
},
"description": "verbs is a list of supported kube verbs (this includes get, list, watch, create, update, patch, delete, deletecollection, and proxy)"
},
"shortNames": {
"type": "array",
"items": {
"type": "string"
},
"description": "shortNames is a list of suggested short names of the resource."
}
}
}
}
}

View file

@ -521,7 +521,15 @@
"type": "boolean",
"paramType": "query",
"name": "orphanDependents",
"description": "Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list.",
"description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.",
"required": false,
"allowMultiple": false
},
{
"type": "string",
"paramType": "query",
"name": "propagationPolicy",
"description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy.",
"required": false,
"allowMultiple": false
},
@ -1174,6 +1182,10 @@
"controller": {
"type": "boolean",
"description": "If true, this reference points to the managing controller."
},
"blockOwnerDeletion": {
"type": "boolean",
"description": "If true, AND if the owner has the \"foregroundDeletion\" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. Defaults to false. To set this field, a user needs \"delete\" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned."
}
}
},
@ -1283,6 +1295,13 @@
},
"description": "List of volumes that can be mounted by containers belonging to the pod. More info: http://kubernetes.io/docs/user-guide/volumes"
},
"initContainers": {
"type": "array",
"items": {
"$ref": "v1.Container"
},
"description": "List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, or Liveness probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/containers"
},
"containers": {
"type": "array",
"items": {
@ -1306,7 +1325,7 @@
},
"dnsPolicy": {
"type": "string",
"description": "Set DNS policy for containers within the pod. One of 'ClusterFirst' or 'Default'. Defaults to \"ClusterFirst\"."
"description": "Set DNS policy for containers within the pod. One of 'ClusterFirstWithHostNet', 'ClusterFirst' or 'Default'. Defaults to \"ClusterFirst\". To have DNS options set along with hostNetwork, you have to specify DNS policy explicitly to 'ClusterFirstWithHostNet'."
},
"nodeSelector": {
"type": "object",
@ -1320,6 +1339,10 @@
"type": "string",
"description": "DeprecatedServiceAccount is a depreciated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead."
},
"automountServiceAccountToken": {
"type": "boolean",
"description": "AutomountServiceAccountToken indicates whether a service account token should be automatically mounted."
},
"nodeName": {
"type": "string",
"description": "NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements."
@ -1359,9 +1382,16 @@
"$ref": "v1.Affinity",
"description": "If specified, the pod's scheduling constraints"
},
"schedulername": {
"schedulerName": {
"type": "string",
"description": "If specified, the pod will be dispatched by specified scheduler. If not specified, the pod will be dispatched by default scheduler."
},
"tolerations": {
"type": "array",
"items": {
"$ref": "v1.Toleration"
},
"description": "If specified, the pod's tolerations."
}
}
},
@ -1467,6 +1497,18 @@
"photonPersistentDisk": {
"$ref": "v1.PhotonPersistentDiskVolumeSource",
"description": "PhotonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine"
},
"projected": {
"$ref": "v1.ProjectedVolumeSource",
"description": "Items for all in one resources secrets, configmaps, and downward API"
},
"portworxVolume": {
"$ref": "v1.PortworxVolumeSource",
"description": "PortworxVolume represents a portworx volume attached and mounted on kubelets host machine"
},
"scaleIO": {
"$ref": "v1.ScaleIOVolumeSource",
"description": "ScaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes."
}
}
},
@ -1670,6 +1712,13 @@
"readOnly": {
"type": "boolean",
"description": "ReadOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false."
},
"portals": {
"type": "array",
"items": {
"type": "string"
},
"description": "iSCSI target portal List. The portal is either an IP or ip_addr:port if the port is other than default (typically TCP ports 860 and 3260)."
}
}
},
@ -2125,6 +2174,172 @@
}
}
},
"v1.ProjectedVolumeSource": {
"id": "v1.ProjectedVolumeSource",
"description": "Represents a projected volume source",
"required": [
"sources"
],
"properties": {
"sources": {
"type": "array",
"items": {
"$ref": "v1.VolumeProjection"
},
"description": "list of volume projections"
},
"defaultMode": {
"type": "integer",
"format": "int32",
"description": "Mode bits to use on created files by default. Must be a value between 0 and 0777. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set."
}
}
},
"v1.VolumeProjection": {
"id": "v1.VolumeProjection",
"description": "Projection that may be projected along with other supported volume types",
"properties": {
"secret": {
"$ref": "v1.SecretProjection",
"description": "information about the secret data to project"
},
"downwardAPI": {
"$ref": "v1.DownwardAPIProjection",
"description": "information about the downwardAPI data to project"
},
"configMap": {
"$ref": "v1.ConfigMapProjection",
"description": "information about the configMap data to project"
}
}
},
"v1.SecretProjection": {
"id": "v1.SecretProjection",
"description": "Adapts a secret into a projected volume.\n\nThe contents of the target Secret's Data field will be presented in a projected volume as files using the keys in the Data field as the file names. Note that this is identical to a secret volume source without the default mode.",
"properties": {
"name": {
"type": "string",
"description": "Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names"
},
"items": {
"type": "array",
"items": {
"$ref": "v1.KeyToPath"
},
"description": "If unspecified, each key-value pair in the Data field of the referenced Secret will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the Secret, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'."
},
"optional": {
"type": "boolean",
"description": "Specify whether the Secret or its key must be defined"
}
}
},
"v1.DownwardAPIProjection": {
"id": "v1.DownwardAPIProjection",
"description": "Represents downward API info for projecting into a projected volume. Note that this is identical to a downwardAPI volume source without the default mode.",
"properties": {
"items": {
"type": "array",
"items": {
"$ref": "v1.DownwardAPIVolumeFile"
},
"description": "Items is a list of DownwardAPIVolume file"
}
}
},
"v1.ConfigMapProjection": {
"id": "v1.ConfigMapProjection",
"description": "Adapts a ConfigMap into a projected volume.\n\nThe contents of the target ConfigMap's Data field will be presented in a projected volume as files using the keys in the Data field as the file names, unless the items element is populated with specific mappings of keys to paths. Note that this is identical to a configmap volume source without the default mode.",
"properties": {
"name": {
"type": "string",
"description": "Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names"
},
"items": {
"type": "array",
"items": {
"$ref": "v1.KeyToPath"
},
"description": "If unspecified, each key-value pair in the Data field of the referenced ConfigMap will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the ConfigMap, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'."
},
"optional": {
"type": "boolean",
"description": "Specify whether the ConfigMap or it's keys must be defined"
}
}
},
"v1.PortworxVolumeSource": {
"id": "v1.PortworxVolumeSource",
"description": "PortworxVolumeSource represents a Portworx volume resource.",
"required": [
"volumeID"
],
"properties": {
"volumeID": {
"type": "string",
"description": "VolumeID uniquely identifies a Portworx volume"
},
"fsType": {
"type": "string",
"description": "FSType represents the filesystem type to mount Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\". Implicitly inferred to be \"ext4\" if unspecified."
},
"readOnly": {
"type": "boolean",
"description": "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts."
}
}
},
"v1.ScaleIOVolumeSource": {
"id": "v1.ScaleIOVolumeSource",
"description": "ScaleIOVolumeSource represents a persistent ScaleIO volume",
"required": [
"gateway",
"system",
"secretRef"
],
"properties": {
"gateway": {
"type": "string",
"description": "The host address of the ScaleIO API Gateway."
},
"system": {
"type": "string",
"description": "The name of the storage system as configured in ScaleIO."
},
"secretRef": {
"$ref": "v1.LocalObjectReference",
"description": "SecretRef references to the secret for ScaleIO user and other sensitive information. If this is not provided, Login operation will fail."
},
"sslEnabled": {
"type": "boolean",
"description": "Flag to enable/disable SSL communication with Gateway, default false"
},
"protectionDomain": {
"type": "string",
"description": "The name of the Protection Domain for the configured storage (defaults to \"default\")."
},
"storagePool": {
"type": "string",
"description": "The Storage Pool associated with the protection domain (defaults to \"default\")."
},
"storageMode": {
"type": "string",
"description": "Indicates whether the storage for a volume should be thick or thin (defaults to \"thin\")."
},
"volumeName": {
"type": "string",
"description": "The name of a volume already created in the ScaleIO system that is associated with this volume source."
},
"fsType": {
"type": "string",
"description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified."
},
"readOnly": {
"type": "boolean",
"description": "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts."
}
}
},
"v1.Container": {
"id": "v1.Container",
"description": "A single application container that you want to run within a pod.",
@ -2170,7 +2385,7 @@
"items": {
"$ref": "v1.EnvFromSource"
},
"description": "List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. An invalid key will prevent the container from starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated."
"description": "List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated."
},
"env": {
"type": "array",
@ -2827,9 +3042,6 @@
"v1.PodAffinityTerm": {
"id": "v1.PodAffinityTerm",
"description": "Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key \u003ctopologyKey\u003e tches that of any node on which a pod of the set of pods is running",
"required": [
"namespaces"
],
"properties": {
"labelSelector": {
"$ref": "v1.LabelSelector",
@ -2840,7 +3052,7 @@
"items": {
"type": "string"
},
"description": "namespaces specifies which namespaces the labelSelector applies to (matches against); nil list means \"this pod's namespace,\" empty list means \"all namespaces\" The json tag here is not \"omitempty\" since we need to distinguish nil and empty. See https://golang.org/pkg/encoding/json/#Marshal for more details."
"description": "namespaces specifies which namespaces the labelSelector applies to (matches against); null or empty list means \"this pod's namespace\""
},
"topologyKey": {
"type": "string",
@ -2887,6 +3099,33 @@
}
}
},
"v1.Toleration": {
"id": "v1.Toleration",
"description": "The pod this Toleration is attached to tolerates any taint that matches the triple \u003ckey,value,effect\u003e using the matching operator \u003coperator\u003e.",
"properties": {
"key": {
"type": "string",
"description": "Key is the taint key that the toleration applies to. Empty means match all taint keys. If the key is empty, operator must be Exists; this combination means to match all values and all keys."
},
"operator": {
"type": "string",
"description": "Operator represents a key's relationship to the value. Valid operators are Exists and Equal. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category."
},
"value": {
"type": "string",
"description": "Value is the taint value the toleration matches to. If the operator is Exists, the value should be empty, otherwise just a regular string."
},
"effect": {
"type": "string",
"description": "Effect indicates the taint effect to match. Empty means match all taint effects. When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute."
},
"tolerationSeconds": {
"type": "integer",
"format": "int64",
"description": "TolerationSeconds represents the period of time the toleration (which must be of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, it is not set, which means tolerate the taint forever (do not evict). Zero and negative values will be treated as 0 (evict immediately) by the system."
}
}
},
"v1.JobStatus": {
"id": "v1.JobStatus",
"description": "JobStatus represents the current state of a Job.",
@ -3087,7 +3326,11 @@
},
"orphanDependents": {
"type": "boolean",
"description": "Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list."
"description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both."
},
"propagationPolicy": {
"$ref": "v1.DeletionPropagation",
"description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy."
}
}
},
@ -3105,6 +3348,10 @@
"id": "types.UID",
"properties": {}
},
"v1.DeletionPropagation": {
"id": "v1.DeletionPropagation",
"properties": {}
},
"v1.APIResourceList": {
"id": "v1.APIResourceList",
"description": "APIResourceList is a list of APIResource, it is used to expose the name of the resources supported in a specific group and version, and if the resource is namespaced.",
@ -3162,6 +3409,13 @@
"type": "string"
},
"description": "verbs is a list of supported kube verbs (this includes get, list, watch, create, update, patch, delete, deletecollection, and proxy)"
},
"shortNames": {
"type": "array",
"items": {
"type": "string"
},
"description": "shortNames is a list of suggested short names of the resource."
}
}
}

View file

@ -90,6 +90,13 @@
"type": "string"
},
"description": "verbs is a list of supported kube verbs (this includes get, list, watch, create, update, patch, delete, deletecollection, and proxy)"
},
"shortNames": {
"type": "array",
"items": {
"type": "string"
},
"description": "shortNames is a list of suggested short names of the resource."
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -465,7 +465,15 @@
"type": "boolean",
"paramType": "query",
"name": "orphanDependents",
"description": "Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list.",
"description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.",
"required": false,
"allowMultiple": false
},
{
"type": "string",
"paramType": "query",
"name": "propagationPolicy",
"description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy.",
"required": false,
"allowMultiple": false
},
@ -881,12 +889,16 @@
"controller": {
"type": "boolean",
"description": "If true, this reference points to the managing controller."
},
"blockOwnerDeletion": {
"type": "boolean",
"description": "If true, AND if the owner has the \"foregroundDeletion\" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. Defaults to false. To set this field, a user needs \"delete\" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned."
}
}
},
"v1beta1.CertificateSigningRequestSpec": {
"id": "v1beta1.CertificateSigningRequestSpec",
"description": "This information is immutable after the request is created. Only the Request and ExtraInfo fields can be set on creation, other fields are derived by Kubernetes and cannot be modified by users.",
"description": "This information is immutable after the request is created. Only the Request and Usages fields can be set on creation, other fields are derived by Kubernetes and cannot be modified by users.",
"required": [
"request"
],
@ -904,16 +916,22 @@
},
"username": {
"type": "string",
"description": "Information about the requesting user (if relevant) See user.Info interface for details"
"description": "Information about the requesting user. See user.Info interface for details."
},
"uid": {
"type": "string"
"type": "string",
"description": "UID information about the requesting user. See user.Info interface for details."
},
"groups": {
"type": "array",
"items": {
"type": "string"
}
},
"description": "Group information about the requesting user. See user.Info interface for details."
},
"extra": {
"type": "object",
"description": "Extra information about the requesting user. See user.Info interface for details."
}
}
},
@ -1091,7 +1109,11 @@
},
"orphanDependents": {
"type": "boolean",
"description": "Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list."
"description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both."
},
"propagationPolicy": {
"$ref": "v1.DeletionPropagation",
"description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy."
}
}
},
@ -1109,6 +1131,10 @@
"id": "types.UID",
"properties": {}
},
"v1.DeletionPropagation": {
"id": "v1.DeletionPropagation",
"properties": {}
},
"v1.APIResourceList": {
"id": "v1.APIResourceList",
"description": "APIResourceList is a list of APIResource, it is used to expose the name of the resources supported in a specific group and version, and if the resource is namespaced.",
@ -1166,6 +1192,13 @@
"type": "string"
},
"description": "verbs is a list of supported kube verbs (this includes get, list, watch, create, update, patch, delete, deletecollection, and proxy)"
},
"shortNames": {
"type": "array",
"items": {
"type": "string"
},
"description": "shortNames is a list of suggested short names of the resource."
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -521,7 +521,15 @@
"type": "boolean",
"paramType": "query",
"name": "orphanDependents",
"description": "Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list.",
"description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.",
"required": false,
"allowMultiple": false
},
{
"type": "string",
"paramType": "query",
"name": "propagationPolicy",
"description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy.",
"required": false,
"allowMultiple": false
},
@ -1171,6 +1179,10 @@
"controller": {
"type": "boolean",
"description": "If true, this reference points to the managing controller."
},
"blockOwnerDeletion": {
"type": "boolean",
"description": "If true, AND if the owner has the \"foregroundDeletion\" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. Defaults to false. To set this field, a user needs \"delete\" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned."
}
}
},
@ -1402,7 +1414,11 @@
},
"orphanDependents": {
"type": "boolean",
"description": "Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list."
"description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both."
},
"propagationPolicy": {
"$ref": "v1.DeletionPropagation",
"description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy."
}
}
},
@ -1420,6 +1436,10 @@
"id": "types.UID",
"properties": {}
},
"v1.DeletionPropagation": {
"id": "v1.DeletionPropagation",
"properties": {}
},
"v1.APIResourceList": {
"id": "v1.APIResourceList",
"description": "APIResourceList is a list of APIResource, it is used to expose the name of the resources supported in a specific group and version, and if the resource is namespaced.",
@ -1477,6 +1497,13 @@
"type": "string"
},
"description": "verbs is a list of supported kube verbs (this includes get, list, watch, create, update, patch, delete, deletecollection, and proxy)"
},
"shortNames": {
"type": "array",
"items": {
"type": "string"
},
"description": "shortNames is a list of suggested short names of the resource."
}
}
}

View file

@ -449,7 +449,15 @@
"type": "boolean",
"paramType": "query",
"name": "orphanDependents",
"description": "Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list.",
"description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.",
"required": false,
"allowMultiple": false
},
{
"type": "string",
"paramType": "query",
"name": "propagationPolicy",
"description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy.",
"required": false,
"allowMultiple": false
},
@ -1008,7 +1016,15 @@
"type": "boolean",
"paramType": "query",
"name": "orphanDependents",
"description": "Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list.",
"description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.",
"required": false,
"allowMultiple": false
},
{
"type": "string",
"paramType": "query",
"name": "propagationPolicy",
"description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy.",
"required": false,
"allowMultiple": false
},
@ -1623,7 +1639,15 @@
"type": "boolean",
"paramType": "query",
"name": "orphanDependents",
"description": "Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list.",
"description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.",
"required": false,
"allowMultiple": false
},
{
"type": "string",
"paramType": "query",
"name": "propagationPolicy",
"description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy.",
"required": false,
"allowMultiple": false
},
@ -2412,7 +2436,15 @@
"type": "boolean",
"paramType": "query",
"name": "orphanDependents",
"description": "Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list.",
"description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.",
"required": false,
"allowMultiple": false
},
{
"type": "string",
"paramType": "query",
"name": "propagationPolicy",
"description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy.",
"required": false,
"allowMultiple": false
},
@ -2907,6 +2939,10 @@
"controller": {
"type": "boolean",
"description": "If true, this reference points to the managing controller."
},
"blockOwnerDeletion": {
"type": "boolean",
"description": "If true, AND if the owner has the \"foregroundDeletion\" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. Defaults to false. To set this field, a user needs \"delete\" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned."
}
}
},
@ -2924,7 +2960,7 @@
},
"apiVersion": {
"type": "string",
"description": "APIVersion holds the API group and version of the referenced object."
"description": "APIVersion holds the API group and version of the referenced subject. Defaults to \"v1\" for ServiceAccount subjects. Defaults to \"rbac.authorization.k8s.io/v1alpha1\" for User and Group subjects."
},
"name": {
"type": "string",
@ -3089,7 +3125,11 @@
},
"orphanDependents": {
"type": "boolean",
"description": "Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list."
"description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both."
},
"propagationPolicy": {
"$ref": "v1.DeletionPropagation",
"description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy."
}
}
},
@ -3107,6 +3147,10 @@
"id": "types.UID",
"properties": {}
},
"v1.DeletionPropagation": {
"id": "v1.DeletionPropagation",
"properties": {}
},
"v1alpha1.ClusterRoleList": {
"id": "v1alpha1.ClusterRoleList",
"description": "ClusterRoleList is a collection of ClusterRoles",
@ -3381,6 +3425,13 @@
"type": "string"
},
"description": "verbs is a list of supported kube verbs (this includes get, list, watch, create, update, patch, delete, deletecollection, and proxy)"
},
"shortNames": {
"type": "array",
"items": {
"type": "string"
},
"description": "shortNames is a list of suggested short names of the resource."
}
}
}

View file

@ -449,7 +449,15 @@
"type": "boolean",
"paramType": "query",
"name": "orphanDependents",
"description": "Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list.",
"description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.",
"required": false,
"allowMultiple": false
},
{
"type": "string",
"paramType": "query",
"name": "propagationPolicy",
"description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy.",
"required": false,
"allowMultiple": false
},
@ -1008,7 +1016,15 @@
"type": "boolean",
"paramType": "query",
"name": "orphanDependents",
"description": "Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list.",
"description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.",
"required": false,
"allowMultiple": false
},
{
"type": "string",
"paramType": "query",
"name": "propagationPolicy",
"description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy.",
"required": false,
"allowMultiple": false
},
@ -1623,7 +1639,15 @@
"type": "boolean",
"paramType": "query",
"name": "orphanDependents",
"description": "Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list.",
"description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.",
"required": false,
"allowMultiple": false
},
{
"type": "string",
"paramType": "query",
"name": "propagationPolicy",
"description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy.",
"required": false,
"allowMultiple": false
},
@ -2412,7 +2436,15 @@
"type": "boolean",
"paramType": "query",
"name": "orphanDependents",
"description": "Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list.",
"description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.",
"required": false,
"allowMultiple": false
},
{
"type": "string",
"paramType": "query",
"name": "propagationPolicy",
"description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy.",
"required": false,
"allowMultiple": false
},
@ -2907,6 +2939,10 @@
"controller": {
"type": "boolean",
"description": "If true, this reference points to the managing controller."
},
"blockOwnerDeletion": {
"type": "boolean",
"description": "If true, AND if the owner has the \"foregroundDeletion\" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. Defaults to false. To set this field, a user needs \"delete\" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned."
}
}
},
@ -2922,9 +2958,9 @@
"type": "string",
"description": "Kind of object being referenced. Values defined by this API group are \"User\", \"Group\", and \"ServiceAccount\". If the Authorizer does not recognized the kind value, the Authorizer should report an error."
},
"apiVersion": {
"apiGroup": {
"type": "string",
"description": "APIVersion holds the API group and version of the referenced object."
"description": "APIGroup holds the API group of the referenced subject. Defaults to \"\" for ServiceAccount subjects. Defaults to \"rbac.authorization.k8s.io\" for User and Group subjects."
},
"name": {
"type": "string",
@ -3089,7 +3125,11 @@
},
"orphanDependents": {
"type": "boolean",
"description": "Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list."
"description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both."
},
"propagationPolicy": {
"$ref": "v1.DeletionPropagation",
"description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy."
}
}
},
@ -3107,6 +3147,10 @@
"id": "types.UID",
"properties": {}
},
"v1.DeletionPropagation": {
"id": "v1.DeletionPropagation",
"properties": {}
},
"v1beta1.ClusterRoleList": {
"id": "v1beta1.ClusterRoleList",
"description": "ClusterRoleList is a collection of ClusterRoles",
@ -3381,6 +3425,13 @@
"type": "string"
},
"description": "verbs is a list of supported kube verbs (this includes get, list, watch, create, update, patch, delete, deletecollection, and proxy)"
},
"shortNames": {
"type": "array",
"items": {
"type": "string"
},
"description": "shortNames is a list of suggested short names of the resource."
}
}
}

View file

@ -22,12 +22,8 @@
"description": "get available API versions"
},
{
"path": "/apis/apps/v1beta1",
"description": "API at /apis/apps/v1beta1"
},
{
"path": "/apis/apps",
"description": "get information of a group"
"path": "/apis/authentication.k8s.io/v1",
"description": "API at /apis/authentication.k8s.io/v1"
},
{
"path": "/apis/authentication.k8s.io/v1beta1",
@ -37,6 +33,10 @@
"path": "/apis/authentication.k8s.io",
"description": "get information of a group"
},
{
"path": "/apis/authorization.k8s.io/v1",
"description": "API at /apis/authorization.k8s.io/v1"
},
{
"path": "/apis/authorization.k8s.io/v1beta1",
"description": "API at /apis/authorization.k8s.io/v1beta1"
@ -49,6 +49,10 @@
"path": "/apis/autoscaling/v1",
"description": "API at /apis/autoscaling/v1"
},
{
"path": "/apis/autoscaling/v2alpha1",
"description": "API at /apis/autoscaling/v2alpha1"
},
{
"path": "/apis/autoscaling",
"description": "get information of a group"
@ -101,13 +105,33 @@
"path": "/apis/rbac.authorization.k8s.io",
"description": "get information of a group"
},
{
"path": "/apis/settings.k8s.io/v1alpha1",
"description": "API at /apis/settings.k8s.io/v1alpha1"
},
{
"path": "/apis/settings.k8s.io",
"description": "get information of a group"
},
{
"path": "/apis/storage.k8s.io/v1beta1",
"description": "API at /apis/storage.k8s.io/v1beta1"
},
{
"path": "/apis/storage.k8s.io/v1",
"description": "API at /apis/storage.k8s.io/v1"
},
{
"path": "/apis/storage.k8s.io",
"description": "get information of a group"
},
{
"path": "/apis/apps/v1beta1",
"description": "API at /apis/apps/v1beta1"
},
{
"path": "/apis/apps",
"description": "get information of a group"
}
],
"apiVersion": "",

View file

@ -0,0 +1,114 @@
{
"swaggerVersion": "1.2",
"apiVersion": "",
"basePath": "https://10.10.10.10:6443",
"resourcePath": "/apis/settings.k8s.io",
"info": {
"title": "",
"description": ""
},
"apis": [
{
"path": "/apis/settings.k8s.io",
"description": "get information of a group",
"operations": [
{
"type": "v1.APIGroup",
"method": "GET",
"summary": "get information of a group",
"nickname": "getAPIGroup",
"parameters": [],
"produces": [
"application/json",
"application/yaml",
"application/vnd.kubernetes.protobuf"
],
"consumes": [
"application/json",
"application/yaml",
"application/vnd.kubernetes.protobuf"
]
}
]
}
],
"models": {
"v1.APIGroup": {
"id": "v1.APIGroup",
"description": "APIGroup contains the name, the supported versions, and the preferred version of a group.",
"required": [
"name",
"versions",
"serverAddressByClientCIDRs"
],
"properties": {
"kind": {
"type": "string",
"description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds"
},
"apiVersion": {
"type": "string",
"description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#resources"
},
"name": {
"type": "string",
"description": "name is the name of the group."
},
"versions": {
"type": "array",
"items": {
"$ref": "v1.GroupVersionForDiscovery"
},
"description": "versions are the versions supported in this group."
},
"preferredVersion": {
"$ref": "v1.GroupVersionForDiscovery",
"description": "preferredVersion is the version preferred by the API server, which probably is the storage version."
},
"serverAddressByClientCIDRs": {
"type": "array",
"items": {
"$ref": "v1.ServerAddressByClientCIDR"
},
"description": "a map of client CIDR to server address that is serving this group. This is to help clients reach servers in the most network-efficient way possible. Clients can use the appropriate server address as per the CIDR that they match. In case of multiple matches, clients should use the longest matching CIDR. The server returns only those CIDRs that it thinks that the client can match. For example: the master will return an internal IP CIDR only, if the client reaches the server using an internal IP. Server looks at X-Forwarded-For header or X-Real-Ip header or request.RemoteAddr (in that order) to get the client IP."
}
}
},
"v1.GroupVersionForDiscovery": {
"id": "v1.GroupVersionForDiscovery",
"description": "GroupVersion contains the \"group/version\" and \"version\" string of a version. It is made a struct to keep extensibility.",
"required": [
"groupVersion",
"version"
],
"properties": {
"groupVersion": {
"type": "string",
"description": "groupVersion specifies the API group and version in the form \"group/version\""
},
"version": {
"type": "string",
"description": "version specifies the version in the form of \"version\". This is to save the clients the trouble of splitting the GroupVersion."
}
}
},
"v1.ServerAddressByClientCIDR": {
"id": "v1.ServerAddressByClientCIDR",
"description": "ServerAddressByClientCIDR helps the client to determine the server address that they should use, depending on the clientCIDR that they match.",
"required": [
"clientCIDR",
"serverAddress"
],
"properties": {
"clientCIDR": {
"type": "string",
"description": "The CIDR with which clients can match their IP to figure out the server address that they should use."
},
"serverAddress": {
"type": "string",
"description": "Address of this server, suitable for a client that matches the above CIDR. This can be a hostname, hostname:port, IP or IP:port."
}
}
}
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -465,7 +465,15 @@
"type": "boolean",
"paramType": "query",
"name": "orphanDependents",
"description": "Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list.",
"description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.",
"required": false,
"allowMultiple": false
},
{
"type": "string",
"paramType": "query",
"name": "propagationPolicy",
"description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy.",
"required": false,
"allowMultiple": false
},
@ -782,6 +790,10 @@
"controller": {
"type": "boolean",
"description": "If true, this reference points to the managing controller."
},
"blockOwnerDeletion": {
"type": "boolean",
"description": "If true, AND if the owner has the \"foregroundDeletion\" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. Defaults to false. To set this field, a user needs \"delete\" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned."
}
}
},
@ -915,7 +927,11 @@
},
"orphanDependents": {
"type": "boolean",
"description": "Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list."
"description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both."
},
"propagationPolicy": {
"$ref": "v1.DeletionPropagation",
"description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy."
}
}
},
@ -933,6 +949,10 @@
"id": "types.UID",
"properties": {}
},
"v1.DeletionPropagation": {
"id": "v1.DeletionPropagation",
"properties": {}
},
"v1.APIResourceList": {
"id": "v1.APIResourceList",
"description": "APIResourceList is a list of APIResource, it is used to expose the name of the resources supported in a specific group and version, and if the resource is namespaced.",
@ -990,6 +1010,13 @@
"type": "string"
},
"description": "verbs is a list of supported kube verbs (this includes get, list, watch, create, update, patch, delete, deletecollection, and proxy)"
},
"shortNames": {
"type": "array",
"items": {
"type": "string"
},
"description": "shortNames is a list of suggested short names of the resource."
}
}
}

View file

@ -698,7 +698,15 @@
"type": "boolean",
"paramType": "query",
"name": "orphanDependents",
"description": "Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list.",
"description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.",
"required": false,
"allowMultiple": false
},
{
"type": "string",
"paramType": "query",
"name": "propagationPolicy",
"description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy.",
"required": false,
"allowMultiple": false
},
@ -1503,7 +1511,15 @@
"type": "boolean",
"paramType": "query",
"name": "orphanDependents",
"description": "Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list.",
"description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.",
"required": false,
"allowMultiple": false
},
{
"type": "string",
"paramType": "query",
"name": "propagationPolicy",
"description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy.",
"required": false,
"allowMultiple": false
},
@ -2308,7 +2324,15 @@
"type": "boolean",
"paramType": "query",
"name": "orphanDependents",
"description": "Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list.",
"description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.",
"required": false,
"allowMultiple": false
},
{
"type": "string",
"paramType": "query",
"name": "propagationPolicy",
"description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy.",
"required": false,
"allowMultiple": false
},
@ -3113,7 +3137,15 @@
"type": "boolean",
"paramType": "query",
"name": "orphanDependents",
"description": "Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list.",
"description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.",
"required": false,
"allowMultiple": false
},
{
"type": "string",
"paramType": "query",
"name": "propagationPolicy",
"description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy.",
"required": false,
"allowMultiple": false
},
@ -3862,7 +3894,15 @@
"type": "boolean",
"paramType": "query",
"name": "orphanDependents",
"description": "Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list.",
"description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.",
"required": false,
"allowMultiple": false
},
{
"type": "string",
"paramType": "query",
"name": "propagationPolicy",
"description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy.",
"required": false,
"allowMultiple": false
},
@ -4631,7 +4671,15 @@
"type": "boolean",
"paramType": "query",
"name": "orphanDependents",
"description": "Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list.",
"description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.",
"required": false,
"allowMultiple": false
},
{
"type": "string",
"paramType": "query",
"name": "propagationPolicy",
"description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy.",
"required": false,
"allowMultiple": false
},
@ -4843,6 +4891,36 @@
"*/*"
]
},
{
"type": "string",
"method": "PATCH",
"summary": "proxy PATCH requests to Node",
"nickname": "proxyPATCHNodeWithPath",
"parameters": [
{
"type": "string",
"paramType": "path",
"name": "name",
"description": "name of the Node",
"required": true,
"allowMultiple": false
},
{
"type": "string",
"paramType": "path",
"name": "path",
"description": "path to the resource",
"required": true,
"allowMultiple": false
}
],
"produces": [
"*/*"
],
"consumes": [
"*/*"
]
},
{
"type": "string",
"method": "DELETE",
@ -5005,6 +5083,28 @@
"*/*"
]
},
{
"type": "string",
"method": "PATCH",
"summary": "proxy PATCH requests to Node",
"nickname": "proxyPATCHNode",
"parameters": [
{
"type": "string",
"paramType": "path",
"name": "name",
"description": "name of the Node",
"required": true,
"allowMultiple": false
}
],
"produces": [
"*/*"
],
"consumes": [
"*/*"
]
},
{
"type": "string",
"method": "DELETE",
@ -6147,7 +6247,15 @@
"type": "boolean",
"paramType": "query",
"name": "orphanDependents",
"description": "Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list.",
"description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.",
"required": false,
"allowMultiple": false
},
{
"type": "string",
"paramType": "query",
"name": "propagationPolicy",
"description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy.",
"required": false,
"allowMultiple": false
},
@ -7061,7 +7169,15 @@
"type": "boolean",
"paramType": "query",
"name": "orphanDependents",
"description": "Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list.",
"description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.",
"required": false,
"allowMultiple": false
},
{
"type": "string",
"paramType": "query",
"name": "propagationPolicy",
"description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy.",
"required": false,
"allowMultiple": false
},
@ -7833,7 +7949,15 @@
"type": "boolean",
"paramType": "query",
"name": "orphanDependents",
"description": "Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list.",
"description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.",
"required": false,
"allowMultiple": false
},
{
"type": "string",
"paramType": "query",
"name": "propagationPolicy",
"description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy.",
"required": false,
"allowMultiple": false
},
@ -8085,6 +8209,44 @@
"*/*"
]
},
{
"type": "string",
"method": "PATCH",
"summary": "proxy PATCH requests to Pod",
"nickname": "proxyPATCHNamespacedPodWithPath",
"parameters": [
{
"type": "string",
"paramType": "path",
"name": "namespace",
"description": "object name and auth scope, such as for teams and projects",
"required": true,
"allowMultiple": false
},
{
"type": "string",
"paramType": "path",
"name": "name",
"description": "name of the Pod",
"required": true,
"allowMultiple": false
},
{
"type": "string",
"paramType": "path",
"name": "path",
"description": "path to the resource",
"required": true,
"allowMultiple": false
}
],
"produces": [
"*/*"
],
"consumes": [
"*/*"
]
},
{
"type": "string",
"method": "DELETE",
@ -8295,6 +8457,36 @@
"*/*"
]
},
{
"type": "string",
"method": "PATCH",
"summary": "proxy PATCH requests to Pod",
"nickname": "proxyPATCHNamespacedPod",
"parameters": [
{
"type": "string",
"paramType": "path",
"name": "namespace",
"description": "object name and auth scope, such as for teams and projects",
"required": true,
"allowMultiple": false
},
{
"type": "string",
"paramType": "path",
"name": "name",
"description": "name of the Pod",
"required": true,
"allowMultiple": false
}
],
"produces": [
"*/*"
],
"consumes": [
"*/*"
]
},
{
"type": "string",
"method": "DELETE",
@ -9095,6 +9287,14 @@
"summary": "connect GET requests to portforward of Pod",
"nickname": "connectGetNamespacedPodPortforward",
"parameters": [
{
"type": "integer",
"paramType": "query",
"name": "ports",
"description": "List of ports to forward Required when using WebSockets",
"required": false,
"allowMultiple": false
},
{
"type": "string",
"paramType": "path",
@ -9125,6 +9325,14 @@
"summary": "connect POST requests to portforward of Pod",
"nickname": "connectPostNamespacedPodPortforward",
"parameters": [
{
"type": "integer",
"paramType": "query",
"name": "ports",
"description": "List of ports to forward Required when using WebSockets",
"required": false,
"allowMultiple": false
},
{
"type": "string",
"paramType": "path",
@ -10345,7 +10553,15 @@
"type": "boolean",
"paramType": "query",
"name": "orphanDependents",
"description": "Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list.",
"description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.",
"required": false,
"allowMultiple": false
},
{
"type": "string",
"paramType": "query",
"name": "propagationPolicy",
"description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy.",
"required": false,
"allowMultiple": false
},
@ -11150,7 +11366,15 @@
"type": "boolean",
"paramType": "query",
"name": "orphanDependents",
"description": "Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list.",
"description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.",
"required": false,
"allowMultiple": false
},
{
"type": "string",
"paramType": "query",
"name": "propagationPolicy",
"description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy.",
"required": false,
"allowMultiple": false
},
@ -12285,7 +12509,15 @@
"type": "boolean",
"paramType": "query",
"name": "orphanDependents",
"description": "Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list.",
"description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.",
"required": false,
"allowMultiple": false
},
{
"type": "string",
"paramType": "query",
"name": "propagationPolicy",
"description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy.",
"required": false,
"allowMultiple": false
},
@ -13255,7 +13487,15 @@
"type": "boolean",
"paramType": "query",
"name": "orphanDependents",
"description": "Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list.",
"description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.",
"required": false,
"allowMultiple": false
},
{
"type": "string",
"paramType": "query",
"name": "propagationPolicy",
"description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy.",
"required": false,
"allowMultiple": false
},
@ -14060,7 +14300,15 @@
"type": "boolean",
"paramType": "query",
"name": "orphanDependents",
"description": "Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list.",
"description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.",
"required": false,
"allowMultiple": false
},
{
"type": "string",
"paramType": "query",
"name": "propagationPolicy",
"description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy.",
"required": false,
"allowMultiple": false
},
@ -15014,6 +15262,44 @@
"*/*"
]
},
{
"type": "string",
"method": "PATCH",
"summary": "proxy PATCH requests to Service",
"nickname": "proxyPATCHNamespacedServiceWithPath",
"parameters": [
{
"type": "string",
"paramType": "path",
"name": "namespace",
"description": "object name and auth scope, such as for teams and projects",
"required": true,
"allowMultiple": false
},
{
"type": "string",
"paramType": "path",
"name": "name",
"description": "name of the Service",
"required": true,
"allowMultiple": false
},
{
"type": "string",
"paramType": "path",
"name": "path",
"description": "path to the resource",
"required": true,
"allowMultiple": false
}
],
"produces": [
"*/*"
],
"consumes": [
"*/*"
]
},
{
"type": "string",
"method": "DELETE",
@ -15224,6 +15510,36 @@
"*/*"
]
},
{
"type": "string",
"method": "PATCH",
"summary": "proxy PATCH requests to Service",
"nickname": "proxyPATCHNamespacedService",
"parameters": [
{
"type": "string",
"paramType": "path",
"name": "namespace",
"description": "object name and auth scope, such as for teams and projects",
"required": true,
"allowMultiple": false
},
{
"type": "string",
"paramType": "path",
"name": "name",
"description": "name of the Service",
"required": true,
"allowMultiple": false
}
],
"produces": [
"*/*"
],
"consumes": [
"*/*"
]
},
{
"type": "string",
"method": "DELETE",
@ -16308,6 +16624,10 @@
"controller": {
"type": "boolean",
"description": "If true, this reference points to the managing controller."
},
"blockOwnerDeletion": {
"type": "boolean",
"description": "If true, AND if the owner has the \"foregroundDeletion\" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. Defaults to false. To set this field, a user needs \"delete\" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned."
}
}
},
@ -16618,7 +16938,11 @@
},
"orphanDependents": {
"type": "boolean",
"description": "Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list."
"description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both."
},
"propagationPolicy": {
"$ref": "v1.DeletionPropagation",
"description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy."
}
}
},
@ -16636,6 +16960,10 @@
"id": "types.UID",
"properties": {}
},
"v1.DeletionPropagation": {
"id": "v1.DeletionPropagation",
"properties": {}
},
"v1.EndpointsList": {
"id": "v1.EndpointsList",
"description": "EndpointsList is a list of endpoints.",
@ -17113,6 +17441,39 @@
"unschedulable": {
"type": "boolean",
"description": "Unschedulable controls node schedulability of new pods. By default, node is schedulable. More info: http://releases.k8s.io/HEAD/docs/admin/node.md#manual-node-administration"
},
"taints": {
"type": "array",
"items": {
"$ref": "v1.Taint"
},
"description": "If specified, the node's taints."
}
}
},
"v1.Taint": {
"id": "v1.Taint",
"description": "The node this Taint is attached to has the effect \"effect\" on any pod that that does not tolerate the Taint.",
"required": [
"key",
"effect"
],
"properties": {
"key": {
"type": "string",
"description": "Required. The taint key to be applied to a node."
},
"value": {
"type": "string",
"description": "Required. The taint value corresponding to the taint key."
},
"effect": {
"type": "string",
"description": "Required. The effect of the taint on pods that do not tolerate the taint. Valid effects are NoSchedule, PreferNoSchedule and NoExecute."
},
"timeAdded": {
"type": "string",
"description": "TimeAdded represents the time at which the taint was added. It is only written for NoExecute taints."
}
}
},
@ -17430,6 +17791,10 @@
"volumeName": {
"type": "string",
"description": "VolumeName is the binding reference to the PersistentVolume backing this claim."
},
"storageClassName": {
"type": "string",
"description": "Name of the StorageClass required by the claim. More info: http://kubernetes.io/docs/user-guide/persistent-volumes#class-1"
}
}
},
@ -17644,6 +18009,14 @@
"$ref": "v1.PhotonPersistentDiskVolumeSource",
"description": "PhotonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine"
},
"portworxVolume": {
"$ref": "v1.PortworxVolumeSource",
"description": "PortworxVolume represents a portworx volume attached and mounted on kubelets host machine"
},
"scaleIO": {
"$ref": "v1.ScaleIOVolumeSource",
"description": "ScaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes."
},
"accessModes": {
"type": "array",
"items": {
@ -17658,6 +18031,10 @@
"persistentVolumeReclaimPolicy": {
"type": "string",
"description": "What happens to a persistent volume when released from its claim. Valid options are Retain (default) and Recycle. Recycling must be supported by the volume plugin underlying this persistent volume. More info: http://kubernetes.io/docs/user-guide/persistent-volumes#recycling-policy"
},
"storageClassName": {
"type": "string",
"description": "Name of StorageClass to which this persistent volume belongs. Empty value means that this volume does not belong to any StorageClass."
}
}
},
@ -17858,6 +18235,13 @@
"readOnly": {
"type": "boolean",
"description": "ReadOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false."
},
"portals": {
"type": "array",
"items": {
"type": "string"
},
"description": "iSCSI target portal List. The portal is either an IP or ip_addr:port if the port is other than default (typically TCP ports 860 and 3260)."
}
}
},
@ -18111,6 +18495,78 @@
}
}
},
"v1.PortworxVolumeSource": {
"id": "v1.PortworxVolumeSource",
"description": "PortworxVolumeSource represents a Portworx volume resource.",
"required": [
"volumeID"
],
"properties": {
"volumeID": {
"type": "string",
"description": "VolumeID uniquely identifies a Portworx volume"
},
"fsType": {
"type": "string",
"description": "FSType represents the filesystem type to mount Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\". Implicitly inferred to be \"ext4\" if unspecified."
},
"readOnly": {
"type": "boolean",
"description": "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts."
}
}
},
"v1.ScaleIOVolumeSource": {
"id": "v1.ScaleIOVolumeSource",
"description": "ScaleIOVolumeSource represents a persistent ScaleIO volume",
"required": [
"gateway",
"system",
"secretRef"
],
"properties": {
"gateway": {
"type": "string",
"description": "The host address of the ScaleIO API Gateway."
},
"system": {
"type": "string",
"description": "The name of the storage system as configured in ScaleIO."
},
"secretRef": {
"$ref": "v1.LocalObjectReference",
"description": "SecretRef references to the secret for ScaleIO user and other sensitive information. If this is not provided, Login operation will fail."
},
"sslEnabled": {
"type": "boolean",
"description": "Flag to enable/disable SSL communication with Gateway, default false"
},
"protectionDomain": {
"type": "string",
"description": "The name of the Protection Domain for the configured storage (defaults to \"default\")."
},
"storagePool": {
"type": "string",
"description": "The Storage Pool associated with the protection domain (defaults to \"default\")."
},
"storageMode": {
"type": "string",
"description": "Indicates whether the storage for a volume should be thick or thin (defaults to \"thin\")."
},
"volumeName": {
"type": "string",
"description": "The name of a volume already created in the ScaleIO system that is associated with this volume source."
},
"fsType": {
"type": "string",
"description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified."
},
"readOnly": {
"type": "boolean",
"description": "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts."
}
}
},
"v1.PersistentVolumeStatus": {
"id": "v1.PersistentVolumeStatus",
"description": "PersistentVolumeStatus is the current status of a persistent volume.",
@ -18197,6 +18653,13 @@
},
"description": "List of volumes that can be mounted by containers belonging to the pod. More info: http://kubernetes.io/docs/user-guide/volumes"
},
"initContainers": {
"type": "array",
"items": {
"$ref": "v1.Container"
},
"description": "List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, or Liveness probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/containers"
},
"containers": {
"type": "array",
"items": {
@ -18220,7 +18683,7 @@
},
"dnsPolicy": {
"type": "string",
"description": "Set DNS policy for containers within the pod. One of 'ClusterFirst' or 'Default'. Defaults to \"ClusterFirst\"."
"description": "Set DNS policy for containers within the pod. One of 'ClusterFirstWithHostNet', 'ClusterFirst' or 'Default'. Defaults to \"ClusterFirst\". To have DNS options set along with hostNetwork, you have to specify DNS policy explicitly to 'ClusterFirstWithHostNet'."
},
"nodeSelector": {
"type": "object",
@ -18234,6 +18697,10 @@
"type": "string",
"description": "DeprecatedServiceAccount is a depreciated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead."
},
"automountServiceAccountToken": {
"type": "boolean",
"description": "AutomountServiceAccountToken indicates whether a service account token should be automatically mounted."
},
"nodeName": {
"type": "string",
"description": "NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements."
@ -18273,9 +18740,16 @@
"$ref": "v1.Affinity",
"description": "If specified, the pod's scheduling constraints"
},
"schedulername": {
"schedulerName": {
"type": "string",
"description": "If specified, the pod will be dispatched by specified scheduler. If not specified, the pod will be dispatched by default scheduler."
},
"tolerations": {
"type": "array",
"items": {
"$ref": "v1.Toleration"
},
"description": "If specified, the pod's tolerations."
}
}
},
@ -18381,6 +18855,18 @@
"photonPersistentDisk": {
"$ref": "v1.PhotonPersistentDiskVolumeSource",
"description": "PhotonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine"
},
"projected": {
"$ref": "v1.ProjectedVolumeSource",
"description": "Items for all in one resources secrets, configmaps, and downward API"
},
"portworxVolume": {
"$ref": "v1.PortworxVolumeSource",
"description": "PortworxVolume represents a portworx volume attached and mounted on kubelets host machine"
},
"scaleIO": {
"$ref": "v1.ScaleIOVolumeSource",
"description": "ScaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes."
}
}
},
@ -18589,6 +19075,100 @@
}
}
},
"v1.ProjectedVolumeSource": {
"id": "v1.ProjectedVolumeSource",
"description": "Represents a projected volume source",
"required": [
"sources"
],
"properties": {
"sources": {
"type": "array",
"items": {
"$ref": "v1.VolumeProjection"
},
"description": "list of volume projections"
},
"defaultMode": {
"type": "integer",
"format": "int32",
"description": "Mode bits to use on created files by default. Must be a value between 0 and 0777. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set."
}
}
},
"v1.VolumeProjection": {
"id": "v1.VolumeProjection",
"description": "Projection that may be projected along with other supported volume types",
"properties": {
"secret": {
"$ref": "v1.SecretProjection",
"description": "information about the secret data to project"
},
"downwardAPI": {
"$ref": "v1.DownwardAPIProjection",
"description": "information about the downwardAPI data to project"
},
"configMap": {
"$ref": "v1.ConfigMapProjection",
"description": "information about the configMap data to project"
}
}
},
"v1.SecretProjection": {
"id": "v1.SecretProjection",
"description": "Adapts a secret into a projected volume.\n\nThe contents of the target Secret's Data field will be presented in a projected volume as files using the keys in the Data field as the file names. Note that this is identical to a secret volume source without the default mode.",
"properties": {
"name": {
"type": "string",
"description": "Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names"
},
"items": {
"type": "array",
"items": {
"$ref": "v1.KeyToPath"
},
"description": "If unspecified, each key-value pair in the Data field of the referenced Secret will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the Secret, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'."
},
"optional": {
"type": "boolean",
"description": "Specify whether the Secret or its key must be defined"
}
}
},
"v1.DownwardAPIProjection": {
"id": "v1.DownwardAPIProjection",
"description": "Represents downward API info for projecting into a projected volume. Note that this is identical to a downwardAPI volume source without the default mode.",
"properties": {
"items": {
"type": "array",
"items": {
"$ref": "v1.DownwardAPIVolumeFile"
},
"description": "Items is a list of DownwardAPIVolume file"
}
}
},
"v1.ConfigMapProjection": {
"id": "v1.ConfigMapProjection",
"description": "Adapts a ConfigMap into a projected volume.\n\nThe contents of the target ConfigMap's Data field will be presented in a projected volume as files using the keys in the Data field as the file names, unless the items element is populated with specific mappings of keys to paths. Note that this is identical to a configmap volume source without the default mode.",
"properties": {
"name": {
"type": "string",
"description": "Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names"
},
"items": {
"type": "array",
"items": {
"$ref": "v1.KeyToPath"
},
"description": "If unspecified, each key-value pair in the Data field of the referenced ConfigMap will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the ConfigMap, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'."
},
"optional": {
"type": "boolean",
"description": "Specify whether the ConfigMap or it's keys must be defined"
}
}
},
"v1.Container": {
"id": "v1.Container",
"description": "A single application container that you want to run within a pod.",
@ -18634,7 +19214,7 @@
"items": {
"$ref": "v1.EnvFromSource"
},
"description": "List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. An invalid key will prevent the container from starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated."
"description": "List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated."
},
"env": {
"type": "array",
@ -19277,9 +19857,6 @@
"v1.PodAffinityTerm": {
"id": "v1.PodAffinityTerm",
"description": "Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key \u003ctopologyKey\u003e tches that of any node on which a pod of the set of pods is running",
"required": [
"namespaces"
],
"properties": {
"labelSelector": {
"$ref": "v1.LabelSelector",
@ -19290,7 +19867,7 @@
"items": {
"type": "string"
},
"description": "namespaces specifies which namespaces the labelSelector applies to (matches against); nil list means \"this pod's namespace,\" empty list means \"all namespaces\" The json tag here is not \"omitempty\" since we need to distinguish nil and empty. See https://golang.org/pkg/encoding/json/#Marshal for more details."
"description": "namespaces specifies which namespaces the labelSelector applies to (matches against); null or empty list means \"this pod's namespace\""
},
"topologyKey": {
"type": "string",
@ -19337,6 +19914,33 @@
}
}
},
"v1.Toleration": {
"id": "v1.Toleration",
"description": "The pod this Toleration is attached to tolerates any taint that matches the triple \u003ckey,value,effect\u003e using the matching operator \u003coperator\u003e.",
"properties": {
"key": {
"type": "string",
"description": "Key is the taint key that the toleration applies to. Empty means match all taint keys. If the key is empty, operator must be Exists; this combination means to match all values and all keys."
},
"operator": {
"type": "string",
"description": "Operator represents a key's relationship to the value. Valid operators are Exists and Equal. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category."
},
"value": {
"type": "string",
"description": "Value is the taint value the toleration matches to. If the operator is Exists, the value should be empty, otherwise just a regular string."
},
"effect": {
"type": "string",
"description": "Effect indicates the taint effect to match. Empty means match all taint effects. When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute."
},
"tolerationSeconds": {
"type": "integer",
"format": "int64",
"description": "TolerationSeconds represents the period of time the toleration (which must be of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, it is not set, which means tolerate the taint forever (do not evict). Zero and negative values will be treated as 0 (evict immediately) by the system."
}
}
},
"v1.PodStatus": {
"id": "v1.PodStatus",
"description": "PodStatus represents information about the status of a pod. Status may trail the actual state of a system.",
@ -19372,6 +19976,13 @@
"type": "string",
"description": "RFC 3339 date and time at which the object was acknowledged by the Kubelet. This is before the Kubelet pulled the container image(s) for the pod."
},
"initContainerStatuses": {
"type": "array",
"items": {
"$ref": "v1.ContainerStatus"
},
"description": "The list has one entry per init container in the manifest. The most recent successful init container will have ready = true, the most recently started container will have startTime set. More info: http://kubernetes.io/docs/user-guide/pod-states#container-statuses"
},
"containerStatuses": {
"type": "array",
"items": {
@ -20040,6 +20651,10 @@
"$ref": "v1.LocalObjectReference"
},
"description": "ImagePullSecrets is a list of references to secrets in the same namespace to use for pulling any images in pods that reference this ServiceAccount. ImagePullSecrets are distinct from Secrets because Secrets can be mounted in the pod, but ImagePullSecrets are only accessed by the kubelet. More info: http://kubernetes.io/docs/user-guide/secrets#manually-specifying-an-imagepullsecret"
},
"automountServiceAccountToken": {
"type": "boolean",
"description": "AutomountServiceAccountToken indicates whether pods running as this service account should have an API token automatically mounted. Can be overridden at the pod level."
}
}
},
@ -20280,6 +20895,13 @@
"type": "string"
},
"description": "verbs is a list of supported kube verbs (this includes get, list, watch, create, update, patch, delete, deletecollection, and proxy)"
},
"shortNames": {
"type": "array",
"items": {
"type": "string"
},
"description": "shortNames is a list of suggested short names of the resource."
}
}
}

View file

@ -63,10 +63,6 @@ DOCKERIZED_BINARIES = {
"base": ":busybox-libc",
"target": "//plugin/cmd/kube-scheduler:kube-scheduler",
},
"kube-aggregator": {
"base": ":busybox-libc",
"target": "//cmd/kube-aggregator:kube-aggregator",
},
"kube-proxy": {
"base": ":busybox-net",
"target": "//cmd/kube-proxy:kube-proxy",

View file

@ -1,12 +1,11 @@
reviewers:
- ihmccreery
- ixdy
- jbeda
- lavalamp
- zmerlynn
approvers:
- ihmccreery
- ixdy
- jbeda
- lavalamp
- zmerlynn
- mikedanese

View file

@ -19,7 +19,7 @@ FROM gcr.io/google_containers/kube-cross:KUBE_BUILD_IMAGE_CROSS_TAG
RUN touch /kube-build-image
# To run as non-root we sometimes need to rebuild go stdlib packages.
RUN chmod -R a+rwx /usr/local/go/pkg ${K8S_PATCHED_GOROOT}/pkg
RUN chmod -R a+rwx /usr/local/go/pkg ${K8S_EDGE_GOROOT}/pkg
# For running integration tests /var/run/kubernetes is required
# and should be writable by user

View file

@ -1 +1 @@
4
5

View file

@ -15,11 +15,11 @@
# This file creates a standard build environment for building cross
# platform go binary for the architecture kubernetes cares about.
FROM golang:1.7.4
FROM golang:1.7.5
ENV GOARM 6
ENV GOARM 7
ENV KUBE_DYNAMIC_CROSSPLATFORMS \
armel \
armhf \
arm64 \
s390x \
ppc64el
@ -72,22 +72,17 @@ RUN mkdir $TMPDIR \
github.com/tools/godep
# Download and symlink etcd. We need this for our integration tests.
RUN export ETCD_VERSION=v3.0.14; \
RUN export ETCD_VERSION=v3.0.17; \
mkdir -p /usr/local/src/etcd \
&& cd /usr/local/src/etcd \
&& curl -fsSL https://github.com/coreos/etcd/releases/download/${ETCD_VERSION}/etcd-${ETCD_VERSION}-linux-amd64.tar.gz | tar -xz \
&& ln -s ../src/etcd/etcd-${ETCD_VERSION}-linux-amd64/etcd /usr/local/bin/
# TODO: Remove the patched GOROOT when we have an official golang that has a working arm and ppc64le linker
ENV K8S_PATCHED_GOLANG_VERSION=1.7.4 \
K8S_PATCHED_GOROOT=/usr/local/go_k8s_patched
RUN mkdir -p ${K8S_PATCHED_GOROOT} \
&& curl -sSL https://github.com/golang/go/archive/go${K8S_PATCHED_GOLANG_VERSION}.tar.gz | tar -xz -C ${K8S_PATCHED_GOROOT} --strip-components=1
# TODO: Remove the need for two golang versions; when we use go1.8 for all other builds we can remove this
ENV K8S_GOLANG_EDGE_VERSION=1.8rc2 \
K8S_EDGE_GOROOT=/usr/local/go_k8s_edge
RUN mkdir -p ${K8S_EDGE_GOROOT} \
&& curl -sSL https://golang.org/dl/go${K8S_GOLANG_EDGE_VERSION}.linux-amd64.tar.gz | tar -xz -C ${K8S_EDGE_GOROOT} --strip-components=1
# We need a patched go1.7.1 for linux/arm (https://github.com/kubernetes/kubernetes/issues/29904)
COPY golang-patches/CL28857-go1.7.1-luxas.patch ${K8S_PATCHED_GOROOT}/
RUN cd ${K8S_PATCHED_GOROOT} \
&& patch -p1 < CL28857-go1.7.1-luxas.patch \
&& cd src \
&& GOROOT_FINAL=${K8S_PATCHED_GOROOT} GOROOT_BOOTSTRAP=/usr/local/go ./make.bash \
&& for platform in linux/arm; do GOOS=${platform%/*} GOARCH=${platform##*/} GOROOT=${K8S_PATCHED_GOROOT} go install std; done
# Prebuild the standard library for those platforms that need the edge GOROOT
RUN for platform in linux/arm linux/ppc64le; do GOOS=${platform%/*} GOARCH=${platform##*/} GOROOT=${K8S_EDGE_GOROOT} go install std; done

View file

@ -1 +1 @@
v1.7.4-1
v1.7.5-2

View file

@ -1,390 +0,0 @@
From cc1015ff9bb020ea92c1854026e9ae395a8504b2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Lucas=20K=C3=A4ldstr=C3=B6m?=
<lucas.kaldstrom@hotmail.co.uk>
Date: Mon, 12 Sep 2016 22:55:42 +0300
Subject: [PATCH] Ported cherrymui's CL 28857 to the go1.7.1 branch
---
src/cmd/compile/internal/arm/ssa.go | 16 ++++++
src/cmd/compile/internal/gc/cgen.go | 5 ++
src/cmd/compile/internal/gc/main.go | 11 ++--
src/cmd/compile/internal/gc/plive.go | 5 ++
src/cmd/internal/obj/arm/asm5.go | 32 ++++++++++++
src/cmd/internal/obj/arm/obj5.go | 3 ++
src/cmd/internal/obj/link.go | 98 +++++++++++++++++++-----------------
src/cmd/link/internal/arm/asm.go | 18 ++++++-
src/cmd/link/internal/ld/lib.go | 4 +-
src/runtime/asm_arm.s | 2 +-
10 files changed, 138 insertions(+), 56 deletions(-)
diff --git a/src/cmd/compile/internal/arm/ssa.go b/src/cmd/compile/internal/arm/ssa.go
index 8f466e3..a066e02 100644
--- a/src/cmd/compile/internal/arm/ssa.go
+++ b/src/cmd/compile/internal/arm/ssa.go
@@ -111,6 +111,22 @@ func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
gc.AddAux(&p.To, v)
case ssa.OpARMCALLstatic:
// TODO: deferreturn
+ if v.Aux.(*gc.Sym) == gc.Deferreturn.Sym {
+ // Deferred calls will appear to be returning to
+ // the CALL deferreturn(SB) that we are about to emit.
+ // However, the stack trace code will show the line
+ // of the instruction byte before the return PC.
+ // To avoid that being an unrelated instruction,
+ // insert an actual hardware NOP that will have the right line number.
+ // This is different from obj.ANOP, which is a virtual no-op
+ // that doesn't make it into the instruction stream.
+ ginsnop()
+ if !gc.Ctxt.Flag_largemodel {
+ // We always back up two instructions.
+ // For non-large build, insert another NOP.
+ ginsnop()
+ }
+ }
p := gc.Prog(obj.ACALL)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
diff --git a/src/cmd/compile/internal/gc/cgen.go b/src/cmd/compile/internal/gc/cgen.go
index 74fe463..7507d5f 100644
--- a/src/cmd/compile/internal/gc/cgen.go
+++ b/src/cmd/compile/internal/gc/cgen.go
@@ -2373,6 +2373,11 @@ func Ginscall(f *Node, proc int) {
Thearch.Ginsnop()
}
}
+ if Ctxt.Arch.Family == sys.ARM && !Ctxt.Flag_largemodel {
+ // On ARM we always back up two instructions.
+ // For non-large build, insert another NOP.
+ Thearch.Ginsnop()
+ }
}
p := Thearch.Gins(obj.ACALL, nil, f)
diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go
index b4df7ed..c0a8aa1 100644
--- a/src/cmd/compile/internal/gc/main.go
+++ b/src/cmd/compile/internal/gc/main.go
@@ -148,9 +148,9 @@ func Main() {
goos = obj.Getgoos()
Nacl = goos == "nacl"
- if Nacl {
- flag_largemodel = true
- }
+ //if Nacl {
+ // flag_largemodel = true
+ //}
flag.BoolVar(&compiling_runtime, "+", false, "compiling runtime")
obj.Flagcount("%", "debug non-static initializers", &Debug['%'])
@@ -204,9 +204,7 @@ func Main() {
flag.BoolVar(&flag_shared, "shared", false, "generate code that can be linked into a shared library")
flag.BoolVar(&flag_dynlink, "dynlink", false, "support references to Go symbols defined in other shared libraries")
}
- if Thearch.LinkArch.Family == sys.AMD64 {
- flag.BoolVar(&flag_largemodel, "largemodel", false, "generate code that assumes a large memory model")
- }
+ flag.BoolVar(&flag_largemodel, "largemodel", false, "generate code that assumes a large memory model")
flag.StringVar(&cpuprofile, "cpuprofile", "", "write cpu profile to `file`")
flag.StringVar(&memprofile, "memprofile", "", "write memory profile to `file`")
flag.Int64Var(&memprofilerate, "memprofilerate", 0, "set runtime.MemProfileRate to `rate`")
@@ -216,6 +214,7 @@ func Main() {
Ctxt.Flag_shared = flag_dynlink || flag_shared
Ctxt.Flag_dynlink = flag_dynlink
Ctxt.Flag_optimize = Debug['N'] == 0
+ Ctxt.Flag_largemodel = flag_largemodel
Ctxt.Debugasm = int32(Debug['S'])
Ctxt.Debugvlog = int32(Debug['v'])
diff --git a/src/cmd/compile/internal/gc/plive.go b/src/cmd/compile/internal/gc/plive.go
index ca0421d..ad26d24 100644
--- a/src/cmd/compile/internal/gc/plive.go
+++ b/src/cmd/compile/internal/gc/plive.go
@@ -1400,6 +1400,11 @@ func livenessepilogue(lv *Liveness) {
// the call.
prev = prev.Opt.(*obj.Prog)
}
+ if Ctxt.Arch.Family == sys.ARM && !Ctxt.Flag_largemodel {
+ // On ARM we always back up two instructions.
+ // For non-large build, there is another NOP.
+ prev = prev.Opt.(*obj.Prog)
+ }
splicebefore(lv, bb, newpcdataprog(prev, pos), prev)
} else {
splicebefore(lv, bb, newpcdataprog(p, pos), p)
diff --git a/src/cmd/internal/obj/arm/asm5.go b/src/cmd/internal/obj/arm/asm5.go
index d37091f..8136492 100644
--- a/src/cmd/internal/obj/arm/asm5.go
+++ b/src/cmd/internal/obj/arm/asm5.go
@@ -585,6 +585,30 @@ func span5(ctxt *obj.Link, cursym *obj.LSym) {
break
}
+ if ctxt.Flag_largemodel && (p.As == AB || p.As == ABL || p.As == obj.ADUFFZERO || p.As == obj.ADUFFCOPY) && p.To.Name == obj.NAME_EXTERN {
+ // in large mode, emit indirect call
+ // MOVW $target, Rtmp
+ // BL (Rtmp)
+ // use REGLINK as Rtmp, as soft div calls expects REGTMP to pass argument
+ tmp := int16(REGLINK)
+ q := obj.Appendp(ctxt, p)
+ q.As = ABL
+ if p.As == AB {
+ q.As = AB
+ tmp = REGTMP // should not clobber REGLINK in this case
+ }
+ q.To.Type = obj.TYPE_MEM
+ q.To.Reg = tmp
+ q.To.Sym = p.To.Sym // tell asmout to emits R_CALLARMLARGE reloc
+
+ p.As = AMOVW
+ p.From = p.To // jump target
+ p.From.Type = obj.TYPE_ADDR
+ p.To = obj.Addr{}
+ p.To.Type = obj.TYPE_REG
+ p.To.Reg = tmp
+ }
+
ctxt.Curp = p
p.Pc = int64(c)
o = oplook(ctxt, p)
@@ -1583,6 +1607,14 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
}
o1 = oprrr(ctxt, ABL, int(p.Scond))
o1 |= (uint32(p.To.Reg) & 15) << 0
+ if p.To.Sym != nil {
+ rel := obj.Addrel(ctxt.Cursym)
+ rel.Off = int32(ctxt.Pc)
+ rel.Siz = 4
+ rel.Sym = p.To.Sym
+ rel.Type = obj.R_CALLARMLARGE
+ break
+ }
rel := obj.Addrel(ctxt.Cursym)
rel.Off = int32(ctxt.Pc)
rel.Siz = 0
diff --git a/src/cmd/internal/obj/arm/obj5.go b/src/cmd/internal/obj/arm/obj5.go
index 9cf2f29..13bf952 100644
--- a/src/cmd/internal/obj/arm/obj5.go
+++ b/src/cmd/internal/obj/arm/obj5.go
@@ -584,6 +584,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
p.As = ABL
p.Lineno = q1.Lineno
p.To.Type = obj.TYPE_BRANCH
+ p.To.Name = obj.NAME_EXTERN
switch o {
case ADIV:
p.To.Sym = ctxt.Sym_div
@@ -687,6 +688,7 @@ func softfloat(ctxt *obj.Link, cursym *obj.LSym) {
p.Link = next
p.As = ABL
p.To.Type = obj.TYPE_BRANCH
+ p.To.Name = obj.NAME_EXTERN
p.To.Sym = symsfloat
p.Lineno = next.Lineno
@@ -820,6 +822,7 @@ func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32) *obj.Prog {
call := obj.Appendp(ctxt, movw)
call.As = obj.ACALL
call.To.Type = obj.TYPE_BRANCH
+ call.To.Name = obj.NAME_EXTERN
morestack := "runtime.morestack"
switch {
case ctxt.Cursym.Cfunc:
diff --git a/src/cmd/internal/obj/link.go b/src/cmd/internal/obj/link.go
index b6861f4..51e9fd3 100644
--- a/src/cmd/internal/obj/link.go
+++ b/src/cmd/internal/obj/link.go
@@ -588,6 +588,11 @@ const (
// R_ADDRMIPSTLS (only used on mips64) resolves to the low 16 bits of a TLS
// address (offset from thread pointer), by encoding it into the instruction.
R_ADDRMIPSTLS
+
+ // R_CALLARMLARGE applies on an indirect CALL with known target used on large mode.
+ // Currently it does nothing but tell the linker the target for stack split check.
+ // In the future linker may optimize this to a NOP and a direct CALL if it is safe.
+ R_CALLARMLARGE
)
type Auto struct {
@@ -617,52 +622,53 @@ const (
// Link holds the context for writing object code from a compiler
// to be linker input or for reading that input into the linker.
type Link struct {
- Goarm int32
- Headtype int
- Arch *LinkArch
- Debugasm int32
- Debugvlog int32
- Debugdivmod int32
- Debugpcln int32
- Flag_shared bool
- Flag_dynlink bool
- Flag_optimize bool
- Bso *bufio.Writer
- Pathname string
- Goroot string
- Goroot_final string
- Hash map[SymVer]*LSym
- LineHist LineHist
- Imports []string
- Plist *Plist
- Plast *Plist
- Sym_div *LSym
- Sym_divu *LSym
- Sym_mod *LSym
- Sym_modu *LSym
- Plan9privates *LSym
- Curp *Prog
- Printp *Prog
- Blitrl *Prog
- Elitrl *Prog
- Rexflag int
- Vexflag int
- Rep int
- Repn int
- Lock int
- Asmode int
- AsmBuf AsmBuf // instruction buffer for x86
- Instoffset int64
- Autosize int32
- Armsize int32
- Pc int64
- DiagFunc func(string, ...interface{})
- Mode int
- Cursym *LSym
- Version int
- Textp *LSym
- Etextp *LSym
- Errors int
+ Goarm int32
+ Headtype int
+ Arch *LinkArch
+ Debugasm int32
+ Debugvlog int32
+ Debugdivmod int32
+ Debugpcln int32
+ Flag_shared bool
+ Flag_dynlink bool
+ Flag_optimize bool
+ Flag_largemodel bool // generate code that assumes a large memory model
+ Bso *bufio.Writer
+ Pathname string
+ Goroot string
+ Goroot_final string
+ Hash map[SymVer]*LSym
+ LineHist LineHist
+ Imports []string
+ Plist *Plist
+ Plast *Plist
+ Sym_div *LSym
+ Sym_divu *LSym
+ Sym_mod *LSym
+ Sym_modu *LSym
+ Plan9privates *LSym
+ Curp *Prog
+ Printp *Prog
+ Blitrl *Prog
+ Elitrl *Prog
+ Rexflag int
+ Vexflag int
+ Rep int
+ Repn int
+ Lock int
+ Asmode int
+ AsmBuf AsmBuf // instruction buffer for x86
+ Instoffset int64
+ Autosize int32
+ Armsize int32
+ Pc int64
+ DiagFunc func(string, ...interface{})
+ Mode int
+ Cursym *LSym
+ Version int
+ Textp *LSym
+ Etextp *LSym
+ Errors int
Framepointer_enabled bool
diff --git a/src/cmd/link/internal/arm/asm.go b/src/cmd/link/internal/arm/asm.go
index 0c3e957..0cf61bb 100644
--- a/src/cmd/link/internal/arm/asm.go
+++ b/src/cmd/link/internal/arm/asm.go
@@ -410,6 +410,11 @@ func machoreloc1(r *ld.Reloc, sectoff int64) int {
return 0
}
+// sign extend a 24-bit integer
+func signext24(x int64) int32 {
+ return (int32(x) << 8) >> 8
+}
+
func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int {
if ld.Linkmode == ld.LinkExternal {
switch r.Type {
@@ -445,6 +450,9 @@ func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int {
*val = int64(braddoff(int32(0xff000000&uint32(r.Add)), int32(0xffffff&uint32(r.Xadd/4))))
return 0
+
+ case obj.R_CALLARMLARGE:
+ return 0
}
return -1
@@ -479,8 +487,16 @@ func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int {
return 0
case obj.R_CALLARM: // bl XXXXXX or b YYYYYY
- *val = int64(braddoff(int32(0xff000000&uint32(r.Add)), int32(0xffffff&uint32((ld.Symaddr(r.Sym)+int64((uint32(r.Add))*4)-(s.Value+int64(r.Off)))/4))))
+ // low 24-bit encodes the target address
+ t := (ld.Symaddr(r.Sym) + int64(signext24(r.Add&0xffffff)*4) - (s.Value + int64(r.Off))) / 4
+ if t > 0x7fffff || t < -0x800000 {
+ ld.Diag("direct call too far %d, should build with -gcflags -largemodel", t)
+ }
+ *val = int64(braddoff(int32(0xff000000&uint32(r.Add)), int32(0xffffff&t)))
+
+ return 0
+ case obj.R_CALLARMLARGE:
return 0
}
diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go
index 14f4fa9..cc3b50e 100644
--- a/src/cmd/link/internal/ld/lib.go
+++ b/src/cmd/link/internal/ld/lib.go
@@ -1826,7 +1826,7 @@ func stkcheck(up *Chain, depth int) int {
r = &s.R[ri]
switch r.Type {
// Direct call.
- case obj.R_CALL, obj.R_CALLARM, obj.R_CALLARM64, obj.R_CALLPOWER, obj.R_CALLMIPS:
+ case obj.R_CALL, obj.R_CALLARM, obj.R_CALLARM64, obj.R_CALLPOWER, obj.R_CALLMIPS, obj.R_CALLARMLARGE:
ch.limit = int(int32(limit) - pcsp.value - int32(callsize()))
ch.sym = r.Sym
if stkcheck(&ch, depth+1) < 0 {
@@ -2164,7 +2164,7 @@ func callgraph() {
if r.Sym == nil {
continue
}
- if (r.Type == obj.R_CALL || r.Type == obj.R_CALLARM || r.Type == obj.R_CALLPOWER || r.Type == obj.R_CALLMIPS) && r.Sym.Type == obj.STEXT {
+ if (r.Type == obj.R_CALL || r.Type == obj.R_CALLARM || r.Type == obj.R_CALLPOWER || r.Type == obj.R_CALLMIPS || r.Type == obj.R_CALLARMLARGE) && r.Sym.Type == obj.STEXT {
fmt.Fprintf(Bso, "%s calls %s\n", s.Name, r.Sym.Name)
}
}
diff --git a/src/runtime/asm_arm.s b/src/runtime/asm_arm.s
index f02297e..0930585 100644
--- a/src/runtime/asm_arm.s
+++ b/src/runtime/asm_arm.s
@@ -465,7 +465,7 @@ CALLFN(·call1073741824, 1073741824)
// (And double-check that pop is atomic in that way.)
TEXT runtime·jmpdefer(SB),NOSPLIT,$0-8
MOVW 0(R13), LR
- MOVW $-4(LR), LR // BL deferreturn
+ MOVW $-8(LR), LR // BL deferreturn
MOVW fv+0(FP), R7
MOVW argp+4(FP), R13
MOVW $-4(R13), R13 // SP is 4 below argp, due to saved LR
--
2.5.0

View file

@ -18,17 +18,18 @@
# [ARCH=amd64] [CNI_RELEASE=v0.3.0] make (build|push)
ARCH?=amd64
CNI_RELEASE?=07a8a28637e97b22eb8dfe710eeae1344f69d16e
CNI_RELEASE?=0799f5732f2a11b329d9e3d51b9c8f2e3759f2ff
CNI_TARBALL=cni-$(ARCH)-$(CNI_RELEASE).tar.gz
CUR_DIR=$(shell pwd)
OUTPUT_DIR=$(CUR_DIR)/output
GOLANG_VERSION=1.7.5
all: build
build:
mkdir -p $(OUTPUT_DIR)
docker run -it -v $(OUTPUT_DIR):/output golang:1.6 /bin/bash -c "\
docker run -it -v $(OUTPUT_DIR):/output golang:$(GOLANG_VERSION) /bin/bash -c "\
git clone https://github.com/containernetworking/cni\
&& cd cni \
&& git checkout $(CNI_RELEASE) \

View file

@ -19,6 +19,9 @@ set -o errexit
set -o nounset
set -o pipefail
USER_ID=$(id -u)
GROUP_ID=$(id -g)
DOCKER_OPTS=${DOCKER_OPTS:-""}
DOCKER=(docker ${DOCKER_OPTS})
DOCKER_HOST=${DOCKER_HOST:-""}
@ -30,15 +33,6 @@ KUBE_ROOT=$(cd $(dirname "${BASH_SOURCE}")/.. && pwd -P)
source "${KUBE_ROOT}/hack/lib/init.sh"
# Set KUBE_BUILD_PPC64LE to y to build for ppc64le in addition to other
# platforms.
# TODO(IBM): remove KUBE_BUILD_PPC64LE and reenable ppc64le compilation by
# default when
# https://github.com/kubernetes/kubernetes/issues/30384 and
# https://github.com/kubernetes/kubernetes/issues/25886 are fixed.
# The majority of the logic is in hack/lib/golang.sh.
readonly KUBE_BUILD_PPC64LE="${KUBE_BUILD_PPC64LE:-n}"
# Constants
readonly KUBE_BUILD_IMAGE_REPO=kube-build
readonly KUBE_BUILD_IMAGE_CROSS_TAG="$(cat ${KUBE_ROOT}/build/build-image/cross/VERSION)"
@ -91,6 +85,7 @@ readonly KUBE_CONTAINER_RSYNC_PORT=8730
#
# $1 - server architecture
kube::build::get_docker_wrapped_binaries() {
debian_iptables_version=v7
case $1 in
"amd64")
local targets=(
@ -98,7 +93,7 @@ kube::build::get_docker_wrapped_binaries() {
kube-controller-manager,busybox
kube-scheduler,busybox
kube-aggregator,busybox
kube-proxy,gcr.io/google_containers/debian-iptables-amd64:v5
kube-proxy,gcr.io/google-containers/debian-iptables-amd64:${debian_iptables_version}
);;
"arm")
local targets=(
@ -106,7 +101,7 @@ kube::build::get_docker_wrapped_binaries() {
kube-controller-manager,armel/busybox
kube-scheduler,armel/busybox
kube-aggregator,armel/busybox
kube-proxy,gcr.io/google_containers/debian-iptables-arm:v5
kube-proxy,gcr.io/google-containers/debian-iptables-arm:${debian_iptables_version}
);;
"arm64")
local targets=(
@ -114,7 +109,7 @@ kube::build::get_docker_wrapped_binaries() {
kube-controller-manager,aarch64/busybox
kube-scheduler,aarch64/busybox
kube-aggregator,aarch64/busybox
kube-proxy,gcr.io/google_containers/debian-iptables-arm64:v5
kube-proxy,gcr.io/google-containers/debian-iptables-arm64:${debian_iptables_version}
);;
"ppc64le")
local targets=(
@ -122,7 +117,7 @@ kube::build::get_docker_wrapped_binaries() {
kube-controller-manager,ppc64le/busybox
kube-scheduler,ppc64le/busybox
kube-aggregator,ppc64le/busybox
kube-proxy,gcr.io/google_containers/debian-iptables-ppc64le:v5
kube-proxy,gcr.io/google-containers/debian-iptables-ppc64le:${debian_iptables_version}
);;
"s390x")
local targets=(
@ -130,7 +125,7 @@ kube::build::get_docker_wrapped_binaries() {
kube-controller-manager,s390x/busybox
kube-scheduler,s390x/busybox
kube-aggregator,s390x/busybox
kube-proxy,gcr.io/google_containers/debian-iptables-s390x:v5
kube-proxy,gcr.io/google-containers/debian-iptables-s390x:${debian_iptables_version}
);;
esac
@ -407,6 +402,8 @@ function kube::build::clean() {
# Set up the context directory for the kube-build image and build it.
function kube::build::build_image() {
mkdir -p "${LOCAL_OUTPUT_BUILD_CONTEXT}"
# Make sure the context directory owned by the right user for syncing sources to container.
chown -R ${USER_ID}:${GROUP_ID} "${LOCAL_OUTPUT_BUILD_CONTEXT}"
cp /etc/localtime "${LOCAL_OUTPUT_BUILD_CONTEXT}/"
@ -494,7 +491,7 @@ function kube::build::ensure_data_container() {
--name "${KUBE_DATA_CONTAINER_NAME}"
--hostname "${HOSTNAME}"
"${KUBE_BUILD_IMAGE}"
chown -R $(id -u).$(id -g)
chown -R ${USER_ID}:${GROUP_ID}
"${REMOTE_ROOT}"
/usr/local/go/pkg/
)
@ -553,7 +550,6 @@ function kube::build::run_build_command_ex() {
docker_run_opts+=(
--env "KUBE_FASTBUILD=${KUBE_FASTBUILD:-false}"
--env "KUBE_BUILDER_OS=${OSTYPE:-notdetected}"
--env "KUBE_BUILD_PPC64LE=${KUBE_BUILD_PPC64LE}" # TODO(IBM): remove
--env "KUBE_VERBOSE=${KUBE_VERBOSE}"
)
@ -708,6 +704,7 @@ function kube::build::copy_output() {
--filter='+ zz_generated.*' \
--filter='+ generated.proto' \
--filter='+ *.pb.go' \
--filter='+ types.go' \
--filter='+ */' \
--filter='- /**' \
"rsync://k8s@${KUBE_RSYNC_ADDR}/k8s/" "${KUBE_ROOT}"

19
vendor/k8s.io/kubernetes/build/debian-base/Dockerfile generated vendored Normal file
View file

@ -0,0 +1,19 @@
# 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.
FROM scratch
ADD rootfs.tar /
CMD ["/bin/sh"]

View file

@ -0,0 +1,102 @@
# 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.
FROM BASEIMAGE
# If we're building for another architecture than amd64, the CROSS_BUILD_ placeholder is removed so
# e.g. CROSS_BUILD_COPY turns into COPY
# If we're building normally, for amd64, CROSS_BUILD lines are removed
CROSS_BUILD_COPY qemu-ARCH-static /usr/bin/
ENV DEBIAN_FRONTEND=noninteractive
# Smaller package install size.
COPY excludes /etc/dpkg/dpkg.cfg.d/excludes
# Convenience script for building on this base image.
COPY clean-install /usr/local/bin/clean-install
# Update system packages.
RUN apt-get update \
&& apt-get dist-upgrade -y
# Hold required packages to avoid breaking the installation of packages
RUN apt-mark hold apt gnupg adduser passwd libsemanage1
# Remove unnecessary packages.
# This list was generated manually by listing the installed packages (`apt list --installed`),
# then running `apt-cache rdepends --installed --no-recommends` to find the "root" packages.
# The root packages were evaluated based on whether they were needed in the container image.
# Several utilities (e.g. ping) were kept for usefulness, but may be removed in later versions.
RUN echo "Yes, do as I say!" | apt-get purge \
bash \
debconf-i18n \
e2fslibs \
e2fsprogs \
gcc-4.8-base \
init \
initscripts \
libcap2-bin \
libkmod2 \
libmount1 \
libsmartcols1 \
libudev1 \
libblkid1 \
libncursesw5 \
libprocps3 \
libslang2 \
libss2 \
libtext-charwidth-perl libtext-iconv-perl libtext-wrapi18n-perl \
ncurses-base \
ncurses-bin \
systemd \
systemd-sysv \
sysv-rc \
tzdata
# No-op stubs replace some unnecessary binaries that may be depended on in the install process (in
# particular we don't run an init process).
WORKDIR /usr/local/bin
RUN touch noop && \
chmod 555 noop && \
ln -s noop runlevel && \
ln -s noop invoke-rc.d && \
ln -s noop update-rc.d
WORKDIR /
# Cleanup cached and unnecessary files.
RUN apt-get autoremove -y && \
apt-get clean -y && \
tar -czf /usr/share/copyrights.tar.gz /usr/share/common-licenses /usr/share/doc/*/copyright && \
rm -rf \
/usr/share/doc \
/usr/share/man \
/usr/share/info \
/usr/share/locale \
/var/lib/apt/lists/* \
/var/log/* \
/var/cache/debconf/* \
/usr/share/common-licenses* \
/usr/share/bash-completion \
~/.bashrc \
~/.profile \
/etc/systemd \
/lib/lsb \
/lib/udev \
/usr/lib/x86_64-linux-gnu/gconv/IBM* \
/usr/lib/x86_64-linux-gnu/gconv/EBC* && \
mkdir -p /usr/share/man/man1 /usr/share/man/man2 \
/usr/share/man/man3 /usr/share/man/man4 \
/usr/share/man/man5 /usr/share/man/man6 \
/usr/share/man/man7 /usr/share/man/man8

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