Merge pull request #48921 from smarterclayton/paging_prototype
Automatic merge from submit-queue (batch tested with PRs 50832, 51119, 51636, 48921, 51712) Alpha list paging implementation Design in kubernetes/community#896 Support `?limit=NUMBER`, `?continue=CONTINUATIONTOKEN`, and a `continue` field on ListMeta and pass through to etcd. Perform minor validation as an example. ``` # first out of three $ curl http://127.0.0.1:8080/api/v1/namespaces?limit=1 { "kind": "NamespaceList", "apiVersion": "v1", "metadata": { "selfLink": "/api/v1/namespaces", "resourceVersion": "146", "next": "ZGVmYXVsdA" }, "items": [ { "metadata": { "name": "default", "selfLink": "/api/v1/namespaces/default", "uid": "f95e1390-6852-11e7-ab03-7831c1b76042", "resourceVersion": "4", "creationTimestamp": "2017-07-14T05:12:03Z" }, "spec": { "finalizers": [ "kubernetes" ] }, "status": { "phase": "Active" } } ] } ... # last $ curl "http://127.0.0.1:8080/api/v1/namespaces?limit=1&continue=a3ViZS1wdWJsaWM" { "kind": "NamespaceList", "apiVersion": "v1", "metadata": { "selfLink": "/api/v1/namespaces", "resourceVersion": "145" }, "items": [ { "metadata": { "name": "kube-system", "selfLink": "/api/v1/namespaces/kube-system", "uid": "f95e9484-6852-11e7-ab03-7831c1b76042", "resourceVersion": "5", "creationTimestamp": "2017-07-14T05:12:03Z" }, "spec": { "finalizers": [ "kubernetes" ] }, "status": { "phase": "Active" } } ] } ``` Kubernetes-commit: 35ffb5c6cf70974c0a571cd1ebdc72ad8d0f8332
This commit is contained in:
commit
ac35203d6c
|
|
@ -856,735 +856,735 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/api/equality",
|
"ImportPath": "k8s.io/apimachinery/pkg/api/equality",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/api/errors",
|
"ImportPath": "k8s.io/apimachinery/pkg/api/errors",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/api/meta",
|
"ImportPath": "k8s.io/apimachinery/pkg/api/meta",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/api/resource",
|
"ImportPath": "k8s.io/apimachinery/pkg/api/resource",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/api/testing",
|
"ImportPath": "k8s.io/apimachinery/pkg/api/testing",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/api/testing/fuzzer",
|
"ImportPath": "k8s.io/apimachinery/pkg/api/testing/fuzzer",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/api/testing/roundtrip",
|
"ImportPath": "k8s.io/apimachinery/pkg/api/testing/roundtrip",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/api/validation",
|
"ImportPath": "k8s.io/apimachinery/pkg/api/validation",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/api/validation/path",
|
"ImportPath": "k8s.io/apimachinery/pkg/api/validation/path",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/apimachinery",
|
"ImportPath": "k8s.io/apimachinery/pkg/apimachinery",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/apimachinery/announced",
|
"ImportPath": "k8s.io/apimachinery/pkg/apimachinery/announced",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/apimachinery/registered",
|
"ImportPath": "k8s.io/apimachinery/pkg/apimachinery/registered",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/fuzzer",
|
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/fuzzer",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/internalversion",
|
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/internalversion",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1",
|
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured",
|
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1/validation",
|
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1/validation",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1alpha1",
|
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1alpha1",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/conversion",
|
"ImportPath": "k8s.io/apimachinery/pkg/conversion",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/conversion/queryparams",
|
"ImportPath": "k8s.io/apimachinery/pkg/conversion/queryparams",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/conversion/unstructured",
|
"ImportPath": "k8s.io/apimachinery/pkg/conversion/unstructured",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/fields",
|
"ImportPath": "k8s.io/apimachinery/pkg/fields",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/labels",
|
"ImportPath": "k8s.io/apimachinery/pkg/labels",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/runtime",
|
"ImportPath": "k8s.io/apimachinery/pkg/runtime",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/runtime/schema",
|
"ImportPath": "k8s.io/apimachinery/pkg/runtime/schema",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer",
|
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/json",
|
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/json",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/protobuf",
|
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/protobuf",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/recognizer",
|
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/recognizer",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/streaming",
|
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/streaming",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/versioning",
|
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/versioning",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/selection",
|
"ImportPath": "k8s.io/apimachinery/pkg/selection",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/types",
|
"ImportPath": "k8s.io/apimachinery/pkg/types",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/util/cache",
|
"ImportPath": "k8s.io/apimachinery/pkg/util/cache",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/util/clock",
|
"ImportPath": "k8s.io/apimachinery/pkg/util/clock",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/util/diff",
|
"ImportPath": "k8s.io/apimachinery/pkg/util/diff",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/util/errors",
|
"ImportPath": "k8s.io/apimachinery/pkg/util/errors",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/util/framer",
|
"ImportPath": "k8s.io/apimachinery/pkg/util/framer",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/util/httpstream",
|
"ImportPath": "k8s.io/apimachinery/pkg/util/httpstream",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/util/intstr",
|
"ImportPath": "k8s.io/apimachinery/pkg/util/intstr",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/util/json",
|
"ImportPath": "k8s.io/apimachinery/pkg/util/json",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/util/mergepatch",
|
"ImportPath": "k8s.io/apimachinery/pkg/util/mergepatch",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/util/net",
|
"ImportPath": "k8s.io/apimachinery/pkg/util/net",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/util/proxy",
|
"ImportPath": "k8s.io/apimachinery/pkg/util/proxy",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/util/rand",
|
"ImportPath": "k8s.io/apimachinery/pkg/util/rand",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/util/runtime",
|
"ImportPath": "k8s.io/apimachinery/pkg/util/runtime",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/util/sets",
|
"ImportPath": "k8s.io/apimachinery/pkg/util/sets",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/util/strategicpatch",
|
"ImportPath": "k8s.io/apimachinery/pkg/util/strategicpatch",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/util/uuid",
|
"ImportPath": "k8s.io/apimachinery/pkg/util/uuid",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/util/validation",
|
"ImportPath": "k8s.io/apimachinery/pkg/util/validation",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/util/validation/field",
|
"ImportPath": "k8s.io/apimachinery/pkg/util/validation/field",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/util/wait",
|
"ImportPath": "k8s.io/apimachinery/pkg/util/wait",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/util/yaml",
|
"ImportPath": "k8s.io/apimachinery/pkg/util/yaml",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/version",
|
"ImportPath": "k8s.io/apimachinery/pkg/version",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/watch",
|
"ImportPath": "k8s.io/apimachinery/pkg/watch",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/third_party/forked/golang/json",
|
"ImportPath": "k8s.io/apimachinery/third_party/forked/golang/json",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/third_party/forked/golang/netutil",
|
"ImportPath": "k8s.io/apimachinery/third_party/forked/golang/netutil",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/third_party/forked/golang/reflect",
|
"ImportPath": "k8s.io/apimachinery/third_party/forked/golang/reflect",
|
||||||
"Rev": "b0b038663fc082407b3c34033ddc9747f701b12e"
|
"Rev": "429c5e743dc2bf27fc89e2d0c56dec9c3bc03859"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/discovery",
|
"ImportPath": "k8s.io/client-go/discovery",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/discovery/fake",
|
"ImportPath": "k8s.io/client-go/discovery/fake",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/informers",
|
"ImportPath": "k8s.io/client-go/informers",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/informers/admissionregistration",
|
"ImportPath": "k8s.io/client-go/informers/admissionregistration",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/informers/admissionregistration/v1alpha1",
|
"ImportPath": "k8s.io/client-go/informers/admissionregistration/v1alpha1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/informers/apps",
|
"ImportPath": "k8s.io/client-go/informers/apps",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/informers/apps/v1beta1",
|
"ImportPath": "k8s.io/client-go/informers/apps/v1beta1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/informers/apps/v1beta2",
|
"ImportPath": "k8s.io/client-go/informers/apps/v1beta2",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/informers/autoscaling",
|
"ImportPath": "k8s.io/client-go/informers/autoscaling",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/informers/autoscaling/v1",
|
"ImportPath": "k8s.io/client-go/informers/autoscaling/v1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/informers/autoscaling/v2alpha1",
|
"ImportPath": "k8s.io/client-go/informers/autoscaling/v2alpha1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/informers/batch",
|
"ImportPath": "k8s.io/client-go/informers/batch",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/informers/batch/v1",
|
"ImportPath": "k8s.io/client-go/informers/batch/v1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/informers/batch/v1beta1",
|
"ImportPath": "k8s.io/client-go/informers/batch/v1beta1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/informers/batch/v2alpha1",
|
"ImportPath": "k8s.io/client-go/informers/batch/v2alpha1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/informers/certificates",
|
"ImportPath": "k8s.io/client-go/informers/certificates",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/informers/certificates/v1beta1",
|
"ImportPath": "k8s.io/client-go/informers/certificates/v1beta1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/informers/core",
|
"ImportPath": "k8s.io/client-go/informers/core",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/informers/core/v1",
|
"ImportPath": "k8s.io/client-go/informers/core/v1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/informers/extensions",
|
"ImportPath": "k8s.io/client-go/informers/extensions",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/informers/extensions/v1beta1",
|
"ImportPath": "k8s.io/client-go/informers/extensions/v1beta1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/informers/internalinterfaces",
|
"ImportPath": "k8s.io/client-go/informers/internalinterfaces",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/informers/networking",
|
"ImportPath": "k8s.io/client-go/informers/networking",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/informers/networking/v1",
|
"ImportPath": "k8s.io/client-go/informers/networking/v1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/informers/policy",
|
"ImportPath": "k8s.io/client-go/informers/policy",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/informers/policy/v1beta1",
|
"ImportPath": "k8s.io/client-go/informers/policy/v1beta1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/informers/rbac",
|
"ImportPath": "k8s.io/client-go/informers/rbac",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/informers/rbac/v1",
|
"ImportPath": "k8s.io/client-go/informers/rbac/v1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/informers/rbac/v1alpha1",
|
"ImportPath": "k8s.io/client-go/informers/rbac/v1alpha1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/informers/rbac/v1beta1",
|
"ImportPath": "k8s.io/client-go/informers/rbac/v1beta1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/informers/scheduling",
|
"ImportPath": "k8s.io/client-go/informers/scheduling",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/informers/scheduling/v1alpha1",
|
"ImportPath": "k8s.io/client-go/informers/scheduling/v1alpha1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/informers/settings",
|
"ImportPath": "k8s.io/client-go/informers/settings",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/informers/settings/v1alpha1",
|
"ImportPath": "k8s.io/client-go/informers/settings/v1alpha1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/informers/storage",
|
"ImportPath": "k8s.io/client-go/informers/storage",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/informers/storage/v1",
|
"ImportPath": "k8s.io/client-go/informers/storage/v1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/informers/storage/v1beta1",
|
"ImportPath": "k8s.io/client-go/informers/storage/v1beta1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes",
|
"ImportPath": "k8s.io/client-go/kubernetes",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes/fake",
|
"ImportPath": "k8s.io/client-go/kubernetes/fake",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes/scheme",
|
"ImportPath": "k8s.io/client-go/kubernetes/scheme",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes/typed/admissionregistration/v1alpha1",
|
"ImportPath": "k8s.io/client-go/kubernetes/typed/admissionregistration/v1alpha1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes/typed/admissionregistration/v1alpha1/fake",
|
"ImportPath": "k8s.io/client-go/kubernetes/typed/admissionregistration/v1alpha1/fake",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes/typed/apps/v1beta1",
|
"ImportPath": "k8s.io/client-go/kubernetes/typed/apps/v1beta1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes/typed/apps/v1beta1/fake",
|
"ImportPath": "k8s.io/client-go/kubernetes/typed/apps/v1beta1/fake",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes/typed/apps/v1beta2",
|
"ImportPath": "k8s.io/client-go/kubernetes/typed/apps/v1beta2",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes/typed/apps/v1beta2/fake",
|
"ImportPath": "k8s.io/client-go/kubernetes/typed/apps/v1beta2/fake",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes/typed/authentication/v1",
|
"ImportPath": "k8s.io/client-go/kubernetes/typed/authentication/v1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes/typed/authentication/v1/fake",
|
"ImportPath": "k8s.io/client-go/kubernetes/typed/authentication/v1/fake",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes/typed/authentication/v1beta1",
|
"ImportPath": "k8s.io/client-go/kubernetes/typed/authentication/v1beta1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes/typed/authentication/v1beta1/fake",
|
"ImportPath": "k8s.io/client-go/kubernetes/typed/authentication/v1beta1/fake",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes/typed/authorization/v1",
|
"ImportPath": "k8s.io/client-go/kubernetes/typed/authorization/v1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes/typed/authorization/v1/fake",
|
"ImportPath": "k8s.io/client-go/kubernetes/typed/authorization/v1/fake",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes/typed/authorization/v1beta1",
|
"ImportPath": "k8s.io/client-go/kubernetes/typed/authorization/v1beta1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes/typed/authorization/v1beta1/fake",
|
"ImportPath": "k8s.io/client-go/kubernetes/typed/authorization/v1beta1/fake",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes/typed/autoscaling/v1",
|
"ImportPath": "k8s.io/client-go/kubernetes/typed/autoscaling/v1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes/typed/autoscaling/v1/fake",
|
"ImportPath": "k8s.io/client-go/kubernetes/typed/autoscaling/v1/fake",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes/typed/autoscaling/v2alpha1",
|
"ImportPath": "k8s.io/client-go/kubernetes/typed/autoscaling/v2alpha1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes/typed/autoscaling/v2alpha1/fake",
|
"ImportPath": "k8s.io/client-go/kubernetes/typed/autoscaling/v2alpha1/fake",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes/typed/batch/v1",
|
"ImportPath": "k8s.io/client-go/kubernetes/typed/batch/v1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes/typed/batch/v1/fake",
|
"ImportPath": "k8s.io/client-go/kubernetes/typed/batch/v1/fake",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes/typed/batch/v1beta1",
|
"ImportPath": "k8s.io/client-go/kubernetes/typed/batch/v1beta1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes/typed/batch/v1beta1/fake",
|
"ImportPath": "k8s.io/client-go/kubernetes/typed/batch/v1beta1/fake",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes/typed/batch/v2alpha1",
|
"ImportPath": "k8s.io/client-go/kubernetes/typed/batch/v2alpha1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes/typed/batch/v2alpha1/fake",
|
"ImportPath": "k8s.io/client-go/kubernetes/typed/batch/v2alpha1/fake",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes/typed/certificates/v1beta1",
|
"ImportPath": "k8s.io/client-go/kubernetes/typed/certificates/v1beta1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes/typed/certificates/v1beta1/fake",
|
"ImportPath": "k8s.io/client-go/kubernetes/typed/certificates/v1beta1/fake",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes/typed/core/v1",
|
"ImportPath": "k8s.io/client-go/kubernetes/typed/core/v1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes/typed/core/v1/fake",
|
"ImportPath": "k8s.io/client-go/kubernetes/typed/core/v1/fake",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes/typed/extensions/v1beta1",
|
"ImportPath": "k8s.io/client-go/kubernetes/typed/extensions/v1beta1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes/typed/extensions/v1beta1/fake",
|
"ImportPath": "k8s.io/client-go/kubernetes/typed/extensions/v1beta1/fake",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes/typed/networking/v1",
|
"ImportPath": "k8s.io/client-go/kubernetes/typed/networking/v1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes/typed/networking/v1/fake",
|
"ImportPath": "k8s.io/client-go/kubernetes/typed/networking/v1/fake",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes/typed/policy/v1beta1",
|
"ImportPath": "k8s.io/client-go/kubernetes/typed/policy/v1beta1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes/typed/policy/v1beta1/fake",
|
"ImportPath": "k8s.io/client-go/kubernetes/typed/policy/v1beta1/fake",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes/typed/rbac/v1",
|
"ImportPath": "k8s.io/client-go/kubernetes/typed/rbac/v1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes/typed/rbac/v1/fake",
|
"ImportPath": "k8s.io/client-go/kubernetes/typed/rbac/v1/fake",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes/typed/rbac/v1alpha1",
|
"ImportPath": "k8s.io/client-go/kubernetes/typed/rbac/v1alpha1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes/typed/rbac/v1alpha1/fake",
|
"ImportPath": "k8s.io/client-go/kubernetes/typed/rbac/v1alpha1/fake",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes/typed/rbac/v1beta1",
|
"ImportPath": "k8s.io/client-go/kubernetes/typed/rbac/v1beta1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes/typed/rbac/v1beta1/fake",
|
"ImportPath": "k8s.io/client-go/kubernetes/typed/rbac/v1beta1/fake",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes/typed/scheduling/v1alpha1",
|
"ImportPath": "k8s.io/client-go/kubernetes/typed/scheduling/v1alpha1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes/typed/scheduling/v1alpha1/fake",
|
"ImportPath": "k8s.io/client-go/kubernetes/typed/scheduling/v1alpha1/fake",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes/typed/settings/v1alpha1",
|
"ImportPath": "k8s.io/client-go/kubernetes/typed/settings/v1alpha1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes/typed/settings/v1alpha1/fake",
|
"ImportPath": "k8s.io/client-go/kubernetes/typed/settings/v1alpha1/fake",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes/typed/storage/v1",
|
"ImportPath": "k8s.io/client-go/kubernetes/typed/storage/v1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes/typed/storage/v1/fake",
|
"ImportPath": "k8s.io/client-go/kubernetes/typed/storage/v1/fake",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes/typed/storage/v1beta1",
|
"ImportPath": "k8s.io/client-go/kubernetes/typed/storage/v1beta1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/kubernetes/typed/storage/v1beta1/fake",
|
"ImportPath": "k8s.io/client-go/kubernetes/typed/storage/v1beta1/fake",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/listers/admissionregistration/v1alpha1",
|
"ImportPath": "k8s.io/client-go/listers/admissionregistration/v1alpha1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/listers/apps/v1beta1",
|
"ImportPath": "k8s.io/client-go/listers/apps/v1beta1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/listers/apps/v1beta2",
|
"ImportPath": "k8s.io/client-go/listers/apps/v1beta2",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/listers/autoscaling/v1",
|
"ImportPath": "k8s.io/client-go/listers/autoscaling/v1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/listers/autoscaling/v2alpha1",
|
"ImportPath": "k8s.io/client-go/listers/autoscaling/v2alpha1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/listers/batch/v1",
|
"ImportPath": "k8s.io/client-go/listers/batch/v1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/listers/batch/v1beta1",
|
"ImportPath": "k8s.io/client-go/listers/batch/v1beta1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/listers/batch/v2alpha1",
|
"ImportPath": "k8s.io/client-go/listers/batch/v2alpha1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/listers/certificates/v1beta1",
|
"ImportPath": "k8s.io/client-go/listers/certificates/v1beta1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/listers/core/v1",
|
"ImportPath": "k8s.io/client-go/listers/core/v1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/listers/extensions/v1beta1",
|
"ImportPath": "k8s.io/client-go/listers/extensions/v1beta1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/listers/networking/v1",
|
"ImportPath": "k8s.io/client-go/listers/networking/v1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/listers/policy/v1beta1",
|
"ImportPath": "k8s.io/client-go/listers/policy/v1beta1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/listers/rbac/v1",
|
"ImportPath": "k8s.io/client-go/listers/rbac/v1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/listers/rbac/v1alpha1",
|
"ImportPath": "k8s.io/client-go/listers/rbac/v1alpha1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/listers/rbac/v1beta1",
|
"ImportPath": "k8s.io/client-go/listers/rbac/v1beta1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/listers/scheduling/v1alpha1",
|
"ImportPath": "k8s.io/client-go/listers/scheduling/v1alpha1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/listers/settings/v1alpha1",
|
"ImportPath": "k8s.io/client-go/listers/settings/v1alpha1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/listers/storage/v1",
|
"ImportPath": "k8s.io/client-go/listers/storage/v1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/listers/storage/v1beta1",
|
"ImportPath": "k8s.io/client-go/listers/storage/v1beta1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/pkg/version",
|
"ImportPath": "k8s.io/client-go/pkg/version",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/rest",
|
"ImportPath": "k8s.io/client-go/rest",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/rest/watch",
|
"ImportPath": "k8s.io/client-go/rest/watch",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/testing",
|
"ImportPath": "k8s.io/client-go/testing",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/tools/auth",
|
"ImportPath": "k8s.io/client-go/tools/auth",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/tools/cache",
|
"ImportPath": "k8s.io/client-go/tools/cache",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/tools/clientcmd",
|
"ImportPath": "k8s.io/client-go/tools/clientcmd",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/tools/clientcmd/api",
|
"ImportPath": "k8s.io/client-go/tools/clientcmd/api",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/tools/clientcmd/api/latest",
|
"ImportPath": "k8s.io/client-go/tools/clientcmd/api/latest",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/tools/clientcmd/api/v1",
|
"ImportPath": "k8s.io/client-go/tools/clientcmd/api/v1",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/tools/metrics",
|
"ImportPath": "k8s.io/client-go/tools/metrics",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/tools/reference",
|
"ImportPath": "k8s.io/client-go/tools/reference",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/transport",
|
"ImportPath": "k8s.io/client-go/transport",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/util/cert",
|
"ImportPath": "k8s.io/client-go/util/cert",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/util/flowcontrol",
|
"ImportPath": "k8s.io/client-go/util/flowcontrol",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/util/homedir",
|
"ImportPath": "k8s.io/client-go/util/homedir",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/client-go/util/integer",
|
"ImportPath": "k8s.io/client-go/util/integer",
|
||||||
"Rev": "48ade6dd30d8e37a8fa612b8b59b3909ac63d0d6"
|
"Rev": "1213be369b8855b478d4d7142096475c11481d14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/kube-openapi/pkg/builder",
|
"ImportPath": "k8s.io/kube-openapi/pkg/builder",
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,13 @@ const (
|
||||||
// Allow asynchronous coordination of object creation.
|
// Allow asynchronous coordination of object creation.
|
||||||
// Auto-enabled by the Initializers admission plugin.
|
// Auto-enabled by the Initializers admission plugin.
|
||||||
Initializers utilfeature.Feature = "Initializers"
|
Initializers utilfeature.Feature = "Initializers"
|
||||||
|
|
||||||
|
// owner: @smarterclayton
|
||||||
|
// alpha: v1.8
|
||||||
|
//
|
||||||
|
// Allow API clients to retrieve resource lists in chunks rather than
|
||||||
|
// all at once.
|
||||||
|
APIListChunking utilfeature.Feature = "APIListChunking"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
@ -68,4 +75,5 @@ var defaultKubernetesFeatureGates = map[utilfeature.Feature]utilfeature.FeatureS
|
||||||
AdvancedAuditing: {Default: false, PreRelease: utilfeature.Alpha},
|
AdvancedAuditing: {Default: false, PreRelease: utilfeature.Alpha},
|
||||||
APIResponseCompression: {Default: false, PreRelease: utilfeature.Alpha},
|
APIResponseCompression: {Default: false, PreRelease: utilfeature.Alpha},
|
||||||
Initializers: {Default: false, PreRelease: utilfeature.Alpha},
|
Initializers: {Default: false, PreRelease: utilfeature.Alpha},
|
||||||
|
APIListChunking: {Default: false, PreRelease: utilfeature.Alpha},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -293,6 +293,8 @@ func (e *Store) ListPredicate(ctx genericapirequest.Context, p storage.Selection
|
||||||
options = &metainternalversion.ListOptions{ResourceVersion: ""}
|
options = &metainternalversion.ListOptions{ResourceVersion: ""}
|
||||||
}
|
}
|
||||||
p.IncludeUninitialized = options.IncludeUninitialized
|
p.IncludeUninitialized = options.IncludeUninitialized
|
||||||
|
p.Limit = options.Limit
|
||||||
|
p.Continue = options.Continue
|
||||||
list := e.NewListFunc()
|
list := e.NewListFunc()
|
||||||
qualifiedResource := e.qualifiedResourceFromContext(ctx)
|
qualifiedResource := e.qualifiedResourceFromContext(ctx)
|
||||||
if name, ok := p.MatchesSingle(); ok {
|
if name, ok := p.MatchesSingle(); ok {
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type EtcdOptions struct {
|
type EtcdOptions struct {
|
||||||
|
// The value of Paging on StorageConfig will be overriden by the
|
||||||
|
// calculated feature gate value.
|
||||||
StorageConfig storagebackend.Config
|
StorageConfig storagebackend.Config
|
||||||
EncryptionProviderConfigFilepath string
|
EncryptionProviderConfigFilepath string
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,8 +43,10 @@ go_library(
|
||||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer/recognizer:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer/recognizer:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||||
|
"//vendor/k8s.io/apiserver/pkg/features:go_default_library",
|
||||||
"//vendor/k8s.io/apiserver/pkg/storage/storagebackend:go_default_library",
|
"//vendor/k8s.io/apiserver/pkg/storage/storagebackend:go_default_library",
|
||||||
"//vendor/k8s.io/apiserver/pkg/storage/value:go_default_library",
|
"//vendor/k8s.io/apiserver/pkg/storage/value:go_default_library",
|
||||||
|
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,10 @@ import (
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
|
"k8s.io/apiserver/pkg/features"
|
||||||
"k8s.io/apiserver/pkg/storage/storagebackend"
|
"k8s.io/apiserver/pkg/storage/storagebackend"
|
||||||
"k8s.io/apiserver/pkg/storage/value"
|
"k8s.io/apiserver/pkg/storage/value"
|
||||||
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Backend describes the storage servers, the information here should be enough
|
// Backend describes the storage servers, the information here should be enough
|
||||||
|
|
@ -112,6 +114,8 @@ type groupResourceOverrides struct {
|
||||||
decoderDecoratorFn func([]runtime.Decoder) []runtime.Decoder
|
decoderDecoratorFn func([]runtime.Decoder) []runtime.Decoder
|
||||||
// transformer is optional and shall encrypt that resource at rest.
|
// transformer is optional and shall encrypt that resource at rest.
|
||||||
transformer value.Transformer
|
transformer value.Transformer
|
||||||
|
// disablePaging will prevent paging on the provided resource.
|
||||||
|
disablePaging bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply overrides the provided config and options if the override has a value in that position
|
// Apply overrides the provided config and options if the override has a value in that position
|
||||||
|
|
@ -138,6 +142,9 @@ func (o groupResourceOverrides) Apply(config *storagebackend.Config, options *St
|
||||||
if o.transformer != nil {
|
if o.transformer != nil {
|
||||||
config.Transformer = o.transformer
|
config.Transformer = o.transformer
|
||||||
}
|
}
|
||||||
|
if o.disablePaging {
|
||||||
|
config.Paging = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ StorageFactory = &DefaultStorageFactory{}
|
var _ StorageFactory = &DefaultStorageFactory{}
|
||||||
|
|
@ -157,6 +164,7 @@ var specialDefaultResourcePrefixes = map[schema.GroupResource]string{
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDefaultStorageFactory(config storagebackend.Config, defaultMediaType string, defaultSerializer runtime.StorageSerializer, resourceEncodingConfig ResourceEncodingConfig, resourceConfig APIResourceConfigSource) *DefaultStorageFactory {
|
func NewDefaultStorageFactory(config storagebackend.Config, defaultMediaType string, defaultSerializer runtime.StorageSerializer, resourceEncodingConfig ResourceEncodingConfig, resourceConfig APIResourceConfigSource) *DefaultStorageFactory {
|
||||||
|
config.Paging = utilfeature.DefaultFeatureGate.Enabled(features.APIListChunking)
|
||||||
if len(defaultMediaType) == 0 {
|
if len(defaultMediaType) == 0 {
|
||||||
defaultMediaType = runtime.ContentTypeJSON
|
defaultMediaType = runtime.ContentTypeJSON
|
||||||
}
|
}
|
||||||
|
|
@ -185,6 +193,14 @@ func (s *DefaultStorageFactory) SetEtcdPrefix(groupResource schema.GroupResource
|
||||||
s.Overrides[groupResource] = overrides
|
s.Overrides[groupResource] = overrides
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetDisableAPIListChunking allows a specific resource to disable paging at the storage layer, to prevent
|
||||||
|
// exposure of key names in continuations. This may be overriden by feature gates.
|
||||||
|
func (s *DefaultStorageFactory) SetDisableAPIListChunking(groupResource schema.GroupResource) {
|
||||||
|
overrides := s.Overrides[groupResource]
|
||||||
|
overrides.disablePaging = true
|
||||||
|
s.Overrides[groupResource] = overrides
|
||||||
|
}
|
||||||
|
|
||||||
// SetResourceEtcdPrefix sets the prefix for a resource, but not the base-dir. You'll end up in `etcdPrefix/resourceEtcdPrefix`.
|
// SetResourceEtcdPrefix sets the prefix for a resource, but not the base-dir. You'll end up in `etcdPrefix/resourceEtcdPrefix`.
|
||||||
func (s *DefaultStorageFactory) SetResourceEtcdPrefix(groupResource schema.GroupResource, prefix string) {
|
func (s *DefaultStorageFactory) SetResourceEtcdPrefix(groupResource schema.GroupResource, prefix string) {
|
||||||
overrides := s.Overrides[groupResource]
|
overrides := s.Overrides[groupResource]
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,8 @@ go_library(
|
||||||
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/watch:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/watch:go_default_library",
|
||||||
|
"//vendor/k8s.io/apiserver/pkg/features:go_default_library",
|
||||||
|
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||||
"//vendor/k8s.io/apiserver/pkg/util/trace:go_default_library",
|
"//vendor/k8s.io/apiserver/pkg/util/trace:go_default_library",
|
||||||
"//vendor/k8s.io/client-go/tools/cache:go_default_library",
|
"//vendor/k8s.io/client-go/tools/cache:go_default_library",
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,8 @@ import (
|
||||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
"k8s.io/apimachinery/pkg/watch"
|
"k8s.io/apimachinery/pkg/watch"
|
||||||
|
"k8s.io/apiserver/pkg/features"
|
||||||
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
utiltrace "k8s.io/apiserver/pkg/util/trace"
|
utiltrace "k8s.io/apiserver/pkg/util/trace"
|
||||||
"k8s.io/client-go/tools/cache"
|
"k8s.io/client-go/tools/cache"
|
||||||
)
|
)
|
||||||
|
|
@ -406,9 +408,11 @@ func (c *Cacher) Get(ctx context.Context, key string, resourceVersion string, ob
|
||||||
|
|
||||||
// Implements storage.Interface.
|
// Implements storage.Interface.
|
||||||
func (c *Cacher) GetToList(ctx context.Context, key string, resourceVersion string, pred SelectionPredicate, listObj runtime.Object) error {
|
func (c *Cacher) GetToList(ctx context.Context, key string, resourceVersion string, pred SelectionPredicate, listObj runtime.Object) error {
|
||||||
if resourceVersion == "" {
|
pagingEnabled := utilfeature.DefaultFeatureGate.Enabled(features.APIListChunking)
|
||||||
|
if resourceVersion == "" || (pagingEnabled && (len(pred.Continue) > 0 || pred.Limit > 0)) {
|
||||||
// If resourceVersion is not specified, serve it from underlying
|
// If resourceVersion is not specified, serve it from underlying
|
||||||
// storage (for backward compatibility).
|
// storage (for backward compatibility). If a continuation or limit is
|
||||||
|
// requested, serve it from the underlying storage as well.
|
||||||
return c.storage.GetToList(ctx, key, resourceVersion, pred, listObj)
|
return c.storage.GetToList(ctx, key, resourceVersion, pred, listObj)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -459,7 +463,7 @@ func (c *Cacher) GetToList(ctx context.Context, key string, resourceVersion stri
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if c.versioner != nil {
|
if c.versioner != nil {
|
||||||
if err := c.versioner.UpdateList(listObj, readResourceVersion); err != nil {
|
if err := c.versioner.UpdateList(listObj, readResourceVersion, ""); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -468,9 +472,11 @@ func (c *Cacher) GetToList(ctx context.Context, key string, resourceVersion stri
|
||||||
|
|
||||||
// Implements storage.Interface.
|
// Implements storage.Interface.
|
||||||
func (c *Cacher) List(ctx context.Context, key string, resourceVersion string, pred SelectionPredicate, listObj runtime.Object) error {
|
func (c *Cacher) List(ctx context.Context, key string, resourceVersion string, pred SelectionPredicate, listObj runtime.Object) error {
|
||||||
if resourceVersion == "" {
|
pagingEnabled := utilfeature.DefaultFeatureGate.Enabled(features.APIListChunking)
|
||||||
|
if resourceVersion == "" || (pagingEnabled && (len(pred.Continue) > 0 || pred.Limit > 0)) {
|
||||||
// If resourceVersion is not specified, serve it from underlying
|
// If resourceVersion is not specified, serve it from underlying
|
||||||
// storage (for backward compatibility).
|
// storage (for backward compatibility). If a continuation or limit is
|
||||||
|
// requested, serve it from the underlying storage as well.
|
||||||
return c.storage.List(ctx, key, resourceVersion, pred, listObj)
|
return c.storage.List(ctx, key, resourceVersion, pred, listObj)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -527,7 +533,7 @@ func (c *Cacher) List(ctx context.Context, key string, resourceVersion string, p
|
||||||
}
|
}
|
||||||
trace.Step(fmt.Sprintf("Filtered %d items", listVal.Len()))
|
trace.Step(fmt.Sprintf("Filtered %d items", listVal.Len()))
|
||||||
if c.versioner != nil {
|
if c.versioner != nil {
|
||||||
if err := c.versioner.UpdateList(listObj, readResourceVersion); err != nil {
|
if err := c.versioner.UpdateList(listObj, readResourceVersion, ""); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ func (a APIObjectVersioner) UpdateObject(obj runtime.Object, resourceVersion uin
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateList implements Versioner
|
// UpdateList implements Versioner
|
||||||
func (a APIObjectVersioner) UpdateList(obj runtime.Object, resourceVersion uint64) error {
|
func (a APIObjectVersioner) UpdateList(obj runtime.Object, resourceVersion uint64, nextKey string) error {
|
||||||
listAccessor, err := meta.ListAccessor(obj)
|
listAccessor, err := meta.ListAccessor(obj)
|
||||||
if err != nil || listAccessor == nil {
|
if err != nil || listAccessor == nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -53,6 +53,7 @@ func (a APIObjectVersioner) UpdateList(obj runtime.Object, resourceVersion uint6
|
||||||
versionString = strconv.FormatUint(resourceVersion, 10)
|
versionString = strconv.FormatUint(resourceVersion, 10)
|
||||||
}
|
}
|
||||||
listAccessor.SetResourceVersion(versionString)
|
listAccessor.SetResourceVersion(versionString)
|
||||||
|
listAccessor.SetContinue(nextKey)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -365,7 +365,7 @@ func (h *etcdHelper) GetToList(ctx context.Context, key string, resourceVersion
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
trace.Step("Object decoded")
|
trace.Step("Object decoded")
|
||||||
if err := h.versioner.UpdateList(listObj, response.Index); err != nil {
|
if err := h.versioner.UpdateList(listObj, response.Index, ""); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -445,7 +445,7 @@ func (h *etcdHelper) List(ctx context.Context, key string, resourceVersion strin
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
trace.Step("Node list decoded")
|
trace.Step("Node list decoded")
|
||||||
if err := h.versioner.UpdateList(listObj, index); err != nil {
|
if err := h.versioner.UpdateList(listObj, index, ""); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -322,6 +322,7 @@ func NewUnsecuredEtcd3TestClientServer(t *testing.T, scheme *runtime.Scheme) (*E
|
||||||
ServerList: server.V3Client.Endpoints(),
|
ServerList: server.V3Client.Endpoints(),
|
||||||
DeserializationCacheSize: etcdtest.DeserializationCacheSize,
|
DeserializationCacheSize: etcdtest.DeserializationCacheSize,
|
||||||
Copier: scheme,
|
Copier: scheme,
|
||||||
|
Paging: true,
|
||||||
}
|
}
|
||||||
return server, config
|
return server, config
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ go_library(
|
||||||
name = "go_default_library",
|
name = "go_default_library",
|
||||||
srcs = [
|
srcs = [
|
||||||
"compact.go",
|
"compact.go",
|
||||||
|
"errors.go",
|
||||||
"event.go",
|
"event.go",
|
||||||
"store.go",
|
"store.go",
|
||||||
"watcher.go",
|
"watcher.go",
|
||||||
|
|
@ -51,8 +52,8 @@ go_library(
|
||||||
"//vendor/github.com/coreos/etcd/mvcc/mvccpb:go_default_library",
|
"//vendor/github.com/coreos/etcd/mvcc/mvccpb:go_default_library",
|
||||||
"//vendor/github.com/golang/glog:go_default_library",
|
"//vendor/github.com/golang/glog:go_default_library",
|
||||||
"//vendor/golang.org/x/net/context:go_default_library",
|
"//vendor/golang.org/x/net/context:go_default_library",
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
|
||||||
"//vendor/k8s.io/apimachinery/pkg/conversion:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/conversion:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package etcd3
|
||||||
|
|
||||||
|
import (
|
||||||
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
|
||||||
|
etcdrpc "github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes"
|
||||||
|
)
|
||||||
|
|
||||||
|
func interpretWatchError(err error) error {
|
||||||
|
switch {
|
||||||
|
case err == etcdrpc.ErrCompacted:
|
||||||
|
return errors.NewResourceExpired("The resourceVersion for the provided watch is too old.")
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func interpretListError(err error, paging bool) error {
|
||||||
|
switch {
|
||||||
|
case err == etcdrpc.ErrCompacted:
|
||||||
|
if paging {
|
||||||
|
return errors.NewResourceExpired("The provided from parameter is too old to display a consistent list result. You must start a new list without the from.")
|
||||||
|
}
|
||||||
|
return errors.NewResourceExpired("The resourceVersion for the provided list is too old.")
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
@ -18,10 +18,13 @@ package etcd3
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"path"
|
"path"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -60,12 +63,13 @@ type store struct {
|
||||||
client *clientv3.Client
|
client *clientv3.Client
|
||||||
// getOpts contains additional options that should be passed
|
// getOpts contains additional options that should be passed
|
||||||
// to all Get() calls.
|
// to all Get() calls.
|
||||||
getOps []clientv3.OpOption
|
getOps []clientv3.OpOption
|
||||||
codec runtime.Codec
|
codec runtime.Codec
|
||||||
versioner storage.Versioner
|
versioner storage.Versioner
|
||||||
transformer value.Transformer
|
transformer value.Transformer
|
||||||
pathPrefix string
|
pathPrefix string
|
||||||
watcher *watcher
|
watcher *watcher
|
||||||
|
pagingEnabled bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type elemForDecode struct {
|
type elemForDecode struct {
|
||||||
|
|
@ -82,23 +86,24 @@ type objState struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns an etcd3 implementation of storage.Interface.
|
// New returns an etcd3 implementation of storage.Interface.
|
||||||
func New(c *clientv3.Client, codec runtime.Codec, prefix string, transformer value.Transformer) storage.Interface {
|
func New(c *clientv3.Client, codec runtime.Codec, prefix string, transformer value.Transformer, pagingEnabled bool) storage.Interface {
|
||||||
return newStore(c, true, codec, prefix, transformer)
|
return newStore(c, true, pagingEnabled, codec, prefix, transformer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewWithNoQuorumRead returns etcd3 implementation of storage.Interface
|
// NewWithNoQuorumRead returns etcd3 implementation of storage.Interface
|
||||||
// where Get operations don't require quorum read.
|
// where Get operations don't require quorum read.
|
||||||
func NewWithNoQuorumRead(c *clientv3.Client, codec runtime.Codec, prefix string, transformer value.Transformer) storage.Interface {
|
func NewWithNoQuorumRead(c *clientv3.Client, codec runtime.Codec, prefix string, transformer value.Transformer, pagingEnabled bool) storage.Interface {
|
||||||
return newStore(c, false, codec, prefix, transformer)
|
return newStore(c, false, pagingEnabled, codec, prefix, transformer)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newStore(c *clientv3.Client, quorumRead bool, codec runtime.Codec, prefix string, transformer value.Transformer) *store {
|
func newStore(c *clientv3.Client, quorumRead, pagingEnabled bool, codec runtime.Codec, prefix string, transformer value.Transformer) *store {
|
||||||
versioner := etcd.APIObjectVersioner{}
|
versioner := etcd.APIObjectVersioner{}
|
||||||
result := &store{
|
result := &store{
|
||||||
client: c,
|
client: c,
|
||||||
codec: codec,
|
codec: codec,
|
||||||
versioner: versioner,
|
versioner: versioner,
|
||||||
transformer: transformer,
|
transformer: transformer,
|
||||||
|
pagingEnabled: pagingEnabled,
|
||||||
// for compatibility with etcd2 impl.
|
// for compatibility with etcd2 impl.
|
||||||
// no-op for default prefix of '/registry'.
|
// no-op for default prefix of '/registry'.
|
||||||
// keeps compatibility with etcd2 impl for custom prefixes that don't start with '/'
|
// keeps compatibility with etcd2 impl for custom prefixes that don't start with '/'
|
||||||
|
|
@ -386,7 +391,66 @@ func (s *store) GetToList(ctx context.Context, key string, resourceVersion strin
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// update version with cluster level revision
|
// update version with cluster level revision
|
||||||
return s.versioner.UpdateList(listObj, uint64(getResp.Header.Revision))
|
return s.versioner.UpdateList(listObj, uint64(getResp.Header.Revision), "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// continueToken is a simple structured object for encoding the state of a continue token.
|
||||||
|
// TODO: if we change the version of the encoded from, we can't start encoding the new version
|
||||||
|
// until all other servers are upgraded (i.e. we need to support rolling schema)
|
||||||
|
// This is a public API struct and cannot change.
|
||||||
|
type continueToken struct {
|
||||||
|
APIVersion string `json:"v"`
|
||||||
|
ResourceVersion int64 `json:"rv"`
|
||||||
|
StartKey string `json:"start"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseFrom transforms an encoded predicate from into a versioned struct.
|
||||||
|
// TODO: return a typed error that instructs clients that they must relist
|
||||||
|
func decodeContinue(continueValue, keyPrefix string) (fromKey string, rv int64, err error) {
|
||||||
|
data, err := base64.RawURLEncoding.DecodeString(continueValue)
|
||||||
|
if err != nil {
|
||||||
|
return "", 0, fmt.Errorf("continue key is not valid: %v", err)
|
||||||
|
}
|
||||||
|
var c continueToken
|
||||||
|
if err := json.Unmarshal(data, &c); err != nil {
|
||||||
|
return "", 0, fmt.Errorf("continue key is not valid: %v", err)
|
||||||
|
}
|
||||||
|
switch c.APIVersion {
|
||||||
|
case "v1alpha1":
|
||||||
|
if c.ResourceVersion == 0 {
|
||||||
|
return "", 0, fmt.Errorf("continue key is not valid: incorrect encoded start resourceVersion (version v1alpha1)")
|
||||||
|
}
|
||||||
|
if len(c.StartKey) == 0 {
|
||||||
|
return "", 0, fmt.Errorf("continue key is not valid: encoded start key empty (version v1alpha1)")
|
||||||
|
}
|
||||||
|
// defend against path traversal attacks by clients - path.Clean will ensure that startKey cannot
|
||||||
|
// be at a higher level of the hierarchy, and so when we append the key prefix we will end up with
|
||||||
|
// continue start key that is fully qualified and cannot range over anything less specific than
|
||||||
|
// keyPrefix.
|
||||||
|
cleaned := path.Clean(c.StartKey)
|
||||||
|
if cleaned != c.StartKey || cleaned == "." || cleaned == "/" {
|
||||||
|
return "", 0, fmt.Errorf("continue key is not valid: %s", cleaned)
|
||||||
|
}
|
||||||
|
if len(cleaned) == 0 {
|
||||||
|
return "", 0, fmt.Errorf("continue key is not valid: encoded start key empty (version 0)")
|
||||||
|
}
|
||||||
|
return keyPrefix + cleaned, c.ResourceVersion, nil
|
||||||
|
default:
|
||||||
|
return "", 0, fmt.Errorf("continue key is not valid: server does not recognize this encoded version %q", c.APIVersion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// encodeContinue returns a string representing the encoded continuation of the current query.
|
||||||
|
func encodeContinue(key, keyPrefix string, resourceVersion int64) (string, error) {
|
||||||
|
nextKey := strings.TrimPrefix(key, keyPrefix)
|
||||||
|
if nextKey == key {
|
||||||
|
return "", fmt.Errorf("unable to encode next field: the key and key prefix do not match")
|
||||||
|
}
|
||||||
|
out, err := json.Marshal(&continueToken{APIVersion: "v1alpha1", ResourceVersion: resourceVersion, StartKey: nextKey})
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return base64.RawURLEncoding.EncodeToString(out), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// List implements storage.Interface.List.
|
// List implements storage.Interface.List.
|
||||||
|
|
@ -402,16 +466,50 @@ func (s *store) List(ctx context.Context, key, resourceVersion string, pred stor
|
||||||
if !strings.HasSuffix(key, "/") {
|
if !strings.HasSuffix(key, "/") {
|
||||||
key += "/"
|
key += "/"
|
||||||
}
|
}
|
||||||
getResp, err := s.client.KV.Get(ctx, key, clientv3.WithPrefix())
|
keyPrefix := key
|
||||||
|
|
||||||
|
// set the appropriate clientv3 options to filter the returned data set
|
||||||
|
options := make([]clientv3.OpOption, 0, 4)
|
||||||
|
if s.pagingEnabled && pred.Limit > 0 {
|
||||||
|
options = append(options, clientv3.WithLimit(pred.Limit))
|
||||||
|
}
|
||||||
|
var returnedRV int64
|
||||||
|
switch {
|
||||||
|
case s.pagingEnabled && len(pred.Continue) > 0:
|
||||||
|
continueKey, continueRV, err := decodeContinue(pred.Continue, keyPrefix)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
options = append(options, clientv3.WithRange(clientv3.GetPrefixRangeEnd(key)))
|
||||||
|
key = continueKey
|
||||||
|
|
||||||
|
options = append(options, clientv3.WithRev(continueRV))
|
||||||
|
returnedRV = continueRV
|
||||||
|
|
||||||
|
case len(resourceVersion) > 0:
|
||||||
|
fromRV, err := strconv.ParseInt(resourceVersion, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid resource version: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
options = append(options, clientv3.WithPrefix(), clientv3.WithRev(fromRV))
|
||||||
|
returnedRV = fromRV
|
||||||
|
|
||||||
|
default:
|
||||||
|
options = append(options, clientv3.WithPrefix())
|
||||||
|
}
|
||||||
|
|
||||||
|
getResp, err := s.client.KV.Get(ctx, key, options...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return interpretListError(err, len(pred.Continue) > 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
elems := make([]*elemForDecode, 0, len(getResp.Kvs))
|
elems := make([]*elemForDecode, 0, len(getResp.Kvs))
|
||||||
for _, kv := range getResp.Kvs {
|
for _, kv := range getResp.Kvs {
|
||||||
data, _, err := s.transformer.TransformFromStorage(kv.Value, authenticatedDataString(kv.Key))
|
data, _, err := s.transformer.TransformFromStorage(kv.Value, authenticatedDataString(kv.Key))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utilruntime.HandleError(fmt.Errorf("unable to transform key %q: %v", key, err))
|
utilruntime.HandleError(fmt.Errorf("unable to transform key %q: %v", kv.Key, err))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -420,11 +518,31 @@ func (s *store) List(ctx context.Context, key, resourceVersion string, pred stor
|
||||||
rev: uint64(kv.ModRevision),
|
rev: uint64(kv.ModRevision),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := decodeList(elems, storage.SimpleFilter(pred), listPtr, s.codec, s.versioner); err != nil {
|
if err := decodeList(elems, storage.SimpleFilter(pred), listPtr, s.codec, s.versioner); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// update version with cluster level revision
|
|
||||||
return s.versioner.UpdateList(listObj, uint64(getResp.Header.Revision))
|
// indicate to the client which resource version was returned
|
||||||
|
if returnedRV == 0 {
|
||||||
|
returnedRV = getResp.Header.Revision
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case !getResp.More:
|
||||||
|
// no continuation
|
||||||
|
return s.versioner.UpdateList(listObj, uint64(returnedRV), "")
|
||||||
|
case len(getResp.Kvs) == 0:
|
||||||
|
return fmt.Errorf("no results were found, but etcd indicated there were more values")
|
||||||
|
default:
|
||||||
|
// we want to start immediately after the last key
|
||||||
|
// TODO: this reveals info about certain keys
|
||||||
|
key := string(getResp.Kvs[len(getResp.Kvs)-1].Key)
|
||||||
|
next, err := encodeContinue(key+"\x00", keyPrefix, returnedRV)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return s.versioner.UpdateList(listObj, uint64(returnedRV), next)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Watch implements storage.Interface.Watch.
|
// Watch implements storage.Interface.Watch.
|
||||||
|
|
@ -548,8 +666,8 @@ func decode(codec runtime.Codec, versioner storage.Versioner, value []byte, objP
|
||||||
|
|
||||||
// decodeList decodes a list of values into a list of objects, with resource version set to corresponding rev.
|
// decodeList decodes a list of values into a list of objects, with resource version set to corresponding rev.
|
||||||
// On success, ListPtr would be set to the list of objects.
|
// On success, ListPtr would be set to the list of objects.
|
||||||
func decodeList(elems []*elemForDecode, filter storage.FilterFunc, ListPtr interface{}, codec runtime.Codec, versioner storage.Versioner) error {
|
func decodeList(elems []*elemForDecode, filter storage.FilterFunc, listPtr interface{}, codec runtime.Codec, versioner storage.Versioner) error {
|
||||||
v, err := conversion.EnforcePtr(ListPtr)
|
v, err := conversion.EnforcePtr(listPtr)
|
||||||
if err != nil || v.Kind() != reflect.Slice {
|
if err != nil || v.Kind() != reflect.Slice {
|
||||||
panic("need ptr to slice")
|
panic("need ptr to slice")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,12 +18,17 @@ package etcd3
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/coreos/etcd/clientv3"
|
||||||
|
"github.com/coreos/etcd/integration"
|
||||||
|
"golang.org/x/net/context"
|
||||||
apitesting "k8s.io/apimachinery/pkg/api/testing"
|
apitesting "k8s.io/apimachinery/pkg/api/testing"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/fields"
|
"k8s.io/apimachinery/pkg/fields"
|
||||||
|
|
@ -37,10 +42,6 @@ import (
|
||||||
"k8s.io/apiserver/pkg/storage"
|
"k8s.io/apiserver/pkg/storage"
|
||||||
storagetests "k8s.io/apiserver/pkg/storage/tests"
|
storagetests "k8s.io/apiserver/pkg/storage/tests"
|
||||||
"k8s.io/apiserver/pkg/storage/value"
|
"k8s.io/apiserver/pkg/storage/value"
|
||||||
|
|
||||||
"github.com/coreos/etcd/clientv3"
|
|
||||||
"github.com/coreos/etcd/integration"
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var scheme = runtime.NewScheme()
|
var scheme = runtime.NewScheme()
|
||||||
|
|
@ -587,7 +588,7 @@ func TestTransformationFailure(t *testing.T) {
|
||||||
codec := apitesting.TestCodec(codecs, examplev1.SchemeGroupVersion)
|
codec := apitesting.TestCodec(codecs, examplev1.SchemeGroupVersion)
|
||||||
cluster := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1})
|
cluster := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1})
|
||||||
defer cluster.Terminate(t)
|
defer cluster.Terminate(t)
|
||||||
store := newStore(cluster.RandClient(), false, codec, "", prefixTransformer{prefix: []byte(defaultTestPrefix)})
|
store := newStore(cluster.RandClient(), false, false, codec, "", prefixTransformer{prefix: []byte(defaultTestPrefix)})
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
preset := []struct {
|
preset := []struct {
|
||||||
|
|
@ -667,7 +668,8 @@ func TestList(t *testing.T) {
|
||||||
codec := apitesting.TestCodec(codecs, examplev1.SchemeGroupVersion)
|
codec := apitesting.TestCodec(codecs, examplev1.SchemeGroupVersion)
|
||||||
cluster := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1})
|
cluster := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1})
|
||||||
defer cluster.Terminate(t)
|
defer cluster.Terminate(t)
|
||||||
store := newStore(cluster.RandClient(), false, codec, "", prefixTransformer{prefix: []byte(defaultTestPrefix)})
|
store := newStore(cluster.RandClient(), false, true, codec, "", prefixTransformer{prefix: []byte(defaultTestPrefix)})
|
||||||
|
disablePagingStore := newStore(cluster.RandClient(), false, false, codec, "", prefixTransformer{prefix: []byte(defaultTestPrefix)})
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
// Setup storage with the following structure:
|
// Setup storage with the following structure:
|
||||||
|
|
@ -704,40 +706,106 @@ func TestList(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
list := &example.PodList{}
|
||||||
|
store.List(ctx, "/two-level", "0", storage.Everything, list)
|
||||||
|
continueRV, _ := strconv.Atoi(list.ResourceVersion)
|
||||||
|
secondContinuation, err := encodeContinue("/two-level/2", "/two-level/", int64(continueRV))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
prefix string
|
disablePaging bool
|
||||||
pred storage.SelectionPredicate
|
prefix string
|
||||||
expectedOut []*example.Pod
|
pred storage.SelectionPredicate
|
||||||
}{{ // test List on existing key
|
expectedOut []*example.Pod
|
||||||
prefix: "/one-level/",
|
expectContinue bool
|
||||||
pred: storage.Everything,
|
}{
|
||||||
expectedOut: []*example.Pod{preset[0].storedObj},
|
{ // test List on existing key
|
||||||
}, { // test List on non-existing key
|
prefix: "/one-level/",
|
||||||
prefix: "/non-existing/",
|
pred: storage.Everything,
|
||||||
pred: storage.Everything,
|
expectedOut: []*example.Pod{preset[0].storedObj},
|
||||||
expectedOut: nil,
|
|
||||||
}, { // test List with pod name matching
|
|
||||||
prefix: "/one-level/",
|
|
||||||
pred: storage.SelectionPredicate{
|
|
||||||
Label: labels.Everything(),
|
|
||||||
Field: fields.ParseSelectorOrDie("metadata.name!=" + preset[0].storedObj.Name),
|
|
||||||
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, bool, error) {
|
|
||||||
pod := obj.(*example.Pod)
|
|
||||||
return nil, fields.Set{"metadata.name": pod.Name}, pod.Initializers != nil, nil
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
expectedOut: nil,
|
{ // test List on non-existing key
|
||||||
}, { // test List with multiple levels of directories and expect flattened result
|
prefix: "/non-existing/",
|
||||||
prefix: "/two-level/",
|
pred: storage.Everything,
|
||||||
pred: storage.Everything,
|
expectedOut: nil,
|
||||||
expectedOut: []*example.Pod{preset[1].storedObj, preset[2].storedObj},
|
},
|
||||||
}}
|
{ // test List with pod name matching
|
||||||
|
prefix: "/one-level/",
|
||||||
|
pred: storage.SelectionPredicate{
|
||||||
|
Label: labels.Everything(),
|
||||||
|
Field: fields.ParseSelectorOrDie("metadata.name!=" + preset[0].storedObj.Name),
|
||||||
|
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, bool, error) {
|
||||||
|
pod := obj.(*example.Pod)
|
||||||
|
return nil, fields.Set{"metadata.name": pod.Name}, pod.Initializers != nil, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedOut: nil,
|
||||||
|
},
|
||||||
|
{ // test List with limit
|
||||||
|
prefix: "/two-level/",
|
||||||
|
pred: storage.SelectionPredicate{
|
||||||
|
Label: labels.Everything(),
|
||||||
|
Field: fields.Everything(),
|
||||||
|
Limit: 1,
|
||||||
|
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, bool, error) {
|
||||||
|
pod := obj.(*example.Pod)
|
||||||
|
return nil, fields.Set{"metadata.name": pod.Name}, pod.Initializers != nil, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedOut: []*example.Pod{preset[1].storedObj},
|
||||||
|
expectContinue: true,
|
||||||
|
},
|
||||||
|
{ // test List with limit when paging disabled
|
||||||
|
disablePaging: true,
|
||||||
|
prefix: "/two-level/",
|
||||||
|
pred: storage.SelectionPredicate{
|
||||||
|
Label: labels.Everything(),
|
||||||
|
Field: fields.Everything(),
|
||||||
|
Limit: 1,
|
||||||
|
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, bool, error) {
|
||||||
|
pod := obj.(*example.Pod)
|
||||||
|
return nil, fields.Set{"metadata.name": pod.Name}, pod.Initializers != nil, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedOut: []*example.Pod{preset[1].storedObj, preset[2].storedObj},
|
||||||
|
expectContinue: false,
|
||||||
|
},
|
||||||
|
{ // test List with pregenerated continue token
|
||||||
|
prefix: "/two-level/",
|
||||||
|
pred: storage.SelectionPredicate{
|
||||||
|
Label: labels.Everything(),
|
||||||
|
Field: fields.Everything(),
|
||||||
|
Limit: 1,
|
||||||
|
Continue: secondContinuation,
|
||||||
|
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, bool, error) {
|
||||||
|
pod := obj.(*example.Pod)
|
||||||
|
return nil, fields.Set{"metadata.name": pod.Name}, pod.Initializers != nil, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedOut: []*example.Pod{preset[2].storedObj},
|
||||||
|
},
|
||||||
|
{ // test List with multiple levels of directories and expect flattened result
|
||||||
|
prefix: "/two-level/",
|
||||||
|
pred: storage.Everything,
|
||||||
|
expectedOut: []*example.Pod{preset[1].storedObj, preset[2].storedObj},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
out := &example.PodList{}
|
out := &example.PodList{}
|
||||||
err := store.List(ctx, tt.prefix, "0", tt.pred, out)
|
var err error
|
||||||
|
if tt.disablePaging {
|
||||||
|
err = disablePagingStore.List(ctx, tt.prefix, "0", tt.pred, out)
|
||||||
|
} else {
|
||||||
|
err = store.List(ctx, tt.prefix, "0", tt.pred, out)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("List failed: %v", err)
|
t.Fatalf("#%d: List failed: %v", i, err)
|
||||||
|
}
|
||||||
|
if (len(out.Continue) > 0) != tt.expectContinue {
|
||||||
|
t.Errorf("#%d: unexpected continue token: %v", i, out.Continue)
|
||||||
}
|
}
|
||||||
if len(tt.expectedOut) != len(out.Items) {
|
if len(tt.expectedOut) != len(out.Items) {
|
||||||
t.Errorf("#%d: length of list want=%d, get=%d", i, len(tt.expectedOut), len(out.Items))
|
t.Errorf("#%d: length of list want=%d, get=%d", i, len(tt.expectedOut), len(out.Items))
|
||||||
|
|
@ -750,12 +818,75 @@ func TestList(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// test continuations
|
||||||
|
out := &example.PodList{}
|
||||||
|
pred := func(limit int64, continueValue string) storage.SelectionPredicate {
|
||||||
|
return storage.SelectionPredicate{
|
||||||
|
Limit: limit,
|
||||||
|
Continue: continueValue,
|
||||||
|
Label: labels.Everything(),
|
||||||
|
Field: fields.Everything(),
|
||||||
|
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, bool, error) {
|
||||||
|
pod := obj.(*example.Pod)
|
||||||
|
return nil, fields.Set{"metadata.name": pod.Name}, pod.Initializers != nil, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := store.List(ctx, "/", "0", pred(1, ""), out); err != nil {
|
||||||
|
t.Fatalf("Unable to get initial list: %v", err)
|
||||||
|
}
|
||||||
|
if len(out.Continue) == 0 {
|
||||||
|
t.Fatalf("No continuation token set")
|
||||||
|
}
|
||||||
|
if len(out.Items) != 1 || !reflect.DeepEqual(&out.Items[0], preset[0].storedObj) {
|
||||||
|
t.Fatalf("Unexpected first page: %#v", out.Items)
|
||||||
|
}
|
||||||
|
|
||||||
|
continueFromSecondItem := out.Continue
|
||||||
|
|
||||||
|
// no limit, should get two items
|
||||||
|
out = &example.PodList{}
|
||||||
|
if err := store.List(ctx, "/", "0", pred(0, continueFromSecondItem), out); err != nil {
|
||||||
|
t.Fatalf("Unable to get second page: %v", err)
|
||||||
|
}
|
||||||
|
if len(out.Continue) != 0 {
|
||||||
|
t.Fatalf("Unexpected continuation token set")
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(out.Items, []example.Pod{*preset[1].storedObj, *preset[2].storedObj}) {
|
||||||
|
key, rv, err := decodeContinue(continueFromSecondItem, "/")
|
||||||
|
t.Logf("continue token was %d %s %v", rv, key, err)
|
||||||
|
t.Fatalf("Unexpected second page: %#v", out.Items)
|
||||||
|
}
|
||||||
|
|
||||||
|
// limit, should get two more pages
|
||||||
|
out = &example.PodList{}
|
||||||
|
if err := store.List(ctx, "/", "0", pred(1, continueFromSecondItem), out); err != nil {
|
||||||
|
t.Fatalf("Unable to get second page: %v", err)
|
||||||
|
}
|
||||||
|
if len(out.Continue) == 0 {
|
||||||
|
t.Fatalf("No continuation token set")
|
||||||
|
}
|
||||||
|
if len(out.Items) != 1 || !reflect.DeepEqual(&out.Items[0], preset[1].storedObj) {
|
||||||
|
t.Fatalf("Unexpected second page: %#v", out.Items)
|
||||||
|
}
|
||||||
|
continueFromThirdItem := out.Continue
|
||||||
|
out = &example.PodList{}
|
||||||
|
if err := store.List(ctx, "/", "0", pred(1, continueFromThirdItem), out); err != nil {
|
||||||
|
t.Fatalf("Unable to get second page: %v", err)
|
||||||
|
}
|
||||||
|
if len(out.Continue) != 0 {
|
||||||
|
t.Fatalf("Unexpected continuation token set")
|
||||||
|
}
|
||||||
|
if len(out.Items) != 1 || !reflect.DeepEqual(&out.Items[0], preset[2].storedObj) {
|
||||||
|
t.Fatalf("Unexpected third page: %#v", out.Items)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testSetup(t *testing.T) (context.Context, *store, *integration.ClusterV3) {
|
func testSetup(t *testing.T) (context.Context, *store, *integration.ClusterV3) {
|
||||||
codec := apitesting.TestCodec(codecs, examplev1.SchemeGroupVersion)
|
codec := apitesting.TestCodec(codecs, examplev1.SchemeGroupVersion)
|
||||||
cluster := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1})
|
cluster := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1})
|
||||||
store := newStore(cluster.RandClient(), false, codec, "", prefixTransformer{prefix: []byte(defaultTestPrefix)})
|
store := newStore(cluster.RandClient(), false, true, codec, "", prefixTransformer{prefix: []byte(defaultTestPrefix)})
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
return ctx, store, cluster
|
return ctx, store, cluster
|
||||||
}
|
}
|
||||||
|
|
@ -787,9 +918,57 @@ func TestPrefix(t *testing.T) {
|
||||||
"/registry": "/registry",
|
"/registry": "/registry",
|
||||||
}
|
}
|
||||||
for configuredPrefix, effectivePrefix := range testcases {
|
for configuredPrefix, effectivePrefix := range testcases {
|
||||||
store := newStore(cluster.RandClient(), false, codec, configuredPrefix, transformer)
|
store := newStore(cluster.RandClient(), false, true, codec, configuredPrefix, transformer)
|
||||||
if store.pathPrefix != effectivePrefix {
|
if store.pathPrefix != effectivePrefix {
|
||||||
t.Errorf("configured prefix of %s, expected effective prefix of %s, got %s", configuredPrefix, effectivePrefix, store.pathPrefix)
|
t.Errorf("configured prefix of %s, expected effective prefix of %s, got %s", configuredPrefix, effectivePrefix, store.pathPrefix)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func encodeContinueOrDie(apiVersion string, resourceVersion int64, nextKey string) string {
|
||||||
|
out, err := json.Marshal(&continueToken{APIVersion: apiVersion, ResourceVersion: resourceVersion, StartKey: nextKey})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return base64.RawURLEncoding.EncodeToString(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_decodeContinue(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
continueValue string
|
||||||
|
keyPrefix string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
wantFromKey string
|
||||||
|
wantRv int64
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{name: "valid", args: args{continueValue: encodeContinueOrDie("v1alpha1", 1, "key"), keyPrefix: "/test/"}, wantRv: 1, wantFromKey: "/test/key"},
|
||||||
|
|
||||||
|
{name: "empty version", args: args{continueValue: encodeContinueOrDie("", 1, "key"), keyPrefix: "/test/"}, wantErr: true},
|
||||||
|
{name: "invalid version", args: args{continueValue: encodeContinueOrDie("v1", 1, "key"), keyPrefix: "/test/"}, wantErr: true},
|
||||||
|
|
||||||
|
{name: "path traversal - parent", args: args{continueValue: encodeContinueOrDie("v1alpha", 1, "../key"), keyPrefix: "/test/"}, wantErr: true},
|
||||||
|
{name: "path traversal - local", args: args{continueValue: encodeContinueOrDie("v1alpha", 1, "./key"), keyPrefix: "/test/"}, wantErr: true},
|
||||||
|
{name: "path traversal - double parent", args: args{continueValue: encodeContinueOrDie("v1alpha", 1, "./../key"), keyPrefix: "/test/"}, wantErr: true},
|
||||||
|
{name: "path traversal - after parent", args: args{continueValue: encodeContinueOrDie("v1alpha", 1, "key/../.."), keyPrefix: "/test/"}, wantErr: true},
|
||||||
|
{name: "path traversal - separator", args: args{continueValue: encodeContinueOrDie("v1alpha", 1, "/"), keyPrefix: "/test/"}, wantErr: true},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
gotFromKey, gotRv, err := decodeContinue(tt.args.continueValue, tt.args.keyPrefix)
|
||||||
|
if (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("decodeContinue() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if gotFromKey != tt.wantFromKey {
|
||||||
|
t.Errorf("decodeContinue() gotFromKey = %v, want %v", gotFromKey, tt.wantFromKey)
|
||||||
|
}
|
||||||
|
if gotRv != tt.wantRv {
|
||||||
|
t.Errorf("decodeContinue() gotRv = %v, want %v", gotRv, tt.wantRv)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,20 +19,18 @@ package etcd3
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
apierrs "k8s.io/apimachinery/pkg/api/errors"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/watch"
|
"k8s.io/apimachinery/pkg/watch"
|
||||||
"k8s.io/apiserver/pkg/storage"
|
"k8s.io/apiserver/pkg/storage"
|
||||||
"k8s.io/apiserver/pkg/storage/value"
|
"k8s.io/apiserver/pkg/storage/value"
|
||||||
|
|
||||||
"github.com/coreos/etcd/clientv3"
|
"github.com/coreos/etcd/clientv3"
|
||||||
etcdrpc "github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes"
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
@ -139,7 +137,7 @@ func (wc *watchChan) run() {
|
||||||
if err == context.Canceled {
|
if err == context.Canceled {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
errResult := parseError(err)
|
errResult := transformErrorToEvent(err)
|
||||||
if errResult != nil {
|
if errResult != nil {
|
||||||
// error result is guaranteed to be received by user before closing ResultChan.
|
// error result is guaranteed to be received by user before closing ResultChan.
|
||||||
select {
|
select {
|
||||||
|
|
@ -319,28 +317,15 @@ func (wc *watchChan) transform(e *event) (res *watch.Event) {
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseError(err error) *watch.Event {
|
func transformErrorToEvent(err error) *watch.Event {
|
||||||
var status *metav1.Status
|
err = interpretWatchError(err)
|
||||||
switch {
|
if _, ok := err.(apierrs.APIStatus); !ok {
|
||||||
case err == etcdrpc.ErrCompacted:
|
err = apierrs.NewInternalError(err)
|
||||||
status = &metav1.Status{
|
|
||||||
Status: metav1.StatusFailure,
|
|
||||||
Message: err.Error(),
|
|
||||||
Code: http.StatusGone,
|
|
||||||
Reason: metav1.StatusReasonExpired,
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
status = &metav1.Status{
|
|
||||||
Status: metav1.StatusFailure,
|
|
||||||
Message: err.Error(),
|
|
||||||
Code: http.StatusInternalServerError,
|
|
||||||
Reason: metav1.StatusReasonInternalError,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
status := err.(apierrs.APIStatus).Status()
|
||||||
return &watch.Event{
|
return &watch.Event{
|
||||||
Type: watch.Error,
|
Type: watch.Error,
|
||||||
Object: status,
|
Object: &status,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -226,13 +226,13 @@ func TestWatchError(t *testing.T) {
|
||||||
codec := &testCodec{apitesting.TestCodec(codecs, examplev1.SchemeGroupVersion)}
|
codec := &testCodec{apitesting.TestCodec(codecs, examplev1.SchemeGroupVersion)}
|
||||||
cluster := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1})
|
cluster := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1})
|
||||||
defer cluster.Terminate(t)
|
defer cluster.Terminate(t)
|
||||||
invalidStore := newStore(cluster.RandClient(), false, codec, "", prefixTransformer{prefix: []byte("test!")})
|
invalidStore := newStore(cluster.RandClient(), false, true, codec, "", prefixTransformer{prefix: []byte("test!")})
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
w, err := invalidStore.Watch(ctx, "/abc", "0", storage.Everything)
|
w, err := invalidStore.Watch(ctx, "/abc", "0", storage.Everything)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Watch failed: %v", err)
|
t.Fatalf("Watch failed: %v", err)
|
||||||
}
|
}
|
||||||
validStore := newStore(cluster.RandClient(), false, codec, "", prefixTransformer{prefix: []byte("test!")})
|
validStore := newStore(cluster.RandClient(), false, true, codec, "", prefixTransformer{prefix: []byte("test!")})
|
||||||
validStore.GuaranteedUpdate(ctx, "/abc", &example.Pod{}, true, nil, storage.SimpleUpdate(
|
validStore.GuaranteedUpdate(ctx, "/abc", &example.Pod{}, true, nil, storage.SimpleUpdate(
|
||||||
func(runtime.Object) (runtime.Object, error) {
|
func(runtime.Object) (runtime.Object, error) {
|
||||||
return &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}, nil
|
return &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}, nil
|
||||||
|
|
|
||||||
|
|
@ -36,8 +36,9 @@ type Versioner interface {
|
||||||
UpdateObject(obj runtime.Object, resourceVersion uint64) error
|
UpdateObject(obj runtime.Object, resourceVersion uint64) error
|
||||||
// UpdateList sets the resource version into an API list object. Returns an error if the object
|
// UpdateList sets the resource version into an API list object. Returns an error if the object
|
||||||
// cannot be updated correctly. May return nil if the requested object does not need metadata
|
// cannot be updated correctly. May return nil if the requested object does not need metadata
|
||||||
// from database.
|
// from database. continueValue is optional and indicates that more results are available if
|
||||||
UpdateList(obj runtime.Object, resourceVersion uint64) error
|
// the client passes that value to the server in a subsequent call.
|
||||||
|
UpdateList(obj runtime.Object, resourceVersion uint64, continueValue string) error
|
||||||
// PrepareObjectForStorage should set SelfLink and ResourceVersion to the empty value. Should
|
// PrepareObjectForStorage should set SelfLink and ResourceVersion to the empty value. Should
|
||||||
// return an error if the specified object cannot be updated.
|
// return an error if the specified object cannot be updated.
|
||||||
PrepareObjectForStorage(obj runtime.Object) error
|
PrepareObjectForStorage(obj runtime.Object) error
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,8 @@ type SelectionPredicate struct {
|
||||||
IncludeUninitialized bool
|
IncludeUninitialized bool
|
||||||
GetAttrs AttrFunc
|
GetAttrs AttrFunc
|
||||||
IndexFields []string
|
IndexFields []string
|
||||||
|
Limit int64
|
||||||
|
Continue string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Matches returns true if the given object's labels and fields (as
|
// Matches returns true if the given object's labels and fields (as
|
||||||
|
|
@ -118,6 +120,9 @@ func (s *SelectionPredicate) MatchesObjectAttributes(l labels.Set, f fields.Set,
|
||||||
// MatchesSingle will return (name, true) if and only if s.Field matches on the object's
|
// MatchesSingle will return (name, true) if and only if s.Field matches on the object's
|
||||||
// name.
|
// name.
|
||||||
func (s *SelectionPredicate) MatchesSingle() (string, bool) {
|
func (s *SelectionPredicate) MatchesSingle() (string, bool) {
|
||||||
|
if len(s.Continue) > 0 {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
// TODO: should be namespace.name
|
// TODO: should be namespace.name
|
||||||
if name, ok := s.Field.RequiresExactMatch("metadata.name"); ok {
|
if name, ok := s.Field.RequiresExactMatch("metadata.name"); ok {
|
||||||
return name, true
|
return name, true
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,11 @@ type Config struct {
|
||||||
CAFile string
|
CAFile string
|
||||||
// Quorum indicates that whether read operations should be quorum-level consistent.
|
// Quorum indicates that whether read operations should be quorum-level consistent.
|
||||||
Quorum bool
|
Quorum bool
|
||||||
|
// Paging indicates whether the server implementation should allow paging (if it is
|
||||||
|
// supported). This is generally configured by feature gating, or by a specific
|
||||||
|
// resource type not wishing to allow paging, and is not intended for end users to
|
||||||
|
// set.
|
||||||
|
Paging bool
|
||||||
// DeserializationCacheSize is the size of cache of deserialized objects.
|
// DeserializationCacheSize is the size of cache of deserialized objects.
|
||||||
// Currently this is only supported in etcd2.
|
// Currently this is only supported in etcd2.
|
||||||
// We will drop the cache once using protobuf.
|
// We will drop the cache once using protobuf.
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ func newETCD3Storage(c storagebackend.Config) (storage.Interface, DestroyFunc, e
|
||||||
transformer = value.IdentityTransformer
|
transformer = value.IdentityTransformer
|
||||||
}
|
}
|
||||||
if c.Quorum {
|
if c.Quorum {
|
||||||
return etcd3.New(client, c.Codec, c.Prefix, transformer), destroyFunc, nil
|
return etcd3.New(client, c.Codec, c.Prefix, transformer, c.Paging), destroyFunc, nil
|
||||||
}
|
}
|
||||||
return etcd3.NewWithNoQuorumRead(client, c.Codec, c.Prefix, transformer), destroyFunc, nil
|
return etcd3.NewWithNoQuorumRead(client, c.Codec, c.Prefix, transformer, c.Paging), destroyFunc, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,7 @@ func AddObjectMetaFieldsSet(source fields.Set, objectMeta *metav1.ObjectMeta, ha
|
||||||
|
|
||||||
func newEtcdTestStorage(t *testing.T, prefix string) (*etcdtesting.EtcdTestServer, storage.Interface) {
|
func newEtcdTestStorage(t *testing.T, prefix string) (*etcdtesting.EtcdTestServer, storage.Interface) {
|
||||||
server, _ := etcdtesting.NewUnsecuredEtcd3TestClientServer(t, scheme)
|
server, _ := etcdtesting.NewUnsecuredEtcd3TestClientServer(t, scheme)
|
||||||
storage := etcd3.New(server.V3Client, apitesting.TestCodec(codecs, examplev1.SchemeGroupVersion), prefix, value.IdentityTransformer)
|
storage := etcd3.New(server.V3Client, apitesting.TestCodec(codecs, examplev1.SchemeGroupVersion), prefix, value.IdentityTransformer, true)
|
||||||
return server, storage
|
return server, storage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue