Compare commits

...

27 Commits

Author SHA1 Message Date
Kubernetes Publisher
f696fa0035 Fix Godeps.json to point to kubernetes-1.11.8-beta.0 tags 2019-01-26 16:17:02 +00:00
Kubernetes Publisher
4022682532 Merge pull request #72839 from liggitt/automated-cherry-pick-of-#72825-upstream-release-1.11
Automated cherry pick of #72825: Find current resourceVersion for waiting for

Kubernetes-commit: 0bc3747c9bb458bc4495547d4a24452702efcd6b
2019-01-17 15:34:10 -08:00
Jordan Liggitt
0a8c852f46 Find current resourceVersion for waiting for deletion/conditions
Kubernetes-commit: b980266971c60df921cf311fa4d4a5b7ba07ae1a
2019-01-11 10:38:18 -05:00
Kubernetes Publisher
573fc71f7a Merge pull request #71325 from cheftako/automated-cherry-pick-of-#71285-upstream-release-1.11
Automated cherry pick of #71285 upstream release 1.11

Kubernetes-commit: 2921f9a19ce3ac9b55d5231ae2deb2bf2f5706e4
2018-12-04 04:01:58 +00:00
Kubernetes Publisher
81f05ccbec Merge pull request #70765 from wenjiaswe/automated-cherry-pick-of-#70663-upstream-release-1.11
Automated cherry pick of #70663: update  godeps for golang.org/x/net/... to

Kubernetes-commit: d95e84d3ac09c12a3800e29a90c6c687adf97ea5
2018-12-04 00:04:43 +00:00
Walter Fender
02c3db7e5d Report KCM as unhealthy if leader election is wedged. Feedback from lavalamp and deads2k. Changed Check() logic to be central to LeaderElector. Further changes, especially cleaning up the test code.
Kubernetes-commit: 746f343fcc467656cb8ac08e0d6447f5ecffff66
2018-11-12 18:45:21 -08:00
Wenjia Zhang
d43910c7ed update staging godeps for golang.org/x/net/... to release-branch.go1.10
Kubernetes-commit: 7234d6621898c8544ba53bb612fa9acd0db41514
2018-11-06 15:49:50 -08:00
Kubernetes Publisher
56e7a63b5e Merge pull request #68842 from wenjiaswe/automated-cherry-pick-of-#68530-upstream-release-1.11
Automated cherry pick of #68530: Rewrite finalURLTemplate used only for metrics of dynamic client

Kubernetes-commit: 011647662b54bd2904ca6eeae59c0e9b5aca83d3
2018-10-10 04:57:04 +00:00
Kubernetes Publisher
02543cdbce Merge pull request #68552 from the-redback/automated-cherry-pick-of-#66078-upstream-release-1.11
Automated cherry pick of #66078: fix dynamic client listing bug

Kubernetes-commit: 9b517e008b9604951f68c859b1d26d5e1a896597
2018-10-04 11:02:59 +00:00
Wenjia Zhang
014054a6d3 Rewrite finalURLTemplate used only for metrics because of dynamic client change
Kubernetes-commit: 2f25fd01640f37d206112da48ff8c3faf4e17275
2018-09-14 11:31:21 -07:00
Kubernetes Publisher
f2f85107ca Merge pull request #67164 from dekkagaijin/automated-cherry-pick-of-#65799-upstream-release-1.11
Automatic merge from submit-queue.

Automated cherry pick of #65799: Escape illegal characters in remote extra keys

Cherry pick of #65799 on release-1.11.

#65799: Escape illegal characters in remote extra keys

```release-note
action required: the API server and client-go libraries have been fixed to support additional non-alpha-numeric characters in UserInfo "extra" data keys. Both should be updated in order to properly support extra data containing "/" characters or other characters disallowed in HTTP headers.
```

Kubernetes-commit: f53fc73da201d21cb12f6093a68f01b5ef594e5c
2018-08-21 07:40:22 +00:00
Kubernetes Publisher
60fd738f89 Merge pull request #67240 from nikhita/automated-cherry-pick-of-#66249-upstream-release-1.11
Automatic merge from submit-queue.

Automated cherry pick of #66249: fill in normal restmapping info with the legacy guess

Fixes 1.11 part of https://github.com/kubernetes/kubernetes/issues/67235

Cherry pick of #66249 on release-1.11.

#66249: fill in normal restmapping info with the legacy guess

```release-note
Fix creation of custom resources when the CRD contains non-conventional pluralization and subresources
```

Kubernetes-commit: 10ecc6db83fd47a93eb0940e2e4434f2b0a5c3ec
2018-08-15 03:28:43 -07:00
Kubernetes Publisher
1f13a808da Merge pull request #66683 from awly/automated-cherry-pick-of-#66395-upstream-release-1.11
Automatic merge from submit-queue.

Automated cherry pick of #66395: Set connrotation dialer via restclient.Config.Dialer

Cherry pick of #66395 on release-1.11.

#66395: Set connrotation dialer via restclient.Config.Dialer

Kubernetes-commit: 77555257773c21d18a2eafa3c532b28973970d3b
2018-08-06 13:40:42 +00:00
Andrew Lytvynov
401c87eddd Set connrotation dialer via restclient.Config.Dialer
Instead of Transport. This fixes ExecPlugin, which fails if
restclient.Config.Transport is set.

Kubernetes-commit: 89322ec5deb057483b3c91816aac48fe7044ecd8
2018-07-25 16:22:32 -07:00
David Eads
897ec2f639 fill in normal restmapping info with the legacy guess
Kubernetes-commit: 4d6d243503535ea290530d5548ac657b1529986f
2018-07-16 13:38:43 -04:00
David Eads
f7a93c0e74 fix dynamic client listing bug
updated bazel

Kubernetes-commit: 1f1711d311188dccd8c5bd6984b01445d8a61f53
2018-07-11 13:05:54 -04:00
Jake Sanders
e07adc6e69 Escape illegal characters in remote extra keys
Signed-off-by: Jake Sanders <jsand@google.com>

Kubernetes-commit: 1db56879e5386ea8baee0f658eed40bc7658b24d
2018-07-03 21:19:15 -07:00
Kubernetes Publisher
e5bc2a7bbb Merge pull request #65283 from liggitt/automated-cherry-pick-of-#65256-upstream-release-1.11
Automatic merge from submit-queue.

Automated cherry pick of #65256: bump(k8s.io/kube-openapi):

Fixes #65243

Cherry pick of #65256 on release-1.11.

#65256: bump(k8s.io/kube-openapi):

```release-note
fixed incorrect OpenAPI schema for CustomResourceDefinition objects
```

Kubernetes-commit: 5f5d6aa92b27bc300c631fd3fe26edc0f55fbb06
2018-06-21 07:31:03 +00:00
Jordan Liggitt
5c54f12e94 bump(k8s.io/kube-openapi): 91cfa479c814065e420cee7ed227db0f63a5854e
Kubernetes-commit: 7936a5bb822845c2a38d6f7da79bf3225e8faf1c
2018-06-19 23:23:00 -04:00
Kubernetes Publisher
11bffe824e Merge remote-tracking branch 'origin/master' into release-1.11
Kubernetes-commit: 61de6239b735f2d22a905247ca281bd6a4229989
2018-06-15 12:20:43 -04:00
Kubernetes Publisher
8560813064 Merge pull request #65034 from caesarxuchao/json-case-sensitive
Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>.

Make kubernetes json serializer case sensitive

This PR imported the latest jsoniterator library so that case sensitivity during unmarhsaling is optional. The PR also set Kubernetes json serializer to be case sensitive.

Kubernetes json serializer had been case sensitive for 1.1-1.7 as we were using ugorji. This PR restores the behavior.

Fix #64612.

```release-notes
Kubernetes json deserializer is now case-sensitive as it was before 1.8.
If your config files contains fields with wrong case, the config files will be now invalid.
```

Kubernetes-commit: a2de1398f829ef38d645579160bdd6bfec8384d3
2018-06-15 19:30:00 +00:00
Kubernetes Publisher
2d1fce8ff8 Merge remote-tracking branch 'origin/master' into release-1.11
Kubernetes-commit: 465c3d5995501b90a608088e647e91f992896636
2018-06-14 15:30:10 +00:00
Andrew Lytvynov
ab6fed13f4 Re-use private key after failed CSR
If we create a new key on each CSR, if CSR fails the next attempt will
create a new one instead of reusing previous CSR.

If approver/signer don't handle CSRs as quickly as new nodes come up,
they can pile up and approver would keep handling old abandoned CSRs and
Nodes would keep timing out on startup.

Kubernetes-commit: 2c0f043957d25da162fe4e1026c50e2587529ff9
2018-06-13 11:27:25 -07:00
Kubernetes Publisher
cd89475a09 sync: update godeps 2018-06-11 19:28:49 +00:00
Chao Xu
0bb1a137a4 vendor the latest json-iterator
Kubernetes-commit: dd69be30a5f296102be6b9706e6ad0c5db8e2c0f
2018-06-10 16:48:43 -07:00
Kubernetes Publisher
94da772526 Merge remote-tracking branch 'origin/master' into release-1.11
Kubernetes-commit: d2d36588b7bf9819e7d6924d668ba217052e17cb
2018-06-08 15:32:04 +00:00
Jordan Liggitt
90da597c77 Revert "Auto-generated code for the Vertical Pod Autoscaler API."
This reverts commit da65f30e2aca4ca8177fcf27e95909e2883b6e20.

Kubernetes-commit: 846cbe4e6b63b198bd9926e59900f750168e6c69
2018-06-07 14:12:22 -04:00
24 changed files with 911 additions and 671 deletions

176
Godeps/Godeps.json generated
View File

