mirror of
https://github.com/kubernetes/client-go.git
synced 2026-06-17 15:25:41 +00:00
Compare commits
1 Commits
v0.25.0-be
...
v0.25.0-al
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
52c50d2fc2 |
@@ -61,7 +61,6 @@ type PodSpecApplyConfiguration struct {
|
||||
TopologySpreadConstraints []TopologySpreadConstraintApplyConfiguration `json:"topologySpreadConstraints,omitempty"`
|
||||
SetHostnameAsFQDN *bool `json:"setHostnameAsFQDN,omitempty"`
|
||||
OS *PodOSApplyConfiguration `json:"os,omitempty"`
|
||||
HostUsers *bool `json:"hostUsers,omitempty"`
|
||||
}
|
||||
|
||||
// PodSpecApplyConfiguration constructs an declarative configuration of the PodSpec type for use with
|
||||
@@ -408,11 +407,3 @@ func (b *PodSpecApplyConfiguration) WithOS(value *PodOSApplyConfiguration) *PodS
|
||||
b.OS = value
|
||||
return b
|
||||
}
|
||||
|
||||
// WithHostUsers sets the HostUsers field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the HostUsers field is set to the value of the last call.
|
||||
func (b *PodSpecApplyConfiguration) WithHostUsers(value bool) *PodSpecApplyConfiguration {
|
||||
b.HostUsers = &value
|
||||
return b
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ type TopologySpreadConstraintApplyConfiguration struct {
|
||||
MinDomains *int32 `json:"minDomains,omitempty"`
|
||||
NodeAffinityPolicy *v1.NodeInclusionPolicy `json:"nodeAffinityPolicy,omitempty"`
|
||||
NodeTaintsPolicy *v1.NodeInclusionPolicy `json:"nodeTaintsPolicy,omitempty"`
|
||||
MatchLabelKeys []string `json:"matchLabelKeys,omitempty"`
|
||||
}
|
||||
|
||||
// TopologySpreadConstraintApplyConfiguration constructs an declarative configuration of the TopologySpreadConstraint type for use with
|
||||
@@ -97,13 +96,3 @@ func (b *TopologySpreadConstraintApplyConfiguration) WithNodeTaintsPolicy(value
|
||||
b.NodeTaintsPolicy = &value
|
||||
return b
|
||||
}
|
||||
|
||||
// WithMatchLabelKeys adds the given value to the MatchLabelKeys field in the declarative configuration
|
||||
// and returns the receiver, so that objects can be build by chaining "With" function invocations.
|
||||
// If called multiple times, values provided by each call will be appended to the MatchLabelKeys field.
|
||||
func (b *TopologySpreadConstraintApplyConfiguration) WithMatchLabelKeys(values ...string) *TopologySpreadConstraintApplyConfiguration {
|
||||
for i := range values {
|
||||
b.MatchLabelKeys = append(b.MatchLabelKeys, values[i])
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
@@ -20,22 +20,22 @@ limitations under the License.
|
||||
Package applyconfigurations provides typesafe go representations of the apply
|
||||
configurations that are used to constructs Server-side Apply requests.
|
||||
|
||||
# Basics
|
||||
Basics
|
||||
|
||||
The Apply functions in the typed client (see the k8s.io/client-go/kubernetes/typed packages) offer
|
||||
a direct and typesafe way of calling Server-side Apply. Each Apply function takes an "apply
|
||||
configuration" type as an argument, which is a structured representation of an Apply request. For
|
||||
example:
|
||||
|
||||
import (
|
||||
...
|
||||
v1ac "k8s.io/client-go/applyconfigurations/autoscaling/v1"
|
||||
)
|
||||
hpaApplyConfig := v1ac.HorizontalPodAutoscaler(autoscalerName, ns).
|
||||
WithSpec(v1ac.HorizontalPodAutoscalerSpec().
|
||||
WithMinReplicas(0)
|
||||
)
|
||||
return hpav1client.Apply(ctx, hpaApplyConfig, metav1.ApplyOptions{FieldManager: "mycontroller", Force: true})
|
||||
import (
|
||||
...
|
||||
v1ac "k8s.io/client-go/applyconfigurations/autoscaling/v1"
|
||||
)
|
||||
hpaApplyConfig := v1ac.HorizontalPodAutoscaler(autoscalerName, ns).
|
||||
WithSpec(v1ac.HorizontalPodAutoscalerSpec().
|
||||
WithMinReplicas(0)
|
||||
)
|
||||
return hpav1client.Apply(ctx, hpaApplyConfig, metav1.ApplyOptions{FieldManager: "mycontroller", Force: true})
|
||||
|
||||
Note in this example that HorizontalPodAutoscaler is imported from an "applyconfigurations"
|
||||
package. Each "apply configuration" type represents the same Kubernetes object kind as the
|
||||
@@ -43,46 +43,46 @@ corresponding go struct, but where all fields are pointers to make them optional
|
||||
requests to be accurately represented. For example, this when the apply configuration in the above
|
||||
example is marshalled to YAML, it produces:
|
||||
|
||||
apiVersion: autoscaling/v1
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: myHPA
|
||||
namespace: myNamespace
|
||||
spec:
|
||||
minReplicas: 0
|
||||
apiVersion: autoscaling/v1
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: myHPA
|
||||
namespace: myNamespace
|
||||
spec:
|
||||
minReplicas: 0
|
||||
|
||||
To understand why this is needed, the above YAML cannot be produced by the
|
||||
v1.HorizontalPodAutoscaler go struct. Take for example:
|
||||
|
||||
hpa := v1.HorizontalPodAutoscaler{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "autoscaling/v1",
|
||||
Kind: "HorizontalPodAutoscaler",
|
||||
},
|
||||
ObjectMeta: ObjectMeta{
|
||||
Namespace: ns,
|
||||
Name: autoscalerName,
|
||||
},
|
||||
Spec: v1.HorizontalPodAutoscalerSpec{
|
||||
MinReplicas: pointer.Int32Ptr(0),
|
||||
},
|
||||
}
|
||||
hpa := v1.HorizontalPodAutoscaler{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "autoscaling/v1",
|
||||
Kind: "HorizontalPodAutoscaler",
|
||||
},
|
||||
ObjectMeta: ObjectMeta{
|
||||
Namespace: ns,
|
||||
Name: autoscalerName,
|
||||
},
|
||||
Spec: v1.HorizontalPodAutoscalerSpec{
|
||||
MinReplicas: pointer.Int32Ptr(0),
|
||||
},
|
||||
}
|
||||
|
||||
The above code attempts to declare the same apply configuration as shown in the previous examples,
|
||||
but when marshalled to YAML, produces:
|
||||
|
||||
kind: HorizontalPodAutoscaler
|
||||
apiVersion: autoscaling/v1
|
||||
metadata:
|
||||
name: myHPA
|
||||
namespace: myNamespace
|
||||
creationTimestamp: null
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
kind: ""
|
||||
name: ""
|
||||
minReplicas: 0
|
||||
maxReplicas: 0
|
||||
kind: HorizontalPodAutoscaler
|
||||
apiVersion: autoscaling/v1
|
||||
metadata:
|
||||
name: myHPA
|
||||
namespace: myNamespace
|
||||
creationTimestamp: null
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
kind: ""
|
||||
name: ""
|
||||
minReplicas: 0
|
||||
maxReplicas: 0
|
||||
|
||||
Which, among other things, contains spec.maxReplicas set to 0. This is almost certainly not what
|
||||
the caller intended (the intended apply configuration says nothing about the maxReplicas field),
|
||||
@@ -102,7 +102,7 @@ general purpose library. In addition to the convenience, the With functions also
|
||||
developers from the underlying representation, which makes it safer for the underlying
|
||||
representation to be changed to support additional features in the future.
|
||||
|
||||
# Controller Support
|
||||
Controller Support
|
||||
|
||||
The new client-go support makes it much easier to use Server-side Apply in controllers, by either of
|
||||
two mechanisms.
|
||||
@@ -130,24 +130,24 @@ accidentally deleted. For such cases, an alternative to mechanism 1 is to replac
|
||||
reconciliation code that performs a "read/modify-in-place/update" (or patch) workflow with a
|
||||
"extract/modify-in-place/apply" workflow. Here's an example of the new workflow:
|
||||
|
||||
fieldMgr := "my-field-manager"
|
||||
deploymentClient := clientset.AppsV1().Deployments("default")
|
||||
// read, could also be read from a shared informer
|
||||
deployment, err := deploymentClient.Get(ctx, "example-deployment", metav1.GetOptions{})
|
||||
if err != nil {
|
||||
// handle error
|
||||
}
|
||||
// extract
|
||||
deploymentApplyConfig, err := appsv1ac.ExtractDeployment(deployment, fieldMgr)
|
||||
if err != nil {
|
||||
// handle error
|
||||
}
|
||||
// modify-in-place
|
||||
deploymentApplyConfig.Spec.Template.Spec.WithContainers(corev1ac.Container().
|
||||
WithName("modify-slice").
|
||||
WithImage("nginx:1.14.2"),
|
||||
)
|
||||
// apply
|
||||
applied, err := deploymentClient.Apply(ctx, extractedDeployment, metav1.ApplyOptions{FieldManager: fieldMgr})
|
||||
fieldMgr := "my-field-manager"
|
||||
deploymentClient := clientset.AppsV1().Deployments("default")
|
||||
// read, could also be read from a shared informer
|
||||
deployment, err := deploymentClient.Get(ctx, "example-deployment", metav1.GetOptions{})
|
||||
if err != nil {
|
||||
// handle error
|
||||
}
|
||||
// extract
|
||||
deploymentApplyConfig, err := appsv1ac.ExtractDeployment(deployment, fieldMgr)
|
||||
if err != nil {
|
||||
// handle error
|
||||
}
|
||||
// modify-in-place
|
||||
deploymentApplyConfig.Spec.Template.Spec.WithContainers(corev1ac.Container().
|
||||
WithName("modify-slice").
|
||||
WithImage("nginx:1.14.2"),
|
||||
)
|
||||
// apply
|
||||
applied, err := deploymentClient.Apply(ctx, extractedDeployment, metav1.ApplyOptions{FieldManager: fieldMgr})
|
||||
*/
|
||||
package applyconfigurations
|
||||
|
||||
@@ -5433,7 +5433,6 @@ var schemaYAML = typed.YAMLObject(`types:
|
||||
- name: claimRef
|
||||
type:
|
||||
namedType: io.k8s.api.core.v1.ObjectReference
|
||||
elementRelationship: separable
|
||||
- name: csi
|
||||
type:
|
||||
namedType: io.k8s.api.core.v1.CSIPersistentVolumeSource
|
||||
@@ -5763,9 +5762,6 @@ var schemaYAML = typed.YAMLObject(`types:
|
||||
- name: hostPID
|
||||
type:
|
||||
scalar: boolean
|
||||
- name: hostUsers
|
||||
type:
|
||||
scalar: boolean
|
||||
- name: hostname
|
||||
type:
|
||||
scalar: string
|
||||
@@ -6862,12 +6858,6 @@ var schemaYAML = typed.YAMLObject(`types:
|
||||
- name: labelSelector
|
||||
type:
|
||||
namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector
|
||||
- name: matchLabelKeys
|
||||
type:
|
||||
list:
|
||||
elementType:
|
||||
scalar: string
|
||||
elementRelationship: atomic
|
||||
- name: maxSkew
|
||||
type:
|
||||
scalar: numeric
|
||||
@@ -10843,9 +10833,6 @@ var schemaYAML = typed.YAMLObject(`types:
|
||||
- name: requiresRepublish
|
||||
type:
|
||||
scalar: boolean
|
||||
- name: seLinuxMount
|
||||
type:
|
||||
scalar: boolean
|
||||
- name: storageCapacity
|
||||
type:
|
||||
scalar: boolean
|
||||
@@ -11198,9 +11185,6 @@ var schemaYAML = typed.YAMLObject(`types:
|
||||
- name: requiresRepublish
|
||||
type:
|
||||
scalar: boolean
|
||||
- name: seLinuxMount
|
||||
type:
|
||||
scalar: boolean
|
||||
- name: storageCapacity
|
||||
type:
|
||||
scalar: boolean
|
||||
|
||||
@@ -32,7 +32,6 @@ type CSIDriverSpecApplyConfiguration struct {
|
||||
FSGroupPolicy *v1.FSGroupPolicy `json:"fsGroupPolicy,omitempty"`
|
||||
TokenRequests []TokenRequestApplyConfiguration `json:"tokenRequests,omitempty"`
|
||||
RequiresRepublish *bool `json:"requiresRepublish,omitempty"`
|
||||
SELinuxMount *bool `json:"seLinuxMount,omitempty"`
|
||||
}
|
||||
|
||||
// CSIDriverSpecApplyConfiguration constructs an declarative configuration of the CSIDriverSpec type for use with
|
||||
@@ -103,11 +102,3 @@ func (b *CSIDriverSpecApplyConfiguration) WithRequiresRepublish(value bool) *CSI
|
||||
b.RequiresRepublish = &value
|
||||
return b
|
||||
}
|
||||
|
||||
// WithSELinuxMount sets the SELinuxMount field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the SELinuxMount field is set to the value of the last call.
|
||||
func (b *CSIDriverSpecApplyConfiguration) WithSELinuxMount(value bool) *CSIDriverSpecApplyConfiguration {
|
||||
b.SELinuxMount = &value
|
||||
return b
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@ type CSIDriverSpecApplyConfiguration struct {
|
||||
FSGroupPolicy *v1beta1.FSGroupPolicy `json:"fsGroupPolicy,omitempty"`
|
||||
TokenRequests []TokenRequestApplyConfiguration `json:"tokenRequests,omitempty"`
|
||||
RequiresRepublish *bool `json:"requiresRepublish,omitempty"`
|
||||
SELinuxMount *bool `json:"seLinuxMount,omitempty"`
|
||||
}
|
||||
|
||||
// CSIDriverSpecApplyConfiguration constructs an declarative configuration of the CSIDriverSpec type for use with
|
||||
@@ -103,11 +102,3 @@ func (b *CSIDriverSpecApplyConfiguration) WithRequiresRepublish(value bool) *CSI
|
||||
b.RequiresRepublish = &value
|
||||
return b
|
||||
}
|
||||
|
||||
// WithSELinuxMount sets the SELinuxMount field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the SELinuxMount field is set to the value of the last call.
|
||||
func (b *CSIDriverSpecApplyConfiguration) WithSELinuxMount(value bool) *CSIDriverSpecApplyConfiguration {
|
||||
b.SELinuxMount = &value
|
||||
return b
|
||||
}
|
||||
|
||||
@@ -17,14 +17,12 @@ limitations under the License.
|
||||
package disk
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/gregjones/httpcache"
|
||||
"github.com/gregjones/httpcache/diskcache"
|
||||
"github.com/peterbourgon/diskv"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
@@ -43,7 +41,7 @@ func newCacheRoundTripper(cacheDir string, rt http.RoundTripper) http.RoundTripp
|
||||
BasePath: cacheDir,
|
||||
TempDir: filepath.Join(cacheDir, ".diskv-temp"),
|
||||
})
|
||||
t := httpcache.NewTransport(&sumDiskCache{disk: d})
|
||||
t := httpcache.NewTransport(diskcache.NewWithDiskv(d))
|
||||
t.Transport = rt
|
||||
|
||||
return &cacheRoundTripper{rt: t}
|
||||
@@ -65,56 +63,3 @@ func (rt *cacheRoundTripper) CancelRequest(req *http.Request) {
|
||||
}
|
||||
|
||||
func (rt *cacheRoundTripper) WrappedRoundTripper() http.RoundTripper { return rt.rt.Transport }
|
||||
|
||||
// A sumDiskCache is a cache backend for github.com/gregjones/httpcache. It is
|
||||
// similar to httpcache's diskcache package, but uses SHA256 sums to ensure
|
||||
// cache integrity at read time rather than fsyncing each cache entry to
|
||||
// increase the likelihood they will be persisted at write time. This avoids
|
||||
// significant performance degradation on MacOS.
|
||||
//
|
||||
// See https://github.com/kubernetes/kubernetes/issues/110753 for more.
|
||||
type sumDiskCache struct {
|
||||
disk *diskv.Diskv
|
||||
}
|
||||
|
||||
// Get the requested key from the cache on disk. If Get encounters an error, or
|
||||
// the returned value is not a SHA256 sum followed by bytes with a matching
|
||||
// checksum it will return false to indicate a cache miss.
|
||||
func (c *sumDiskCache) Get(key string) ([]byte, bool) {
|
||||
b, err := c.disk.Read(sanitize(key))
|
||||
if err != nil || len(b) < sha256.Size {
|
||||
return []byte{}, false
|
||||
}
|
||||
|
||||
response := b[sha256.Size:]
|
||||
want := b[:sha256.Size] // The first 32 bytes of the file should be the SHA256 sum.
|
||||
got := sha256.Sum256(response)
|
||||
if !bytes.Equal(want, got[:]) {
|
||||
return []byte{}, false
|
||||
}
|
||||
|
||||
return response, true
|
||||
}
|
||||
|
||||
// Set writes the response to a file on disk. The filename will be the SHA256
|
||||
// sum of the key. The file will contain a SHA256 sum of the response bytes,
|
||||
// followed by said response bytes.
|
||||
func (c *sumDiskCache) Set(key string, response []byte) {
|
||||
s := sha256.Sum256(response)
|
||||
_ = c.disk.Write(sanitize(key), append(s[:], response...)) // Nothing we can do with this error.
|
||||
}
|
||||
|
||||
func (c *sumDiskCache) Delete(key string) {
|
||||
_ = c.disk.Erase(sanitize(key)) // Nothing we can do with this error.
|
||||
}
|
||||
|
||||
// Sanitize an httpcache key such that it can be used as a diskv key, which must
|
||||
// be a valid filename. The httpcache key will either be the requested URL (if
|
||||
// the request method was GET) or "<method> <url>" for other methods, per the
|
||||
// httpcache.cacheKey function.
|
||||
func sanitize(key string) string {
|
||||
// These keys are not sensitive. We use sha256 to avoid a (potentially
|
||||
// malicious) collision causing the wrong cache data to be written or
|
||||
// accessed.
|
||||
return fmt.Sprintf("%x", sha256.Sum256([]byte(key)))
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ package disk
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@@ -26,7 +25,6 @@ import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/peterbourgon/diskv"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -42,35 +40,6 @@ func (rt *testRoundTripper) RoundTrip(req *http.Request) (*http.Response, error)
|
||||
return rt.Response, rt.Err
|
||||
}
|
||||
|
||||
func BenchmarkDiskCache(b *testing.B) {
|
||||
cacheDir, err := ioutil.TempDir("", "cache-rt")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(cacheDir)
|
||||
|
||||
d := diskv.New(diskv.Options{
|
||||
PathPerm: os.FileMode(0750),
|
||||
FilePerm: os.FileMode(0660),
|
||||
BasePath: cacheDir,
|
||||
TempDir: filepath.Join(cacheDir, ".diskv-temp"),
|
||||
})
|
||||
|
||||
k := "localhost:8080/apis/batch/v1.json"
|
||||
v, err := ioutil.ReadFile("../../testdata/apis/batch/v1.json")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
c := sumDiskCache{disk: d}
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
c.Set(k, v)
|
||||
c.Get(k)
|
||||
c.Delete(k)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCacheRoundTripper(t *testing.T) {
|
||||
rt := &testRoundTripper{}
|
||||
cacheDir, err := ioutil.TempDir("", "cache-rt")
|
||||
@@ -176,146 +145,3 @@ func TestCacheRoundTripperPathPerm(t *testing.T) {
|
||||
})
|
||||
assert.NoError(err)
|
||||
}
|
||||
|
||||
func TestSumDiskCache(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
// Ensure that we'll return a cache miss if the backing file doesn't exist.
|
||||
t.Run("NoSuchKey", func(t *testing.T) {
|
||||
cacheDir, err := ioutil.TempDir("", "cache-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(cacheDir)
|
||||
d := diskv.New(diskv.Options{BasePath: cacheDir, TempDir: filepath.Join(cacheDir, ".diskv-temp")})
|
||||
c := &sumDiskCache{disk: d}
|
||||
|
||||
key := "testing"
|
||||
|
||||
got, ok := c.Get(key)
|
||||
assert.False(ok)
|
||||
assert.Equal([]byte{}, got)
|
||||
})
|
||||
|
||||
// Ensure that we'll return a cache miss if the backing file is empty.
|
||||
t.Run("EmptyFile", func(t *testing.T) {
|
||||
cacheDir, err := ioutil.TempDir("", "cache-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(cacheDir)
|
||||
d := diskv.New(diskv.Options{BasePath: cacheDir, TempDir: filepath.Join(cacheDir, ".diskv-temp")})
|
||||
c := &sumDiskCache{disk: d}
|
||||
|
||||
key := "testing"
|
||||
|
||||
f, err := os.Create(filepath.Join(cacheDir, sanitize(key)))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
f.Close()
|
||||
|
||||
got, ok := c.Get(key)
|
||||
assert.False(ok)
|
||||
assert.Equal([]byte{}, got)
|
||||
})
|
||||
|
||||
// Ensure that we'll return a cache miss if the backing has an invalid
|
||||
// checksum.
|
||||
t.Run("InvalidChecksum", func(t *testing.T) {
|
||||
cacheDir, err := ioutil.TempDir("", "cache-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(cacheDir)
|
||||
d := diskv.New(diskv.Options{BasePath: cacheDir, TempDir: filepath.Join(cacheDir, ".diskv-temp")})
|
||||
c := &sumDiskCache{disk: d}
|
||||
|
||||
key := "testing"
|
||||
value := []byte("testing")
|
||||
mismatchedValue := []byte("testink")
|
||||
sum := sha256.Sum256(value)
|
||||
|
||||
// Create a file with the sum of 'value' followed by the bytes of
|
||||
// 'mismatchedValue'.
|
||||
f, err := os.Create(filepath.Join(cacheDir, sanitize(key)))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
f.Write(sum[:])
|
||||
f.Write(mismatchedValue)
|
||||
f.Close()
|
||||
|
||||
// The mismatched checksum should result in a cache miss.
|
||||
got, ok := c.Get(key)
|
||||
assert.False(ok)
|
||||
assert.Equal([]byte{}, got)
|
||||
})
|
||||
|
||||
// Ensure that our disk cache will happily cache over the top of an existing
|
||||
// value. We depend on this behaviour to recover from corrupted cache
|
||||
// entries. When Get detects a bad checksum it will return a cache miss.
|
||||
// This should cause httpcache to fall back to its underlying transport and
|
||||
// to subsequently cache the new value, overwriting the corrupt one.
|
||||
t.Run("OverwriteExistingKey", func(t *testing.T) {
|
||||
cacheDir, err := ioutil.TempDir("", "cache-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(cacheDir)
|
||||
d := diskv.New(diskv.Options{BasePath: cacheDir, TempDir: filepath.Join(cacheDir, ".diskv-temp")})
|
||||
c := &sumDiskCache{disk: d}
|
||||
|
||||
key := "testing"
|
||||
value := []byte("cool value!")
|
||||
|
||||
// Write a value.
|
||||
c.Set(key, value)
|
||||
got, ok := c.Get(key)
|
||||
|
||||
// Ensure we can read back what we wrote.
|
||||
assert.True(ok)
|
||||
assert.Equal(value, got)
|
||||
|
||||
differentValue := []byte("I'm different!")
|
||||
|
||||
// Write a different value.
|
||||
c.Set(key, differentValue)
|
||||
got, ok = c.Get(key)
|
||||
|
||||
// Ensure we can read back the different value.
|
||||
assert.True(ok)
|
||||
assert.Equal(differentValue, got)
|
||||
})
|
||||
|
||||
// Ensure that deleting a key does in fact delete it.
|
||||
t.Run("DeleteKey", func(t *testing.T) {
|
||||
cacheDir, err := ioutil.TempDir("", "cache-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(cacheDir)
|
||||
d := diskv.New(diskv.Options{BasePath: cacheDir, TempDir: filepath.Join(cacheDir, ".diskv-temp")})
|
||||
c := &sumDiskCache{disk: d}
|
||||
|
||||
key := "testing"
|
||||
value := []byte("coolValue")
|
||||
|
||||
c.Set(key, value)
|
||||
|
||||
// Ensure we successfully set the value.
|
||||
got, ok := c.Get(key)
|
||||
assert.True(ok)
|
||||
assert.Equal(value, got)
|
||||
|
||||
c.Delete(key)
|
||||
|
||||
// Ensure the value is gone.
|
||||
got, ok = c.Get(key)
|
||||
assert.False(ok)
|
||||
assert.Equal([]byte{}, got)
|
||||
|
||||
// Ensure that deleting a non-existent value is a no-op.
|
||||
c.Delete(key)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -52,9 +52,6 @@ const (
|
||||
// defaultTimeout is the maximum amount of time per request when no timeout has been set on a RESTClient.
|
||||
// Defaults to 32s in order to have a distinguishable length of time, relative to other timeouts that exist.
|
||||
defaultTimeout = 32 * time.Second
|
||||
|
||||
// defaultBurst is the default burst to be used with the discovery client's token bucket rate limiter
|
||||
defaultBurst = 300
|
||||
)
|
||||
|
||||
// DiscoveryInterface holds the methods that discover server-supported API groups,
|
||||
@@ -459,13 +456,12 @@ func setDiscoveryDefaults(config *restclient.Config) error {
|
||||
if config.Timeout == 0 {
|
||||
config.Timeout = defaultTimeout
|
||||
}
|
||||
// if a burst limit is not already configured
|
||||
if config.Burst == 0 {
|
||||
if config.Burst == 0 && config.QPS < 100 {
|
||||
// discovery is expected to be bursty, increase the default burst
|
||||
// to accommodate looking up resource info for many API groups.
|
||||
// matches burst set by ConfigFlags#ToDiscoveryClient().
|
||||
// see https://issue.k8s.io/86149
|
||||
config.Burst = defaultBurst
|
||||
config.Burst = 100
|
||||
}
|
||||
codec := runtime.NoopEncoder{Decoder: scheme.Codecs.UniversalDecoder()}
|
||||
config.NegotiatedSerializer = serializer.NegotiatedSerializerWrapper(runtime.SerializerInfo{Serializer: codec})
|
||||
|
||||
@@ -491,7 +491,8 @@ func (c *dynamicResourceClient) Apply(ctx context.Context, name string, obj *uns
|
||||
if err := c.client.scheme.Convert(uncastRet, ret, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
return ret, err
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (c *dynamicResourceClient) ApplyStatus(ctx context.Context, name string, obj *unstructured.Unstructured, options metav1.ApplyOptions) (*unstructured.Unstructured, error) {
|
||||
|
||||
22
go.mod
22
go.mod
@@ -2,7 +2,7 @@
|
||||
|
||||
module k8s.io/client-go
|
||||
|
||||
go 1.19
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
@@ -15,21 +15,21 @@ require (
|
||||
github.com/google/gofuzz v1.1.0
|
||||
github.com/google/uuid v1.1.2
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7
|
||||
github.com/imdario/mergo v0.3.6
|
||||
github.com/imdario/mergo v0.3.5
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/stretchr/testify v1.7.0
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f
|
||||
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
|
||||
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8
|
||||
google.golang.org/protobuf v1.28.0
|
||||
k8s.io/api v0.25.0-beta.0
|
||||
k8s.io/apimachinery v0.25.0-beta.0
|
||||
k8s.io/api v0.25.0-alpha.3
|
||||
k8s.io/apimachinery v0.25.0-alpha.3
|
||||
k8s.io/klog/v2 v2.70.1
|
||||
k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1
|
||||
k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.3
|
||||
k8s.io/kube-openapi v0.0.0-20220627174259-011e075b9cb8
|
||||
k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.1
|
||||
sigs.k8s.io/yaml v1.2.0
|
||||
)
|
||||
|
||||
@@ -51,7 +51,7 @@ require (
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
|
||||
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
@@ -61,6 +61,6 @@ require (
|
||||
)
|
||||
|
||||
replace (
|
||||
k8s.io/api => k8s.io/api v0.25.0-beta.0
|
||||
k8s.io/apimachinery => k8s.io/apimachinery v0.25.0-beta.0
|
||||
k8s.io/api => k8s.io/api v0.25.0-alpha.3
|
||||
k8s.io/apimachinery => k8s.io/apimachinery v0.25.0-alpha.3
|
||||
)
|
||||
|
||||
34
go.sum
34
go.sum
@@ -143,10 +143,11 @@ github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:Fecb
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28=
|
||||
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q=
|
||||
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
@@ -168,6 +169,7 @@ github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0Gq
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
@@ -267,8 +269,8 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -311,8 +313,8 @@ golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 h1:OH54vjqzRWmbJ62fjuhxy7AxFFgoHN0/DPc/UrL8cAs=
|
||||
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -477,23 +479,23 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
k8s.io/api v0.25.0-beta.0 h1:7gOhPDIb64uxBn9IADZDU2Kz3R7/HzYGMOQ4REsz6rE=
|
||||
k8s.io/api v0.25.0-beta.0/go.mod h1:N4fOjxA9rFu2Trkid57zYj46pZjtumuyuhDaq30UquA=
|
||||
k8s.io/apimachinery v0.25.0-beta.0 h1:D+AORv53PNNqTb98A0MgWonJP4bTPpWXfNDLWM++0ro=
|
||||
k8s.io/apimachinery v0.25.0-beta.0/go.mod h1:qMx9eAk0sZQGsXGu86fab8tZdffHbwUfsvzqKn4mfB0=
|
||||
k8s.io/api v0.25.0-alpha.3 h1:X06i1cr337n4rnxwCaNUh9AnpUWTOQOiORtBV+EO9QI=
|
||||
k8s.io/api v0.25.0-alpha.3/go.mod h1:E7rr+8x/VyOMSMKAstibqDXCK8Rt3ErGpwJE0AF+W1w=
|
||||
k8s.io/apimachinery v0.25.0-alpha.3 h1:MeZ8wyrmuSM7MthRQM+ezMT2I6PqOLAYnHSFesEcp1I=
|
||||
k8s.io/apimachinery v0.25.0-alpha.3/go.mod h1:E6C2QnwpsJTXktTwPqd4nAJ/xmsdi1AHqDD0iPB61j0=
|
||||
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
|
||||
k8s.io/klog/v2 v2.70.1 h1:7aaoSdahviPmR+XkS7FyxlkkXs6tHISSG03RxleQAVQ=
|
||||
k8s.io/klog/v2 v2.70.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
|
||||
k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 h1:MQ8BAZPZlWk3S9K4a9NCkIFQtZShWqoha7snGixVgEA=
|
||||
k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1/go.mod h1:C/N6wCaBHeBHkHUesQOQy2/MZqGgMAFPqGsGQLdbZBU=
|
||||
k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed h1:jAne/RjBTyawwAy0utX5eqigAwz/lQhTmy+Hr/Cpue4=
|
||||
k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||
k8s.io/kube-openapi v0.0.0-20220627174259-011e075b9cb8 h1:yEQKdMCjzAOvGeiTwG4hO/hNVNtDOuUFvMUZ0OlaIzs=
|
||||
k8s.io/kube-openapi v0.0.0-20220627174259-011e075b9cb8/go.mod h1:mbJ+NSUoAhuR14N0S63bPkh8MGVSo3VYSGZtH/mfMe0=
|
||||
k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 h1:HNSDgDCrr/6Ly3WEGKZftiE7IY19Vz2GdbOCyI4qqhc=
|
||||
k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k=
|
||||
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.1 h1:bKCqE9GvQ5tiVHn5rfn1r+yao3aLQEaLzkkmAkf+A6Y=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=
|
||||
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
|
||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||
|
||||
@@ -125,14 +125,14 @@ var localSchemeBuilder = runtime.SchemeBuilder{
|
||||
// AddToScheme adds all types of this clientset into the given scheme. This allows composition
|
||||
// of clientsets, like in:
|
||||
//
|
||||
// import (
|
||||
// "k8s.io/client-go/kubernetes"
|
||||
// clientsetscheme "k8s.io/client-go/kubernetes/scheme"
|
||||
// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme"
|
||||
// )
|
||||
// import (
|
||||
// "k8s.io/client-go/kubernetes"
|
||||
// clientsetscheme "k8s.io/client-go/kubernetes/scheme"
|
||||
// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme"
|
||||
// )
|
||||
//
|
||||
// kclientset, _ := kubernetes.NewForConfig(c)
|
||||
// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme)
|
||||
// kclientset, _ := kubernetes.NewForConfig(c)
|
||||
// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme)
|
||||
//
|
||||
// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types
|
||||
// correctly.
|
||||
|
||||
@@ -125,14 +125,14 @@ var localSchemeBuilder = runtime.SchemeBuilder{
|
||||
// AddToScheme adds all types of this clientset into the given scheme. This allows composition
|
||||
// of clientsets, like in:
|
||||
//
|
||||
// import (
|
||||
// "k8s.io/client-go/kubernetes"
|
||||
// clientsetscheme "k8s.io/client-go/kubernetes/scheme"
|
||||
// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme"
|
||||
// )
|
||||
// import (
|
||||
// "k8s.io/client-go/kubernetes"
|
||||
// clientsetscheme "k8s.io/client-go/kubernetes/scheme"
|
||||
// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme"
|
||||
// )
|
||||
//
|
||||
// kclientset, _ := kubernetes.NewForConfig(c)
|
||||
// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme)
|
||||
// kclientset, _ := kubernetes.NewForConfig(c)
|
||||
// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme)
|
||||
//
|
||||
// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types
|
||||
// correctly.
|
||||
|
||||
@@ -82,8 +82,7 @@ func (e *events) UpdateWithEventNamespace(event *v1beta1.Event) (*v1beta1.Event,
|
||||
// It returns the copy of the event that the server returns, or an error.
|
||||
// The namespace and name of the target event is deduced from the event.
|
||||
// The namespace must either match this event client's namespace, or this event client must
|
||||
//
|
||||
// have been created with the "" namespace.
|
||||
// have been created with the "" namespace.
|
||||
func (e *events) PatchWithEventNamespace(event *v1beta1.Event, data []byte) (*v1beta1.Event, error) {
|
||||
if e.ns != "" && event.Namespace != e.ns {
|
||||
return nil, fmt.Errorf("can't patch an event with namespace '%v' in namespace '%v'", event.Namespace, e.ns)
|
||||
|
||||
@@ -52,8 +52,7 @@ type Interface interface {
|
||||
// ClientContentConfig controls how RESTClient communicates with the server.
|
||||
//
|
||||
// TODO: ContentConfig will be updated to accept a Negotiator instead of a
|
||||
//
|
||||
// NegotiatedSerializer and NegotiatedSerializer will be removed.
|
||||
// NegotiatedSerializer and NegotiatedSerializer will be removed.
|
||||
type ClientContentConfig struct {
|
||||
// AcceptContentTypes specifies the types the client will accept and is optional.
|
||||
// If not set, ContentType will be used to define the Accept header
|
||||
@@ -160,14 +159,13 @@ func readExpBackoffConfig() BackoffManager {
|
||||
// c, err := NewRESTClient(...)
|
||||
// if err != nil { ... }
|
||||
// resp, err := c.Verb("GET").
|
||||
//
|
||||
// Path("pods").
|
||||
// SelectorParam("labels", "area=staging").
|
||||
// Timeout(10*time.Second).
|
||||
// Do()
|
||||
//
|
||||
// Path("pods").
|
||||
// SelectorParam("labels", "area=staging").
|
||||
// Timeout(10*time.Second).
|
||||
// Do()
|
||||
// if err != nil { ... }
|
||||
// list, ok := resp.(*api.PodList)
|
||||
//
|
||||
func (c *RESTClient) Verb(verb string) *Request {
|
||||
return NewRequest(c).Verb(verb)
|
||||
}
|
||||
|
||||
@@ -169,7 +169,7 @@ func TestReconnectBrokenTCP(t *testing.T) {
|
||||
// 2. the connection has keepalive enabled so it will be reused
|
||||
// 3. break the TCP connection stopping the proxy
|
||||
// 4. close the idle connection to force creating a new connection
|
||||
// 5. count that there are 2 connections to the server (we didn't reuse the original connection)
|
||||
// 5. count that there are 2 connection to the server (we didn't reuse the original connection)
|
||||
func TestReconnectBrokenTCP_HTTP1(t *testing.T) {
|
||||
ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, "Hello, %s", r.Proto)
|
||||
@@ -245,7 +245,7 @@ func TestReconnectBrokenTCP_HTTP1(t *testing.T) {
|
||||
// 1. connect to https server with http1.1 using a TCP proxy making the connection to timeout
|
||||
// 2. the connection has keepalive enabled so it will be reused
|
||||
// 3. close the in-flight connection to force creating a new connection
|
||||
// 4. count that there are 2 connections on the LB but only one succeeds
|
||||
// 4. count that there are 2 connection on the LB but only one succeeds
|
||||
func TestReconnectBrokenTCPInFlight_HTTP1(t *testing.T) {
|
||||
done := make(chan struct{})
|
||||
defer close(done)
|
||||
|
||||
@@ -36,10 +36,9 @@ type AuthProvider interface {
|
||||
}
|
||||
|
||||
// Factory generates an AuthProvider plugin.
|
||||
//
|
||||
// clusterAddress is the address of the current cluster.
|
||||
// config is the initial configuration for this plugin.
|
||||
// persister allows the plugin to save updated configuration.
|
||||
// clusterAddress is the address of the current cluster.
|
||||
// config is the initial configuration for this plugin.
|
||||
// persister allows the plugin to save updated configuration.
|
||||
type Factory func(clusterAddress string, config map[string]string, persister AuthProviderConfigPersister) (AuthProvider, error)
|
||||
|
||||
// AuthProviderConfigPersister allows a plugin to persist configuration info
|
||||
|
||||
@@ -917,8 +917,8 @@ func (r *Request) request(ctx context.Context, fn func(*http.Request, *http.Resp
|
||||
// processing.
|
||||
//
|
||||
// Error type:
|
||||
// - If the server responds with a status: *errors.StatusError or *errors.UnexpectedObjectError
|
||||
// - http.Client.Do errors are returned directly.
|
||||
// * If the server responds with a status: *errors.StatusError or *errors.UnexpectedObjectError
|
||||
// * http.Client.Do errors are returned directly.
|
||||
func (r *Request) Do(ctx context.Context) Result {
|
||||
var result Result
|
||||
err := r.request(ctx, func(req *http.Request, resp *http.Response) {
|
||||
@@ -1085,15 +1085,15 @@ const maxUnstructuredResponseTextBytes = 2048
|
||||
// unexpected responses. The rough structure is:
|
||||
//
|
||||
// 1. Assume the server sends you something sane - JSON + well defined error objects + proper codes
|
||||
// - this is the happy path
|
||||
// - when you get this output, trust what the server sends
|
||||
// 2. Guard against empty fields / bodies in received JSON and attempt to cull sufficient info from them to
|
||||
// generate a reasonable facsimile of the original failure.
|
||||
// - Be sure to use a distinct error type or flag that allows a client to distinguish between this and error 1 above
|
||||
// 3. Handle true disconnect failures / completely malformed data by moving up to a more generic client error
|
||||
// 4. Distinguish between various connection failures like SSL certificates, timeouts, proxy errors, unexpected
|
||||
// initial contact, the presence of mismatched body contents from posted content types
|
||||
// - Give these a separate distinct error type and capture as much as possible of the original message
|
||||
// - this is the happy path
|
||||
// - when you get this output, trust what the server sends
|
||||
// 2. Guard against empty fields / bodies in received JSON and attempt to cull sufficient info from them to
|
||||
// generate a reasonable facsimile of the original failure.
|
||||
// - Be sure to use a distinct error type or flag that allows a client to distinguish between this and error 1 above
|
||||
// 3. Handle true disconnect failures / completely malformed data by moving up to a more generic client error
|
||||
// 4. Distinguish between various connection failures like SSL certificates, timeouts, proxy errors, unexpected
|
||||
// initial contact, the presence of mismatched body contents from posted content types
|
||||
// - Give these a separate distinct error type and capture as much as possible of the original message
|
||||
//
|
||||
// TODO: introduce transformation of generic http.Client.Do() errors that separates 4.
|
||||
func (r *Request) transformUnstructuredResponseError(resp *http.Response, req *http.Request, body []byte) error {
|
||||
|
||||
@@ -40,9 +40,9 @@ var (
|
||||
|
||||
// SetDefaultWarningHandler sets the default handler clients use when warning headers are encountered.
|
||||
// By default, warnings are logged. Several built-in implementations are provided:
|
||||
// - NoWarnings suppresses warnings.
|
||||
// - WarningLogger logs warnings.
|
||||
// - NewWarningWriter() outputs warnings to the provided writer.
|
||||
// - NoWarnings suppresses warnings.
|
||||
// - WarningLogger logs warnings.
|
||||
// - NewWarningWriter() outputs warnings to the provided writer.
|
||||
func SetDefaultWarningHandler(l WarningHandler) {
|
||||
defaultWarningHandlerLock.Lock()
|
||||
defer defaultWarningHandlerLock.Unlock()
|
||||
|
||||
@@ -222,7 +222,7 @@ func (r *withRetry) Before(ctx context.Context, request *Request) error {
|
||||
}
|
||||
}
|
||||
|
||||
// if we are here, we have made attempt(s) at least once before.
|
||||
// if we are here, we have made attempt(s) al least once before.
|
||||
if request.backoff != nil {
|
||||
delay := request.backoff.CalculateBackoff(url)
|
||||
if r.retryAfter.Wait > delay {
|
||||
|
||||
@@ -45,20 +45,20 @@ client.Client from an authcfg.Info.
|
||||
|
||||
Example:
|
||||
|
||||
import (
|
||||
"pkg/client"
|
||||
"pkg/client/auth"
|
||||
)
|
||||
import (
|
||||
"pkg/client"
|
||||
"pkg/client/auth"
|
||||
)
|
||||
|
||||
info, err := auth.LoadFromFile(filename)
|
||||
if err != nil {
|
||||
// handle error
|
||||
}
|
||||
clientConfig = client.Config{}
|
||||
clientConfig.Host = "example.com:4901"
|
||||
clientConfig = info.MergeWithConfig()
|
||||
client := client.New(clientConfig)
|
||||
client.Pods(ns).List()
|
||||
info, err := auth.LoadFromFile(filename)
|
||||
if err != nil {
|
||||
// handle error
|
||||
}
|
||||
clientConfig = client.Config{}
|
||||
clientConfig.Host = "example.com:4901"
|
||||
clientConfig = info.MergeWithConfig()
|
||||
client := client.New(clientConfig)
|
||||
client.Pods(ns).List()
|
||||
*/
|
||||
package auth
|
||||
|
||||
|
||||
77
tools/cache/controller.go
vendored
77
tools/cache/controller.go
vendored
@@ -199,17 +199,17 @@ func (c *controller) processLoop() {
|
||||
// can't return an error. The handlers MUST NOT modify the objects
|
||||
// received; this concerns not only the top level of structure but all
|
||||
// the data structures reachable from it.
|
||||
// - OnAdd is called when an object is added.
|
||||
// - OnUpdate is called when an object is modified. Note that oldObj is the
|
||||
// last known state of the object-- it is possible that several changes
|
||||
// were combined together, so you can't use this to see every single
|
||||
// change. OnUpdate is also called when a re-list happens, and it will
|
||||
// get called even if nothing changed. This is useful for periodically
|
||||
// evaluating or syncing something.
|
||||
// - OnDelete will get the final state of the item if it is known, otherwise
|
||||
// it will get an object of type DeletedFinalStateUnknown. This can
|
||||
// happen if the watch is closed and misses the delete event and we don't
|
||||
// notice the deletion until the subsequent re-list.
|
||||
// * OnAdd is called when an object is added.
|
||||
// * OnUpdate is called when an object is modified. Note that oldObj is the
|
||||
// last known state of the object-- it is possible that several changes
|
||||
// were combined together, so you can't use this to see every single
|
||||
// change. OnUpdate is also called when a re-list happens, and it will
|
||||
// get called even if nothing changed. This is useful for periodically
|
||||
// evaluating or syncing something.
|
||||
// * OnDelete will get the final state of the item if it is known, otherwise
|
||||
// it will get an object of type DeletedFinalStateUnknown. This can
|
||||
// happen if the watch is closed and misses the delete event and we don't
|
||||
// notice the deletion until the subsequent re-list.
|
||||
type ResourceEventHandler interface {
|
||||
OnAdd(obj interface{})
|
||||
OnUpdate(oldObj, newObj interface{})
|
||||
@@ -305,14 +305,15 @@ func DeletionHandlingMetaNamespaceKeyFunc(obj interface{}) (string, error) {
|
||||
// notifications to be faulty.
|
||||
//
|
||||
// Parameters:
|
||||
// - lw is list and watch functions for the source of the resource you want to
|
||||
// be informed of.
|
||||
// - objType is an object of the type that you expect to receive.
|
||||
// - resyncPeriod: if non-zero, will re-list this often (you will get OnUpdate
|
||||
// calls, even if nothing changed). Otherwise, re-list will be delayed as
|
||||
// long as possible (until the upstream source closes the watch or times out,
|
||||
// or you stop the controller).
|
||||
// - h is the object you want notifications sent to.
|
||||
// * lw is list and watch functions for the source of the resource you want to
|
||||
// be informed of.
|
||||
// * objType is an object of the type that you expect to receive.
|
||||
// * resyncPeriod: if non-zero, will re-list this often (you will get OnUpdate
|
||||
// calls, even if nothing changed). Otherwise, re-list will be delayed as
|
||||
// long as possible (until the upstream source closes the watch or times out,
|
||||
// or you stop the controller).
|
||||
// * h is the object you want notifications sent to.
|
||||
//
|
||||
func NewInformer(
|
||||
lw ListerWatcher,
|
||||
objType runtime.Object,
|
||||
@@ -331,15 +332,16 @@ func NewInformer(
|
||||
// notifications to be faulty.
|
||||
//
|
||||
// Parameters:
|
||||
// - lw is list and watch functions for the source of the resource you want to
|
||||
// be informed of.
|
||||
// - objType is an object of the type that you expect to receive.
|
||||
// - resyncPeriod: if non-zero, will re-list this often (you will get OnUpdate
|
||||
// calls, even if nothing changed). Otherwise, re-list will be delayed as
|
||||
// long as possible (until the upstream source closes the watch or times out,
|
||||
// or you stop the controller).
|
||||
// - h is the object you want notifications sent to.
|
||||
// - indexers is the indexer for the received object type.
|
||||
// * lw is list and watch functions for the source of the resource you want to
|
||||
// be informed of.
|
||||
// * objType is an object of the type that you expect to receive.
|
||||
// * resyncPeriod: if non-zero, will re-list this often (you will get OnUpdate
|
||||
// calls, even if nothing changed). Otherwise, re-list will be delayed as
|
||||
// long as possible (until the upstream source closes the watch or times out,
|
||||
// or you stop the controller).
|
||||
// * h is the object you want notifications sent to.
|
||||
// * indexers is the indexer for the received object type.
|
||||
//
|
||||
func NewIndexerInformer(
|
||||
lw ListerWatcher,
|
||||
objType runtime.Object,
|
||||
@@ -452,15 +454,16 @@ func processDeltas(
|
||||
// providing event notifications.
|
||||
//
|
||||
// Parameters
|
||||
// - lw is list and watch functions for the source of the resource you want to
|
||||
// be informed of.
|
||||
// - objType is an object of the type that you expect to receive.
|
||||
// - resyncPeriod: if non-zero, will re-list this often (you will get OnUpdate
|
||||
// calls, even if nothing changed). Otherwise, re-list will be delayed as
|
||||
// long as possible (until the upstream source closes the watch or times out,
|
||||
// or you stop the controller).
|
||||
// - h is the object you want notifications sent to.
|
||||
// - clientState is the store you want to populate
|
||||
// * lw is list and watch functions for the source of the resource you want to
|
||||
// be informed of.
|
||||
// * objType is an object of the type that you expect to receive.
|
||||
// * resyncPeriod: if non-zero, will re-list this often (you will get OnUpdate
|
||||
// calls, even if nothing changed). Otherwise, re-list will be delayed as
|
||||
// long as possible (until the upstream source closes the watch or times out,
|
||||
// or you stop the controller).
|
||||
// * h is the object you want notifications sent to.
|
||||
// * clientState is the store you want to populate
|
||||
//
|
||||
func newInformer(
|
||||
lw ListerWatcher,
|
||||
objType runtime.Object,
|
||||
|
||||
38
tools/cache/delta_fifo.go
vendored
38
tools/cache/delta_fifo.go
vendored
@@ -74,11 +74,11 @@ type DeltaFIFOOptions struct {
|
||||
// the Pop() method.
|
||||
//
|
||||
// DeltaFIFO solves this use case:
|
||||
// - You want to process every object change (delta) at most once.
|
||||
// - When you process an object, you want to see everything
|
||||
// that's happened to it since you last processed it.
|
||||
// - You want to process the deletion of some of the objects.
|
||||
// - You might want to periodically reprocess objects.
|
||||
// * You want to process every object change (delta) at most once.
|
||||
// * When you process an object, you want to see everything
|
||||
// that's happened to it since you last processed it.
|
||||
// * You want to process the deletion of some of the objects.
|
||||
// * You might want to periodically reprocess objects.
|
||||
//
|
||||
// DeltaFIFO's Pop(), Get(), and GetByKey() methods return
|
||||
// interface{} to satisfy the Store/Queue interfaces, but they
|
||||
@@ -179,21 +179,21 @@ type Deltas []Delta
|
||||
// "known" keys when Pop() is called. Have to think about how that
|
||||
// affects error retrying.
|
||||
//
|
||||
// NOTE: It is possible to misuse this and cause a race when using an
|
||||
// external known object source.
|
||||
// Whether there is a potential race depends on how the consumer
|
||||
// modifies knownObjects. In Pop(), process function is called under
|
||||
// lock, so it is safe to update data structures in it that need to be
|
||||
// in sync with the queue (e.g. knownObjects).
|
||||
// NOTE: It is possible to misuse this and cause a race when using an
|
||||
// external known object source.
|
||||
// Whether there is a potential race depends on how the consumer
|
||||
// modifies knownObjects. In Pop(), process function is called under
|
||||
// lock, so it is safe to update data structures in it that need to be
|
||||
// in sync with the queue (e.g. knownObjects).
|
||||
//
|
||||
// Example:
|
||||
// In case of sharedIndexInformer being a consumer
|
||||
// (https://github.com/kubernetes/kubernetes/blob/0cdd940f/staging/src/k8s.io/client-go/tools/cache/shared_informer.go#L192),
|
||||
// there is no race as knownObjects (s.indexer) is modified safely
|
||||
// under DeltaFIFO's lock. The only exceptions are GetStore() and
|
||||
// GetIndexer() methods, which expose ways to modify the underlying
|
||||
// storage. Currently these two methods are used for creating Lister
|
||||
// and internal tests.
|
||||
// Example:
|
||||
// In case of sharedIndexInformer being a consumer
|
||||
// (https://github.com/kubernetes/kubernetes/blob/0cdd940f/staging/src/k8s.io/client-go/tools/cache/shared_informer.go#L192),
|
||||
// there is no race as knownObjects (s.indexer) is modified safely
|
||||
// under DeltaFIFO's lock. The only exceptions are GetStore() and
|
||||
// GetIndexer() methods, which expose ways to modify the underlying
|
||||
// storage. Currently these two methods are used for creating Lister
|
||||
// and internal tests.
|
||||
//
|
||||
// Also see the comment on DeltaFIFO.
|
||||
//
|
||||
|
||||
15
tools/cache/expiration_cache.go
vendored
15
tools/cache/expiration_cache.go
vendored
@@ -25,14 +25,13 @@ import (
|
||||
)
|
||||
|
||||
// ExpirationCache implements the store interface
|
||||
// 1. All entries are automatically time stamped on insert
|
||||
// a. The key is computed based off the original item/keyFunc
|
||||
// b. The value inserted under that key is the timestamped item
|
||||
// 2. Expiration happens lazily on read based on the expiration policy
|
||||
// a. No item can be inserted into the store while we're expiring
|
||||
// *any* item in the cache.
|
||||
// 3. Time-stamps are stripped off unexpired entries before return
|
||||
//
|
||||
// 1. All entries are automatically time stamped on insert
|
||||
// a. The key is computed based off the original item/keyFunc
|
||||
// b. The value inserted under that key is the timestamped item
|
||||
// 2. Expiration happens lazily on read based on the expiration policy
|
||||
// a. No item can be inserted into the store while we're expiring
|
||||
// *any* item in the cache.
|
||||
// 3. Time-stamps are stripped off unexpired entries before return
|
||||
// Note that the ExpirationCache is inherently slower than a normal
|
||||
// threadSafeStore because it takes a write lock every time it checks if
|
||||
// an item has expired.
|
||||
|
||||
9
tools/cache/fifo.go
vendored
9
tools/cache/fifo.go
vendored
@@ -103,11 +103,10 @@ func Pop(queue Queue) interface{} {
|
||||
// recent version will be processed. This can't be done with a channel
|
||||
//
|
||||
// FIFO solves this use case:
|
||||
// - You want to process every object (exactly) once.
|
||||
// - You want to process the most recent version of the object when you process it.
|
||||
// - You do not want to process deleted objects, they should be removed from the queue.
|
||||
// - You do not want to periodically reprocess objects.
|
||||
//
|
||||
// * You want to process every object (exactly) once.
|
||||
// * You want to process the most recent version of the object when you process it.
|
||||
// * You do not want to process deleted objects, they should be removed from the queue.
|
||||
// * You do not want to periodically reprocess objects.
|
||||
// Compare with DeltaFIFO for other use cases.
|
||||
type FIFO struct {
|
||||
lock sync.RWMutex
|
||||
|
||||
8
tools/cache/index.go
vendored
8
tools/cache/index.go
vendored
@@ -28,10 +28,10 @@ import (
|
||||
// Delete).
|
||||
//
|
||||
// There are three kinds of strings here:
|
||||
// 1. a storage key, as defined in the Store interface,
|
||||
// 2. a name of an index, and
|
||||
// 3. an "indexed value", which is produced by an IndexFunc and
|
||||
// can be a field value or any other string computed from the object.
|
||||
// 1. a storage key, as defined in the Store interface,
|
||||
// 2. a name of an index, and
|
||||
// 3. an "indexed value", which is produced by an IndexFunc and
|
||||
// can be a field value or any other string computed from the object.
|
||||
type Indexer interface {
|
||||
Store
|
||||
// Index returns the stored objects whose set of indexed values
|
||||
|
||||
10
tools/cache/reflector.go
vendored
10
tools/cache/reflector.go
vendored
@@ -71,8 +71,6 @@ type Reflector struct {
|
||||
backoffManager wait.BackoffManager
|
||||
// initConnBackoffManager manages backoff the initial connection with the Watch call of ListAndWatch.
|
||||
initConnBackoffManager wait.BackoffManager
|
||||
// MaxInternalErrorRetryDuration defines how long we should retry internal errors returned by watch.
|
||||
MaxInternalErrorRetryDuration time.Duration
|
||||
|
||||
resyncPeriod time.Duration
|
||||
// ShouldResync is invoked periodically and whenever it returns `true` the Store's Resync operation is invoked
|
||||
@@ -289,7 +287,6 @@ func (r *Reflector) ListAndWatch(stopCh <-chan struct{}) error {
|
||||
}
|
||||
}()
|
||||
|
||||
retry := NewRetryWithDeadline(r.MaxInternalErrorRetryDuration, time.Minute, apierrors.IsInternalError, r.clock)
|
||||
for {
|
||||
// give the stopCh a chance to stop the loop, even in case of continue statements further down on errors
|
||||
select {
|
||||
@@ -326,9 +323,7 @@ func (r *Reflector) ListAndWatch(stopCh <-chan struct{}) error {
|
||||
return err
|
||||
}
|
||||
|
||||
err = watchHandler(start, w, r.store, r.expectedType, r.expectedGVK, r.name, r.expectedTypeName, r.setLastSyncResourceVersion, r.clock, resyncerrc, stopCh)
|
||||
retry.After(err)
|
||||
if err != nil {
|
||||
if err := watchHandler(start, w, r.store, r.expectedType, r.expectedGVK, r.name, r.expectedTypeName, r.setLastSyncResourceVersion, r.clock, resyncerrc, stopCh); err != nil {
|
||||
if err != errorStopRequested {
|
||||
switch {
|
||||
case isExpiredError(err):
|
||||
@@ -340,9 +335,6 @@ func (r *Reflector) ListAndWatch(stopCh <-chan struct{}) error {
|
||||
klog.V(2).Infof("%s: watch of %v returned 429 - backing off", r.name, r.expectedTypeName)
|
||||
<-r.initConnBackoffManager.Backoff().C()
|
||||
continue
|
||||
case apierrors.IsInternalError(err) && retry.ShouldRetry():
|
||||
klog.V(2).Infof("%s: retrying watch of %v internal error: %v", r.name, r.expectedTypeName, err)
|
||||
continue
|
||||
default:
|
||||
klog.Warningf("%s: watch of %v ended with: %v", r.name, r.expectedTypeName, err)
|
||||
}
|
||||
|
||||
73
tools/cache/reflector_test.go
vendored
73
tools/cache/reflector_test.go
vendored
@@ -487,79 +487,6 @@ func TestBackoffOnTooManyRequests(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRetryInternalError(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
maxInternalDuration time.Duration
|
||||
rewindTime int
|
||||
wantRetries int
|
||||
}{
|
||||
{
|
||||
name: "retries off",
|
||||
maxInternalDuration: time.Duration(0),
|
||||
wantRetries: 0,
|
||||
},
|
||||
{
|
||||
name: "retries on, all calls fail",
|
||||
maxInternalDuration: time.Second * 30,
|
||||
wantRetries: 31,
|
||||
},
|
||||
{
|
||||
name: "retries on, one call successful",
|
||||
maxInternalDuration: time.Second * 30,
|
||||
rewindTime: 10,
|
||||
wantRetries: 40,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
err := apierrors.NewInternalError(fmt.Errorf("etcdserver: no leader"))
|
||||
fakeClock := testingclock.NewFakeClock(time.Now())
|
||||
bm := &fakeBackoff{clock: fakeClock}
|
||||
|
||||
counter := 0
|
||||
|
||||
lw := &testLW{
|
||||
ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
|
||||
return &v1.PodList{ListMeta: metav1.ListMeta{ResourceVersion: "1"}}, nil
|
||||
},
|
||||
WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
|
||||
counter = counter + 1
|
||||
t.Logf("Counter: %v", counter)
|
||||
if counter == tc.rewindTime {
|
||||
t.Logf("Rewinding")
|
||||
fakeClock.Step(time.Minute)
|
||||
}
|
||||
|
||||
fakeClock.Step(time.Second)
|
||||
w := watch.NewFakeWithChanSize(1, false)
|
||||
status := err.Status()
|
||||
w.Error(&status)
|
||||
return w, nil
|
||||
},
|
||||
}
|
||||
|
||||
r := &Reflector{
|
||||
name: "test-reflector",
|
||||
listerWatcher: lw,
|
||||
store: NewFIFO(MetaNamespaceKeyFunc),
|
||||
initConnBackoffManager: bm,
|
||||
clock: fakeClock,
|
||||
watchErrorHandler: WatchErrorHandler(DefaultWatchErrorHandler),
|
||||
}
|
||||
|
||||
r.MaxInternalErrorRetryDuration = tc.maxInternalDuration
|
||||
|
||||
stopCh := make(chan struct{})
|
||||
r.ListAndWatch(stopCh)
|
||||
close(stopCh)
|
||||
|
||||
if counter-1 != tc.wantRetries {
|
||||
t.Errorf("%v unexpected number of retries: %d", tc, counter-1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestReflectorResync(t *testing.T) {
|
||||
iteration := 0
|
||||
stopCh := make(chan struct{})
|
||||
|
||||
78
tools/cache/retry_with_deadline.go
vendored
78
tools/cache/retry_with_deadline.go
vendored
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
Copyright 2022 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cache
|
||||
|
||||
import (
|
||||
"k8s.io/utils/clock"
|
||||
"time"
|
||||
)
|
||||
|
||||
type RetryWithDeadline interface {
|
||||
After(error)
|
||||
ShouldRetry() bool
|
||||
}
|
||||
|
||||
type retryWithDeadlineImpl struct {
|
||||
firstErrorTime time.Time
|
||||
lastErrorTime time.Time
|
||||
maxRetryDuration time.Duration
|
||||
minResetPeriod time.Duration
|
||||
isRetryable func(error) bool
|
||||
clock clock.Clock
|
||||
}
|
||||
|
||||
func NewRetryWithDeadline(maxRetryDuration, minResetPeriod time.Duration, isRetryable func(error) bool, clock clock.Clock) RetryWithDeadline {
|
||||
return &retryWithDeadlineImpl{
|
||||
firstErrorTime: time.Time{},
|
||||
lastErrorTime: time.Time{},
|
||||
maxRetryDuration: maxRetryDuration,
|
||||
minResetPeriod: minResetPeriod,
|
||||
isRetryable: isRetryable,
|
||||
clock: clock,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *retryWithDeadlineImpl) reset() {
|
||||
r.firstErrorTime = time.Time{}
|
||||
r.lastErrorTime = time.Time{}
|
||||
}
|
||||
|
||||
func (r *retryWithDeadlineImpl) After(err error) {
|
||||
if r.isRetryable(err) {
|
||||
if r.clock.Now().Sub(r.lastErrorTime) >= r.minResetPeriod {
|
||||
r.reset()
|
||||
}
|
||||
|
||||
if r.firstErrorTime.IsZero() {
|
||||
r.firstErrorTime = r.clock.Now()
|
||||
}
|
||||
r.lastErrorTime = r.clock.Now()
|
||||
}
|
||||
}
|
||||
|
||||
func (r *retryWithDeadlineImpl) ShouldRetry() bool {
|
||||
if r.maxRetryDuration <= time.Duration(0) {
|
||||
return false
|
||||
}
|
||||
|
||||
if r.clock.Now().Sub(r.firstErrorTime) <= r.maxRetryDuration {
|
||||
return true
|
||||
}
|
||||
|
||||
r.reset()
|
||||
return false
|
||||
}
|
||||
143
tools/cache/retry_with_deadline_test.go
vendored
143
tools/cache/retry_with_deadline_test.go
vendored
@@ -1,143 +0,0 @@
|
||||
/*
|
||||
Copyright 2022 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cache
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
testingclock "k8s.io/utils/clock/testing"
|
||||
)
|
||||
|
||||
type retryScenarioStep struct {
|
||||
clockStep time.Duration
|
||||
err error
|
||||
wantRetry bool
|
||||
}
|
||||
|
||||
func TestRetryWithDeadline(t *testing.T) {
|
||||
internalError := apierrors.NewInternalError(fmt.Errorf("etcdserver: no leader"))
|
||||
otherError := fmt.Errorf("some other error")
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
duration time.Duration
|
||||
reset time.Duration
|
||||
isRetryable func(error) bool
|
||||
scenario []retryScenarioStep
|
||||
}{
|
||||
{
|
||||
name: "Never retry when duration is zero",
|
||||
duration: time.Duration(0),
|
||||
reset: time.Second * 30,
|
||||
isRetryable: func(err error) bool { return false },
|
||||
scenario: []retryScenarioStep{
|
||||
{
|
||||
clockStep: time.Second * 1,
|
||||
err: nil,
|
||||
wantRetry: false,
|
||||
},
|
||||
{
|
||||
clockStep: time.Second * 0,
|
||||
err: internalError,
|
||||
wantRetry: false,
|
||||
},
|
||||
{
|
||||
clockStep: time.Second * 1,
|
||||
err: otherError,
|
||||
wantRetry: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Retry when internal error happens only within duration",
|
||||
duration: time.Second * 1,
|
||||
reset: time.Second * 30,
|
||||
isRetryable: apierrors.IsInternalError,
|
||||
scenario: []retryScenarioStep{
|
||||
{
|
||||
clockStep: time.Second * 1,
|
||||
err: internalError,
|
||||
wantRetry: true,
|
||||
},
|
||||
{
|
||||
clockStep: time.Second * 1,
|
||||
err: internalError,
|
||||
wantRetry: true,
|
||||
},
|
||||
{
|
||||
clockStep: time.Second * 1,
|
||||
err: internalError,
|
||||
wantRetry: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Don't retry when other error happens",
|
||||
duration: time.Second * 1,
|
||||
reset: time.Second * 30,
|
||||
isRetryable: apierrors.IsInternalError,
|
||||
scenario: []retryScenarioStep{
|
||||
{
|
||||
clockStep: time.Second * 1,
|
||||
err: otherError,
|
||||
wantRetry: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Ignore other errors for retries",
|
||||
duration: time.Second * 1,
|
||||
reset: time.Second * 30,
|
||||
isRetryable: apierrors.IsInternalError,
|
||||
scenario: []retryScenarioStep{
|
||||
{
|
||||
clockStep: time.Second * 1,
|
||||
err: internalError,
|
||||
wantRetry: true,
|
||||
},
|
||||
{
|
||||
clockStep: time.Second * 0,
|
||||
err: otherError,
|
||||
wantRetry: true,
|
||||
},
|
||||
{
|
||||
clockStep: time.Second * 1,
|
||||
err: internalError,
|
||||
wantRetry: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
fakeClock := testingclock.NewFakeClock(time.Now())
|
||||
retry := NewRetryWithDeadline(tc.duration, tc.reset, tc.isRetryable, fakeClock)
|
||||
|
||||
for i, step := range tc.scenario {
|
||||
fakeClock.Step(step.clockStep)
|
||||
retry.After(step.err)
|
||||
result := retry.ShouldRetry()
|
||||
if result != step.wantRetry {
|
||||
t.Errorf("%v unexpected retry, step %d, result %v want %v", tc, i, result, step.wantRetry)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -160,10 +160,8 @@ func NewDefaultClientConfigLoadingRules() *ClientConfigLoadingRules {
|
||||
|
||||
// Load starts by running the MigrationRules and then
|
||||
// takes the loading rules and returns a Config object based on following rules.
|
||||
//
|
||||
// if the ExplicitPath, return the unmerged explicit file
|
||||
// Otherwise, return a merged config based on the Precedence slice
|
||||
//
|
||||
// if the ExplicitPath, return the unmerged explicit file
|
||||
// Otherwise, return a merged config based on the Precedence slice
|
||||
// A missing ExplicitPath file produces an error. Empty filenames or other missing files are ignored.
|
||||
// Read errors or files with non-deserializable content produce errors.
|
||||
// The first file to set a particular map key wins and map key's value is never changed.
|
||||
|
||||
@@ -161,7 +161,7 @@ type LeaderElectionConfig struct {
|
||||
// lifecycle events of the LeaderElector. These are invoked asynchronously.
|
||||
//
|
||||
// possible future callbacks:
|
||||
// - OnChallenge()
|
||||
// * OnChallenge()
|
||||
type LeaderCallbacks struct {
|
||||
// OnStartedLeading is called when a LeaderElector client starts leading
|
||||
OnStartedLeading func(context.Context)
|
||||
|
||||
@@ -62,18 +62,18 @@ type ForwardedPort struct {
|
||||
}
|
||||
|
||||
/*
|
||||
valid port specifications:
|
||||
valid port specifications:
|
||||
|
||||
5000
|
||||
- forwards from localhost:5000 to pod:5000
|
||||
5000
|
||||
- forwards from localhost:5000 to pod:5000
|
||||
|
||||
8888:5000
|
||||
- forwards from localhost:8888 to pod:5000
|
||||
8888:5000
|
||||
- forwards from localhost:8888 to pod:5000
|
||||
|
||||
0:5000
|
||||
:5000
|
||||
- selects a random available local port,
|
||||
forwards from localhost:<random port> to pod:5000
|
||||
0:5000
|
||||
:5000
|
||||
- selects a random available local port,
|
||||
forwards from localhost:<random port> to pod:5000
|
||||
*/
|
||||
func parsePorts(ports []string) ([]ForwardedPort, error) {
|
||||
var forwards []ForwardedPort
|
||||
|
||||
@@ -235,10 +235,10 @@ type aggregateRecord struct {
|
||||
// EventAggregate checks if a similar event has been seen according to the
|
||||
// aggregation configuration (max events, max interval, etc) and returns:
|
||||
//
|
||||
// - The (potentially modified) event that should be created
|
||||
// - The cache key for the event, for correlation purposes. This will be set to
|
||||
// the full key for normal events, and to the result of
|
||||
// EventAggregatorMessageFunc for aggregate events.
|
||||
// - The (potentially modified) event that should be created
|
||||
// - The cache key for the event, for correlation purposes. This will be set to
|
||||
// the full key for normal events, and to the result of
|
||||
// EventAggregatorMessageFunc for aggregate events.
|
||||
func (e *EventAggregator) EventAggregate(newEvent *v1.Event) (*v1.Event, string) {
|
||||
now := metav1.NewTime(e.clock.Now())
|
||||
var record aggregateRecord
|
||||
@@ -427,14 +427,14 @@ type EventCorrelateResult struct {
|
||||
// prior to interacting with the API server to record the event.
|
||||
//
|
||||
// The default behavior is as follows:
|
||||
// - Aggregation is performed if a similar event is recorded 10 times
|
||||
// * Aggregation is performed if a similar event is recorded 10 times
|
||||
// in a 10 minute rolling interval. A similar event is an event that varies only by
|
||||
// the Event.Message field. Rather than recording the precise event, aggregation
|
||||
// will create a new event whose message reports that it has combined events with
|
||||
// the same reason.
|
||||
// - Events are incrementally counted if the exact same event is encountered multiple
|
||||
// * Events are incrementally counted if the exact same event is encountered multiple
|
||||
// times.
|
||||
// - A source may burst 25 events about an object, but has a refill rate budget
|
||||
// * A source may burst 25 events about an object, but has a refill rate budget
|
||||
// per object of 1 event every 5 minutes to control long-tail of spam.
|
||||
func NewEventCorrelator(clock clock.PassiveClock) *EventCorrelator {
|
||||
cacheSize := maxLruCacheEntries
|
||||
|
||||
@@ -101,9 +101,7 @@ func UntilWithoutRetry(ctx context.Context, watcher watch.Interface, conditions
|
||||
// It guarantees you to see all events and in the order they happened.
|
||||
// Due to this guarantee there is no way it can deal with 'Resource version too old error'. It will fail in this case.
|
||||
// (See `UntilWithSync` if you'd prefer to recover from all the errors including RV too old by re-listing
|
||||
//
|
||||
// those items. In normal code you should care about being level driven so you'd not care about not seeing all the edges.)
|
||||
//
|
||||
// those items. In normal code you should care about being level driven so you'd not care about not seeing all the edges.)
|
||||
// The most frequent usage for Until would be a test where you want to verify exact order of events ("edges").
|
||||
func Until(ctx context.Context, initialResourceVersion string, watcherClient cache.Watcher, conditions ...ConditionFunc) (*watch.Event, error) {
|
||||
w, err := NewRetryWatcher(initialResourceVersion, watcherClient)
|
||||
|
||||
@@ -478,7 +478,7 @@ func isBool(s string) bool {
|
||||
return s == "true" || s == "false"
|
||||
}
|
||||
|
||||
// UnquoteExtend is almost same as strconv.Unquote(), but it support parse single quotes as a string
|
||||
//UnquoteExtend is almost same as strconv.Unquote(), but it support parse single quotes as a string
|
||||
func UnquoteExtend(s string) (string, error) {
|
||||
n := len(s)
|
||||
if n < 2 {
|
||||
|
||||
@@ -74,30 +74,30 @@ func OnError(backoff wait.Backoff, retriable func(error) bool, fn func() error)
|
||||
// backoff, and then try again. On a non-"Conflict" error, or if it retries too many times
|
||||
// and gives up, RetryOnConflict will return an error to the caller.
|
||||
//
|
||||
// err := retry.RetryOnConflict(retry.DefaultRetry, func() error {
|
||||
// // Fetch the resource here; you need to refetch it on every try, since
|
||||
// // if you got a conflict on the last update attempt then you need to get
|
||||
// // the current version before making your own changes.
|
||||
// pod, err := c.Pods("mynamespace").Get(name, metav1.GetOptions{})
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// err := retry.RetryOnConflict(retry.DefaultRetry, func() error {
|
||||
// // Fetch the resource here; you need to refetch it on every try, since
|
||||
// // if you got a conflict on the last update attempt then you need to get
|
||||
// // the current version before making your own changes.
|
||||
// pod, err := c.Pods("mynamespace").Get(name, metav1.GetOptions{})
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// // Make whatever updates to the resource are needed
|
||||
// pod.Status.Phase = v1.PodFailed
|
||||
// // Make whatever updates to the resource are needed
|
||||
// pod.Status.Phase = v1.PodFailed
|
||||
//
|
||||
// // Try to update
|
||||
// _, err = c.Pods("mynamespace").UpdateStatus(pod)
|
||||
// // You have to return err itself here (not wrapped inside another error)
|
||||
// // so that RetryOnConflict can identify it correctly.
|
||||
// return err
|
||||
// })
|
||||
// if err != nil {
|
||||
// // May be conflict if max retries were hit, or may be something unrelated
|
||||
// // like permissions or a network error
|
||||
// return err
|
||||
// }
|
||||
// ...
|
||||
// // Try to update
|
||||
// _, err = c.Pods("mynamespace").UpdateStatus(pod)
|
||||
// // You have to return err itself here (not wrapped inside another error)
|
||||
// // so that RetryOnConflict can identify it correctly.
|
||||
// return err
|
||||
// })
|
||||
// if err != nil {
|
||||
// // May be conflict if max retries were hit, or may be something unrelated
|
||||
// // like permissions or a network error
|
||||
// return err
|
||||
// }
|
||||
// ...
|
||||
//
|
||||
// TODO: Make Backoff an interface?
|
||||
func RetryOnConflict(backoff wait.Backoff, fn func() error) error {
|
||||
|
||||
@@ -42,9 +42,8 @@ type FakeOpenAPIServer struct {
|
||||
// API server.
|
||||
//
|
||||
// specsPath - Give a path to some test data organized so that each GroupVersion
|
||||
//
|
||||
// has its own OpenAPI V3 JSON file.
|
||||
// i.e. apps/v1beta1 is stored in <specsPath>/apps/v1beta1.json
|
||||
// has its own OpenAPI V3 JSON file.
|
||||
// i.e. apps/v1beta1 is stored in <specsPath>/apps/v1beta1.json
|
||||
func NewFakeOpenAPIV3Server(specsPath string) (*FakeOpenAPIServer, error) {
|
||||
mux := &testMux{
|
||||
counts: map[string]int{},
|
||||
|
||||
@@ -16,11 +16,11 @@ limitations under the License.
|
||||
|
||||
// Package workqueue provides a simple queue that supports the following
|
||||
// features:
|
||||
// - Fair: items processed in the order in which they are added.
|
||||
// - Stingy: a single item will not be processed multiple times concurrently,
|
||||
// and if an item is added multiple times before it can be processed, it
|
||||
// will only be processed once.
|
||||
// - Multiple consumers and producers. In particular, it is allowed for an
|
||||
// item to be reenqueued while it is being processed.
|
||||
// - Shutdown notifications.
|
||||
// * Fair: items processed in the order in which they are added.
|
||||
// * Stingy: a single item will not be processed multiple times concurrently,
|
||||
// and if an item is added multiple times before it can be processed, it
|
||||
// will only be processed once.
|
||||
// * Multiple consumers and producers. In particular, it is allowed for an
|
||||
// item to be reenqueued while it is being processed.
|
||||
// * Shutdown notifications.
|
||||
package workqueue // import "k8s.io/client-go/util/workqueue"
|
||||
|
||||
@@ -50,13 +50,6 @@ func NewNamedRateLimitingQueue(rateLimiter RateLimiter, name string) RateLimitin
|
||||
}
|
||||
}
|
||||
|
||||
func NewRateLimitingQueueWithDelayingInterface(di DelayingInterface, rateLimiter RateLimiter) RateLimitingInterface {
|
||||
return &rateLimitingType{
|
||||
DelayingInterface: di,
|
||||
rateLimiter: rateLimiter,
|
||||
}
|
||||
}
|
||||
|
||||
// rateLimitingType wraps an Interface and provides rateLimited re-enquing
|
||||
type rateLimitingType struct {
|
||||
DelayingInterface
|
||||
|
||||
Reference in New Issue
Block a user