mirror of
https://github.com/kubernetes/client-go.git
synced 2026-06-23 10:15:02 +00:00
Compare commits
27 Commits
kubernetes
...
kubernetes
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
23c0fac11e | ||
|
|
5633e3a616 | ||
|
|
2705570f5e | ||
|
|
1777665291 | ||
|
|
1b0f6441b0 | ||
|
|
f2f74ab51a | ||
|
|
f0d5882143 | ||
|
|
7c7fe05819 | ||
|
|
65905f29c1 | ||
|
|
52d188ef6c | ||
|
|
b9d8bc3e50 | ||
|
|
cba5305359 | ||
|
|
7621a5ebb8 | ||
|
|
adcd4a6e94 | ||
|
|
f8e521c159 | ||
|
|
99c0ce7cfe | ||
|
|
21a91e3c21 | ||
|
|
86dbf26d38 | ||
|
|
2c40298487 | ||
|
|
73dad31f02 | ||
|
|
db82653708 | ||
|
|
abf3a7067f | ||
|
|
1a246d9074 | ||
|
|
49478befba | ||
|
|
cde316a81d | ||
|
|
7f18b87519 | ||
|
|
d71b6c1c6d |
@@ -3,8 +3,7 @@ language: go
|
||||
go_import_path: k8s.io/client-go
|
||||
|
||||
go:
|
||||
- 1.11.1
|
||||
- 1.11.2
|
||||
|
||||
script:
|
||||
- if [ "$TRAVIS_BRANCH" != "master" ]; then godep restore; fi
|
||||
- go build ./...
|
||||
- go build ./...
|
||||
|
||||
108
CHANGELOG.md
108
CHANGELOG.md
@@ -5,6 +5,114 @@ https://github.com/kubernetes/test-infra/issues/5843.
|
||||
Changes in `k8s.io/api` and `k8s.io/apimachinery` are mentioned here
|
||||
because `k8s.io/client-go` depends on them.
|
||||
|
||||
# v10.0.0
|
||||
|
||||
**Breaking Changes:**
|
||||
|
||||
* Action required: client-go will no longer have bootstrap
|
||||
(`k8s.io/client-go/tools/bootstrap`) related code. Any reference to it will
|
||||
break. Please redirect all references to `k8s.io/bootstrap` instead.
|
||||
([#67356](https://github.com/kubernetes/kubernetes/pull/67356))
|
||||
|
||||
* The methods `NewSelfSignedCACert` and `NewSignedCert` now use `crypto.Signer`
|
||||
interface instead of `rsa.PrivateKey` for certificate creation. This is done
|
||||
to allow different kind of private keys (for example: ecdsa).
|
||||
([#69329](https://github.com/kubernetes/kubernetes/pull/69329))
|
||||
|
||||
* `GetScale` and `UpdateScale` methods have been added for `apps/v1` clients
|
||||
and with this, no-verb scale clients have been removed.
|
||||
([#70437](https://github.com/kubernetes/kubernetes/pull/70437))
|
||||
|
||||
* `k8s.io/client-go/util/cert/triple` package has been removed.
|
||||
([#70966](https://github.com/kubernetes/kubernetes/pull/70966))
|
||||
|
||||
**New Features:**
|
||||
|
||||
* `unfinished_work_microseconds` is added to the workqueue metrics.
|
||||
It can be used to detect stuck worker threads (kube-controller-manager runs many workqueues.).
|
||||
([#70884](https://github.com/kubernetes/kubernetes/pull/70884))
|
||||
|
||||
* A method `GetPorts` is added to expose the ports that were forwarded.
|
||||
This can be used to retrieve the locally-bound port in cases where the input was port 0.
|
||||
([#67513](https://github.com/kubernetes/kubernetes/pull/67513))
|
||||
|
||||
* Dynamic listers and informers, that work with `runtime.Unstructured` objects,
|
||||
are added. These are useful for writing generic, non-generated controllers.
|
||||
([68748](https://github.com/kubernetes/kubernetes/pull/68748))
|
||||
|
||||
* The dynamic fake client now supports JSONPatch.
|
||||
([#69330](https://github.com/kubernetes/kubernetes/pull/69330))
|
||||
|
||||
* The `GetBinding` method is added for pods in the fake client.
|
||||
([#69412](https://github.com/kubernetes/kubernetes/pull/69412))
|
||||
|
||||
**Bug fixes and Improvements:**
|
||||
|
||||
* The `apiVersion` and action name values for fake evictions are now set.
|
||||
([#69035](https://github.com/kubernetes/kubernetes/pull/69035))
|
||||
|
||||
* PEM files containing both TLS certificate and key can now be parsed in
|
||||
arbitrary order. Previously key was always required to be first.
|
||||
([#69536](https://github.com/kubernetes/kubernetes/pull/69536))
|
||||
|
||||
* Go clients created from a kubeconfig that specifies a `TokenFile` now
|
||||
periodically reload the token from the specified file.
|
||||
([#70606](https://github.com/kubernetes/kubernetes/pull/70606))
|
||||
|
||||
* It is now ensured that oversized data frames are not written to
|
||||
spdystreams in `remotecommand.NewSPDYExecutor`.
|
||||
([#70999](https://github.com/kubernetes/kubernetes/pull/70999))
|
||||
|
||||
* A panic occuring on calling `scheme.Convert` is fixed by populating the fake
|
||||
dynamic client scheme. ([#69125](https://github.com/kubernetes/kubernetes/pull/69125))
|
||||
|
||||
* Add step to correctly setup permissions for the in-cluster-client-configuration example.
|
||||
([#69232](https://github.com/kubernetes/kubernetes/pull/69232))
|
||||
|
||||
* The function `Parallelize` is deprecated. Use `ParallelizeUntil` instead.
|
||||
([#68403](https://github.com/kubernetes/kubernetes/pull/68403))
|
||||
|
||||
* [k8s.io/apimachinery] Restrict redirect following from the apiserver to
|
||||
same-host redirects, and ignore redirects in some cases.
|
||||
([#66516](https://github.com/kubernetes/kubernetes/pull/66516))
|
||||
|
||||
## API changes
|
||||
|
||||
**New Features:**
|
||||
|
||||
* GlusterFS PersistentVolumes sources can now reference endpoints in any
|
||||
namespace using the `spec.glusterfs.endpointsNamespace` field.
|
||||
Ensure all kubelets are upgraded to 1.13+ before using this capability.
|
||||
([#60195](https://github.com/kubernetes/kubernetes/pull/60195))
|
||||
|
||||
* The [dynamic audit configuration](https://github.com/kubernetes/community/blob/master/keps/sig-auth/0014-dynamic-audit-configuration.md)
|
||||
API is added. ([#67547](https://github.com/kubernetes/kubernetes/pull/67547))
|
||||
|
||||
* A new field `EnableServiceLinks` is added to the `PodSpec` to indicate whether
|
||||
information about services should be injected into pod's environment variables.
|
||||
([#68754](https://github.com/kubernetes/kubernetes/pull/68754))
|
||||
|
||||
* `CSIPersistentVolume` feature, i.e. `PersistentVolumes` with
|
||||
`CSIPersistentVolumeSource`, is GA. `CSIPersistentVolume` feature gate is now
|
||||
deprecated and will be removed according to deprecation policy.
|
||||
([#69929](https://github.com/kubernetes/kubernetes/pull/69929))
|
||||
|
||||
* Raw block volume support is promoted to beta, and enabled by default.
|
||||
This is accessible via the `volumeDevices` container field in pod specs,
|
||||
and the `volumeMode` field in persistent volume and persistent volume claims definitions.
|
||||
([#71167](https://github.com/kubernetes/kubernetes/pull/71167))
|
||||
|
||||
**Bug fixes and Improvements:**
|
||||
|
||||
* The default value of extensions/v1beta1 Deployment's `RevisionHistoryLimit`
|
||||
is set to `MaxInt32`. ([#66605](https://github.com/kubernetes/kubernetes/pull/66605))
|
||||
|
||||
* `procMount` field is no longer incorrectly marked as required in openapi schema.
|
||||
([#69694](https://github.com/kubernetes/kubernetes/pull/69694))
|
||||
|
||||
* The caBundle and service fields in admission webhook API objects now correctly
|
||||
indicate they are optional. ([#70138](https://github.com/kubernetes/kubernetes/pull/70138))
|
||||
|
||||
# v9.0.0
|
||||
|
||||
**Breaking Changes:**
|
||||
|
||||
172
Godeps/Godeps.json
generated
172
Godeps/Godeps.json
generated
@@ -56,7 +56,7 @@
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/evanphx/json-patch",
|
||||
"Rev": "36442dbdb585210f8d5a1b45e67aa323c197d5c4"
|
||||
"Rev": "5858425f75500d40c52783dce87d085a483ce135"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/gogo/protobuf/proto",
|
||||
@@ -272,339 +272,339 @@
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/api/admissionregistration/v1alpha1",
|
||||
"Rev": "89a74a8d264df0e993299876a8cde88379b940ee"
|
||||
"Rev": "9a6558301daeb24d8ac1cc79ea0ccd1a23f1410d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/api/admissionregistration/v1beta1",
|
||||
"Rev": "89a74a8d264df0e993299876a8cde88379b940ee"
|
||||
"Rev": "9a6558301daeb24d8ac1cc79ea0ccd1a23f1410d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/api/apps/v1",
|
||||
"Rev": "89a74a8d264df0e993299876a8cde88379b940ee"
|
||||
"Rev": "9a6558301daeb24d8ac1cc79ea0ccd1a23f1410d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/api/apps/v1beta1",
|
||||
"Rev": "89a74a8d264df0e993299876a8cde88379b940ee"
|
||||
"Rev": "9a6558301daeb24d8ac1cc79ea0ccd1a23f1410d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/api/apps/v1beta2",
|
||||
"Rev": "89a74a8d264df0e993299876a8cde88379b940ee"
|
||||
"Rev": "9a6558301daeb24d8ac1cc79ea0ccd1a23f1410d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/api/auditregistration/v1alpha1",
|
||||
"Rev": "89a74a8d264df0e993299876a8cde88379b940ee"
|
||||
"Rev": "9a6558301daeb24d8ac1cc79ea0ccd1a23f1410d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/api/authentication/v1",
|
||||
"Rev": "89a74a8d264df0e993299876a8cde88379b940ee"
|
||||
"Rev": "9a6558301daeb24d8ac1cc79ea0ccd1a23f1410d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/api/authentication/v1beta1",
|
||||
"Rev": "89a74a8d264df0e993299876a8cde88379b940ee"
|
||||
"Rev": "9a6558301daeb24d8ac1cc79ea0ccd1a23f1410d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/api/authorization/v1",
|
||||
"Rev": "89a74a8d264df0e993299876a8cde88379b940ee"
|
||||
"Rev": "9a6558301daeb24d8ac1cc79ea0ccd1a23f1410d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/api/authorization/v1beta1",
|
||||
"Rev": "89a74a8d264df0e993299876a8cde88379b940ee"
|
||||
"Rev": "9a6558301daeb24d8ac1cc79ea0ccd1a23f1410d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/api/autoscaling/v1",
|
||||
"Rev": "89a74a8d264df0e993299876a8cde88379b940ee"
|
||||
"Rev": "9a6558301daeb24d8ac1cc79ea0ccd1a23f1410d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/api/autoscaling/v2beta1",
|
||||
"Rev": "89a74a8d264df0e993299876a8cde88379b940ee"
|
||||
"Rev": "9a6558301daeb24d8ac1cc79ea0ccd1a23f1410d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/api/autoscaling/v2beta2",
|
||||
"Rev": "89a74a8d264df0e993299876a8cde88379b940ee"
|
||||
"Rev": "9a6558301daeb24d8ac1cc79ea0ccd1a23f1410d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/api/batch/v1",
|
||||
"Rev": "89a74a8d264df0e993299876a8cde88379b940ee"
|
||||
"Rev": "9a6558301daeb24d8ac1cc79ea0ccd1a23f1410d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/api/batch/v1beta1",
|
||||
"Rev": "89a74a8d264df0e993299876a8cde88379b940ee"
|
||||
"Rev": "9a6558301daeb24d8ac1cc79ea0ccd1a23f1410d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/api/batch/v2alpha1",
|
||||
"Rev": "89a74a8d264df0e993299876a8cde88379b940ee"
|
||||
"Rev": "9a6558301daeb24d8ac1cc79ea0ccd1a23f1410d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/api/certificates/v1beta1",
|
||||
"Rev": "89a74a8d264df0e993299876a8cde88379b940ee"
|
||||
"Rev": "9a6558301daeb24d8ac1cc79ea0ccd1a23f1410d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/api/coordination/v1beta1",
|
||||
"Rev": "89a74a8d264df0e993299876a8cde88379b940ee"
|
||||
"Rev": "9a6558301daeb24d8ac1cc79ea0ccd1a23f1410d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/api/core/v1",
|
||||
"Rev": "89a74a8d264df0e993299876a8cde88379b940ee"
|
||||
"Rev": "9a6558301daeb24d8ac1cc79ea0ccd1a23f1410d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/api/events/v1beta1",
|
||||
"Rev": "89a74a8d264df0e993299876a8cde88379b940ee"
|
||||
"Rev": "9a6558301daeb24d8ac1cc79ea0ccd1a23f1410d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/api/extensions/v1beta1",
|
||||
"Rev": "89a74a8d264df0e993299876a8cde88379b940ee"
|
||||
"Rev": "9a6558301daeb24d8ac1cc79ea0ccd1a23f1410d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/api/imagepolicy/v1alpha1",
|
||||
"Rev": "89a74a8d264df0e993299876a8cde88379b940ee"
|
||||
"Rev": "9a6558301daeb24d8ac1cc79ea0ccd1a23f1410d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/api/networking/v1",
|
||||
"Rev": "89a74a8d264df0e993299876a8cde88379b940ee"
|
||||
"Rev": "9a6558301daeb24d8ac1cc79ea0ccd1a23f1410d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/api/policy/v1beta1",
|
||||
"Rev": "89a74a8d264df0e993299876a8cde88379b940ee"
|
||||
"Rev": "9a6558301daeb24d8ac1cc79ea0ccd1a23f1410d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/api/rbac/v1",
|
||||
"Rev": "89a74a8d264df0e993299876a8cde88379b940ee"
|
||||
"Rev": "9a6558301daeb24d8ac1cc79ea0ccd1a23f1410d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/api/rbac/v1alpha1",
|
||||
"Rev": "89a74a8d264df0e993299876a8cde88379b940ee"
|
||||
"Rev": "9a6558301daeb24d8ac1cc79ea0ccd1a23f1410d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/api/rbac/v1beta1",
|
||||
"Rev": "89a74a8d264df0e993299876a8cde88379b940ee"
|
||||
"Rev": "9a6558301daeb24d8ac1cc79ea0ccd1a23f1410d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/api/scheduling/v1alpha1",
|
||||
"Rev": "89a74a8d264df0e993299876a8cde88379b940ee"
|
||||
"Rev": "9a6558301daeb24d8ac1cc79ea0ccd1a23f1410d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/api/scheduling/v1beta1",
|
||||
"Rev": "89a74a8d264df0e993299876a8cde88379b940ee"
|
||||
"Rev": "9a6558301daeb24d8ac1cc79ea0ccd1a23f1410d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/api/settings/v1alpha1",
|
||||
"Rev": "89a74a8d264df0e993299876a8cde88379b940ee"
|
||||
"Rev": "9a6558301daeb24d8ac1cc79ea0ccd1a23f1410d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/api/storage/v1",
|
||||
"Rev": "89a74a8d264df0e993299876a8cde88379b940ee"
|
||||
"Rev": "9a6558301daeb24d8ac1cc79ea0ccd1a23f1410d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/api/storage/v1alpha1",
|
||||
"Rev": "89a74a8d264df0e993299876a8cde88379b940ee"
|
||||
"Rev": "9a6558301daeb24d8ac1cc79ea0ccd1a23f1410d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/api/storage/v1beta1",
|
||||
"Rev": "89a74a8d264df0e993299876a8cde88379b940ee"
|
||||
"Rev": "9a6558301daeb24d8ac1cc79ea0ccd1a23f1410d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/pkg/api/apitesting",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/pkg/api/apitesting/fuzzer",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/pkg/api/apitesting/roundtrip",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/pkg/api/equality",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/pkg/api/errors",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/pkg/api/meta",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/pkg/api/resource",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/fuzzer",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/internalversion",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1beta1",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/pkg/conversion",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/pkg/conversion/queryparams",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/pkg/fields",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/pkg/labels",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/pkg/runtime",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/pkg/runtime/schema",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/json",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/protobuf",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/recognizer",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/streaming",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/versioning",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/pkg/selection",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/pkg/types",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/pkg/util/cache",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/pkg/util/clock",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/pkg/util/diff",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/pkg/util/errors",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/pkg/util/framer",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/pkg/util/httpstream",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/pkg/util/httpstream/spdy",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/pkg/util/intstr",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/pkg/util/json",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/pkg/util/mergepatch",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/pkg/util/naming",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/pkg/util/net",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/pkg/util/remotecommand",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/pkg/util/runtime",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/pkg/util/sets",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/pkg/util/strategicpatch",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/pkg/util/validation",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/pkg/util/validation/field",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/pkg/util/wait",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/pkg/util/yaml",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/pkg/version",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/pkg/watch",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/third_party/forked/golang/json",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/third_party/forked/golang/netutil",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/apimachinery/third_party/forked/golang/reflect",
|
||||
"Rev": "2b1284ed4c93a43499e781493253e2ac5959c4fd"
|
||||
"Rev": "e20a3a656cff908c57d43ba1c2ccec3b269e8ad7"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/klog",
|
||||
@@ -612,7 +612,7 @@
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kube-openapi/pkg/util/proto",
|
||||
"Rev": "c59034cc13d587f5ef4e85ca0ade0c1866ae8e1d"
|
||||
"Rev": "fd52d7a69c206aae478f9aff43cb10eaf25e5c2a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "sigs.k8s.io/yaml",
|
||||
|
||||
25
README.md
25
README.md
@@ -2,7 +2,7 @@
|
||||
|
||||
Go clients for talking to a [kubernetes](http://kubernetes.io/) cluster.
|
||||
|
||||
We currently recommend using the v9.0.0 tag. See [INSTALL.md](/INSTALL.md) for
|
||||
We currently recommend using the v10.0.0 tag. See [INSTALL.md](/INSTALL.md) for
|
||||
detailed installation instructions. `go get k8s.io/client-go/...` works, but
|
||||
will build `master`, which doesn't handle the dependencies well.
|
||||
|
||||
@@ -91,16 +91,16 @@ We will backport bugfixes--but not new features--into older versions of
|
||||
|
||||
#### Compatibility matrix
|
||||
|
||||
| | Kubernetes 1.6 | Kubernetes 1.7 | Kubernetes 1.8 | Kubernetes 1.9 | Kubernetes 1.10 | Kubernetes 1.11 | Kubernetes 1.12 |
|
||||
|---------------------|----------------|----------------|----------------|----------------|-----------------|-----------------|-----------------|
|
||||
| client-go 3.0 | ✓ | - | +- | +- | +- | +- | +- |
|
||||
| client-go 4.0 | +- | ✓ | +- | +- | +- | +- | +- |
|
||||
| client-go 5.0 | +- | +- | ✓ | +- | +- | +- | +- |
|
||||
| client-go 6.0 | +- | +- | +- | ✓ | +- | +- | +- |
|
||||
| client-go 7.0 | +- | +- | +- | +- | ✓ | +- | +- |
|
||||
| client-go 8.0 | +- | +- | +- | +- | +- | ✓ | +- |
|
||||
| client-go 9.0 | +- | +- | +- | +- | +- | +- | ✓ |
|
||||
| client-go HEAD | +- | +- | +- | +- | +- | +- | +- |
|
||||
| | Kubernetes 1.7 | Kubernetes 1.8 | Kubernetes 1.9 | Kubernetes 1.10 | Kubernetes 1.11 | Kubernetes 1.12 | Kubernetes 1.13 |
|
||||
|---------------------|----------------|----------------|----------------|-----------------|-----------------|-----------------|-----------------|
|
||||
| client-go 4.0 | ✓ | +- | +- | +- | +- | +- | +- |
|
||||
| client-go 5.0 | +- | ✓ | +- | +- | +- | +- | +- |
|
||||
| client-go 6.0 | +- | +- | ✓ | +- | +- | +- | +- |
|
||||
| client-go 7.0 | +- | +- | +- | ✓ | +- | +- | +- |
|
||||
| client-go 8.0 | +- | +- | +- | +- | ✓ | +- | +- |
|
||||
| client-go 9.0 | +- | +- | +- | +- | +- | ✓ | +- |
|
||||
| client-go 10.0 | +- | +- | +- | +- | +- | +- | ✓ |
|
||||
| client-go HEAD | +- | +- | +- | +- | +- | +- | +- |
|
||||
|
||||
Key:
|
||||
|
||||
@@ -128,9 +128,10 @@ between client-go versions.
|
||||
| client-go 4.0 | Kubernetes main repo, 1.7 branch | = - |
|
||||
| client-go 5.0 | Kubernetes main repo, 1.8 branch | = - |
|
||||
| client-go 6.0 | Kubernetes main repo, 1.9 branch | = - |
|
||||
| client-go 7.0 | Kubernetes main repo, 1.10 branch | ✓ |
|
||||
| client-go 7.0 | Kubernetes main repo, 1.10 branch | = - |
|
||||
| client-go 8.0 | Kubernetes main repo, 1.11 branch | ✓ |
|
||||
| client-go 9.0 | Kubernetes main repo, 1.12 branch | ✓ |
|
||||
| client-go 10.0 | Kubernetes main repo, 1.13 branch | ✓ |
|
||||
| client-go HEAD | Kubernetes main repo, master branch | ✓ |
|
||||
|
||||
Key:
|
||||
|
||||
@@ -164,7 +164,7 @@ func (d *CachedDiscoveryClient) getCachedFile(filename string) ([]byte, error) {
|
||||
}
|
||||
|
||||
func (d *CachedDiscoveryClient) writeCachedFile(filename string, obj runtime.Object) error {
|
||||
if err := os.MkdirAll(filepath.Dir(filename), 0755); err != nil {
|
||||
if err := os.MkdirAll(filepath.Dir(filename), 0750); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -183,7 +183,7 @@ func (d *CachedDiscoveryClient) writeCachedFile(filename string, obj runtime.Obj
|
||||
return err
|
||||
}
|
||||
|
||||
err = os.Chmod(f.Name(), 0755)
|
||||
err = os.Chmod(f.Name(), 0660)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package discovery
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -95,6 +96,32 @@ func TestNewCachedDiscoveryClient_TTL(t *testing.T) {
|
||||
assert.Equal(c.groupCalls, 2)
|
||||
}
|
||||
|
||||
func TestNewCachedDiscoveryClient_PathPerm(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
d, err := ioutil.TempDir("", "")
|
||||
assert.NoError(err)
|
||||
os.RemoveAll(d)
|
||||
defer os.RemoveAll(d)
|
||||
|
||||
c := fakeDiscoveryClient{}
|
||||
cdc := newCachedDiscoveryClient(&c, d, 1*time.Nanosecond)
|
||||
cdc.ServerGroups()
|
||||
|
||||
err = filepath.Walk(d, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if info.IsDir() {
|
||||
assert.Equal(os.FileMode(0750), info.Mode().Perm())
|
||||
} else {
|
||||
assert.Equal(os.FileMode(0660), info.Mode().Perm())
|
||||
}
|
||||
return nil
|
||||
})
|
||||
assert.NoError(err)
|
||||
}
|
||||
|
||||
type fakeDiscoveryClient struct {
|
||||
groupCalls int
|
||||
resourceCalls int
|
||||
|
||||
@@ -18,6 +18,7 @@ package discovery
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/gregjones/httpcache"
|
||||
@@ -35,6 +36,8 @@ type cacheRoundTripper struct {
|
||||
// corresponding requests.
|
||||
func newCacheRoundTripper(cacheDir string, rt http.RoundTripper) http.RoundTripper {
|
||||
d := diskv.New(diskv.Options{
|
||||
PathPerm: os.FileMode(0750),
|
||||
FilePerm: os.FileMode(0660),
|
||||
BasePath: cacheDir,
|
||||
TempDir: filepath.Join(cacheDir, ".diskv-temp"),
|
||||
})
|
||||
|
||||
@@ -22,7 +22,10 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// copied from k8s.io/client-go/transport/round_trippers_test.go
|
||||
@@ -93,3 +96,52 @@ func TestCacheRoundTripper(t *testing.T) {
|
||||
t.Errorf("Invalid content read from cache %q", string(content))
|
||||
}
|
||||
}
|
||||
|
||||
func TestCacheRoundTripperPathPerm(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
rt := &testRoundTripper{}
|
||||
cacheDir, err := ioutil.TempDir("", "cache-rt")
|
||||
os.RemoveAll(cacheDir)
|
||||
defer os.RemoveAll(cacheDir)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cache := newCacheRoundTripper(cacheDir, rt)
|
||||
|
||||
// First call, caches the response
|
||||
req := &http.Request{
|
||||
Method: http.MethodGet,
|
||||
URL: &url.URL{Host: "localhost"},
|
||||
}
|
||||
rt.Response = &http.Response{
|
||||
Header: http.Header{"ETag": []string{`"123456"`}},
|
||||
Body: ioutil.NopCloser(bytes.NewReader([]byte("Content"))),
|
||||
StatusCode: http.StatusOK,
|
||||
}
|
||||
resp, err := cache.RoundTrip(req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
content, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(content) != "Content" {
|
||||
t.Errorf(`Expected Body to be "Content", got %q`, string(content))
|
||||
}
|
||||
|
||||
err = filepath.Walk(cacheDir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if info.IsDir() {
|
||||
assert.Equal(os.FileMode(0750), info.Mode().Perm())
|
||||
} else {
|
||||
assert.Equal(os.FileMode(0660), info.Mode().Perm())
|
||||
}
|
||||
return nil
|
||||
})
|
||||
assert.NoError(err)
|
||||
}
|
||||
|
||||
@@ -303,6 +303,7 @@ func (c *dynamicResourceClient) List(opts metav1.ListOptions) (*unstructured.Uns
|
||||
}
|
||||
|
||||
list := &unstructured.UnstructuredList{}
|
||||
list.SetResourceVersion(entireList.GetResourceVersion())
|
||||
for i := range entireList.Items {
|
||||
item := &entireList.Items[i]
|
||||
metadata, err := meta.Accessor(item)
|
||||
|
||||
@@ -17,6 +17,7 @@ limitations under the License.
|
||||
package dynamic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
@@ -94,6 +95,9 @@ func (c *dynamicResourceClient) Create(obj *unstructured.Unstructured, opts meta
|
||||
return nil, err
|
||||
}
|
||||
name = accessor.GetName()
|
||||
if len(name) == 0 {
|
||||
return nil, fmt.Errorf("name is required")
|
||||
}
|
||||
}
|
||||
|
||||
result := c.client.client.
|
||||
@@ -122,6 +126,10 @@ func (c *dynamicResourceClient) Update(obj *unstructured.Unstructured, opts meta
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
name := accessor.GetName()
|
||||
if len(name) == 0 {
|
||||
return nil, fmt.Errorf("name is required")
|
||||
}
|
||||
outBytes, err := runtime.Encode(unstructured.UnstructuredJSONScheme, obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -129,7 +137,7 @@ func (c *dynamicResourceClient) Update(obj *unstructured.Unstructured, opts meta
|
||||
|
||||
result := c.client.client.
|
||||
Put().
|
||||
AbsPath(append(c.makeURLSegments(accessor.GetName()), subresources...)...).
|
||||
AbsPath(append(c.makeURLSegments(name), subresources...)...).
|
||||
Body(outBytes).
|
||||
SpecificallyVersionedParams(&opts, dynamicParameterCodec, versionV1).
|
||||
Do()
|
||||
@@ -153,6 +161,10 @@ func (c *dynamicResourceClient) UpdateStatus(obj *unstructured.Unstructured, opt
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
name := accessor.GetName()
|
||||
if len(name) == 0 {
|
||||
return nil, fmt.Errorf("name is required")
|
||||
}
|
||||
|
||||
outBytes, err := runtime.Encode(unstructured.UnstructuredJSONScheme, obj)
|
||||
if err != nil {
|
||||
@@ -161,7 +173,7 @@ func (c *dynamicResourceClient) UpdateStatus(obj *unstructured.Unstructured, opt
|
||||
|
||||
result := c.client.client.
|
||||
Put().
|
||||
AbsPath(append(c.makeURLSegments(accessor.GetName()), "status")...).
|
||||
AbsPath(append(c.makeURLSegments(name), "status")...).
|
||||
Body(outBytes).
|
||||
SpecificallyVersionedParams(&opts, dynamicParameterCodec, versionV1).
|
||||
Do()
|
||||
@@ -181,6 +193,9 @@ func (c *dynamicResourceClient) UpdateStatus(obj *unstructured.Unstructured, opt
|
||||
}
|
||||
|
||||
func (c *dynamicResourceClient) Delete(name string, opts *metav1.DeleteOptions, subresources ...string) error {
|
||||
if len(name) == 0 {
|
||||
return fmt.Errorf("name is required")
|
||||
}
|
||||
if opts == nil {
|
||||
opts = &metav1.DeleteOptions{}
|
||||
}
|
||||
@@ -216,6 +231,9 @@ func (c *dynamicResourceClient) DeleteCollection(opts *metav1.DeleteOptions, lis
|
||||
}
|
||||
|
||||
func (c *dynamicResourceClient) Get(name string, opts metav1.GetOptions, subresources ...string) (*unstructured.Unstructured, error) {
|
||||
if len(name) == 0 {
|
||||
return nil, fmt.Errorf("name is required")
|
||||
}
|
||||
result := c.client.client.Get().AbsPath(append(c.makeURLSegments(name), subresources...)...).SpecificallyVersionedParams(&opts, dynamicParameterCodec, versionV1).Do()
|
||||
if err := result.Error(); err != nil {
|
||||
return nil, err
|
||||
@@ -284,6 +302,9 @@ func (c *dynamicResourceClient) Watch(opts metav1.ListOptions) (watch.Interface,
|
||||
}
|
||||
|
||||
func (c *dynamicResourceClient) Patch(name string, pt types.PatchType, data []byte, opts metav1.UpdateOptions, subresources ...string) (*unstructured.Unstructured, error) {
|
||||
if len(name) == 0 {
|
||||
return nil, fmt.Errorf("name is required")
|
||||
}
|
||||
result := c.client.client.
|
||||
Patch(pt).
|
||||
AbsPath(append(c.makeURLSegments(name), subresources...)...).
|
||||
|
||||
@@ -145,6 +145,7 @@ func (r *azureRoundTripper) WrappedRoundTripper() http.RoundTripper { return r.r
|
||||
|
||||
type azureToken struct {
|
||||
token adal.Token
|
||||
environment string
|
||||
clientID string
|
||||
tenantID string
|
||||
apiserverID string
|
||||
@@ -219,6 +220,10 @@ func (ts *azureTokenSource) retrieveTokenFromCfg() (*azureToken, error) {
|
||||
if refreshToken == "" {
|
||||
return nil, fmt.Errorf("no refresh token in cfg: %s", cfgRefreshToken)
|
||||
}
|
||||
environment := ts.cfg[cfgEnvironment]
|
||||
if environment == "" {
|
||||
return nil, fmt.Errorf("no environment in cfg: %s", cfgEnvironment)
|
||||
}
|
||||
clientID := ts.cfg[cfgClientID]
|
||||
if clientID == "" {
|
||||
return nil, fmt.Errorf("no client ID in cfg: %s", cfgClientID)
|
||||
@@ -250,6 +255,7 @@ func (ts *azureTokenSource) retrieveTokenFromCfg() (*azureToken, error) {
|
||||
Resource: fmt.Sprintf("spn:%s", apiserverID),
|
||||
Type: tokenType,
|
||||
},
|
||||
environment: environment,
|
||||
clientID: clientID,
|
||||
tenantID: tenantID,
|
||||
apiserverID: apiserverID,
|
||||
@@ -260,6 +266,7 @@ func (ts *azureTokenSource) storeTokenInCfg(token *azureToken) error {
|
||||
newCfg := make(map[string]string)
|
||||
newCfg[cfgAccessToken] = token.token.AccessToken
|
||||
newCfg[cfgRefreshToken] = token.token.RefreshToken
|
||||
newCfg[cfgEnvironment] = token.environment
|
||||
newCfg[cfgClientID] = token.clientID
|
||||
newCfg[cfgTenantID] = token.tenantID
|
||||
newCfg[cfgApiserverID] = token.apiserverID
|
||||
@@ -275,7 +282,12 @@ func (ts *azureTokenSource) storeTokenInCfg(token *azureToken) error {
|
||||
}
|
||||
|
||||
func (ts *azureTokenSource) refreshToken(token *azureToken) (*azureToken, error) {
|
||||
oauthConfig, err := adal.NewOAuthConfig(azure.PublicCloud.ActiveDirectoryEndpoint, token.tenantID)
|
||||
env, err := azure.EnvironmentFromName(token.environment)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
oauthConfig, err := adal.NewOAuthConfig(env.ActiveDirectoryEndpoint, token.tenantID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("building the OAuth configuration for token refresh: %v", err)
|
||||
}
|
||||
@@ -299,6 +311,7 @@ func (ts *azureTokenSource) refreshToken(token *azureToken) (*azureToken, error)
|
||||
|
||||
return &azureToken{
|
||||
token: spt.Token(),
|
||||
environment: token.environment,
|
||||
clientID: token.clientID,
|
||||
tenantID: token.tenantID,
|
||||
apiserverID: token.apiserverID,
|
||||
@@ -353,6 +366,7 @@ func (ts *azureTokenSourceDeviceCode) Token() (*azureToken, error) {
|
||||
|
||||
return &azureToken{
|
||||
token: *token,
|
||||
environment: ts.environment.Name,
|
||||
clientID: ts.clientID,
|
||||
tenantID: ts.tenantID,
|
||||
apiserverID: ts.apiserverID,
|
||||
|
||||
@@ -53,6 +53,13 @@ func TestAzureTokenSource(t *testing.T) {
|
||||
|
||||
wantCfg := token2Cfg(token)
|
||||
persistedCfg := persiter.Cache()
|
||||
|
||||
wantCfgLen := len(wantCfg)
|
||||
persistedCfgLen := len(persistedCfg)
|
||||
if wantCfgLen != persistedCfgLen {
|
||||
t.Errorf("wantCfgLen and persistedCfgLen do not match, wantCfgLen=%v, persistedCfgLen=%v", wantCfgLen, persistedCfgLen)
|
||||
}
|
||||
|
||||
for k, v := range persistedCfg {
|
||||
if strings.Compare(v, wantCfg[k]) != 0 {
|
||||
t.Errorf("Token() persisted cfg %s: got %v, want %v", k, v, wantCfg[k])
|
||||
@@ -103,6 +110,7 @@ type fakeTokenSource struct {
|
||||
func (ts *fakeTokenSource) Token() (*azureToken, error) {
|
||||
return &azureToken{
|
||||
token: newFackeAzureToken(ts.accessToken, ts.expiresOn),
|
||||
environment: "testenv",
|
||||
clientID: "fake",
|
||||
tenantID: "fake",
|
||||
apiserverID: "fake",
|
||||
@@ -113,6 +121,7 @@ func token2Cfg(token *azureToken) map[string]string {
|
||||
cfg := make(map[string]string)
|
||||
cfg[cfgAccessToken] = token.token.AccessToken
|
||||
cfg[cfgRefreshToken] = token.token.RefreshToken
|
||||
cfg[cfgEnvironment] = token.environment
|
||||
cfg[cfgClientID] = token.clientID
|
||||
cfg[cfgTenantID] = token.tenantID
|
||||
cfg[cfgApiserverID] = token.apiserverID
|
||||
|
||||
@@ -70,6 +70,11 @@ type Config struct {
|
||||
// TODO: demonstrate an OAuth2 compatible client.
|
||||
BearerToken string
|
||||
|
||||
// Path to a file containing a BearerToken.
|
||||
// If set, the contents are periodically read.
|
||||
// The last successfully read value takes precedence over BearerToken.
|
||||
BearerTokenFile string
|
||||
|
||||
// Impersonate is the configuration that RESTClient will use for impersonation.
|
||||
Impersonate ImpersonationConfig
|
||||
|
||||
@@ -322,9 +327,8 @@ func InClusterConfig() (*Config, error) {
|
||||
return nil, ErrNotInCluster
|
||||
}
|
||||
|
||||
ts := NewCachedFileTokenSource(tokenFile)
|
||||
|
||||
if _, err := ts.Token(); err != nil {
|
||||
token, err := ioutil.ReadFile(tokenFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -340,7 +344,8 @@ func InClusterConfig() (*Config, error) {
|
||||
// TODO: switch to using cluster DNS.
|
||||
Host: "https://" + net.JoinHostPort(host, port),
|
||||
TLSClientConfig: tlsClientConfig,
|
||||
WrapTransport: TokenSourceWrapTransport(ts),
|
||||
BearerToken: string(token),
|
||||
BearerTokenFile: tokenFile,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -430,12 +435,13 @@ func AnonymousClientConfig(config *Config) *Config {
|
||||
// CopyConfig returns a copy of the given config
|
||||
func CopyConfig(config *Config) *Config {
|
||||
return &Config{
|
||||
Host: config.Host,
|
||||
APIPath: config.APIPath,
|
||||
ContentConfig: config.ContentConfig,
|
||||
Username: config.Username,
|
||||
Password: config.Password,
|
||||
BearerToken: config.BearerToken,
|
||||
Host: config.Host,
|
||||
APIPath: config.APIPath,
|
||||
ContentConfig: config.ContentConfig,
|
||||
Username: config.Username,
|
||||
Password: config.Password,
|
||||
BearerToken: config.BearerToken,
|
||||
BearerTokenFile: config.BearerTokenFile,
|
||||
Impersonate: ImpersonationConfig{
|
||||
Groups: config.Impersonate.Groups,
|
||||
Extra: config.Impersonate.Extra,
|
||||
|
||||
@@ -264,6 +264,7 @@ func TestAnonymousConfig(t *testing.T) {
|
||||
// is added to Config, update AnonymousClientConfig to preserve the field otherwise.
|
||||
expected.Impersonate = ImpersonationConfig{}
|
||||
expected.BearerToken = ""
|
||||
expected.BearerTokenFile = ""
|
||||
expected.Username = ""
|
||||
expected.Password = ""
|
||||
expected.AuthProvider = nil
|
||||
|
||||
@@ -74,9 +74,10 @@ func (c *Config) TransportConfig() (*transport.Config, error) {
|
||||
KeyFile: c.KeyFile,
|
||||
KeyData: c.KeyData,
|
||||
},
|
||||
Username: c.Username,
|
||||
Password: c.Password,
|
||||
BearerToken: c.BearerToken,
|
||||
Username: c.Username,
|
||||
Password: c.Password,
|
||||
BearerToken: c.BearerToken,
|
||||
BearerTokenFile: c.BearerTokenFile,
|
||||
Impersonate: transport.ImpersonationConfig{
|
||||
UserName: c.Impersonate.UserName,
|
||||
Groups: c.Impersonate.Groups,
|
||||
|
||||
27
tools/cache/reflector.go
vendored
27
tools/cache/reflector.go
vendored
@@ -24,10 +24,8 @@ import (
|
||||
"net"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
@@ -95,17 +93,10 @@ func NewReflector(lw ListerWatcher, expectedType interface{}, store Store, resyn
|
||||
return NewNamedReflector(naming.GetNameFromCallsite(internalPackages...), lw, expectedType, store, resyncPeriod)
|
||||
}
|
||||
|
||||
// reflectorDisambiguator is used to disambiguate started reflectors.
|
||||
// initialized to an unstable value to ensure meaning isn't attributed to the suffix.
|
||||
var reflectorDisambiguator = int64(time.Now().UnixNano() % 12345)
|
||||
|
||||
// NewNamedReflector same as NewReflector, but with a specified name for logging
|
||||
func NewNamedReflector(name string, lw ListerWatcher, expectedType interface{}, store Store, resyncPeriod time.Duration) *Reflector {
|
||||
reflectorSuffix := atomic.AddInt64(&reflectorDisambiguator, 1)
|
||||
r := &Reflector{
|
||||
name: name,
|
||||
// we need this to be unique per process (some names are still the same) but obvious who it belongs to
|
||||
metrics: newReflectorMetrics(makeValidPrometheusMetricLabel(fmt.Sprintf("reflector_"+name+"_%d", reflectorSuffix))),
|
||||
name: name,
|
||||
listerWatcher: lw,
|
||||
store: store,
|
||||
expectedType: reflect.TypeOf(expectedType),
|
||||
@@ -173,13 +164,10 @@ func (r *Reflector) ListAndWatch(stopCh <-chan struct{}) error {
|
||||
// to be served from cache and potentially be delayed relative to
|
||||
// etcd contents. Reflector framework will catch up via Watch() eventually.
|
||||
options := metav1.ListOptions{ResourceVersion: "0"}
|
||||
r.metrics.numberOfLists.Inc()
|
||||
start := r.clock.Now()
|
||||
list, err := r.listerWatcher.List(options)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: Failed to list %v: %v", r.name, r.expectedType, err)
|
||||
}
|
||||
r.metrics.listDuration.Observe(time.Since(start).Seconds())
|
||||
listMetaInterface, err := meta.ListAccessor(list)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: Unable to understand list result %#v: %v", r.name, list, err)
|
||||
@@ -189,7 +177,6 @@ func (r *Reflector) ListAndWatch(stopCh <-chan struct{}) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: Unable to understand list result %#v (%v)", r.name, list, err)
|
||||
}
|
||||
r.metrics.numberOfItemsInList.Observe(float64(len(items)))
|
||||
if err := r.syncWith(items, resourceVersion); err != nil {
|
||||
return fmt.Errorf("%s: Unable to sync list result: %v", r.name, err)
|
||||
}
|
||||
@@ -239,7 +226,6 @@ func (r *Reflector) ListAndWatch(stopCh <-chan struct{}) error {
|
||||
TimeoutSeconds: &timeoutSeconds,
|
||||
}
|
||||
|
||||
r.metrics.numberOfWatches.Inc()
|
||||
w, err := r.listerWatcher.Watch(options)
|
||||
if err != nil {
|
||||
switch err {
|
||||
@@ -291,11 +277,6 @@ func (r *Reflector) watchHandler(w watch.Interface, resourceVersion *string, err
|
||||
// Stopping the watcher should be idempotent and if we return from this function there's no way
|
||||
// we're coming back in with the same watch interface.
|
||||
defer w.Stop()
|
||||
// update metrics
|
||||
defer func() {
|
||||
r.metrics.numberOfItemsInWatch.Observe(float64(eventCount))
|
||||
r.metrics.watchDuration.Observe(time.Since(start).Seconds())
|
||||
}()
|
||||
|
||||
loop:
|
||||
for {
|
||||
@@ -351,7 +332,6 @@ loop:
|
||||
|
||||
watchDuration := r.clock.Now().Sub(start)
|
||||
if watchDuration < 1*time.Second && eventCount == 0 {
|
||||
r.metrics.numberOfShortWatches.Inc()
|
||||
return fmt.Errorf("very short watch: %s: Unexpected watch close - watch lasted less than a second and no items received", r.name)
|
||||
}
|
||||
klog.V(4).Infof("%s: Watch close - %v total %v items received", r.name, r.expectedType, eventCount)
|
||||
@@ -370,9 +350,4 @@ func (r *Reflector) setLastSyncResourceVersion(v string) {
|
||||
r.lastSyncResourceVersionMutex.Lock()
|
||||
defer r.lastSyncResourceVersionMutex.Unlock()
|
||||
r.lastSyncResourceVersion = v
|
||||
|
||||
rv, err := strconv.Atoi(v)
|
||||
if err == nil {
|
||||
r.metrics.lastResourceVersion.Set(float64(rv))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,12 +228,14 @@ func (config *DirectClientConfig) getUserIdentificationPartialConfig(configAuthI
|
||||
// blindly overwrite existing values based on precedence
|
||||
if len(configAuthInfo.Token) > 0 {
|
||||
mergedConfig.BearerToken = configAuthInfo.Token
|
||||
mergedConfig.BearerTokenFile = configAuthInfo.TokenFile
|
||||
} else if len(configAuthInfo.TokenFile) > 0 {
|
||||
ts := restclient.NewCachedFileTokenSource(configAuthInfo.TokenFile)
|
||||
if _, err := ts.Token(); err != nil {
|
||||
tokenBytes, err := ioutil.ReadFile(configAuthInfo.TokenFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mergedConfig.WrapTransport = restclient.TokenSourceWrapTransport(ts)
|
||||
mergedConfig.BearerToken = string(tokenBytes)
|
||||
mergedConfig.BearerTokenFile = configAuthInfo.TokenFile
|
||||
}
|
||||
if len(configAuthInfo.Impersonate) > 0 {
|
||||
mergedConfig.Impersonate = restclient.ImpersonationConfig{
|
||||
@@ -498,8 +500,9 @@ func (config *inClusterClientConfig) ClientConfig() (*restclient.Config, error)
|
||||
if server := config.overrides.ClusterInfo.Server; len(server) > 0 {
|
||||
icc.Host = server
|
||||
}
|
||||
if token := config.overrides.AuthInfo.Token; len(token) > 0 {
|
||||
icc.BearerToken = token
|
||||
if len(config.overrides.AuthInfo.Token) > 0 || len(config.overrides.AuthInfo.TokenFile) > 0 {
|
||||
icc.BearerToken = config.overrides.AuthInfo.Token
|
||||
icc.BearerTokenFile = config.overrides.AuthInfo.TokenFile
|
||||
}
|
||||
if certificateAuthorityFile := config.overrides.ClusterInfo.CertificateAuthority; len(certificateAuthorityFile) > 0 {
|
||||
icc.TLSClientConfig.CAFile = certificateAuthorityFile
|
||||
|
||||
@@ -18,7 +18,6 @@ package clientcmd
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
@@ -334,19 +333,7 @@ func TestBasicTokenFile(t *testing.T) {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
var out *http.Request
|
||||
clientConfig.WrapTransport(fakeTransport(func(req *http.Request) (*http.Response, error) {
|
||||
out = req
|
||||
return &http.Response{}, nil
|
||||
})).RoundTrip(&http.Request{})
|
||||
|
||||
matchStringArg(token, strings.TrimPrefix(out.Header.Get("Authorization"), "Bearer "), t)
|
||||
}
|
||||
|
||||
type fakeTransport func(*http.Request) (*http.Response, error)
|
||||
|
||||
func (ft fakeTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
return ft(req)
|
||||
matchStringArg(token, clientConfig.BearerToken, t)
|
||||
}
|
||||
|
||||
func TestPrecedenceTokenFile(t *testing.T) {
|
||||
@@ -561,6 +548,30 @@ func TestInClusterClientConfigPrecedence(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
overrides: &ConfigOverrides{
|
||||
ClusterInfo: clientcmdapi.Cluster{
|
||||
Server: "https://host-from-overrides.com",
|
||||
CertificateAuthority: "/path/to/ca-from-overrides.crt",
|
||||
},
|
||||
AuthInfo: clientcmdapi.AuthInfo{
|
||||
Token: "token-from-override",
|
||||
TokenFile: "tokenfile-from-override",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
overrides: &ConfigOverrides{
|
||||
ClusterInfo: clientcmdapi.Cluster{
|
||||
Server: "https://host-from-overrides.com",
|
||||
CertificateAuthority: "/path/to/ca-from-overrides.crt",
|
||||
},
|
||||
AuthInfo: clientcmdapi.AuthInfo{
|
||||
Token: "",
|
||||
TokenFile: "tokenfile-from-override",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
overrides: &ConfigOverrides{},
|
||||
},
|
||||
@@ -569,13 +580,15 @@ func TestInClusterClientConfigPrecedence(t *testing.T) {
|
||||
for _, tc := range tt {
|
||||
expectedServer := "https://host-from-cluster.com"
|
||||
expectedToken := "token-from-cluster"
|
||||
expectedTokenFile := "tokenfile-from-cluster"
|
||||
expectedCAFile := "/path/to/ca-from-cluster.crt"
|
||||
|
||||
icc := &inClusterClientConfig{
|
||||
inClusterConfigProvider: func() (*restclient.Config, error) {
|
||||
return &restclient.Config{
|
||||
Host: expectedServer,
|
||||
BearerToken: expectedToken,
|
||||
Host: expectedServer,
|
||||
BearerToken: expectedToken,
|
||||
BearerTokenFile: expectedTokenFile,
|
||||
TLSClientConfig: restclient.TLSClientConfig{
|
||||
CAFile: expectedCAFile,
|
||||
},
|
||||
@@ -592,8 +605,9 @@ func TestInClusterClientConfigPrecedence(t *testing.T) {
|
||||
if overridenServer := tc.overrides.ClusterInfo.Server; len(overridenServer) > 0 {
|
||||
expectedServer = overridenServer
|
||||
}
|
||||
if overridenToken := tc.overrides.AuthInfo.Token; len(overridenToken) > 0 {
|
||||
expectedToken = overridenToken
|
||||
if len(tc.overrides.AuthInfo.Token) > 0 || len(tc.overrides.AuthInfo.TokenFile) > 0 {
|
||||
expectedToken = tc.overrides.AuthInfo.Token
|
||||
expectedTokenFile = tc.overrides.AuthInfo.TokenFile
|
||||
}
|
||||
if overridenCAFile := tc.overrides.ClusterInfo.CertificateAuthority; len(overridenCAFile) > 0 {
|
||||
expectedCAFile = overridenCAFile
|
||||
@@ -605,6 +619,9 @@ func TestInClusterClientConfigPrecedence(t *testing.T) {
|
||||
if clientConfig.BearerToken != expectedToken {
|
||||
t.Errorf("Expected token %v, got %v", expectedToken, clientConfig.BearerToken)
|
||||
}
|
||||
if clientConfig.BearerTokenFile != expectedTokenFile {
|
||||
t.Errorf("Expected tokenfile %v, got %v", expectedTokenFile, clientConfig.BearerTokenFile)
|
||||
}
|
||||
if clientConfig.TLSClientConfig.CAFile != expectedCAFile {
|
||||
t.Errorf("Expected Certificate Authority %v, got %v", expectedCAFile, clientConfig.TLSClientConfig.CAFile)
|
||||
}
|
||||
|
||||
@@ -39,6 +39,11 @@ type Config struct {
|
||||
// Bearer token for authentication
|
||||
BearerToken string
|
||||
|
||||
// Path to a file containing a BearerToken.
|
||||
// If set, the contents are periodically read.
|
||||
// The last successfully read value takes precedence over BearerToken.
|
||||
BearerTokenFile string
|
||||
|
||||
// Impersonate is the config that this Config will impersonate using
|
||||
Impersonate ImpersonationConfig
|
||||
|
||||
@@ -80,7 +85,7 @@ func (c *Config) HasBasicAuth() bool {
|
||||
|
||||
// HasTokenAuth returns whether the configuration has token authentication or not.
|
||||
func (c *Config) HasTokenAuth() bool {
|
||||
return len(c.BearerToken) != 0
|
||||
return len(c.BearerToken) != 0 || len(c.BearerTokenFile) != 0
|
||||
}
|
||||
|
||||
// HasCertAuth returns whether the configuration has certificate authentication or not.
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
"k8s.io/klog"
|
||||
|
||||
utilnet "k8s.io/apimachinery/pkg/util/net"
|
||||
@@ -44,7 +45,11 @@ func HTTPWrappersForConfig(config *Config, rt http.RoundTripper) (http.RoundTrip
|
||||
case config.HasBasicAuth() && config.HasTokenAuth():
|
||||
return nil, fmt.Errorf("username/password or bearer token may be set, but not both")
|
||||
case config.HasTokenAuth():
|
||||
rt = NewBearerAuthRoundTripper(config.BearerToken, rt)
|
||||
var err error
|
||||
rt, err = NewBearerAuthWithRefreshRoundTripper(config.BearerToken, config.BearerTokenFile, rt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case config.HasBasicAuth():
|
||||
rt = NewBasicAuthRoundTripper(config.Username, config.Password, rt)
|
||||
}
|
||||
@@ -265,13 +270,35 @@ func (rt *impersonatingRoundTripper) WrappedRoundTripper() http.RoundTripper { r
|
||||
|
||||
type bearerAuthRoundTripper struct {
|
||||
bearer string
|
||||
source oauth2.TokenSource
|
||||
rt http.RoundTripper
|
||||
}
|
||||
|
||||
// NewBearerAuthRoundTripper adds the provided bearer token to a request
|
||||
// unless the authorization header has already been set.
|
||||
func NewBearerAuthRoundTripper(bearer string, rt http.RoundTripper) http.RoundTripper {
|
||||
return &bearerAuthRoundTripper{bearer, rt}
|
||||
return &bearerAuthRoundTripper{bearer, nil, rt}
|
||||
}
|
||||
|
||||
// NewBearerAuthRoundTripper adds the provided bearer token to a request
|
||||
// unless the authorization header has already been set.
|
||||
// If tokenFile is non-empty, it is periodically read,
|
||||
// and the last successfully read content is used as the bearer token.
|
||||
// If tokenFile is non-empty and bearer is empty, the tokenFile is read
|
||||
// immediately to populate the initial bearer token.
|
||||
func NewBearerAuthWithRefreshRoundTripper(bearer string, tokenFile string, rt http.RoundTripper) (http.RoundTripper, error) {
|
||||
if len(tokenFile) == 0 {
|
||||
return &bearerAuthRoundTripper{bearer, nil, rt}, nil
|
||||
}
|
||||
source := NewCachedFileTokenSource(tokenFile)
|
||||
if len(bearer) == 0 {
|
||||
token, err := source.Token()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bearer = token.AccessToken
|
||||
}
|
||||
return &bearerAuthRoundTripper{bearer, source, rt}, nil
|
||||
}
|
||||
|
||||
func (rt *bearerAuthRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
@@ -280,7 +307,13 @@ func (rt *bearerAuthRoundTripper) RoundTrip(req *http.Request) (*http.Response,
|
||||
}
|
||||
|
||||
req = utilnet.CloneRequest(req)
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", rt.bearer))
|
||||
token := rt.bearer
|
||||
if rt.source != nil {
|
||||
if refreshedToken, err := rt.source.Token(); err == nil {
|
||||
token = refreshedToken.AccessToken
|
||||
}
|
||||
}
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
|
||||
return rt.rt.RoundTrip(req)
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package rest
|
||||
package transport
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -47,14 +47,14 @@ func TokenSourceWrapTransport(ts oauth2.TokenSource) func(http.RoundTripper) htt
|
||||
func NewCachedFileTokenSource(path string) oauth2.TokenSource {
|
||||
return &cachingTokenSource{
|
||||
now: time.Now,
|
||||
leeway: 1 * time.Minute,
|
||||
leeway: 10 * time.Second,
|
||||
base: &fileTokenSource{
|
||||
path: path,
|
||||
// This period was picked because it is half of the minimum validity
|
||||
// duration for a token provisioned by they TokenRequest API. This is
|
||||
// unsophisticated and should induce rotation at a frequency that should
|
||||
// work with the token volume source.
|
||||
period: 5 * time.Minute,
|
||||
// This period was picked because it is half of the duration between when the kubelet
|
||||
// refreshes a projected service account token and when the original token expires.
|
||||
// Default token lifetime is 10 minutes, and the kubelet starts refreshing at 80% of lifetime.
|
||||
// This should induce re-reading at a frequency that works with the token volume source.
|
||||
period: time.Minute,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package rest
|
||||
package transport
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
Reference in New Issue
Block a user