@@ -156,7 +156,7 @@
},
{
"ImportPath": "github.com/json-iterator/go",
"Rev": "2ddf6d758266fcb080a4f9e054b9f292c85e6798"
"Rev": "f2b4162afba35581b6d4a50d3b8f34e33c144682"
},
{
"ImportPath": "github.com/modern-go/concurrent",
@@ -188,27 +188,27 @@
},
{
"ImportPath": "golang.org/x/net/context",
"Rev": "1c05540f6879653db88113bc4a2b70aec4bd491f"
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
},
{
"ImportPath": "golang.org/x/net/context/ctxhttp",
"Rev": "1c05540f6879653db88113bc4a2b70aec4bd491f"
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
},
{
"ImportPath": "golang.org/x/net/http2",
"Rev": "1c05540f6879653db88113bc4a2b70aec4bd491f"
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
},
{
"ImportPath": "golang.org/x/net/http2/hpack",
"Rev": "1c05540f6879653db88113bc4a2b70aec4bd491f"
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
},
{
"ImportPath": "golang.org/x/net/idna",
"Rev": "1c05540f6879653db88113bc4a2b70aec4bd491f"
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
},
{
"ImportPath": "golang.org/x/net/lex/httplex",
"Rev": "1c05540f6879653db88113bc4a2b70aec4bd491f"
"Rev": "0ed95abb35c445290478a5348a7b38bb154135fd"
},
{
"ImportPath": "golang.org/x/oauth2",
@@ -268,327 +268,327 @@
},
{
"ImportPath": "k8s.io/api/admissionregistration/v1alpha1",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "04be5c24491b0d3167ab30fcd2fb0460748d5acd"
},
{
"ImportPath": "k8s.io/api/admissionregistration/v1beta1",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "04be5c24491b0d3167ab30fcd2fb0460748d5acd"
},
{
"ImportPath": "k8s.io/api/apps/v1",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "04be5c24491b0d3167ab30fcd2fb0460748d5acd"
},
{
"ImportPath": "k8s.io/api/apps/v1beta1",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "04be5c24491b0d3167ab30fcd2fb0460748d5acd"
},
{
"ImportPath": "k8s.io/api/apps/v1beta2",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "04be5c24491b0d3167ab30fcd2fb0460748d5acd"
},
{
"ImportPath": "k8s.io/api/authentication/v1",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "04be5c24491b0d3167ab30fcd2fb0460748d5acd"
},
{
"ImportPath": "k8s.io/api/authentication/v1beta1",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "04be5c24491b0d3167ab30fcd2fb0460748d5acd"
},
{
"ImportPath": "k8s.io/api/authorization/v1",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "04be5c24491b0d3167ab30fcd2fb0460748d5acd"
},
{
"ImportPath": "k8s.io/api/authorization/v1beta1",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "04be5c24491b0d3167ab30fcd2fb0460748d5acd"
},
{
"ImportPath": "k8s.io/api/autoscaling/v1",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "04be5c24491b0d3167ab30fcd2fb0460748d5acd"
},
{
"ImportPath": "k8s.io/api/autoscaling/v2beta1",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "04be5c24491b0d3167ab30fcd2fb0460748d5acd"
},
{
"ImportPath": "k8s.io/api/batch/v1",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "04be5c24491b0d3167ab30fcd2fb0460748d5acd"
},
{
"ImportPath": "k8s.io/api/batch/v1beta1",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "04be5c24491b0d3167ab30fcd2fb0460748d5acd"
},
{
"ImportPath": "k8s.io/api/batch/v2alpha1",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "04be5c24491b0d3167ab30fcd2fb0460748d5acd"
},
{
"ImportPath": "k8s.io/api/certificates/v1beta1",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "04be5c24491b0d3167ab30fcd2fb0460748d5acd"
},
{
"ImportPath": "k8s.io/api/core/v1",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "04be5c24491b0d3167ab30fcd2fb0460748d5acd"
},
{
"ImportPath": "k8s.io/api/events/v1beta1",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "04be5c24491b0d3167ab30fcd2fb0460748d5acd"
},
{
"ImportPath": "k8s.io/api/extensions/v1beta1",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "04be5c24491b0d3167ab30fcd2fb0460748d5acd"
},
{
"ImportPath": "k8s.io/api/imagepolicy/v1alpha1",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "04be5c24491b0d3167ab30fcd2fb0460748d5acd"
},
{
"ImportPath": "k8s.io/api/networking/v1",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "04be5c24491b0d3167ab30fcd2fb0460748d5acd"
},
{
"ImportPath": "k8s.io/api/policy/v1beta1",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "04be5c24491b0d3167ab30fcd2fb0460748d5acd"
},
{
"ImportPath": "k8s.io/api/rbac/v1",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "04be5c24491b0d3167ab30fcd2fb0460748d5acd"
},
{
"ImportPath": "k8s.io/api/rbac/v1alpha1",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "04be5c24491b0d3167ab30fcd2fb0460748d5acd"
},
{
"ImportPath": "k8s.io/api/rbac/v1beta1",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "04be5c24491b0d3167ab30fcd2fb0460748d5acd"
},
{
"ImportPath": "k8s.io/api/scheduling/v1alpha1",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "04be5c24491b0d3167ab30fcd2fb0460748d5acd"
},
{
"ImportPath": "k8s.io/api/scheduling/v1beta1",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "04be5c24491b0d3167ab30fcd2fb0460748d5acd"
},
{
"ImportPath": "k8s.io/api/settings/v1alpha1",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "04be5c24491b0d3167ab30fcd2fb0460748d5acd"
},
{
"ImportPath": "k8s.io/api/storage/v1",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "04be5c24491b0d3167ab30fcd2fb0460748d5acd"
},
{
"ImportPath": "k8s.io/api/storage/v1alpha1",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "04be5c24491b0d3167ab30fcd2fb0460748d5acd"
},
{
"ImportPath": "k8s.io/api/storage/v1beta1",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "04be5c24491b0d3167ab30fcd2fb0460748d5acd"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/api/equality",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/api/errors",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/api/meta",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/api/resource",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/api/testing",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/api/testing/fuzzer",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/api/testing/roundtrip",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/fuzzer",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/internalversion",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1beta1",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/conversion",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/conversion/queryparams",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/fields",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/labels",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/runtime",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/runtime/schema",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/json",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/protobuf",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/recognizer",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/streaming",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/versioning",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/selection",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/types",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/util/cache",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/util/clock",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/util/diff",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/util/errors",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/util/framer",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/util/httpstream",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/util/httpstream/spdy",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/util/intstr",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/util/json",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/util/mergepatch",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/util/net",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/util/remotecommand",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/util/runtime",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/util/sets",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/util/strategicpatch",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/util/validation",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/util/validation/field",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/util/wait",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/util/yaml",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/version",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/watch",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
},
{
"ImportPath": "k8s.io/apimachinery/third_party/forked/golang/json",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
},
{
"ImportPath": "k8s.io/apimachinery/third_party/forked/golang/netutil",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
},
{
"ImportPath": "k8s.io/apimachinery/third_party/forked/golang/reflect",
"Rev": "8c14244ab7eeffb1e4acd1ac1afb315b30fe67bf"
"Rev": "1525e4dadd2df06debdeb27ee24ac7db5b6413b1"
},
{
"ImportPath": "k8s.io/kube-openapi/pkg/util/proto",
"Rev": "8a9b82f00b3a86eac24681da3f9fe6c34c01cea2"
"Rev": "91cfa479c814065e420cee7ed227db0f63a5854e"
}
]
}

View File

@@ -33,6 +33,10 @@ import (
)
func NewSimpleDynamicClient(scheme *runtime.Scheme, objects ...runtime.Object) *FakeDynamicClient {
// In order to use List with this client, you have to have the v1.List registered in your scheme. Neat thing though
// it does NOT have to be the *same* list
scheme.AddKnownTypeWithName(schema.GroupVersionKind{Group: "fake-dynamic-client-group", Version: "v1", Kind: "List"}, &unstructured.UnstructuredList{})
codecs := serializer.NewCodecFactory(scheme)
o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder())
for _, obj := range objects {
@@ -272,11 +276,11 @@ func (c *dynamicResourceClient) List(opts metav1.ListOptions) (*unstructured.Uns
switch {
case len(c.namespace) == 0:
obj, err = c.client.Fake.
Invokes(testing.NewRootListAction(c.resource, schema.GroupVersionKind{Version: "v1", Kind: "List"}, opts), &metav1.Status{Status: "dynamic list fail"})
Invokes(testing.NewRootListAction(c.resource, schema.GroupVersionKind{Group: "fake-dynamic-client-group", Version: "v1", Kind: "" /*List is appended by the tracker automatically*/}, opts), &metav1.Status{Status: "dynamic list fail"})
case len(c.namespace) > 0:
obj, err = c.client.Fake.
Invokes(testing.NewListAction(c.resource, schema.GroupVersionKind{Version: "v1", Kind: "List"}, c.namespace, opts), &metav1.Status{Status: "dynamic list fail"})
Invokes(testing.NewListAction(c.resource, schema.GroupVersionKind{Group: "fake-dynamic-client-group", Version: "v1", Kind: "" /*List is appended by the tracker automatically*/}, c.namespace, opts), &metav1.Status{Status: "dynamic list fail"})
}
@@ -299,13 +303,15 @@ func (c *dynamicResourceClient) List(opts metav1.ListOptions) (*unstructured.Uns
}
list := &unstructured.UnstructuredList{}
for _, item := range entireList.Items {
list.SetResourceVersion(entireList.GetResourceVersion())
for i := range entireList.Items {
item := &entireList.Items[i]
metadata, err := meta.Accessor(item)
if err != nil {
return nil, err
}
if label.Matches(labels.Set(metadata.GetLabels())) {
list.Items = append(list.Items, item)
list.Items = append(list.Items, *item)
}
}
return list, nil

View File

@@ -0,0 +1,66 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package fake
import (
"testing"
"k8s.io/apimachinery/pkg/api/equality"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/diff"
)
func newUnstructured(apiVersion, kind, namespace, name string) *unstructured.Unstructured {
return &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": apiVersion,
"kind": kind,
"metadata": map[string]interface{}{
"namespace": namespace,
"name": name,
},
},
}
}
func TestList(t *testing.T) {
scheme := runtime.NewScheme()
client := NewSimpleDynamicClient(scheme,
newUnstructured("group/version", "TheKind", "ns-foo", "name-foo"),
newUnstructured("group2/version", "TheKind", "ns-foo", "name2-foo"),
newUnstructured("group/version", "TheKind", "ns-foo", "name-bar"),
newUnstructured("group/version", "TheKind", "ns-foo", "name-baz"),
newUnstructured("group2/version", "TheKind", "ns-foo", "name2-baz"),
)
listFirst, err := client.Resource(schema.GroupVersionResource{Group: "group", Version: "version", Resource: "thekinds"}).List(metav1.ListOptions{})
if err != nil {
t.Fatal(err)
}
expected := []unstructured.Unstructured{
*newUnstructured("group/version", "TheKind", "ns-foo", "name-foo"),
*newUnstructured("group/version", "TheKind", "ns-foo", "name-bar"),
*newUnstructured("group/version", "TheKind", "ns-foo", "name-baz"),
}
if !equality.Semantic.DeepEqual(listFirst.Items, expected) {
t.Fatal(diff.ObjectGoPrintDiff(expected, listFirst.Items))
}
}

View File

@@ -26,8 +26,6 @@ import (
type Interface interface {
// HorizontalPodAutoscalers returns a HorizontalPodAutoscalerInformer.
HorizontalPodAutoscalers() HorizontalPodAutoscalerInformer
// VerticalPodAutoscalers returns a VerticalPodAutoscalerInformer.
VerticalPodAutoscalers() VerticalPodAutoscalerInformer
}
type version struct {
@@ -45,8 +43,3 @@ func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakList
func (v *version) HorizontalPodAutoscalers() HorizontalPodAutoscalerInformer {
return &horizontalPodAutoscalerInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
}
// VerticalPodAutoscalers returns a VerticalPodAutoscalerInformer.
func (v *version) VerticalPodAutoscalers() VerticalPodAutoscalerInformer {
return &verticalPodAutoscalerInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
}

View File

@@ -1,89 +0,0 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by informer-gen. DO NOT EDIT.
package v2beta1
import (
time "time"
autoscaling_v2beta1 "k8s.io/api/autoscaling/v2beta1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
watch "k8s.io/apimachinery/pkg/watch"
internalinterfaces "k8s.io/client-go/informers/internalinterfaces"
kubernetes "k8s.io/client-go/kubernetes"
v2beta1 "k8s.io/client-go/listers/autoscaling/v2beta1"
cache "k8s.io/client-go/tools/cache"
)
// VerticalPodAutoscalerInformer provides access to a shared informer and lister for
// VerticalPodAutoscalers.
type VerticalPodAutoscalerInformer interface {
Informer() cache.SharedIndexInformer
Lister() v2beta1.VerticalPodAutoscalerLister
}
type verticalPodAutoscalerInformer struct {
factory internalinterfaces.SharedInformerFactory
tweakListOptions internalinterfaces.TweakListOptionsFunc
namespace string
}
// NewVerticalPodAutoscalerInformer constructs a new informer for VerticalPodAutoscaler type.
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewVerticalPodAutoscalerInformer(client kubernetes.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
return NewFilteredVerticalPodAutoscalerInformer(client, namespace, resyncPeriod, indexers, nil)
}
// NewFilteredVerticalPodAutoscalerInformer constructs a new informer for VerticalPodAutoscaler type.
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewFilteredVerticalPodAutoscalerInformer(client kubernetes.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {
return cache.NewSharedIndexInformer(
&cache.ListWatch{
ListFunc: func(options v1.ListOptions) (runtime.Object, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.AutoscalingV2beta1().VerticalPodAutoscalers(namespace).List(options)
},
WatchFunc: func(options v1.ListOptions) (watch.Interface, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.AutoscalingV2beta1().VerticalPodAutoscalers(namespace).Watch(options)
},
},
&autoscaling_v2beta1.VerticalPodAutoscaler{},
resyncPeriod,
indexers,
)
}
func (f *verticalPodAutoscalerInformer) defaultInformer(client kubernetes.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
return NewFilteredVerticalPodAutoscalerInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
}
func (f *verticalPodAutoscalerInformer) Informer() cache.SharedIndexInformer {
return f.factory.InformerFor(&autoscaling_v2beta1.VerticalPodAutoscaler{}, f.defaultInformer)
}
func (f *verticalPodAutoscalerInformer) Lister() v2beta1.VerticalPodAutoscalerLister {
return v2beta1.NewVerticalPodAutoscalerLister(f.Informer().GetIndexer())
}

View File

@@ -125,8 +125,6 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource
// Group=autoscaling, Version=v2beta1
case v2beta1.SchemeGroupVersion.WithResource("horizontalpodautoscalers"):
return &genericInformer{resource: resource.GroupResource(), informer: f.Autoscaling().V2beta1().HorizontalPodAutoscalers().Informer()}, nil
case v2beta1.SchemeGroupVersion.WithResource("verticalpodautoscalers"):
return &genericInformer{resource: resource.GroupResource(), informer: f.Autoscaling().V2beta1().VerticalPodAutoscalers().Informer()}, nil
// Group=batch, Version=v1
case batch_v1.SchemeGroupVersion.WithResource("jobs"):

View File

@@ -28,7 +28,6 @@ import (
type AutoscalingV2beta1Interface interface {
RESTClient() rest.Interface
HorizontalPodAutoscalersGetter
VerticalPodAutoscalersGetter
}
// AutoscalingV2beta1Client is used to interact with features provided by the autoscaling group.
@@ -40,10 +39,6 @@ func (c *AutoscalingV2beta1Client) HorizontalPodAutoscalers(namespace string) Ho
return newHorizontalPodAutoscalers(c, namespace)
}
func (c *AutoscalingV2beta1Client) VerticalPodAutoscalers(namespace string) VerticalPodAutoscalerInterface {
return newVerticalPodAutoscalers(c, namespace)
}
// NewForConfig creates a new AutoscalingV2beta1Client for the given config.
func NewForConfig(c *rest.Config) (*AutoscalingV2beta1Client, error) {
config := *c

View File

@@ -32,10 +32,6 @@ func (c *FakeAutoscalingV2beta1) HorizontalPodAutoscalers(namespace string) v2be
return &FakeHorizontalPodAutoscalers{c, namespace}
}
func (c *FakeAutoscalingV2beta1) VerticalPodAutoscalers(namespace string) v2beta1.VerticalPodAutoscalerInterface {
return &FakeVerticalPodAutoscalers{c, namespace}
}
// RESTClient returns a RESTClient that is used to communicate
// with API server by this client implementation.
func (c *FakeAutoscalingV2beta1) RESTClient() rest.Interface {

View File

@@ -1,140 +0,0 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by client-gen. DO NOT EDIT.
package fake
import (
v2beta1 "k8s.io/api/autoscaling/v2beta1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
labels "k8s.io/apimachinery/pkg/labels"
schema "k8s.io/apimachinery/pkg/runtime/schema"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
testing "k8s.io/client-go/testing"
)
// FakeVerticalPodAutoscalers implements VerticalPodAutoscalerInterface
type FakeVerticalPodAutoscalers struct {
Fake *FakeAutoscalingV2beta1
ns string
}
var verticalpodautoscalersResource = schema.GroupVersionResource{Group: "autoscaling", Version: "v2beta1", Resource: "verticalpodautoscalers"}
var verticalpodautoscalersKind = schema.GroupVersionKind{Group: "autoscaling", Version: "v2beta1", Kind: "VerticalPodAutoscaler"}
// Get takes name of the verticalPodAutoscaler, and returns the corresponding verticalPodAutoscaler object, and an error if there is any.
func (c *FakeVerticalPodAutoscalers) Get(name string, options v1.GetOptions) (result *v2beta1.VerticalPodAutoscaler, err error) {
obj, err := c.Fake.
Invokes(testing.NewGetAction(verticalpodautoscalersResource, c.ns, name), &v2beta1.VerticalPodAutoscaler{})
if obj == nil {
return nil, err
}
return obj.(*v2beta1.VerticalPodAutoscaler), err
}
// List takes label and field selectors, and returns the list of VerticalPodAutoscalers that match those selectors.
func (c *FakeVerticalPodAutoscalers) List(opts v1.ListOptions) (result *v2beta1.VerticalPodAutoscalerList, err error) {
obj, err := c.Fake.
Invokes(testing.NewListAction(verticalpodautoscalersResource, verticalpodautoscalersKind, c.ns, opts), &v2beta1.VerticalPodAutoscalerList{})
if obj == nil {
return nil, err
}
label, _, _ := testing.ExtractFromListOptions(opts)
if label == nil {
label = labels.Everything()
}
list := &v2beta1.VerticalPodAutoscalerList{ListMeta: obj.(*v2beta1.VerticalPodAutoscalerList).ListMeta}
for _, item := range obj.(*v2beta1.VerticalPodAutoscalerList).Items {
if label.Matches(labels.Set(item.Labels)) {
list.Items = append(list.Items, item)
}
}
return list, err
}
// Watch returns a watch.Interface that watches the requested verticalPodAutoscalers.
func (c *FakeVerticalPodAutoscalers) Watch(opts v1.ListOptions) (watch.Interface, error) {
return c.Fake.
InvokesWatch(testing.NewWatchAction(verticalpodautoscalersResource, c.ns, opts))
}
// Create takes the representation of a verticalPodAutoscaler and creates it. Returns the server's representation of the verticalPodAutoscaler, and an error, if there is any.
func (c *FakeVerticalPodAutoscalers) Create(verticalPodAutoscaler *v2beta1.VerticalPodAutoscaler) (result *v2beta1.VerticalPodAutoscaler, err error) {
obj, err := c.Fake.
Invokes(testing.NewCreateAction(verticalpodautoscalersResource, c.ns, verticalPodAutoscaler), &v2beta1.VerticalPodAutoscaler{})
if obj == nil {
return nil, err
}
return obj.(*v2beta1.VerticalPodAutoscaler), err
}
// Update takes the representation of a verticalPodAutoscaler and updates it. Returns the server's representation of the verticalPodAutoscaler, and an error, if there is any.
func (c *FakeVerticalPodAutoscalers) Update(verticalPodAutoscaler *v2beta1.VerticalPodAutoscaler) (result *v2beta1.VerticalPodAutoscaler, err error) {
obj, err := c.Fake.
Invokes(testing.NewUpdateAction(verticalpodautoscalersResource, c.ns, verticalPodAutoscaler), &v2beta1.VerticalPodAutoscaler{})
if obj == nil {
return nil, err
}
return obj.(*v2beta1.VerticalPodAutoscaler), err
}
// UpdateStatus was generated because the type contains a Status member.
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
func (c *FakeVerticalPodAutoscalers) UpdateStatus(verticalPodAutoscaler *v2beta1.VerticalPodAutoscaler) (*v2beta1.VerticalPodAutoscaler, error) {
obj, err := c.Fake.
Invokes(testing.NewUpdateSubresourceAction(verticalpodautoscalersResource, "status", c.ns, verticalPodAutoscaler), &v2beta1.VerticalPodAutoscaler{})
if obj == nil {
return nil, err
}
return obj.(*v2beta1.VerticalPodAutoscaler), err
}
// Delete takes name of the verticalPodAutoscaler and deletes it. Returns an error if one occurs.
func (c *FakeVerticalPodAutoscalers) Delete(name string, options *v1.DeleteOptions) error {
_, err := c.Fake.
Invokes(testing.NewDeleteAction(verticalpodautoscalersResource, c.ns, name), &v2beta1.VerticalPodAutoscaler{})
return err
}
// DeleteCollection deletes a collection of objects.
func (c *FakeVerticalPodAutoscalers) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
action := testing.NewDeleteCollectionAction(verticalpodautoscalersResource, c.ns, listOptions)
_, err := c.Fake.Invokes(action, &v2beta1.VerticalPodAutoscalerList{})
return err
}
// Patch applies the patch and returns the patched verticalPodAutoscaler.
func (c *FakeVerticalPodAutoscalers) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v2beta1.VerticalPodAutoscaler, err error) {
obj, err := c.Fake.
Invokes(testing.NewPatchSubresourceAction(verticalpodautoscalersResource, c.ns, name, data, subresources...), &v2beta1.VerticalPodAutoscaler{})
if obj == nil {
return nil, err
}
return obj.(*v2beta1.VerticalPodAutoscaler), err
}

View File

@@ -19,5 +19,3 @@ limitations under the License.
package v2beta1
type HorizontalPodAutoscalerExpansion interface{}
type VerticalPodAutoscalerExpansion interface{}

View File

@@ -1,174 +0,0 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by client-gen. DO NOT EDIT.
package v2beta1
import (
v2beta1 "k8s.io/api/autoscaling/v2beta1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
scheme "k8s.io/client-go/kubernetes/scheme"
rest "k8s.io/client-go/rest"
)
// VerticalPodAutoscalersGetter has a method to return a VerticalPodAutoscalerInterface.
// A group's client should implement this interface.
type VerticalPodAutoscalersGetter interface {
VerticalPodAutoscalers(namespace string) VerticalPodAutoscalerInterface
}
// VerticalPodAutoscalerInterface has methods to work with VerticalPodAutoscaler resources.
type VerticalPodAutoscalerInterface interface {
Create(*v2beta1.VerticalPodAutoscaler) (*v2beta1.VerticalPodAutoscaler, error)
Update(*v2beta1.VerticalPodAutoscaler) (*v2beta1.VerticalPodAutoscaler, error)
UpdateStatus(*v2beta1.VerticalPodAutoscaler) (*v2beta1.VerticalPodAutoscaler, error)
Delete(name string, options *v1.DeleteOptions) error
DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error
Get(name string, options v1.GetOptions) (*v2beta1.VerticalPodAutoscaler, error)
List(opts v1.ListOptions) (*v2beta1.VerticalPodAutoscalerList, error)
Watch(opts v1.ListOptions) (watch.Interface, error)
Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v2beta1.VerticalPodAutoscaler, err error)
VerticalPodAutoscalerExpansion
}
// verticalPodAutoscalers implements VerticalPodAutoscalerInterface
type verticalPodAutoscalers struct {
client rest.Interface
ns string
}
// newVerticalPodAutoscalers returns a VerticalPodAutoscalers
func newVerticalPodAutoscalers(c *AutoscalingV2beta1Client, namespace string) *verticalPodAutoscalers {
return &verticalPodAutoscalers{
client: c.RESTClient(),
ns: namespace,
}
}
// Get takes name of the verticalPodAutoscaler, and returns the corresponding verticalPodAutoscaler object, and an error if there is any.
func (c *verticalPodAutoscalers) Get(name string, options v1.GetOptions) (result *v2beta1.VerticalPodAutoscaler, err error) {
result = &v2beta1.VerticalPodAutoscaler{}
err = c.client.Get().
Namespace(c.ns).
Resource("verticalpodautoscalers").
Name(name).
VersionedParams(&options, scheme.ParameterCodec).
Do().
Into(result)
return
}
// List takes label and field selectors, and returns the list of VerticalPodAutoscalers that match those selectors.
func (c *verticalPodAutoscalers) List(opts v1.ListOptions) (result *v2beta1.VerticalPodAutoscalerList, err error) {
result = &v2beta1.VerticalPodAutoscalerList{}
err = c.client.Get().
Namespace(c.ns).
Resource("verticalpodautoscalers").
VersionedParams(&opts, scheme.ParameterCodec).
Do().
Into(result)
return
}
// Watch returns a watch.Interface that watches the requested verticalPodAutoscalers.
func (c *verticalPodAutoscalers) Watch(opts v1.ListOptions) (watch.Interface, error) {
opts.Watch = true
return c.client.Get().
Namespace(c.ns).
Resource("verticalpodautoscalers").
VersionedParams(&opts, scheme.ParameterCodec).
Watch()
}
// Create takes the representation of a verticalPodAutoscaler and creates it. Returns the server's representation of the verticalPodAutoscaler, and an error, if there is any.
func (c *verticalPodAutoscalers) Create(verticalPodAutoscaler *v2beta1.VerticalPodAutoscaler) (result *v2beta1.VerticalPodAutoscaler, err error) {
result = &v2beta1.VerticalPodAutoscaler{}
err = c.client.Post().
Namespace(c.ns).
Resource("verticalpodautoscalers").
Body(verticalPodAutoscaler).
Do().
Into(result)
return
}
// Update takes the representation of a verticalPodAutoscaler and updates it. Returns the server's representation of the verticalPodAutoscaler, and an error, if there is any.
func (c *verticalPodAutoscalers) Update(verticalPodAutoscaler *v2beta1.VerticalPodAutoscaler) (result *v2beta1.VerticalPodAutoscaler, err error) {
result = &v2beta1.VerticalPodAutoscaler{}
err = c.client.Put().
Namespace(c.ns).
Resource("verticalpodautoscalers").
Name(verticalPodAutoscaler.Name).
Body(verticalPodAutoscaler).
Do().
Into(result)
return
}
// UpdateStatus was generated because the type contains a Status member.
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
func (c *verticalPodAutoscalers) UpdateStatus(verticalPodAutoscaler *v2beta1.VerticalPodAutoscaler) (result *v2beta1.VerticalPodAutoscaler, err error) {
result = &v2beta1.VerticalPodAutoscaler{}
err = c.client.Put().
Namespace(c.ns).
Resource("verticalpodautoscalers").
Name(verticalPodAutoscaler.Name).
SubResource("status").
Body(verticalPodAutoscaler).
Do().
Into(result)
return
}
// Delete takes name of the verticalPodAutoscaler and deletes it. Returns an error if one occurs.
func (c *verticalPodAutoscalers) Delete(name string, options *v1.DeleteOptions) error {
return c.client.Delete().
Namespace(c.ns).
Resource("verticalpodautoscalers").
Name(name).
Body(options).
Do().
Error()
}
// DeleteCollection deletes a collection of objects.
func (c *verticalPodAutoscalers) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
return c.client.Delete().
Namespace(c.ns).
Resource("verticalpodautoscalers").
VersionedParams(&listOptions, scheme.ParameterCodec).
Body(options).
Do().
Error()
}
// Patch applies the patch and returns the patched verticalPodAutoscaler.
func (c *verticalPodAutoscalers) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v2beta1.VerticalPodAutoscaler, err error) {
result = &v2beta1.VerticalPodAutoscaler{}
err = c.client.Patch(pt).
Namespace(c.ns).
Resource("verticalpodautoscalers").
SubResource(subresources...).
Name(name).
Body(data).
Do().
Into(result)
return
}

View File

@@ -25,11 +25,3 @@ type HorizontalPodAutoscalerListerExpansion interface{}
// HorizontalPodAutoscalerNamespaceListerExpansion allows custom methods to be added to
// HorizontalPodAutoscalerNamespaceLister.
type HorizontalPodAutoscalerNamespaceListerExpansion interface{}
// VerticalPodAutoscalerListerExpansion allows custom methods to be added to
// VerticalPodAutoscalerLister.
type VerticalPodAutoscalerListerExpansion interface{}
// VerticalPodAutoscalerNamespaceListerExpansion allows custom methods to be added to
// VerticalPodAutoscalerNamespaceLister.
type VerticalPodAutoscalerNamespaceListerExpansion interface{}

View File

@@ -1,94 +0,0 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by lister-gen. DO NOT EDIT.
package v2beta1
import (
v2beta1 "k8s.io/api/autoscaling/v2beta1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/tools/cache"
)
// VerticalPodAutoscalerLister helps list VerticalPodAutoscalers.
type VerticalPodAutoscalerLister interface {
// List lists all VerticalPodAutoscalers in the indexer.
List(selector labels.Selector) (ret []*v2beta1.VerticalPodAutoscaler, err error)
// VerticalPodAutoscalers returns an object that can list and get VerticalPodAutoscalers.
VerticalPodAutoscalers(namespace string) VerticalPodAutoscalerNamespaceLister
VerticalPodAutoscalerListerExpansion
}
// verticalPodAutoscalerLister implements the VerticalPodAutoscalerLister interface.
type verticalPodAutoscalerLister struct {
indexer cache.Indexer
}
// NewVerticalPodAutoscalerLister returns a new VerticalPodAutoscalerLister.
func NewVerticalPodAutoscalerLister(indexer cache.Indexer) VerticalPodAutoscalerLister {
return &verticalPodAutoscalerLister{indexer: indexer}
}
// List lists all VerticalPodAutoscalers in the indexer.
func (s *verticalPodAutoscalerLister) List(selector labels.Selector) (ret []*v2beta1.VerticalPodAutoscaler, err error) {
err = cache.ListAll(s.indexer, selector, func(m interface{}) {
ret = append(ret, m.(*v2beta1.VerticalPodAutoscaler))
})
return ret, err
}
// VerticalPodAutoscalers returns an object that can list and get VerticalPodAutoscalers.
func (s *verticalPodAutoscalerLister) VerticalPodAutoscalers(namespace string) VerticalPodAutoscalerNamespaceLister {
return verticalPodAutoscalerNamespaceLister{indexer: s.indexer, namespace: namespace}
}
// VerticalPodAutoscalerNamespaceLister helps list and get VerticalPodAutoscalers.
type VerticalPodAutoscalerNamespaceLister interface {
// List lists all VerticalPodAutoscalers in the indexer for a given namespace.
List(selector labels.Selector) (ret []*v2beta1.VerticalPodAutoscaler, err error)
// Get retrieves the VerticalPodAutoscaler from the indexer for a given namespace and name.
Get(name string) (*v2beta1.VerticalPodAutoscaler, error)
VerticalPodAutoscalerNamespaceListerExpansion
}
// verticalPodAutoscalerNamespaceLister implements the VerticalPodAutoscalerNamespaceLister
// interface.
type verticalPodAutoscalerNamespaceLister struct {
indexer cache.Indexer
namespace string
}
// List lists all VerticalPodAutoscalers in the indexer for a given namespace.
func (s verticalPodAutoscalerNamespaceLister) List(selector labels.Selector) (ret []*v2beta1.VerticalPodAutoscaler, err error) {
err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) {
ret = append(ret, m.(*v2beta1.VerticalPodAutoscaler))
})
return ret, err
}
// Get retrieves the VerticalPodAutoscaler from the indexer for a given namespace and name.
func (s verticalPodAutoscalerNamespaceLister) Get(name string) (*v2beta1.VerticalPodAutoscaler, error) {
obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name)
if err != nil {
return nil, err
}
if !exists {
return nil, errors.NewNotFound(v2beta1.Resource("verticalpodautoscaler"), name)
}
return obj.(*v2beta1.VerticalPodAutoscaler), nil
}

View File

@@ -20,6 +20,7 @@ import (
"bytes"
"context"
"crypto/tls"
"errors"
"fmt"
"io"
"net"
@@ -178,21 +179,10 @@ func (a *Authenticator) UpdateTransportConfig(c *transport.Config) error {
return &roundTripper{a, rt}
}
getCert := c.TLS.GetCert
c.TLS.GetCert = func() (*tls.Certificate, error) {
// If previous GetCert is present and returns a valid non-nil
// certificate, use that. Otherwise use cert from exec plugin.
if getCert != nil {
cert, err := getCert()
if err != nil {
return nil, err
}
if cert != nil {
return cert, nil
}
}
return a.cert()
if c.TLS.GetCert != nil {
return errors.New("can't add TLS certificate callback: transport.Config.TLS.GetCert already set")
}
c.TLS.GetCert = a.cert
var dial func(ctx context.Context, network, addr string) (net.Conn, error)
if c.Dial != nil {

View File

@@ -455,17 +455,9 @@ func (r *Request) URL() *url.URL {
// finalURLTemplate is similar to URL(), but will make all specific parameter values equal
// - instead of name or namespace, "{name}" and "{namespace}" will be used, and all query
// parameters will be reset. This creates a copy of the request so as not to change the
// underlying object. This means some useful request info (like the types of field
// selectors in use) will be lost.
// TODO: preserve field selector keys
// parameters will be reset. This creates a copy of the url so as not to change the
// underlying object.
func (r Request) finalURLTemplate() url.URL {
if len(r.resourceName) != 0 {
r.resourceName = "{name}"
}
if r.namespaceSet && len(r.namespace) != 0 {
r.namespace = "{namespace}"
}
newParams := url.Values{}
v := []string{"{value}"}
for k := range r.params {
@@ -473,6 +465,59 @@ func (r Request) finalURLTemplate() url.URL {
}
r.params = newParams
url := r.URL()
segments := strings.Split(r.URL().Path, "/")
groupIndex := 0
index := 0
if r.URL() != nil && r.baseURL != nil && strings.Contains(r.URL().Path, r.baseURL.Path) {
groupIndex += len(strings.Split(r.baseURL.Path, "/"))
}
if groupIndex >= len(segments) {
return *url
}
const CoreGroupPrefix = "api"
const NamedGroupPrefix = "apis"
isCoreGroup := segments[groupIndex] == CoreGroupPrefix
isNamedGroup := segments[groupIndex] == NamedGroupPrefix
if isCoreGroup {
// checking the case of core group with /api/v1/... format
index = groupIndex + 2
} else if isNamedGroup {
// checking the case of named group with /apis/apps/v1/... format
index = groupIndex + 3
} else {
// this should not happen that the only two possibilities are /api... and /apis..., just want to put an
// outlet here in case more API groups are added in future if ever possible:
// https://kubernetes.io/docs/concepts/overview/kubernetes-api/#api-groups
// if a wrong API groups name is encountered, return the {prefix} for url.Path
url.Path = "/{prefix}"
url.RawQuery = ""
return *url
}
//switch segLength := len(segments) - index; segLength {
switch {
// case len(segments) - index == 1:
// resource (with no name) do nothing
case len(segments)-index == 2:
// /$RESOURCE/$NAME: replace $NAME with {name}
segments[index+1] = "{name}"
case len(segments)-index == 3:
if segments[index+2] == "finalize" || segments[index+2] == "status" {
// /$RESOURCE/$NAME/$SUBRESOURCE: replace $NAME with {name}
segments[index+1] = "{name}"
} else {
// /namespace/$NAMESPACE/$RESOURCE: replace $NAMESPACE with {namespace}
segments[index+1] = "{namespace}"
}
case len(segments)-index >= 4:
segments[index+1] = "{namespace}"
// /namespace/$NAMESPACE/$RESOURCE/$NAME: replace $NAMESPACE with {namespace}, $NAME with {name}
if segments[index+3] != "finalize" && segments[index+3] != "status" {
// /$RESOURCE/$NAME/$SUBRESOURCE: replace $NAME with {name}
segments[index+3] = "{name}"
}
}
url.Path = path.Join(segments...)
return *url
}

View File

@@ -340,21 +340,169 @@ func TestResultIntoWithNoBodyReturnsErr(t *testing.T) {
}
func TestURLTemplate(t *testing.T) {
uri, _ := url.Parse("http://localhost")
r := NewRequest(nil, "POST", uri, "", ContentConfig{GroupVersion: &schema.GroupVersion{Group: "test"}}, Serializers{}, nil, nil, 0)
r.Prefix("pre1").Resource("r1").Namespace("ns").Name("nm").Param("p0", "v0")
full := r.URL()
if full.String() != "http://localhost/pre1/namespaces/ns/r1/nm?p0=v0" {
t.Errorf("unexpected initial URL: %s", full)
uri, _ := url.Parse("http://localhost/some/base/url/path")
testCases := []struct {
Request *Request
ExpectedFullURL string
ExpectedFinalURL string
}{
{
// non dynamic client
Request: NewRequest(nil, "POST", uri, "", ContentConfig{GroupVersion: &schema.GroupVersion{Group: "test"}}, Serializers{}, nil, nil, 0).
Prefix("api", "v1").Resource("r1").Namespace("ns").Name("nm").Param("p0", "v0"),
ExpectedFullURL: "http://localhost/some/base/url/path/api/v1/namespaces/ns/r1/nm?p0=v0",
ExpectedFinalURL: "http://localhost/some/base/url/path/api/v1/namespaces/%7Bnamespace%7D/r1/%7Bname%7D?p0=%7Bvalue%7D",
},
{
// non dynamic client with wrong api group
Request: NewRequest(nil, "POST", uri, "", ContentConfig{GroupVersion: &schema.GroupVersion{Group: "test"}}, Serializers{}, nil, nil, 0).
Prefix("pre1", "v1").Resource("r1").Namespace("ns").Name("nm").Param("p0", "v0"),
ExpectedFullURL: "http://localhost/some/base/url/path/pre1/v1/namespaces/ns/r1/nm?p0=v0",
ExpectedFinalURL: "http://localhost/%7Bprefix%7D",
},
{
// dynamic client with core group + namespace + resourceResource (with name)
// /api/$RESOURCEVERSION/namespaces/$NAMESPACE/$RESOURCE/%NAME
Request: NewRequest(nil, "DELETE", uri, "", ContentConfig{GroupVersion: &schema.GroupVersion{Group: "test"}}, Serializers{}, nil, nil, 0).
Prefix("/api/v1/namespaces/ns/r1/name1"),
ExpectedFullURL: "http://localhost/some/base/url/path/api/v1/namespaces/ns/r1/name1",
ExpectedFinalURL: "http://localhost/some/base/url/path/api/v1/namespaces/%7Bnamespace%7D/r1/%7Bname%7D",
},
{
// dynamic client with named group + namespace + resourceResource (with name)
// /apis/$NAMEDGROUPNAME/$RESOURCEVERSION/namespaces/$NAMESPACE/$RESOURCE/%NAME
Request: NewRequest(nil, "DELETE", uri, "", ContentConfig{GroupVersion: &schema.GroupVersion{Group: "test"}}, Serializers{}, nil, nil, 0).
Prefix("/apis/g1/v1/namespaces/ns/r1/name1"),
ExpectedFullURL: "http://localhost/some/base/url/path/apis/g1/v1/namespaces/ns/r1/name1",
ExpectedFinalURL: "http://localhost/some/base/url/path/apis/g1/v1/namespaces/%7Bnamespace%7D/r1/%7Bname%7D",
},
{
// dynamic client with core group + namespace + resourceResource (with NO name)
// /api/$RESOURCEVERSION/namespaces/$NAMESPACE/$RESOURCE
Request: NewRequest(nil, "DELETE", uri, "", ContentConfig{GroupVersion: &schema.GroupVersion{Group: "test"}}, Serializers{}, nil, nil, 0).
Prefix("/api/v1/namespaces/ns/r1"),
ExpectedFullURL: "http://localhost/some/base/url/path/api/v1/namespaces/ns/r1",
ExpectedFinalURL: "http://localhost/some/base/url/path/api/v1/namespaces/%7Bnamespace%7D/r1",
},
{
// dynamic client with named group + namespace + resourceResource (with NO name)
// /apis/$NAMEDGROUPNAME/$RESOURCEVERSION/namespaces/$NAMESPACE/$RESOURCE
Request: NewRequest(nil, "DELETE", uri, "", ContentConfig{GroupVersion: &schema.GroupVersion{Group: "test"}}, Serializers{}, nil, nil, 0).
Prefix("/apis/g1/v1/namespaces/ns/r1"),
ExpectedFullURL: "http://localhost/some/base/url/path/apis/g1/v1/namespaces/ns/r1",
ExpectedFinalURL: "http://localhost/some/base/url/path/apis/g1/v1/namespaces/%7Bnamespace%7D/r1",
},
{
// dynamic client with core group + resourceResource (with name)
// /api/$RESOURCEVERSION/$RESOURCE/%NAME
Request: NewRequest(nil, "DELETE", uri, "", ContentConfig{GroupVersion: &schema.GroupVersion{Group: "test"}}, Serializers{}, nil, nil, 0).
Prefix("/api/v1/r1/name1"),
ExpectedFullURL: "http://localhost/some/base/url/path/api/v1/r1/name1",
ExpectedFinalURL: "http://localhost/some/base/url/path/api/v1/r1/%7Bname%7D",
},
{
// dynamic client with named group + resourceResource (with name)
// /apis/$NAMEDGROUPNAME/$RESOURCEVERSION/$RESOURCE/%NAME
Request: NewRequest(nil, "DELETE", uri, "", ContentConfig{GroupVersion: &schema.GroupVersion{Group: "test"}}, Serializers{}, nil, nil, 0).
Prefix("/apis/g1/v1/r1/name1"),
ExpectedFullURL: "http://localhost/some/base/url/path/apis/g1/v1/r1/name1",
ExpectedFinalURL: "http://localhost/some/base/url/path/apis/g1/v1/r1/%7Bname%7D",
},
{
// dynamic client with named group + namespace + resourceResource (with name) + subresource
// /apis/$NAMEDGROUPNAME/$RESOURCEVERSION/namespaces/$NAMESPACE/$RESOURCE/%NAME/$SUBRESOURCE
Request: NewRequest(nil, "DELETE", uri, "", ContentConfig{GroupVersion: &schema.GroupVersion{Group: "test"}}, Serializers{}, nil, nil, 0).
Prefix("/apis/namespaces/namespaces/namespaces/namespaces/namespaces/namespaces/finalize"),
ExpectedFullURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces/namespaces/namespaces/namespaces/finalize",
ExpectedFinalURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces/%7Bnamespace%7D/namespaces/%7Bname%7D/finalize",
},
{
// dynamic client with named group + namespace + resourceResource (with name)
// /apis/$NAMEDGROUPNAME/$RESOURCEVERSION/namespaces/$NAMESPACE/$RESOURCE/%NAME
Request: NewRequest(nil, "DELETE", uri, "", ContentConfig{GroupVersion: &schema.GroupVersion{Group: "test"}}, Serializers{}, nil, nil, 0).
Prefix("/apis/namespaces/namespaces/namespaces/namespaces/namespaces/namespaces"),
ExpectedFullURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces/namespaces/namespaces/namespaces",
ExpectedFinalURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces/%7Bnamespace%7D/namespaces/%7Bname%7D",
},
{
// dynamic client with named group + namespace + resourceResource (with NO name) + subresource
// /apis/$NAMEDGROUPNAME/$RESOURCEVERSION/namespaces/$NAMESPACE/$RESOURCE/%SUBRESOURCE
Request: NewRequest(nil, "DELETE", uri, "", ContentConfig{GroupVersion: &schema.GroupVersion{Group: "test"}}, Serializers{}, nil, nil, 0).
Prefix("/apis/namespaces/namespaces/namespaces/namespaces/namespaces/finalize"),
ExpectedFullURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces/namespaces/namespaces/finalize",
ExpectedFinalURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces/%7Bnamespace%7D/namespaces/finalize",
},
{
// dynamic client with named group + namespace + resourceResource (with NO name) + subresource
// /apis/$NAMEDGROUPNAME/$RESOURCEVERSION/namespaces/$NAMESPACE/$RESOURCE/%SUBRESOURCE
Request: NewRequest(nil, "DELETE", uri, "", ContentConfig{GroupVersion: &schema.GroupVersion{Group: "test"}}, Serializers{}, nil, nil, 0).
Prefix("/apis/namespaces/namespaces/namespaces/namespaces/namespaces/status"),
ExpectedFullURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces/namespaces/namespaces/status",
ExpectedFinalURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces/%7Bnamespace%7D/namespaces/status",
},
{
// dynamic client with named group + namespace + resourceResource (with no name)
// /apis/$NAMEDGROUPNAME/$RESOURCEVERSION/namespaces/$NAMESPACE/$RESOURCE/%NAME
Request: NewRequest(nil, "DELETE", uri, "", ContentConfig{GroupVersion: &schema.GroupVersion{Group: "test"}}, Serializers{}, nil, nil, 0).
Prefix("/apis/namespaces/namespaces/namespaces/namespaces/namespaces"),
ExpectedFullURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces/namespaces/namespaces",
ExpectedFinalURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces/%7Bnamespace%7D/namespaces",
},
{
// dynamic client with named group + resourceResource (with name) + subresource
// /apis/$NAMEDGROUPNAME/$RESOURCEVERSION/namespaces/$NAMESPACE/$RESOURCE/%NAME
Request: NewRequest(nil, "DELETE", uri, "", ContentConfig{GroupVersion: &schema.GroupVersion{Group: "test"}}, Serializers{}, nil, nil, 0).
Prefix("/apis/namespaces/namespaces/namespaces/namespaces/finalize"),
ExpectedFullURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces/namespaces/finalize",
ExpectedFinalURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces/%7Bname%7D/finalize",
},
{
// dynamic client with named group + resourceResource (with name) + subresource
// /apis/$NAMEDGROUPNAME/$RESOURCEVERSION/namespaces/$NAMESPACE/$RESOURCE/%NAME
Request: NewRequest(nil, "DELETE", uri, "", ContentConfig{GroupVersion: &schema.GroupVersion{Group: "test"}}, Serializers{}, nil, nil, 0).
Prefix("/apis/namespaces/namespaces/namespaces/namespaces/status"),
ExpectedFullURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces/namespaces/status",
ExpectedFinalURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces/%7Bname%7D/status",
},
{
// dynamic client with named group + resourceResource (with name)
// /apis/$NAMEDGROUPNAME/$RESOURCEVERSION/$RESOURCE/%NAME
Request: NewRequest(nil, "DELETE", uri, "", ContentConfig{GroupVersion: &schema.GroupVersion{Group: "test"}}, Serializers{}, nil, nil, 0).
Prefix("/apis/namespaces/namespaces/namespaces/namespaces"),
ExpectedFullURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces/namespaces",
ExpectedFinalURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces/%7Bname%7D",
},
{
// dynamic client with named group + resourceResource (with no name)
// /apis/$NAMEDGROUPNAME/$RESOURCEVERSION/$RESOURCE/%NAME
Request: NewRequest(nil, "DELETE", uri, "", ContentConfig{GroupVersion: &schema.GroupVersion{Group: "test"}}, Serializers{}, nil, nil, 0).
Prefix("/apis/namespaces/namespaces/namespaces"),
ExpectedFullURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces",
ExpectedFinalURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces",
},
{
// dynamic client with wrong api group + namespace + resourceResource (with name) + subresource
// /apis/$NAMEDGROUPNAME/$RESOURCEVERSION/namespaces/$NAMESPACE/$RESOURCE/%NAME/$SUBRESOURCE
Request: NewRequest(nil, "DELETE", uri, "", ContentConfig{GroupVersion: &schema.GroupVersion{Group: "test"}}, Serializers{}, nil, nil, 0).
Prefix("/pre1/namespaces/namespaces/namespaces/namespaces/namespaces/namespaces/finalize"),
ExpectedFullURL: "http://localhost/some/base/url/path/pre1/namespaces/namespaces/namespaces/namespaces/namespaces/namespaces/finalize",
ExpectedFinalURL: "http://localhost/%7Bprefix%7D",
},
}
actualURL := r.finalURLTemplate()
actual := actualURL.String()
expected := "http://localhost/pre1/namespaces/%7Bnamespace%7D/r1/%7Bname%7D?p0=%7Bvalue%7D"
if actual != expected {
t.Errorf("unexpected URL template: %s %s", actual, expected)
}
if r.URL().String() != full.String() {
t.Errorf("creating URL template changed request: %s -> %s", full.String(), r.URL().String())
for i, testCase := range testCases {
r := testCase.Request
full := r.URL()
if full.String() != testCase.ExpectedFullURL {
t.Errorf("%d: unexpected initial URL: %s %s", i, full, testCase.ExpectedFullURL)
}
actualURL := r.finalURLTemplate()
actual := actualURL.String()
if actual != testCase.ExpectedFinalURL {
t.Errorf("%d: unexpected URL template: %s %s", i, actual, testCase.ExpectedFinalURL)
}
if r.URL().String() != full.String() {
t.Errorf("%d, creating URL template changed request: %s -> %s", i, full.String(), r.URL().String())
}
}
}

View File

@@ -99,18 +99,20 @@ func NewDiscoveryRESTMapper(groupResources []*APIGroupResources) meta.RESTMapper
scope = meta.RESTScopeRoot
}
// this is for legacy resources and servers which don't list singular forms. For those we must still guess.
if len(resource.SingularName) == 0 {
versionMapper.Add(gv.WithKind(resource.Kind), scope)
// TODO this is producing unsafe guesses that don't actually work, but it matches previous behavior
versionMapper.Add(gv.WithKind(resource.Kind+"List"), scope)
// if we have a slash, then this is a subresource and we shouldn't create mappings for those.
if strings.Contains(resource.Name, "/") {
continue
}
plural := gv.WithResource(resource.Name)
singular := gv.WithResource(resource.SingularName)
versionMapper.AddSpecific(gv.WithKind(resource.Kind), plural, singular, scope)
// this is for legacy resources and servers which don't list singular forms. For those we must still guess.
if len(resource.SingularName) == 0 {
_, singular = meta.UnsafeGuessKindToResource(gv.WithKind(resource.Kind))
}
versionMapper.AddSpecific(gv.WithKind(strings.ToLower(resource.Kind)), plural, singular, scope)
versionMapper.AddSpecific(gv.WithKind(resource.Kind), plural, singular, scope)
// TODO this is producing unsafe guesses that don't actually work, but it matches previous behavior
versionMapper.Add(gv.WithKind(resource.Kind+"List"), scope)
}

View File

@@ -0,0 +1,69 @@
/*
Copyright 2015 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package leaderelection
import (
"net/http"
"sync"
"time"
)
// HealthzAdaptor associates the /healthz endpoint with the LeaderElection object.
// It helps deal with the /healthz endpoint being set up prior to the LeaderElection.
// This contains the code needed to act as an adaptor between the leader
// election code the health check code. It allows us to provide health
// status about the leader election. Most specifically about if the leader
// has failed to renew without exiting the process. In that case we should
// report not healthy and rely on the kubelet to take down the process.
type HealthzAdaptor struct {
pointerLock sync.Mutex
le *LeaderElector
timeout time.Duration
}
// Name returns the name of the health check we are implementing.
func (l *HealthzAdaptor) Name() string {
return "leaderElection"
}
// Check is called by the healthz endpoint handler.
// It fails (returns an error) if we own the lease but had not been able to renew it.
func (l *HealthzAdaptor) Check(req *http.Request) error {
l.pointerLock.Lock()
defer l.pointerLock.Unlock()
if l.le == nil {
return nil
}
return l.le.Check(l.timeout)
}
// SetLeaderElection ties a leader election object to a HealthzAdaptor
func (l *HealthzAdaptor) SetLeaderElection(le *LeaderElector) {
l.pointerLock.Lock()
defer l.pointerLock.Unlock()
l.le = le
}
// NewLeaderHealthzAdaptor creates a basic healthz adaptor to monitor a leader election.
// timeout determines the time beyond the lease expiry to be allowed for timeout.
// checks within the timeout period after the lease expires will still return healthy.
func NewLeaderHealthzAdaptor(timeout time.Duration) *HealthzAdaptor {
result := &HealthzAdaptor{
timeout: timeout,
}
return result
}

View File

@@ -0,0 +1,175 @@
/*
Copyright 2015 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package leaderelection
import (
"fmt"
"testing"
"time"
"k8s.io/apimachinery/pkg/util/clock"
rl "k8s.io/client-go/tools/leaderelection/resourcelock"
"net/http"
)
type fakeLock struct {
identity string
}
// Get is a dummy to allow us to have a fakeLock for testing.
func (fl *fakeLock) Get() (ler *rl.LeaderElectionRecord, err error) {
return nil, nil
}
// Create is a dummy to allow us to have a fakeLock for testing.
func (fl *fakeLock) Create(ler rl.LeaderElectionRecord) error {
return nil
}
// Update is a dummy to allow us to have a fakeLock for testing.
func (fl *fakeLock) Update(ler rl.LeaderElectionRecord) error {
return nil
}
// RecordEvent is a dummy to allow us to have a fakeLock for testing.
func (fl *fakeLock) RecordEvent(string) {}
// Identity is a dummy to allow us to have a fakeLock for testing.
func (fl *fakeLock) Identity() string {
return fl.identity
}
// Describe is a dummy to allow us to have a fakeLock for testing.
func (fl *fakeLock) Describe() string {
return "Dummy implementation of lock for testing"
}
// TestLeaderElectionHealthChecker tests that the healthcheck for leader election handles its edge cases.
func TestLeaderElectionHealthChecker(t *testing.T) {
current := time.Now()
req := &http.Request{}
tests := []struct {
description string
expected error
adaptorTimeout time.Duration
elector *LeaderElector
}{
{
description: "call check before leader elector initialized",
expected: nil,
adaptorTimeout: time.Second * 20,
elector: nil,
},
{
description: "call check when the the lease is far expired",
expected: fmt.Errorf("failed election to renew leadership on lease %s", "foo"),
adaptorTimeout: time.Second * 20,
elector: &LeaderElector{
config: LeaderElectionConfig{
Lock: &fakeLock{identity: "healthTest"},
LeaseDuration: time.Minute,
Name: "foo",
},
observedRecord: rl.LeaderElectionRecord{
HolderIdentity: "healthTest",
},
observedTime: current,
clock: clock.NewFakeClock(current.Add(time.Hour)),
},
},
{
description: "call check when the the lease is far expired but held by another server",
expected: nil,
adaptorTimeout: time.Second * 20,
elector: &LeaderElector{
config: LeaderElectionConfig{
Lock: &fakeLock{identity: "healthTest"},
LeaseDuration: time.Minute,
Name: "foo",
},
observedRecord: rl.LeaderElectionRecord{
HolderIdentity: "otherServer",
},
observedTime: current,
clock: clock.NewFakeClock(current.Add(time.Hour)),
},
},
{
description: "call check when the the lease is not expired",
expected: nil,
adaptorTimeout: time.Second * 20,
elector: &LeaderElector{
config: LeaderElectionConfig{
Lock: &fakeLock{identity: "healthTest"},
LeaseDuration: time.Minute,
Name: "foo",
},
observedRecord: rl.LeaderElectionRecord{
HolderIdentity: "healthTest",
},
observedTime: current,
clock: clock.NewFakeClock(current),
},
},
{
description: "call check when the the lease is expired but inside the timeout",
expected: nil,
adaptorTimeout: time.Second * 20,
elector: &LeaderElector{
config: LeaderElectionConfig{
Lock: &fakeLock{identity: "healthTest"},
LeaseDuration: time.Minute,
Name: "foo",
},
observedRecord: rl.LeaderElectionRecord{
HolderIdentity: "healthTest",
},
observedTime: current,
clock: clock.NewFakeClock(current.Add(time.Minute).Add(time.Second)),
},
},
}
for _, test := range tests {
adaptor := NewLeaderHealthzAdaptor(test.adaptorTimeout)
if adaptor.le != nil {
t.Errorf("[%s] leaderChecker started with a LeaderElector %v", test.description, adaptor.le)
}
if test.elector != nil {
test.elector.config.WatchDog = adaptor
adaptor.SetLeaderElection(test.elector)
if adaptor.le == nil {
t.Errorf("[%s] adaptor failed to set the LeaderElector", test.description)
}
}
err := adaptor.Check(req)
if test.expected == nil {
if err == nil {
continue
}
t.Errorf("[%s] called check, expected no error but received \"%v\"", test.description, err)
} else {
if err == nil {
t.Errorf("[%s] called check and failed to received the expected error \"%v\"", test.description, test.expected)
}
if err.Error() != test.expected.Error() {
t.Errorf("[%s] called check, expected %v, received %v", test.description, test.expected, err)
}
}
}
}

View File

@@ -55,6 +55,7 @@ import (
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/clock"
"k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/wait"
rl "k8s.io/client-go/tools/leaderelection/resourcelock"
@@ -89,6 +90,7 @@ func NewLeaderElector(lec LeaderElectionConfig) (*LeaderElector, error) {
}
return &LeaderElector{
config: lec,
clock: clock.RealClock{},
}, nil
}
@@ -110,6 +112,13 @@ type LeaderElectionConfig struct {
// Callbacks are callbacks that are triggered during certain lifecycle
// events of the LeaderElector
Callbacks LeaderCallbacks
// WatchDog is the associated health checker
// WatchDog may be null if its not needed/configured.
WatchDog *HealthzAdaptor
// Name is the name of the resource lock for debugging
Name string
}
// LeaderCallbacks are callbacks that are triggered during certain
@@ -142,6 +151,12 @@ type LeaderElector struct {
// value observedRecord.HolderIdentity if the transition has
// not yet been reported.
reportedLeader string
// clock is wrapper around time to allow for less flaky testing
clock clock.Clock
// name is the name of the resource lock for debugging
name string
}
// Run starts the leader election loop
@@ -164,6 +179,9 @@ func RunOrDie(lec LeaderElectionConfig) {
if err != nil {
panic(err)
}
if lec.WatchDog != nil {
lec.WatchDog.SetLeaderElection(le)
}
le.Run()
}
@@ -239,14 +257,14 @@ func (le *LeaderElector) tryAcquireOrRenew() bool {
return false
}
le.observedRecord = leaderElectionRecord
le.observedTime = time.Now()
le.observedTime = le.clock.Now()
return true
}
// 2. Record obtained, check the Identity & Time
if !reflect.DeepEqual(le.observedRecord, *oldLeaderElectionRecord) {
le.observedRecord = *oldLeaderElectionRecord
le.observedTime = time.Now()
le.observedTime = le.clock.Now()
}
if le.observedTime.Add(le.config.LeaseDuration).After(now.Time) &&
oldLeaderElectionRecord.HolderIdentity != le.config.Lock.Identity() {
@@ -269,7 +287,7 @@ func (le *LeaderElector) tryAcquireOrRenew() bool {
return false
}
le.observedRecord = leaderElectionRecord
le.observedTime = time.Now()
le.observedTime = le.clock.Now()
return true
}
@@ -282,3 +300,19 @@ func (l *LeaderElector) maybeReportTransition() {
go l.config.Callbacks.OnNewLeader(l.reportedLeader)
}
}
// Check will determine if the current lease is expired by more than timeout.
func (le *LeaderElector) Check(maxTolerableExpiredLease time.Duration) error {
if !le.IsLeader() {
// Currently not concerned with the case that we are hot standby
return nil
}
// If we are more than timeout seconds after the lease duration that is past the timeout
// on the lease renew. Time to start reporting ourselves as unhealthy. We should have
// died but conditions like deadlock can prevent this. (See #70819)
if le.clock.Since(le.observedTime) > le.config.LeaseDuration+maxTolerableExpiredLease {
return fmt.Errorf("failed election to renew leadership on lease %s", le.config.Name)
}
return nil
}

View File

@@ -26,6 +26,7 @@ import (
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/clock"
fakecorev1 "k8s.io/client-go/kubernetes/typed/core/v1/fake"
core "k8s.io/client-go/testing"
rl "k8s.io/client-go/tools/leaderelection/resourcelock"
@@ -257,6 +258,7 @@ func testTryAcquireOrRenew(t *testing.T, objectType string) {
config: lec,
observedRecord: test.observedRecord,
observedTime: test.observedTime,
clock: clock.RealClock{},
}
if test.expectSuccess != le.tryAcquireOrRenew() {

View File

@@ -129,7 +129,7 @@ func SetAuthProxyHeaders(req *http.Request, username string, groups []string, ex
}
for key, values := range extra {
for _, value := range values {
req.Header.Add("X-Remote-Extra-"+key, value)
req.Header.Add("X-Remote-Extra-"+headerKeyEscape(key), value)
}
}
}
@@ -246,7 +246,7 @@ func (rt *impersonatingRoundTripper) RoundTrip(req *http.Request) (*http.Respons
}
for k, vv := range rt.impersonate.Extra {
for _, v := range vv {
req.Header.Add(ImpersonateUserExtraHeaderPrefix+k, v)
req.Header.Add(ImpersonateUserExtraHeaderPrefix+headerKeyEscape(k), v)
}
}
@@ -422,3 +422,110 @@ func (rt *debuggingRoundTripper) RoundTrip(req *http.Request) (*http.Response, e
func (rt *debuggingRoundTripper) WrappedRoundTripper() http.RoundTripper {
return rt.delegatedRoundTripper
}
func legalHeaderByte(b byte) bool {
return int(b) < len(legalHeaderKeyBytes) && legalHeaderKeyBytes[b]
}
func shouldEscape(b byte) bool {
// url.PathUnescape() returns an error if any '%' is not followed by two
// hexadecimal digits, so we'll intentionally encode it.
return !legalHeaderByte(b) || b == '%'
}
func headerKeyEscape(key string) string {
buf := strings.Builder{}
for i := 0; i < len(key); i++ {
b := key[i]
if shouldEscape(b) {
// %-encode bytes that should be escaped:
// https://tools.ietf.org/html/rfc3986#section-2.1
fmt.Fprintf(&buf, "%%%02X", b)
continue
}
buf.WriteByte(b)
}
return buf.String()
}
// legalHeaderKeyBytes was copied from net/http/lex.go's isTokenTable.
// See https://httpwg.github.io/specs/rfc7230.html#rule.token.separators
var legalHeaderKeyBytes = [127]bool{
'%': true,
'!': true,
'#': true,
'$': true,
'&': true,
'\'': true,
'*': true,
'+': true,
'-': true,
'.': true,
'0': true,
'1': true,
'2': true,
'3': true,
'4': true,
'5': true,
'6': true,
'7': true,
'8': true,
'9': true,
'A': true,
'B': true,
'C': true,
'D': true,
'E': true,
'F': true,
'G': true,
'H': true,
'I': true,
'J': true,
'K': true,
'L': true,
'M': true,
'N': true,
'O': true,
'P': true,
'Q': true,
'R': true,
'S': true,
'T': true,
'U': true,
'W': true,
'V': true,
'X': true,
'Y': true,
'Z': true,
'^': true,
'_': true,
'`': true,
'a': true,
'b': true,
'c': true,
'd': true,
'e': true,
'f': true,
'g': true,
'h': true,
'i': true,
'j': true,
'k': true,
'l': true,
'm': true,
'n': true,
'o': true,
'p': true,
'q': true,
'r': true,
's': true,
't': true,
'u': true,
'v': true,
'w': true,
'x': true,
'y': true,
'z': true,
'|': true,
'~': true,
}

View File

@@ -18,6 +18,7 @@ package transport
import (
"net/http"
"net/url"
"reflect"
"strings"
"testing"
@@ -125,6 +126,32 @@ func TestImpersonationRoundTripper(t *testing.T) {
ImpersonateUserExtraHeaderPrefix + "Second": {"B", "b"},
},
},
{
name: "escape handling",
impersonationConfig: ImpersonationConfig{
UserName: "user",
Extra: map[string][]string{
"test.example.com/thing.thing": {"A", "a"},
},
},
expected: map[string][]string{
ImpersonateUserHeader: {"user"},
ImpersonateUserExtraHeaderPrefix + `Test.example.com%2fthing.thing`: {"A", "a"},
},
},
{
name: "double escape handling",
impersonationConfig: ImpersonationConfig{
UserName: "user",
Extra: map[string][]string{
"test.example.com/thing.thing%20another.thing": {"A", "a"},
},
},
expected: map[string][]string{
ImpersonateUserHeader: {"user"},
ImpersonateUserExtraHeaderPrefix + `Test.example.com%2fthing.thing%2520another.thing`: {"A", "a"},
},
},
}
for _, tc := range tcs {
@@ -159,9 +186,10 @@ func TestImpersonationRoundTripper(t *testing.T) {
func TestAuthProxyRoundTripper(t *testing.T) {
for n, tc := range map[string]struct {
username string
groups []string
extra map[string][]string
username string
groups []string
extra map[string][]string
expectedExtra map[string][]string
}{
"allfields": {
username: "user",
@@ -170,6 +198,34 @@ func TestAuthProxyRoundTripper(t *testing.T) {
"one": {"alpha", "bravo"},
"two": {"charlie", "delta"},
},
expectedExtra: map[string][]string{
"one": {"alpha", "bravo"},
"two": {"charlie", "delta"},
},
},
"escaped extra": {
username: "user",
groups: []string{"groupA", "groupB"},
extra: map[string][]string{
"one": {"alpha", "bravo"},
"example.com/two": {"charlie", "delta"},
},
expectedExtra: map[string][]string{
"one": {"alpha", "bravo"},
"example.com%2ftwo": {"charlie", "delta"},
},
},
"double escaped extra": {
username: "user",
groups: []string{"groupA", "groupB"},
extra: map[string][]string{
"one": {"alpha", "bravo"},
"example.com/two%20three": {"charlie", "delta"},
},
expectedExtra: map[string][]string{
"one": {"alpha", "bravo"},
"example.com%2ftwo%2520three": {"charlie", "delta"},
},
},
} {
rt := &testRoundTripper{}
@@ -210,9 +266,64 @@ func TestAuthProxyRoundTripper(t *testing.T) {
actualExtra[extraKey] = append(actualExtra[key], values...)
}
}
if e, a := tc.extra, actualExtra; !reflect.DeepEqual(e, a) {
if e, a := tc.expectedExtra, actualExtra; !reflect.DeepEqual(e, a) {
t.Errorf("%s expected %v, got %v", n, e, a)
continue
}
}
}
// TestHeaderEscapeRoundTrip tests to see if foo == url.PathUnescape(headerEscape(foo))
// This behavior is important for client -> API server transmission of extra values.
func TestHeaderEscapeRoundTrip(t *testing.T) {
t.Parallel()
testCases := []struct {
name string
key string
}{
{
name: "alpha",
key: "alphabetical",
},
{
name: "alphanumeric",
key: "alph4num3r1c",
},
{
name: "percent encoded",
key: "percent%20encoded",
},
{
name: "almost percent encoded",
key: "almost%zzpercent%xxencoded",
},
{
name: "illegal char & percent encoding",
key: "example.com/percent%20encoded",
},
{
name: "weird unicode stuff",
key: "example.com/ᛒᚥᛏᛖᚥᚢとロビン",
},
{
name: "header legal chars",
key: "abc123!#$+.-_*\\^`~|'",
},
{
name: "legal path, illegal header",
key: "@=:",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
escaped := headerKeyEscape(tc.key)
unescaped, err := url.PathUnescape(escaped)
if err != nil {
t.Fatalf("url.PathUnescape(%q) returned error: %v", escaped, err)
}
if tc.key != unescaped {
t.Errorf("url.PathUnescape(headerKeyEscape(%q)) returned %q, wanted %q", tc.key, unescaped, tc.key)
}
})
}
}

View File

@@ -88,7 +88,8 @@ func WriteKey(keyPath string, data []byte) error {
// can't find one, it will generate a new key and store it there.
func LoadOrGenerateKeyFile(keyPath string) (data []byte, wasGenerated bool, err error) {
loadedData, err := ioutil.ReadFile(keyPath)
if err == nil {
// Call verifyKeyData to ensure the file wasn't empty/corrupt.
if err == nil && verifyKeyData(loadedData) {
return loadedData, false, err
}
if !os.IsNotExist(err) {
@@ -181,3 +182,12 @@ func PublicKeysFromFile(file string) ([]interface{}, error) {
}
return keys, nil
}
// verifyKeyData returns true if the provided data appears to be a valid private key.
func verifyKeyData(data []byte) bool {
if len(data) == 0 {
return false
}
_, err := ParsePrivateKeyPEM(data)
return err == nil
}