Compare commits

..

1 Commits

Author SHA1 Message Date
Kubernetes Publisher
6fccb112f1 Update dependencies to v0.26.0-alpha.0 tag 2022-08-09 23:57:07 +00:00
97 changed files with 574 additions and 3267 deletions

1
OWNERS
View File

@@ -9,7 +9,6 @@ approvers:
- sttts
- yliaog
reviewers:
- aojea
- caesarxuchao
- deads2k
- jpbetz

View File

@@ -18,7 +18,7 @@ package disk
import (
"errors"
"io"
"io/ioutil"
"net/http"
"os"
"path/filepath"
@@ -157,7 +157,7 @@ func (d *CachedDiscoveryClient) getCachedFile(filename string) ([]byte, error) {
}
// the cache is present and its valid. Try to read and use it.
cachedBytes, err := io.ReadAll(file)
cachedBytes, err := ioutil.ReadAll(file)
if err != nil {
return nil, err
}
@@ -179,7 +179,7 @@ func (d *CachedDiscoveryClient) writeCachedFile(filename string, obj runtime.Obj
return err
}
f, err := os.CreateTemp(filepath.Dir(filename), filepath.Base(filename)+".")
f, err := ioutil.TempFile(filepath.Dir(filename), filepath.Base(filename)+".")
if err != nil {
return err
}

View File

@@ -17,6 +17,7 @@ limitations under the License.
package disk
import (
"io/ioutil"
"os"
"path/filepath"
"strings"
@@ -41,7 +42,7 @@ import (
func TestCachedDiscoveryClient_Fresh(t *testing.T) {
assert := assert.New(t)
d, err := os.MkdirTemp("", "")
d, err := ioutil.TempDir("", "")
assert.NoError(err)
defer os.RemoveAll(d)
@@ -85,7 +86,7 @@ func TestCachedDiscoveryClient_Fresh(t *testing.T) {
func TestNewCachedDiscoveryClient_TTL(t *testing.T) {
assert := assert.New(t)
d, err := os.MkdirTemp("", "")
d, err := ioutil.TempDir("", "")
assert.NoError(err)
defer os.RemoveAll(d)
@@ -103,7 +104,7 @@ func TestNewCachedDiscoveryClient_TTL(t *testing.T) {
func TestNewCachedDiscoveryClient_PathPerm(t *testing.T) {
assert := assert.New(t)
d, err := os.MkdirTemp("", "")
d, err := ioutil.TempDir("", "")
assert.NoError(err)
os.RemoveAll(d)
defer os.RemoveAll(d)
@@ -130,13 +131,13 @@ func TestNewCachedDiscoveryClient_PathPerm(t *testing.T) {
// successive calls
func TestOpenAPIDiskCache(t *testing.T) {
// Create discovery cache dir (unused)
discoCache, err := os.MkdirTemp("", "")
discoCache, err := ioutil.TempDir("", "")
require.NoError(t, err)
os.RemoveAll(discoCache)
defer os.RemoveAll(discoCache)
// Create http cache dir
httpCache, err := os.MkdirTemp("", "")
httpCache, err := ioutil.TempDir("", "")
require.NoError(t, err)
os.RemoveAll(httpCache)
defer os.RemoveAll(httpCache)

View File

@@ -19,7 +19,7 @@ package disk
import (
"bytes"
"crypto/sha256"
"io"
"io/ioutil"
"net/http"
"net/url"
"os"
@@ -43,7 +43,7 @@ func (rt *testRoundTripper) RoundTrip(req *http.Request) (*http.Response, error)
}
func BenchmarkDiskCache(b *testing.B) {
cacheDir, err := os.MkdirTemp("", "cache-rt")
cacheDir, err := ioutil.TempDir("", "cache-rt")
if err != nil {
b.Fatal(err)
}
@@ -57,7 +57,7 @@ func BenchmarkDiskCache(b *testing.B) {
})
k := "localhost:8080/apis/batch/v1.json"
v, err := os.ReadFile("../../testdata/apis/batch/v1.json")
v, err := ioutil.ReadFile("../../testdata/apis/batch/v1.json")
if err != nil {
b.Fatal(err)
}
@@ -73,7 +73,7 @@ func BenchmarkDiskCache(b *testing.B) {
func TestCacheRoundTripper(t *testing.T) {
rt := &testRoundTripper{}
cacheDir, err := os.MkdirTemp("", "cache-rt")
cacheDir, err := ioutil.TempDir("", "cache-rt")
defer os.RemoveAll(cacheDir)
if err != nil {
t.Fatal(err)
@@ -87,14 +87,14 @@ func TestCacheRoundTripper(t *testing.T) {
}
rt.Response = &http.Response{
Header: http.Header{"ETag": []string{`"123456"`}},
Body: io.NopCloser(bytes.NewReader([]byte("Content"))),
Body: ioutil.NopCloser(bytes.NewReader([]byte("Content"))),
StatusCode: http.StatusOK,
}
resp, err := cache.RoundTrip(req)
if err != nil {
t.Fatal(err)
}
content, err := io.ReadAll(resp.Body)
content, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Fatal(err)
}
@@ -109,7 +109,7 @@ func TestCacheRoundTripper(t *testing.T) {
}
rt.Response = &http.Response{
StatusCode: http.StatusNotModified,
Body: io.NopCloser(bytes.NewReader([]byte("Other Content"))),
Body: ioutil.NopCloser(bytes.NewReader([]byte("Other Content"))),
}
resp, err = cache.RoundTrip(req)
@@ -118,7 +118,7 @@ func TestCacheRoundTripper(t *testing.T) {
}
// Read body and make sure we have the initial content
content, err = io.ReadAll(resp.Body)
content, err = ioutil.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
t.Fatal(err)
@@ -132,7 +132,7 @@ func TestCacheRoundTripperPathPerm(t *testing.T) {
assert := assert.New(t)
rt := &testRoundTripper{}
cacheDir, err := os.MkdirTemp("", "cache-rt")
cacheDir, err := ioutil.TempDir("", "cache-rt")
os.RemoveAll(cacheDir)
defer os.RemoveAll(cacheDir)
@@ -148,14 +148,14 @@ func TestCacheRoundTripperPathPerm(t *testing.T) {
}
rt.Response = &http.Response{
Header: http.Header{"ETag": []string{`"123456"`}},
Body: io.NopCloser(bytes.NewReader([]byte("Content"))),
Body: ioutil.NopCloser(bytes.NewReader([]byte("Content"))),
StatusCode: http.StatusOK,
}
resp, err := cache.RoundTrip(req)
if err != nil {
t.Fatal(err)
}
content, err := io.ReadAll(resp.Body)
content, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Fatal(err)
}
@@ -182,7 +182,7 @@ func TestSumDiskCache(t *testing.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 := os.MkdirTemp("", "cache-test")
cacheDir, err := ioutil.TempDir("", "cache-test")
if err != nil {
t.Fatal(err)
}
@@ -199,7 +199,7 @@ func TestSumDiskCache(t *testing.T) {
// Ensure that we'll return a cache miss if the backing file is empty.
t.Run("EmptyFile", func(t *testing.T) {
cacheDir, err := os.MkdirTemp("", "cache-test")
cacheDir, err := ioutil.TempDir("", "cache-test")
if err != nil {
t.Fatal(err)
}
@@ -223,7 +223,7 @@ func TestSumDiskCache(t *testing.T) {
// Ensure that we'll return a cache miss if the backing has an invalid
// checksum.
t.Run("InvalidChecksum", func(t *testing.T) {
cacheDir, err := os.MkdirTemp("", "cache-test")
cacheDir, err := ioutil.TempDir("", "cache-test")
if err != nil {
t.Fatal(err)
}
@@ -258,7 +258,7 @@ func TestSumDiskCache(t *testing.T) {
// 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 := os.MkdirTemp("", "cache-test")
cacheDir, err := ioutil.TempDir("", "cache-test")
if err != nil {
t.Fatal(err)
}
@@ -290,7 +290,7 @@ func TestSumDiskCache(t *testing.T) {
// Ensure that deleting a key does in fact delete it.
t.Run("DeleteKey", func(t *testing.T) {
cacheDir, err := os.MkdirTemp("", "cache-test")
cacheDir, err := ioutil.TempDir("", "cache-test")
if err != nil {
t.Fatal(err)
}

View File

@@ -21,6 +21,7 @@ import (
"encoding/json"
"errors"
"io"
"io/ioutil"
"net/http"
"strings"
"testing"
@@ -40,7 +41,7 @@ func objBody(object interface{}) io.ReadCloser {
if err != nil {
panic(err)
}
return io.NopCloser(bytes.NewReader([]byte(output)))
return ioutil.NopCloser(bytes.NewReader([]byte(output)))
}
func TestServerSupportsVersion(t *testing.T) {

View File

@@ -20,11 +20,10 @@ import (
"bytes"
"context"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/http/httptest"
"reflect"
"strings"
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -406,7 +405,7 @@ func TestCreate(t *testing.T) {
}
w.Header().Set("Content-Type", runtime.ContentTypeJSON)
data, err := io.ReadAll(r.Body)
data, err := ioutil.ReadAll(r.Body)
if err != nil {
t.Errorf("Create(%q) unexpected error reading body: %v", tc.name, err)
w.WriteHeader(http.StatusInternalServerError)
@@ -488,7 +487,7 @@ func TestUpdate(t *testing.T) {
}
w.Header().Set("Content-Type", runtime.ContentTypeJSON)
data, err := io.ReadAll(r.Body)
data, err := ioutil.ReadAll(r.Body)
if err != nil {
t.Errorf("Update(%q) unexpected error reading body: %v", tc.name, err)
w.WriteHeader(http.StatusInternalServerError)
@@ -646,7 +645,7 @@ func TestPatch(t *testing.T) {
t.Errorf("Patch(%q) got Content-Type %s. wanted %s", tc.name, content, types.StrategicMergePatchType)
}
data, err := io.ReadAll(r.Body)
data, err := ioutil.ReadAll(r.Body)
if err != nil {
t.Errorf("Patch(%q) unexpected error reading body: %v", tc.name, err)
w.WriteHeader(http.StatusInternalServerError)
@@ -673,107 +672,3 @@ func TestPatch(t *testing.T) {
}
}
}
func TestInvalidSegments(t *testing.T) {
name := "bad/name"
namespace := "bad/namespace"
resource := schema.GroupVersionResource{Group: "gtest", Version: "vtest", Resource: "rtest"}
obj := &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "vtest",
"kind": "vkind",
"metadata": map[string]interface{}{
"name": name,
},
},
}
cl, err := NewForConfig(&restclient.Config{
Host: "127.0.0.1",
})
if err != nil {
t.Fatalf("Failed to create config: %v", err)
}
_, err = cl.Resource(resource).Namespace(namespace).Create(context.TODO(), obj, metav1.CreateOptions{})
if err == nil || !strings.Contains(err.Error(), "invalid namespace") {
t.Fatalf("Expected `invalid namespace` error, got: %v", err)
}
_, err = cl.Resource(resource).Update(context.TODO(), obj, metav1.UpdateOptions{})
if err == nil || !strings.Contains(err.Error(), "invalid resource name") {
t.Fatalf("Expected `invalid resource name` error, got: %v", err)
}
_, err = cl.Resource(resource).Namespace(namespace).Update(context.TODO(), obj, metav1.UpdateOptions{})
if err == nil || !strings.Contains(err.Error(), "invalid namespace") {
t.Fatalf("Expected `invalid namespace` error, got: %v", err)
}
_, err = cl.Resource(resource).UpdateStatus(context.TODO(), obj, metav1.UpdateOptions{})
if err == nil || !strings.Contains(err.Error(), "invalid resource name") {
t.Fatalf("Expected `invalid resource name` error, got: %v", err)
}
_, err = cl.Resource(resource).Namespace(namespace).UpdateStatus(context.TODO(), obj, metav1.UpdateOptions{})
if err == nil || !strings.Contains(err.Error(), "invalid namespace") {
t.Fatalf("Expected `invalid namespace` error, got: %v", err)
}
err = cl.Resource(resource).Delete(context.TODO(), name, metav1.DeleteOptions{})
if err == nil || !strings.Contains(err.Error(), "invalid resource name") {
t.Fatalf("Expected `invalid resource name` error, got: %v", err)
}
err = cl.Resource(resource).Namespace(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{})
if err == nil || !strings.Contains(err.Error(), "invalid namespace") {
t.Fatalf("Expected `invalid namespace` error, got: %v", err)
}
err = cl.Resource(resource).Namespace(namespace).DeleteCollection(context.TODO(), metav1.DeleteOptions{}, metav1.ListOptions{})
if err == nil || !strings.Contains(err.Error(), "invalid namespace") {
t.Fatalf("Expected `invalid namespace` error, got: %v", err)
}
_, err = cl.Resource(resource).Get(context.TODO(), name, metav1.GetOptions{})
if err == nil || !strings.Contains(err.Error(), "invalid resource name") {
t.Fatalf("Expected `invalid resource name` error, got: %v", err)
}
_, err = cl.Resource(resource).Namespace(namespace).Get(context.TODO(), name, metav1.GetOptions{})
if err == nil || !strings.Contains(err.Error(), "invalid namespace") {
t.Fatalf("Expected `invalid namespace` error, got: %v", err)
}
_, err = cl.Resource(resource).Namespace(namespace).List(context.TODO(), metav1.ListOptions{})
if err == nil || !strings.Contains(err.Error(), "invalid namespace") {
t.Fatalf("Expected `invalid namespace` error, got: %v", err)
}
_, err = cl.Resource(resource).Namespace(namespace).Watch(context.TODO(), metav1.ListOptions{})
if err == nil || !strings.Contains(err.Error(), "invalid namespace") {
t.Fatalf("Expected `invalid namespace` error, got: %v", err)
}
_, err = cl.Resource(resource).Patch(context.TODO(), name, types.StrategicMergePatchType, []byte("{}"), metav1.PatchOptions{})
if err == nil || !strings.Contains(err.Error(), "invalid resource name") {
t.Fatalf("Expected `invalid resource name` error, got: %v", err)
}
_, err = cl.Resource(resource).Namespace(namespace).Patch(context.TODO(), name, types.StrategicMergePatchType, []byte("{}"), metav1.PatchOptions{})
if err == nil || !strings.Contains(err.Error(), "invalid namespace") {
t.Fatalf("Expected `invalid namespace` error, got: %v", err)
}
_, err = cl.Resource(resource).Apply(context.TODO(), name, obj, metav1.ApplyOptions{})
if err == nil || !strings.Contains(err.Error(), "invalid resource name") {
t.Fatalf("Expected `invalid resource name` error, got: %v", err)
}
_, err = cl.Resource(resource).Namespace(namespace).Apply(context.TODO(), name, obj, metav1.ApplyOptions{})
if err == nil || !strings.Contains(err.Error(), "invalid namespace") {
t.Fatalf("Expected `invalid namespace` error, got: %v", err)
}
_, err = cl.Resource(resource).ApplyStatus(context.TODO(), name, obj, metav1.ApplyOptions{})
if err == nil || !strings.Contains(err.Error(), "invalid resource name") {
t.Fatalf("Expected `invalid resource name` error, got: %v", err)
}
_, err = cl.Resource(resource).Namespace(namespace).ApplyStatus(context.TODO(), name, obj, metav1.ApplyOptions{})
if err == nil || !strings.Contains(err.Error(), "invalid namespace") {
t.Fatalf("Expected `invalid namespace` error, got: %v", err)
}
}

View File

@@ -120,9 +120,6 @@ func (c *dynamicResourceClient) Create(ctx context.Context, obj *unstructured.Un
return nil, fmt.Errorf("name is required")
}
}
if err := validateNamespaceWithOptionalName(c.namespace, name); err != nil {
return nil, err
}
result := c.client.client.
Post().
@@ -155,9 +152,6 @@ func (c *dynamicResourceClient) Update(ctx context.Context, obj *unstructured.Un
if len(name) == 0 {
return nil, fmt.Errorf("name is required")
}
if err := validateNamespaceWithOptionalName(c.namespace, name); err != nil {
return nil, err
}
outBytes, err := runtime.Encode(unstructured.UnstructuredJSONScheme, obj)
if err != nil {
return nil, err
@@ -194,9 +188,7 @@ func (c *dynamicResourceClient) UpdateStatus(ctx context.Context, obj *unstructu
if len(name) == 0 {
return nil, fmt.Errorf("name is required")
}
if err := validateNamespaceWithOptionalName(c.namespace, name); err != nil {
return nil, err
}
outBytes, err := runtime.Encode(unstructured.UnstructuredJSONScheme, obj)
if err != nil {
return nil, err
@@ -228,9 +220,6 @@ func (c *dynamicResourceClient) Delete(ctx context.Context, name string, opts me
if len(name) == 0 {
return fmt.Errorf("name is required")
}
if err := validateNamespaceWithOptionalName(c.namespace, name); err != nil {
return err
}
deleteOptionsByte, err := runtime.Encode(deleteOptionsCodec.LegacyCodec(schema.GroupVersion{Version: "v1"}), &opts)
if err != nil {
return err
@@ -246,10 +235,6 @@ func (c *dynamicResourceClient) Delete(ctx context.Context, name string, opts me
}
func (c *dynamicResourceClient) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOptions metav1.ListOptions) error {
if err := validateNamespaceWithOptionalName(c.namespace); err != nil {
return err
}
deleteOptionsByte, err := runtime.Encode(deleteOptionsCodec.LegacyCodec(schema.GroupVersion{Version: "v1"}), &opts)
if err != nil {
return err
@@ -269,9 +254,6 @@ func (c *dynamicResourceClient) Get(ctx context.Context, name string, opts metav
if len(name) == 0 {
return nil, fmt.Errorf("name is required")
}
if err := validateNamespaceWithOptionalName(c.namespace, name); err != nil {
return nil, err
}
result := c.client.client.Get().AbsPath(append(c.makeURLSegments(name), subresources...)...).SpecificallyVersionedParams(&opts, dynamicParameterCodec, versionV1).Do(ctx)
if err := result.Error(); err != nil {
return nil, err
@@ -288,9 +270,6 @@ func (c *dynamicResourceClient) Get(ctx context.Context, name string, opts metav
}
func (c *dynamicResourceClient) List(ctx context.Context, opts metav1.ListOptions) (*unstructured.UnstructuredList, error) {
if err := validateNamespaceWithOptionalName(c.namespace); err != nil {
return nil, err
}
result := c.client.client.Get().AbsPath(c.makeURLSegments("")...).SpecificallyVersionedParams(&opts, dynamicParameterCodec, versionV1).Do(ctx)
if err := result.Error(); err != nil {
return nil, err
@@ -316,9 +295,6 @@ func (c *dynamicResourceClient) List(ctx context.Context, opts metav1.ListOption
func (c *dynamicResourceClient) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) {
opts.Watch = true
if err := validateNamespaceWithOptionalName(c.namespace); err != nil {
return nil, err
}
return c.client.client.Get().AbsPath(c.makeURLSegments("")...).
SpecificallyVersionedParams(&opts, dynamicParameterCodec, versionV1).
Watch(ctx)
@@ -328,9 +304,6 @@ func (c *dynamicResourceClient) Patch(ctx context.Context, name string, pt types
if len(name) == 0 {
return nil, fmt.Errorf("name is required")
}
if err := validateNamespaceWithOptionalName(c.namespace, name); err != nil {
return nil, err
}
result := c.client.client.
Patch(pt).
AbsPath(append(c.makeURLSegments(name), subresources...)...).
@@ -355,9 +328,6 @@ func (c *dynamicResourceClient) Apply(ctx context.Context, name string, obj *uns
if len(name) == 0 {
return nil, fmt.Errorf("name is required")
}
if err := validateNamespaceWithOptionalName(c.namespace, name); err != nil {
return nil, err
}
outBytes, err := runtime.Encode(unstructured.UnstructuredJSONScheme, obj)
if err != nil {
return nil, err
@@ -396,20 +366,6 @@ func (c *dynamicResourceClient) ApplyStatus(ctx context.Context, name string, ob
return c.Apply(ctx, name, obj, opts, "status")
}
func validateNamespaceWithOptionalName(namespace string, name ...string) error {
if msgs := rest.IsValidPathSegmentName(namespace); len(msgs) != 0 {
return fmt.Errorf("invalid namespace %q: %v", namespace, msgs)
}
if len(name) > 1 {
panic("Invalid number of names")
} else if len(name) == 1 {
if msgs := rest.IsValidPathSegmentName(name[0]); len(msgs) != 0 {
return fmt.Errorf("invalid resource name %q: %v", name[0], msgs)
}
}
return nil
}
func (c *dynamicResourceClient) makeURLSegments(name string) []string {
url := []string{}
if len(c.resource.Group) == 0 {

View File

@@ -19,6 +19,7 @@ Or you can load specific auth plugins:
import _ "k8s.io/client-go/plugin/pkg/client/auth/azure"
import _ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
import _ "k8s.io/client-go/plugin/pkg/client/auth/oidc"
import _ "k8s.io/client-go/plugin/pkg/client/auth/openstack"
```
### Configuration
@@ -37,7 +38,7 @@ import _ "k8s.io/client-go/plugin/pkg/client/auth/oidc"
- [**Work queues**](./workqueue): Create a hotloop-free controller with the
rate-limited workqueue and the [informer framework][informer].
- [**Custom Resource Definition (CRD)**](https://git.k8s.io/apiextensions-apiserver/examples/client-go):
- [**Custom Resource Definition (successor of TPR)**](https://git.k8s.io/apiextensions-apiserver/examples/client-go):
Register a custom resource type with the API, create/update/query this custom
type, and write a controller that drives the cluster state based on the changes to
the custom resources.

View File

@@ -40,6 +40,7 @@ import (
// _ "k8s.io/client-go/plugin/pkg/client/auth/azure"
// _ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
// _ "k8s.io/client-go/plugin/pkg/client/auth/oidc"
// _ "k8s.io/client-go/plugin/pkg/client/auth/openstack"
)
func main() {

View File

@@ -41,6 +41,7 @@ import (
// _ "k8s.io/client-go/plugin/pkg/client/auth/azure"
// _ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
// _ "k8s.io/client-go/plugin/pkg/client/auth/oidc"
// _ "k8s.io/client-go/plugin/pkg/client/auth/openstack"
)
func main() {
@@ -52,6 +53,8 @@ func main() {
}
flag.Parse()
namespace := "default"
config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
if err != nil {
panic(err)
@@ -106,7 +109,7 @@ func main() {
// Create Deployment
fmt.Println("Creating deployment...")
result, err := client.Resource(deploymentRes).Namespace(apiv1.NamespaceDefault).Create(context.TODO(), deployment, metav1.CreateOptions{})
result, err := client.Resource(deploymentRes).Namespace(namespace).Create(context.TODO(), deployment, metav1.CreateOptions{})
if err != nil {
panic(err)
}
@@ -131,7 +134,7 @@ func main() {
retryErr := retry.RetryOnConflict(retry.DefaultRetry, func() error {
// Retrieve the latest version of Deployment before attempting update
// RetryOnConflict uses exponential backoff to avoid exhausting the apiserver
result, getErr := client.Resource(deploymentRes).Namespace(apiv1.NamespaceDefault).Get(context.TODO(), "demo-deployment", metav1.GetOptions{})
result, getErr := client.Resource(deploymentRes).Namespace(namespace).Get(context.TODO(), "demo-deployment", metav1.GetOptions{})
if getErr != nil {
panic(fmt.Errorf("failed to get latest version of Deployment: %v", getErr))
}
@@ -155,7 +158,7 @@ func main() {
panic(err)
}
_, updateErr := client.Resource(deploymentRes).Namespace(apiv1.NamespaceDefault).Update(context.TODO(), result, metav1.UpdateOptions{})
_, updateErr := client.Resource(deploymentRes).Namespace(namespace).Update(context.TODO(), result, metav1.UpdateOptions{})
return updateErr
})
if retryErr != nil {
@@ -166,7 +169,7 @@ func main() {
// List Deployments
prompt()
fmt.Printf("Listing deployments in namespace %q:\n", apiv1.NamespaceDefault)
list, err := client.Resource(deploymentRes).Namespace(apiv1.NamespaceDefault).List(context.TODO(), metav1.ListOptions{})
list, err := client.Resource(deploymentRes).Namespace(namespace).List(context.TODO(), metav1.ListOptions{})
if err != nil {
panic(err)
}
@@ -186,7 +189,7 @@ func main() {
deleteOptions := metav1.DeleteOptions{
PropagationPolicy: &deletePolicy,
}
if err := client.Resource(deploymentRes).Namespace(apiv1.NamespaceDefault).Delete(context.TODO(), "demo-deployment", deleteOptions); err != nil {
if err := client.Resource(deploymentRes).Namespace(namespace).Delete(context.TODO(), "demo-deployment", deleteOptions); err != nil {
panic(err)
}

View File

@@ -34,6 +34,7 @@ import (
// _ "k8s.io/client-go/plugin/pkg/client/auth/azure"
// _ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
// _ "k8s.io/client-go/plugin/pkg/client/auth/oidc"
// _ "k8s.io/client-go/plugin/pkg/client/auth/openstack"
)
func main() {

View File

@@ -37,6 +37,7 @@ import (
// _ "k8s.io/client-go/plugin/pkg/client/auth/azure"
// _ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
// _ "k8s.io/client-go/plugin/pkg/client/auth/oidc"
// _ "k8s.io/client-go/plugin/pkg/client/auth/openstack"
)
func main() {

20
go.mod
View File

@@ -11,22 +11,22 @@ require (
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da
github.com/golang/protobuf v1.5.2
github.com/google/gnostic v0.5.7-v3refs
github.com/google/go-cmp v0.5.9
github.com/google/go-cmp v0.5.6
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/peterbourgon/diskv v2.0.1+incompatible
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.8.0
github.com/stretchr/testify v1.7.0
golang.org/x/net v0.0.0-20220722155237-a158d28d115b
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b
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.1
k8s.io/api v0.26.0-alpha.1
k8s.io/apimachinery v0.26.0-alpha.1
k8s.io/klog/v2 v2.80.1
google.golang.org/protobuf v1.28.0
k8s.io/api v0.26.0-alpha.0
k8s.io/apimachinery v0.26.0-alpha.0
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
@@ -36,7 +36,7 @@ require (
require (
github.com/PuerkitoBio/purell v1.1.1 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/emicklei/go-restful/v3 v3.9.0 // indirect
github.com/emicklei/go-restful/v3 v3.8.0 // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.19.5 // indirect
@@ -61,6 +61,6 @@ require (
)
replace (
k8s.io/api => k8s.io/api v0.26.0-alpha.1
k8s.io/apimachinery => k8s.io/apimachinery v0.26.0-alpha.1
k8s.io/api => k8s.io/api v0.26.0-alpha.0
k8s.io/apimachinery => k8s.io/apimachinery v0.26.0-alpha.0
)

42
go.sum
View File

@@ -50,8 +50,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc=
github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE=
github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/emicklei/go-restful/v3 v3.8.0 h1:eCZ8ulSerjdAiaNpF7GxXIE7ZCMo1moN1qX+S609eVw=
github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
@@ -118,8 +118,8 @@ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@@ -174,8 +174,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/onsi/ginkgo/v2 v2.2.0 h1:3ZNA3L1c5FYDFTTxbFeVGGD8jYvjYauHD30YgLxVsNI=
github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q=
github.com/onsi/ginkgo/v2 v2.1.4 h1:GNapqRSid3zijZ9H77KrgVG4/8KqiyRsxcSxe+7ApXY=
github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw=
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@@ -189,14 +189,12 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@@ -269,7 +267,6 @@ 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-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
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/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -277,8 +274,8 @@ golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4Iltr
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b h1:clP8eMhB30EHdc0bd2Twtq6kgU7yl5ub2cQLSdrv1Dg=
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg=
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -314,8 +311,6 @@ 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-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
@@ -377,6 +372,7 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
@@ -456,8 +452,8 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -481,13 +477,13 @@ 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.26.0-alpha.1 h1:0uaX04eLS9dwIcuWgRvu+WoB63hXFdc9s8fSeR6C1PI=
k8s.io/api v0.26.0-alpha.1/go.mod h1:snuTxVDYyZ0s0Ftc/3Cvl8jZ9zLPDn7PQliEqn93Rrs=
k8s.io/apimachinery v0.26.0-alpha.1 h1:9ZD9i3tISdlxc18MpS3SGc3Hlsyqtkf8/FeRucTfUTI=
k8s.io/apimachinery v0.26.0-alpha.1/go.mod h1:YxcSfgHt+jqvurbA0MLOvpo1OlrnkzW3sZTxMu+hrgI=
k8s.io/api v0.26.0-alpha.0 h1:qPrGA9HWlxvzgva6VVFYPzyDDRvfATo6JYH4ubLX14U=
k8s.io/api v0.26.0-alpha.0/go.mod h1:M1GSIofJb+kzkUClOd6SgjdzGoczbiFas1Zc5yrbRpA=
k8s.io/apimachinery v0.26.0-alpha.0 h1:cnXW2EigxCOrD+s52R9r5AZOcu1Nbv508gOCgSQkbo4=
k8s.io/apimachinery v0.26.0-alpha.0/go.mod h1:qMx9eAk0sZQGsXGu86fab8tZdffHbwUfsvzqKn4mfB0=
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4=
k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
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=

View File

@@ -64,11 +64,6 @@ type sharedInformerFactory struct {
// startedInformers is used for tracking which informers have been started.
// This allows Start() to be called multiple times safely.
startedInformers map[reflect.Type]bool
// wg tracks how many goroutines were started.
wg sync.WaitGroup
// shuttingDown is true when Shutdown has been called. It may still be running
// because it needs to wait for goroutines.
shuttingDown bool
}
// WithCustomResyncConfig sets a custom resync period for the specified informer types.
@@ -129,39 +124,20 @@ func NewSharedInformerFactoryWithOptions(client kubernetes.Interface, defaultRes
return factory
}
// Start initializes all requested informers.
func (f *sharedInformerFactory) Start(stopCh <-chan struct{}) {
f.lock.Lock()
defer f.lock.Unlock()
if f.shuttingDown {
return
}
for informerType, informer := range f.informers {
if !f.startedInformers[informerType] {
f.wg.Add(1)
// We need a new variable in each loop iteration,
// otherwise the goroutine would use the loop variable
// and that keeps changing.
informer := informer
go func() {
defer f.wg.Done()
informer.Run(stopCh)
}()
go informer.Run(stopCh)
f.startedInformers[informerType] = true
}
}
}
func (f *sharedInformerFactory) Shutdown() {
f.lock.Lock()
f.shuttingDown = true
f.lock.Unlock()
// Will return immediately if there is nothing to wait for.
f.wg.Wait()
}
// WaitForCacheSync waits for all started informers' cache were synced.
func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool {
informers := func() map[reflect.Type]cache.SharedIndexInformer {
f.lock.Lock()
@@ -208,57 +184,10 @@ func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internal
// SharedInformerFactory provides shared informers for resources in all known
// API group versions.
//
// It is typically used like this:
//
// ctx, cancel := context.Background()
// defer cancel()
// factory := NewSharedInformerFactory(client, resyncPeriod)
// defer factory.WaitForStop() // Returns immediately if nothing was started.
// genericInformer := factory.ForResource(resource)
// typedInformer := factory.SomeAPIGroup().V1().SomeType()
// factory.Start(ctx.Done()) // Start processing these informers.
// synced := factory.WaitForCacheSync(ctx.Done())
// for v, ok := range synced {
// if !ok {
// fmt.Fprintf(os.Stderr, "caches failed to sync: %v", v)
// return
// }
// }
//
// // Creating informers can also be created after Start, but then
// // Start must be called again:
// anotherGenericInformer := factory.ForResource(resource)
// factory.Start(ctx.Done())
type SharedInformerFactory interface {
internalinterfaces.SharedInformerFactory
// Start initializes all requested informers. They are handled in goroutines
// which run until the stop channel gets closed.
Start(stopCh <-chan struct{})
// Shutdown marks a factory as shutting down. At that point no new
// informers can be started anymore and Start will return without
// doing anything.
//
// In addition, Shutdown blocks until all goroutines have terminated. For that
// to happen, the close channel(s) that they were started with must be closed,
// either before Shutdown gets called or while it is waiting.
//
// Shutdown may be called multiple times, even concurrently. All such calls will
// block until all goroutines have terminated.
Shutdown()
// WaitForCacheSync blocks until all started informers' caches were synced
// or the stop channel gets closed.
WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool
// ForResource gives generic access to a shared informer of the matching type.
ForResource(resource schema.GroupVersionResource) (GenericInformer, error)
// InternalInformerFor returns the SharedIndexInformer for obj using an internal
// client.
InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer
WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool
Admissionregistration() admissionregistration.Interface
Internal() apiserverinternal.Interface

View File

@@ -30,7 +30,6 @@ import (
appsv1beta1 "k8s.io/client-go/kubernetes/typed/apps/v1beta1"
appsv1beta2 "k8s.io/client-go/kubernetes/typed/apps/v1beta2"
authenticationv1 "k8s.io/client-go/kubernetes/typed/authentication/v1"
authenticationv1alpha1 "k8s.io/client-go/kubernetes/typed/authentication/v1alpha1"
authenticationv1beta1 "k8s.io/client-go/kubernetes/typed/authentication/v1beta1"
authorizationv1 "k8s.io/client-go/kubernetes/typed/authorization/v1"
authorizationv1beta1 "k8s.io/client-go/kubernetes/typed/authorization/v1beta1"
@@ -83,7 +82,6 @@ type Interface interface {
AppsV1beta1() appsv1beta1.AppsV1beta1Interface
AppsV1beta2() appsv1beta2.AppsV1beta2Interface
AuthenticationV1() authenticationv1.AuthenticationV1Interface
AuthenticationV1alpha1() authenticationv1alpha1.AuthenticationV1alpha1Interface
AuthenticationV1beta1() authenticationv1beta1.AuthenticationV1beta1Interface
AuthorizationV1() authorizationv1.AuthorizationV1Interface
AuthorizationV1beta1() authorizationv1beta1.AuthorizationV1beta1Interface
@@ -136,7 +134,6 @@ type Clientset struct {
appsV1beta1 *appsv1beta1.AppsV1beta1Client
appsV1beta2 *appsv1beta2.AppsV1beta2Client
authenticationV1 *authenticationv1.AuthenticationV1Client
authenticationV1alpha1 *authenticationv1alpha1.AuthenticationV1alpha1Client
authenticationV1beta1 *authenticationv1beta1.AuthenticationV1beta1Client
authorizationV1 *authorizationv1.AuthorizationV1Client
authorizationV1beta1 *authorizationv1beta1.AuthorizationV1beta1Client
@@ -213,11 +210,6 @@ func (c *Clientset) AuthenticationV1() authenticationv1.AuthenticationV1Interfac
return c.authenticationV1
}
// AuthenticationV1alpha1 retrieves the AuthenticationV1alpha1Client
func (c *Clientset) AuthenticationV1alpha1() authenticationv1alpha1.AuthenticationV1alpha1Interface {
return c.authenticationV1alpha1
}
// AuthenticationV1beta1 retrieves the AuthenticationV1beta1Client
func (c *Clientset) AuthenticationV1beta1() authenticationv1beta1.AuthenticationV1beta1Interface {
return c.authenticationV1beta1
@@ -485,10 +477,6 @@ func NewForConfigAndClient(c *rest.Config, httpClient *http.Client) (*Clientset,
if err != nil {
return nil, err
}
cs.authenticationV1alpha1, err = authenticationv1alpha1.NewForConfigAndClient(&configShallowCopy, httpClient)
if err != nil {
return nil, err
}
cs.authenticationV1beta1, err = authenticationv1beta1.NewForConfigAndClient(&configShallowCopy, httpClient)
if err != nil {
return nil, err
@@ -673,7 +661,6 @@ func New(c rest.Interface) *Clientset {
cs.appsV1beta1 = appsv1beta1.New(c)
cs.appsV1beta2 = appsv1beta2.New(c)
cs.authenticationV1 = authenticationv1.New(c)
cs.authenticationV1alpha1 = authenticationv1alpha1.New(c)
cs.authenticationV1beta1 = authenticationv1beta1.New(c)
cs.authorizationV1 = authorizationv1.New(c)
cs.authorizationV1beta1 = authorizationv1beta1.New(c)

View File

@@ -38,8 +38,6 @@ import (
fakeappsv1beta2 "k8s.io/client-go/kubernetes/typed/apps/v1beta2/fake"
authenticationv1 "k8s.io/client-go/kubernetes/typed/authentication/v1"
fakeauthenticationv1 "k8s.io/client-go/kubernetes/typed/authentication/v1/fake"
authenticationv1alpha1 "k8s.io/client-go/kubernetes/typed/authentication/v1alpha1"
fakeauthenticationv1alpha1 "k8s.io/client-go/kubernetes/typed/authentication/v1alpha1/fake"
authenticationv1beta1 "k8s.io/client-go/kubernetes/typed/authentication/v1beta1"
fakeauthenticationv1beta1 "k8s.io/client-go/kubernetes/typed/authentication/v1beta1/fake"
authorizationv1 "k8s.io/client-go/kubernetes/typed/authorization/v1"
@@ -206,11 +204,6 @@ func (c *Clientset) AuthenticationV1() authenticationv1.AuthenticationV1Interfac
return &fakeauthenticationv1.FakeAuthenticationV1{Fake: &c.Fake}
}
// AuthenticationV1alpha1 retrieves the AuthenticationV1alpha1Client
func (c *Clientset) AuthenticationV1alpha1() authenticationv1alpha1.AuthenticationV1alpha1Interface {
return &fakeauthenticationv1alpha1.FakeAuthenticationV1alpha1{Fake: &c.Fake}
}
// AuthenticationV1beta1 retrieves the AuthenticationV1beta1Client
func (c *Clientset) AuthenticationV1beta1() authenticationv1beta1.AuthenticationV1beta1Interface {
return &fakeauthenticationv1beta1.FakeAuthenticationV1beta1{Fake: &c.Fake}

View File

@@ -26,7 +26,6 @@ import (
appsv1beta1 "k8s.io/api/apps/v1beta1"
appsv1beta2 "k8s.io/api/apps/v1beta2"
authenticationv1 "k8s.io/api/authentication/v1"
authenticationv1alpha1 "k8s.io/api/authentication/v1alpha1"
authenticationv1beta1 "k8s.io/api/authentication/v1beta1"
authorizationv1 "k8s.io/api/authorization/v1"
authorizationv1beta1 "k8s.io/api/authorization/v1beta1"
@@ -84,7 +83,6 @@ var localSchemeBuilder = runtime.SchemeBuilder{
appsv1beta1.AddToScheme,
appsv1beta2.AddToScheme,
authenticationv1.AddToScheme,
authenticationv1alpha1.AddToScheme,
authenticationv1beta1.AddToScheme,
authorizationv1.AddToScheme,
authorizationv1beta1.AddToScheme,

View File

@@ -26,7 +26,6 @@ import (
appsv1beta1 "k8s.io/api/apps/v1beta1"
appsv1beta2 "k8s.io/api/apps/v1beta2"
authenticationv1 "k8s.io/api/authentication/v1"
authenticationv1alpha1 "k8s.io/api/authentication/v1alpha1"
authenticationv1beta1 "k8s.io/api/authentication/v1beta1"
authorizationv1 "k8s.io/api/authorization/v1"
authorizationv1beta1 "k8s.io/api/authorization/v1beta1"
@@ -84,7 +83,6 @@ var localSchemeBuilder = runtime.SchemeBuilder{
appsv1beta1.AddToScheme,
appsv1beta2.AddToScheme,
authenticationv1.AddToScheme,
authenticationv1alpha1.AddToScheme,
authenticationv1beta1.AddToScheme,
authorizationv1.AddToScheme,
authorizationv1beta1.AddToScheme,

View File

@@ -1,107 +0,0 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by client-gen. DO NOT EDIT.
package v1alpha1
import (
"net/http"
v1alpha1 "k8s.io/api/authentication/v1alpha1"
"k8s.io/client-go/kubernetes/scheme"
rest "k8s.io/client-go/rest"
)
type AuthenticationV1alpha1Interface interface {
RESTClient() rest.Interface
SelfSubjectReviewsGetter
}
// AuthenticationV1alpha1Client is used to interact with features provided by the authentication.k8s.io group.
type AuthenticationV1alpha1Client struct {
restClient rest.Interface
}
func (c *AuthenticationV1alpha1Client) SelfSubjectReviews() SelfSubjectReviewInterface {
return newSelfSubjectReviews(c)
}
// NewForConfig creates a new AuthenticationV1alpha1Client for the given config.
// NewForConfig is equivalent to NewForConfigAndClient(c, httpClient),
// where httpClient was generated with rest.HTTPClientFor(c).
func NewForConfig(c *rest.Config) (*AuthenticationV1alpha1Client, error) {
config := *c
if err := setConfigDefaults(&config); err != nil {
return nil, err
}
httpClient, err := rest.HTTPClientFor(&config)
if err != nil {
return nil, err
}
return NewForConfigAndClient(&config, httpClient)
}
// NewForConfigAndClient creates a new AuthenticationV1alpha1Client for the given config and http client.
// Note the http client provided takes precedence over the configured transport values.
func NewForConfigAndClient(c *rest.Config, h *http.Client) (*AuthenticationV1alpha1Client, error) {
config := *c
if err := setConfigDefaults(&config); err != nil {
return nil, err
}
client, err := rest.RESTClientForConfigAndClient(&config, h)
if err != nil {
return nil, err
}
return &AuthenticationV1alpha1Client{client}, nil
}
// NewForConfigOrDie creates a new AuthenticationV1alpha1Client for the given config and
// panics if there is an error in the config.
func NewForConfigOrDie(c *rest.Config) *AuthenticationV1alpha1Client {
client, err := NewForConfig(c)
if err != nil {
panic(err)
}
return client
}
// New creates a new AuthenticationV1alpha1Client for the given RESTClient.
func New(c rest.Interface) *AuthenticationV1alpha1Client {
return &AuthenticationV1alpha1Client{c}
}
func setConfigDefaults(config *rest.Config) error {
gv := v1alpha1.SchemeGroupVersion
config.GroupVersion = &gv
config.APIPath = "/apis"
config.NegotiatedSerializer = scheme.Codecs.WithoutConversion()
if config.UserAgent == "" {
config.UserAgent = rest.DefaultKubernetesUserAgent()
}
return nil
}
// RESTClient returns a RESTClient that is used to communicate
// with API server by this client implementation.
func (c *AuthenticationV1alpha1Client) RESTClient() rest.Interface {
if c == nil {
return nil
}
return c.restClient
}

View File

@@ -1,20 +0,0 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by client-gen. DO NOT EDIT.
// This package has the automatically generated typed clients.
package v1alpha1

View File

@@ -1,20 +0,0 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by client-gen. DO NOT EDIT.
// Package fake has the automatically generated clients.
package fake

View File

@@ -1,40 +0,0 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by client-gen. DO NOT EDIT.
package fake
import (
v1alpha1 "k8s.io/client-go/kubernetes/typed/authentication/v1alpha1"
rest "k8s.io/client-go/rest"
testing "k8s.io/client-go/testing"
)
type FakeAuthenticationV1alpha1 struct {
*testing.Fake
}
func (c *FakeAuthenticationV1alpha1) SelfSubjectReviews() v1alpha1.SelfSubjectReviewInterface {
return &FakeSelfSubjectReviews{c}
}
// RESTClient returns a RESTClient that is used to communicate
// with API server by this client implementation.
func (c *FakeAuthenticationV1alpha1) RESTClient() rest.Interface {
var ret *rest.RESTClient
return ret
}

View File

@@ -1,47 +0,0 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by client-gen. DO NOT EDIT.
package fake
import (
"context"
v1alpha1 "k8s.io/api/authentication/v1alpha1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
schema "k8s.io/apimachinery/pkg/runtime/schema"
testing "k8s.io/client-go/testing"
)
// FakeSelfSubjectReviews implements SelfSubjectReviewInterface
type FakeSelfSubjectReviews struct {
Fake *FakeAuthenticationV1alpha1
}
var selfsubjectreviewsResource = schema.GroupVersionResource{Group: "authentication.k8s.io", Version: "v1alpha1", Resource: "selfsubjectreviews"}
var selfsubjectreviewsKind = schema.GroupVersionKind{Group: "authentication.k8s.io", Version: "v1alpha1", Kind: "SelfSubjectReview"}
// Create takes the representation of a selfSubjectReview and creates it. Returns the server's representation of the selfSubjectReview, and an error, if there is any.
func (c *FakeSelfSubjectReviews) Create(ctx context.Context, selfSubjectReview *v1alpha1.SelfSubjectReview, opts v1.CreateOptions) (result *v1alpha1.SelfSubjectReview, err error) {
obj, err := c.Fake.
Invokes(testing.NewRootCreateAction(selfsubjectreviewsResource, selfSubjectReview), &v1alpha1.SelfSubjectReview{})
if obj == nil {
return nil, err
}
return obj.(*v1alpha1.SelfSubjectReview), err
}

View File

@@ -1,21 +0,0 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by client-gen. DO NOT EDIT.
package v1alpha1
type SelfSubjectReviewExpansion interface{}

View File

@@ -1,64 +0,0 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by client-gen. DO NOT EDIT.
package v1alpha1
import (
"context"
v1alpha1 "k8s.io/api/authentication/v1alpha1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
scheme "k8s.io/client-go/kubernetes/scheme"
rest "k8s.io/client-go/rest"
)
// SelfSubjectReviewsGetter has a method to return a SelfSubjectReviewInterface.
// A group's client should implement this interface.
type SelfSubjectReviewsGetter interface {
SelfSubjectReviews() SelfSubjectReviewInterface
}
// SelfSubjectReviewInterface has methods to work with SelfSubjectReview resources.
type SelfSubjectReviewInterface interface {
Create(ctx context.Context, selfSubjectReview *v1alpha1.SelfSubjectReview, opts v1.CreateOptions) (*v1alpha1.SelfSubjectReview, error)
SelfSubjectReviewExpansion
}
// selfSubjectReviews implements SelfSubjectReviewInterface
type selfSubjectReviews struct {
client rest.Interface
}
// newSelfSubjectReviews returns a SelfSubjectReviews
func newSelfSubjectReviews(c *AuthenticationV1alpha1Client) *selfSubjectReviews {
return &selfSubjectReviews{
client: c.RESTClient(),
}
}
// Create takes the representation of a selfSubjectReview and creates it. Returns the server's representation of the selfSubjectReview, and an error, if there is any.
func (c *selfSubjectReviews) Create(ctx context.Context, selfSubjectReview *v1alpha1.SelfSubjectReview, opts v1.CreateOptions) (result *v1alpha1.SelfSubjectReview, err error) {
result = &v1alpha1.SelfSubjectReview{}
err = c.client.Post().
Resource("selfsubjectreviews").
VersionedParams(&opts, scheme.ParameterCodec).
Body(selfSubjectReview).
Do(ctx).
Into(result)
return
}

View File

@@ -19,7 +19,7 @@ package fake
import (
"context"
"fmt"
"io"
"io/ioutil"
"net/http"
"strings"
@@ -68,7 +68,7 @@ func (c *FakePods) GetLogs(name string, opts *v1.PodLogOptions) *restclient.Requ
Client: fakerest.CreateHTTPClient(func(request *http.Request) (*http.Response, error) {
resp := &http.Response{
StatusCode: http.StatusOK,
Body: io.NopCloser(strings.NewReader("fake logs")),
Body: ioutil.NopCloser(strings.NewReader("fake logs")),
}
return resp, nil
}),

View File

@@ -19,7 +19,7 @@ package kubernetes_test
import (
"bytes"
"context"
"io"
"io/ioutil"
"net/http"
"testing"
@@ -41,7 +41,7 @@ func TestListTimeout(t *testing.T) {
if req.URL.Query().Get("timeout") != "21s" {
t.Fatal(spew.Sdump(req.URL.Query()))
}
return &http.Response{StatusCode: http.StatusNotFound, Body: io.NopCloser(&bytes.Buffer{})}, nil
return &http.Response{StatusCode: http.StatusNotFound, Body: ioutil.NopCloser(&bytes.Buffer{})}, nil
}),
}
clientConfig := &rest.Config{

View File

@@ -19,7 +19,7 @@ package metadata
import (
"context"
"encoding/json"
"io"
"io/ioutil"
"net/http"
"net/http/httptest"
"reflect"
@@ -245,7 +245,7 @@ func TestClient(t *testing.T) {
t.Fatal(req.URL.String())
}
defer req.Body.Close()
buf, err := io.ReadAll(req.Body)
buf, err := ioutil.ReadAll(req.Body)
if err != nil {
t.Fatal(err)
}
@@ -272,7 +272,7 @@ func TestClient(t *testing.T) {
t.Fatal(req.URL.String())
}
defer req.Body.Close()
buf, err := io.ReadAll(req.Body)
buf, err := ioutil.ReadAll(req.Body)
if err != nil {
t.Fatal(err)
}

View File

@@ -98,11 +98,6 @@ type Cluster struct {
// cluster.
// +optional
ProxyURL string
// DisableCompression allows client to opt-out of response compression for all requests to the server. This is useful
// to speed up requests (specifically lists) when client-server network bandwidth is ample, by saving time on
// compression (server-side) and decompression (client-side): https://github.com/kubernetes/kubernetes/issues/112296.
// +optional
DisableCompression bool
// Config holds additional config data that is specific to the exec
// plugin with regards to the cluster being authenticated to.
//

View File

@@ -96,11 +96,6 @@ type Cluster struct {
// cluster.
// +optional
ProxyURL string `json:"proxy-url,omitempty"`
// DisableCompression allows client to opt-out of response compression for all requests to the server. This is useful
// to speed up requests (specifically lists) when client-server network bandwidth is ample, by saving time on
// compression (server-side) and decompression (client-side): https://github.com/kubernetes/kubernetes/issues/112296.
// +optional
DisableCompression bool `json:"disable-compression,omitempty"`
// Config holds additional config data that is specific to the exec
// plugin with regards to the cluster being authenticated to.
//

View File

@@ -86,7 +86,6 @@ func autoConvert_v1_Cluster_To_clientauthentication_Cluster(in *Cluster, out *cl
out.InsecureSkipTLSVerify = in.InsecureSkipTLSVerify
out.CertificateAuthorityData = *(*[]byte)(unsafe.Pointer(&in.CertificateAuthorityData))
out.ProxyURL = in.ProxyURL
out.DisableCompression = in.DisableCompression
if err := runtime.Convert_runtime_RawExtension_To_runtime_Object(&in.Config, &out.Config, s); err != nil {
return err
}
@@ -104,7 +103,6 @@ func autoConvert_clientauthentication_Cluster_To_v1_Cluster(in *clientauthentica
out.InsecureSkipTLSVerify = in.InsecureSkipTLSVerify
out.CertificateAuthorityData = *(*[]byte)(unsafe.Pointer(&in.CertificateAuthorityData))
out.ProxyURL = in.ProxyURL
out.DisableCompression = in.DisableCompression
if err := runtime.Convert_runtime_Object_To_runtime_RawExtension(&in.Config, &out.Config, s); err != nil {
return err
}

View File

@@ -96,11 +96,6 @@ type Cluster struct {
// cluster.
// +optional
ProxyURL string `json:"proxy-url,omitempty"`
// DisableCompression allows client to opt-out of response compression for all requests to the server. This is useful
// to speed up requests (specifically lists) when client-server network bandwidth is ample, by saving time on
// compression (server-side) and decompression (client-side): https://github.com/kubernetes/kubernetes/issues/112296.
// +optional
DisableCompression bool `json:"disable-compression,omitempty"`
// Config holds additional config data that is specific to the exec
// plugin with regards to the cluster being authenticated to.
//

View File

@@ -86,7 +86,6 @@ func autoConvert_v1beta1_Cluster_To_clientauthentication_Cluster(in *Cluster, ou
out.InsecureSkipTLSVerify = in.InsecureSkipTLSVerify
out.CertificateAuthorityData = *(*[]byte)(unsafe.Pointer(&in.CertificateAuthorityData))
out.ProxyURL = in.ProxyURL
out.DisableCompression = in.DisableCompression
if err := runtime.Convert_runtime_RawExtension_To_runtime_Object(&in.Config, &out.Config, s); err != nil {
return err
}
@@ -104,7 +103,6 @@ func autoConvert_clientauthentication_Cluster_To_v1beta1_Cluster(in *clientauthe
out.InsecureSkipTLSVerify = in.InsecureSkipTLSVerify
out.CertificateAuthorityData = *(*[]byte)(unsafe.Pointer(&in.CertificateAuthorityData))
out.ProxyURL = in.ProxyURL
out.DisableCompression = in.DisableCompression
if err := runtime.Convert_runtime_Object_To_runtime_RawExtension(&in.Config, &out.Config, s); err != nil {
return err
}

View File

@@ -199,18 +199,14 @@ func newAuthenticator(c *cache, isTerminalFunc func(int) bool, config *api.ExecC
now: time.Now,
environ: os.Environ,
connTracker: connTracker,
defaultDialer: defaultDialer,
connTracker: connTracker,
}
for _, env := range config.Env {
a.env = append(a.env, env.Name+"="+env.Value)
}
// these functions are made comparable and stored in the cache so that repeated clientset
// construction with the same rest.Config results in a single TLS cache and Authenticator
a.getCert = &transport.GetCertHolder{GetCert: a.cert}
a.dial = &transport.DialHolder{Dial: defaultDialer.DialContext}
return c.put(key, a), nil
}
@@ -265,6 +261,8 @@ type Authenticator struct {
now func() time.Time
environ func() []string
// defaultDialer is used for clients which don't specify a custom dialer
defaultDialer *connrotation.Dialer
// connTracker tracks all connections opened that we need to close when rotating a client certificate
connTracker *connrotation.ConnectionTracker
@@ -275,12 +273,6 @@ type Authenticator struct {
mu sync.Mutex
cachedCreds *credentials
exp time.Time
// getCert makes Authenticator.cert comparable to support TLS config caching
getCert *transport.GetCertHolder
// dial is used for clients which do not specify a custom dialer
// it is comparable to support TLS config caching
dial *transport.DialHolder
}
type credentials struct {
@@ -308,21 +300,18 @@ func (a *Authenticator) UpdateTransportConfig(c *transport.Config) error {
if c.HasCertCallback() {
return errors.New("can't add TLS certificate callback: transport.Config.TLS.GetCert already set")
}
c.TLS.GetCertHolder = a.getCert // comparable for TLS config caching
if c.DialHolder != nil {
if c.DialHolder.Dial == nil {
return errors.New("invalid transport.Config.DialHolder: wrapped Dial function is nil")
}
c.TLS.GetCert = a.cert
var d *connrotation.Dialer
if c.Dial != nil {
// if c has a custom dialer, we have to wrap it
// TLS config caching is not supported for this config
d := connrotation.NewDialerWithTracker(c.DialHolder.Dial, a.connTracker)
c.DialHolder = &transport.DialHolder{Dial: d.DialContext}
d = connrotation.NewDialerWithTracker(c.Dial, a.connTracker)
} else {
c.DialHolder = a.dial // comparable for TLS config caching
d = a.defaultDialer
}
c.Dial = d.DialContext
return nil
}

View File

@@ -1,106 +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 exec_test // separate package to prevent circular import
import (
"context"
"testing"
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
utilnet "k8s.io/apimachinery/pkg/util/net"
clientset "k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
)
// TestExecTLSCache asserts the semantics of the TLS cache when exec auth is used.
//
// In particular, when:
// - multiple identical rest configs exist as distinct objects, and
// - these rest configs use exec auth, and
// - these rest configs are used to create distinct clientsets, then
//
// the underlying TLS config is shared between those clientsets.
func TestExecTLSCache(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
t.Cleanup(cancel)
config1 := &rest.Config{
Host: "https://localhost",
ExecProvider: &clientcmdapi.ExecConfig{
Command: "./testdata/test-plugin.sh",
APIVersion: "client.authentication.k8s.io/v1",
InteractiveMode: clientcmdapi.IfAvailableExecInteractiveMode,
},
}
client1 := clientset.NewForConfigOrDie(config1)
config2 := &rest.Config{
Host: "https://localhost",
ExecProvider: &clientcmdapi.ExecConfig{
Command: "./testdata/test-plugin.sh",
APIVersion: "client.authentication.k8s.io/v1",
InteractiveMode: clientcmdapi.IfAvailableExecInteractiveMode,
},
}
client2 := clientset.NewForConfigOrDie(config2)
config3 := &rest.Config{
Host: "https://localhost",
ExecProvider: &clientcmdapi.ExecConfig{
Command: "./testdata/test-plugin.sh",
Args: []string{"make this exec auth different"},
APIVersion: "client.authentication.k8s.io/v1",
InteractiveMode: clientcmdapi.IfAvailableExecInteractiveMode,
},
}
client3 := clientset.NewForConfigOrDie(config3)
_, _ = client1.CoreV1().Nodes().List(ctx, metav1.ListOptions{})
_, _ = client2.CoreV1().Namespaces().List(ctx, metav1.ListOptions{})
_, _ = client3.CoreV1().PersistentVolumes().List(ctx, metav1.ListOptions{})
rt1 := client1.RESTClient().(*rest.RESTClient).Client.Transport
rt2 := client2.RESTClient().(*rest.RESTClient).Client.Transport
rt3 := client3.RESTClient().(*rest.RESTClient).Client.Transport
tlsConfig1, err := utilnet.TLSClientConfig(rt1)
if err != nil {
t.Fatal(err)
}
tlsConfig2, err := utilnet.TLSClientConfig(rt2)
if err != nil {
t.Fatal(err)
}
tlsConfig3, err := utilnet.TLSClientConfig(rt3)
if err != nil {
t.Fatal(err)
}
if tlsConfig1 == nil || tlsConfig2 == nil || tlsConfig3 == nil {
t.Fatal("expected non-nil TLS configs")
}
if tlsConfig1 != tlsConfig2 {
t.Fatal("expected the same TLS config for matching exec config via rest config")
}
if tlsConfig1 == tlsConfig3 {
t.Fatal("expected different TLS config for non-matching exec config via rest config")
}
}

View File

@@ -27,7 +27,7 @@ import (
"encoding/json"
"encoding/pem"
"fmt"
"io"
"io/ioutil"
"math/big"
"net/http"
"net/http/httptest"
@@ -888,7 +888,7 @@ func TestRoundTripper(t *testing.T) {
}
a.environ = environ
a.now = now
a.stderr = io.Discard
a.stderr = ioutil.Discard
tc := &transport.Config{}
if err := a.UpdateTransportConfig(tc); err != nil {
@@ -1005,7 +1005,7 @@ func TestAuthorizationHeaderPresentCancelsExecAction(t *testing.T) {
cert := func() (*tls.Certificate, error) {
return nil, nil
}
tc := &transport.Config{TLS: transport.TLSConfig{Insecure: true, GetCertHolder: &transport.GetCertHolder{GetCert: cert}}}
tc := &transport.Config{TLS: transport.TLSConfig{Insecure: true, GetCert: cert}}
test.setTransportConfig(tc)
if err := a.UpdateTransportConfig(tc); err != nil {
@@ -1051,7 +1051,7 @@ func TestTLSCredentials(t *testing.T) {
return []string{"TEST_OUTPUT=" + string(data)}
}
a.now = func() time.Time { return now }
a.stderr = io.Discard
a.stderr = ioutil.Discard
// We're not interested in server's cert, this test is about client cert.
tc := &transport.Config{TLS: transport.TLSConfig{Insecure: true}}
@@ -1134,7 +1134,7 @@ func TestConcurrentUpdateTransportConfig(t *testing.T) {
}
a.environ = environ
a.now = now
a.stderr = io.Discard
a.stderr = ioutil.Discard
stopCh := make(chan struct{})
defer close(stopCh)

View File

@@ -22,7 +22,7 @@ import (
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
"strings"
"sync"
@@ -301,7 +301,7 @@ func tokenEndpoint(client *http.Client, issuer string) (string, error) {
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}

View File

@@ -0,0 +1,36 @@
/*
Copyright 2020 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 openstack
import (
"errors"
"k8s.io/client-go/rest"
"k8s.io/klog/v2"
)
func init() {
if err := rest.RegisterAuthProviderPlugin("openstack", newOpenstackAuthProvider); err != nil {
klog.Fatalf("Failed to register openstack auth plugin: %s", err)
}
}
func newOpenstackAuthProvider(_ string, _ map[string]string, _ rest.AuthProviderConfigPersister) (rest.AuthProvider, error) {
return nil, errors.New(`The openstack auth plugin has been removed.
Please use the "client-keystone-auth" kubectl/client-go credential plugin instead.
See https://github.com/kubernetes/cloud-provider-openstack/blob/master/docs/using-client-keystone-auth.md for further details`)
}

View File

@@ -23,4 +23,5 @@ import (
// Initialize client auth plugins for cloud providers.
_ "k8s.io/client-go/plugin/pkg/client/auth/azure"
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
_ "k8s.io/client-go/plugin/pkg/client/auth/openstack"
)

View File

@@ -20,6 +20,7 @@ import (
"context"
"errors"
"fmt"
"io/ioutil"
"net"
"net/http"
"net/url"
@@ -518,7 +519,7 @@ func InClusterConfig() (*Config, error) {
return nil, ErrNotInCluster
}
token, err := os.ReadFile(tokenFile)
token, err := ioutil.ReadFile(tokenFile)
if err != nil {
return nil, err
}
@@ -571,7 +572,10 @@ func LoadTLSFiles(c *Config) error {
}
c.KeyData, err = dataFromSliceOrFile(c.KeyData, c.KeyFile)
return err
if err != nil {
return err
}
return nil
}
// dataFromSliceOrFile returns data from the slice (if non-empty), or from the file,
@@ -581,7 +585,7 @@ func dataFromSliceOrFile(data []byte, file string) ([]byte, error) {
return data, nil
}
if len(file) > 0 {
fileData, err := os.ReadFile(file)
fileData, err := ioutil.ReadFile(file)
if err != nil {
return []byte{}, err
}

View File

@@ -163,22 +163,18 @@ func TestRESTClientLimiter(t *testing.T) {
Limiter flowcontrol.RateLimiter
}{
{
Name: "with no QPS",
Config: Config{},
Limiter: flowcontrol.NewTokenBucketRateLimiter(5, 10),
},
{
Name: "with QPS:10",
Config: Config{QPS: 10},
Limiter: flowcontrol.NewTokenBucketRateLimiter(10, 10),
},
{
Name: "with QPS:-1",
Config: Config{QPS: -1},
Limiter: nil,
},
{
Name: "with RateLimiter",
Config: Config{
RateLimiter: flowcontrol.NewTokenBucketRateLimiter(11, 12),
},
@@ -195,7 +191,7 @@ func TestRESTClientLimiter(t *testing.T) {
t.Fatalf("unexpected error: %v", err)
}
if !reflect.DeepEqual(testCase.Limiter, client.rateLimiter) {
t.Fatalf("unexpected rate limiter: %#v, expected %#v at %s", client.rateLimiter, testCase.Limiter, testCase.Name)
t.Fatalf("unexpected rate limiter: %#v", client.rateLimiter)
}
})
t.Run("Unversioned_"+testCase.Name, func(t *testing.T) {
@@ -207,7 +203,7 @@ func TestRESTClientLimiter(t *testing.T) {
t.Fatalf("unexpected error: %v", err)
}
if !reflect.DeepEqual(testCase.Limiter, client.rateLimiter) {
t.Fatalf("unexpected rate limiter: %#v, expected %#v at %s", client.rateLimiter, testCase.Limiter, testCase.Name)
t.Fatalf("unexpected rate limiter: %#v", client.rateLimiter)
}
})
}

View File

@@ -55,7 +55,6 @@ func ConfigToExecCluster(config *Config) (*clientauthenticationapi.Cluster, erro
InsecureSkipTLSVerify: config.Insecure,
CertificateAuthorityData: caData,
ProxyURL: proxyURL,
DisableCompression: config.DisableCompression,
Config: config.ExecProvider.Config,
}, nil
}
@@ -80,7 +79,6 @@ func ExecClusterToConfig(cluster *clientauthenticationapi.Cluster) (*Config, err
ServerName: cluster.TLSServerName,
CAData: cluster.CertificateAuthorityData,
},
Proxy: proxy,
DisableCompression: cluster.DisableCompression,
Proxy: proxy,
}, nil
}

View File

@@ -22,10 +22,10 @@ import (
"encoding/hex"
"fmt"
"io"
"io/ioutil"
"mime"
"net/http"
"net/url"
"os"
"path"
"reflect"
"strconv"
@@ -437,7 +437,7 @@ func (r *Request) Body(obj interface{}) *Request {
}
switch t := obj.(type) {
case string:
data, err := os.ReadFile(t)
data, err := ioutil.ReadFile(t)
if err != nil {
r.err = err
return r
@@ -508,87 +508,6 @@ func (r *Request) URL() *url.URL {
return finalURL
}
// finalURLTemplate is similar to URL(), but will make all specific parameter values equal
// - instead of name or namespace, "{name}" and "{namespace}" will be used, and all query
// parameters will be reset. This creates a copy of the url so as not to change the
// underlying object.
func (r Request) finalURLTemplate() url.URL {
newParams := url.Values{}
v := []string{"{value}"}
for k := range r.params {
newParams[k] = v
}
r.params = newParams
u := r.URL()
if u == nil {
return url.URL{}
}
segments := strings.Split(u.Path, "/")
groupIndex := 0
index := 0
trimmedBasePath := ""
if r.c.base != nil && strings.Contains(u.Path, r.c.base.Path) {
p := strings.TrimPrefix(u.Path, r.c.base.Path)
if !strings.HasPrefix(p, "/") {
p = "/" + p
}
// store the base path that we have trimmed so we can append it
// before returning the URL
trimmedBasePath = r.c.base.Path
segments = strings.Split(p, "/")
groupIndex = 1
}
if len(segments) <= 2 {
return *u
}
const CoreGroupPrefix = "api"
const NamedGroupPrefix = "apis"
isCoreGroup := segments[groupIndex] == CoreGroupPrefix
isNamedGroup := segments[groupIndex] == NamedGroupPrefix
if isCoreGroup {
// checking the case of core group with /api/v1/... format
index = groupIndex + 2
} else if isNamedGroup {
// checking the case of named group with /apis/apps/v1/... format
index = groupIndex + 3
} else {
// this should not happen that the only two possibilities are /api... and /apis..., just want to put an
// outlet here in case more API groups are added in future if ever possible:
// https://kubernetes.io/docs/concepts/overview/kubernetes-api/#api-groups
// if a wrong API groups name is encountered, return the {prefix} for url.Path
u.Path = "/{prefix}"
u.RawQuery = ""
return *u
}
// switch segLength := len(segments) - index; segLength {
switch {
// case len(segments) - index == 1:
// resource (with no name) do nothing
case len(segments)-index == 2:
// /$RESOURCE/$NAME: replace $NAME with {name}
segments[index+1] = "{name}"
case len(segments)-index == 3:
if segments[index+2] == "finalize" || segments[index+2] == "status" {
// /$RESOURCE/$NAME/$SUBRESOURCE: replace $NAME with {name}
segments[index+1] = "{name}"
} else {
// /namespace/$NAMESPACE/$RESOURCE: replace $NAMESPACE with {namespace}
segments[index+1] = "{namespace}"
}
case len(segments)-index >= 4:
segments[index+1] = "{namespace}"
// /namespace/$NAMESPACE/$RESOURCE/$NAME: replace $NAMESPACE with {namespace}, $NAME with {name}
if segments[index+3] != "finalize" && segments[index+3] != "status" {
// /$RESOURCE/$NAME/$SUBRESOURCE: replace $NAME with {name}
segments[index+3] = "{name}"
}
}
u.Path = path.Join(trimmedBasePath, path.Join(segments...))
return *u
}
func (r *Request) tryThrottleWithInfo(ctx context.Context, retryInfo string) error {
if r.rateLimiter == nil {
return nil
@@ -618,7 +537,7 @@ func (r *Request) tryThrottleWithInfo(ctx context.Context, retryInfo string) err
// but we use a throttled logger to prevent spamming.
globalThrottledLogger.Infof("%s", message)
}
metrics.RateLimiterLatency.Observe(ctx, r.verb, r.finalURLTemplate(), latency)
metrics.RateLimiterLatency.Observe(ctx, r.verb, *r.URL(), latency)
return err
}
@@ -826,7 +745,7 @@ func (r *Request) Stream(ctx context.Context) (io.ReadCloser, error) {
return nil, err
}
if r.body != nil {
req.Body = io.NopCloser(r.body)
req.Body = ioutil.NopCloser(r.body)
}
resp, err := client.Do(req)
updateURLMetrics(ctx, r, resp, err)
@@ -907,7 +826,7 @@ func (r *Request) request(ctx context.Context, fn func(*http.Request, *http.Resp
// Metrics for total request latency
start := time.Now()
defer func() {
metrics.RequestLatency.Observe(ctx, r.verb, r.finalURLTemplate(), time.Since(start))
metrics.RequestLatency.Observe(ctx, r.verb, *r.URL(), time.Since(start))
}()
if r.err != nil {
@@ -1018,7 +937,7 @@ func (r *Request) Do(ctx context.Context) Result {
func (r *Request) DoRaw(ctx context.Context) ([]byte, error) {
var result Result
err := r.request(ctx, func(req *http.Request, resp *http.Response) {
result.body, result.err = io.ReadAll(resp.Body)
result.body, result.err = ioutil.ReadAll(resp.Body)
glogBody("Response Body", result.body)
if resp.StatusCode < http.StatusOK || resp.StatusCode > http.StatusPartialContent {
result.err = r.transformUnstructuredResponseError(resp, req, result.body)
@@ -1037,7 +956,7 @@ func (r *Request) DoRaw(ctx context.Context) ([]byte, error) {
func (r *Request) transformResponse(resp *http.Response, req *http.Request) Result {
var body []byte
if resp.Body != nil {
data, err := io.ReadAll(resp.Body)
data, err := ioutil.ReadAll(resp.Body)
switch err.(type) {
case nil:
body = data
@@ -1179,7 +1098,7 @@ const maxUnstructuredResponseTextBytes = 2048
// 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 {
if body == nil && resp.Body != nil {
if data, err := io.ReadAll(&io.LimitedReader{R: resp.Body, N: maxUnstructuredResponseTextBytes}); err == nil {
if data, err := ioutil.ReadAll(&io.LimitedReader{R: resp.Body, N: maxUnstructuredResponseTextBytes}); err == nil {
body = data
}
}

View File

@@ -23,6 +23,7 @@ import (
"flag"
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
"net/http/httptest"
@@ -87,7 +88,7 @@ func TestRequestSetsHeaders(t *testing.T) {
}
return &http.Response{
StatusCode: http.StatusForbidden,
Body: io.NopCloser(bytes.NewReader([]byte{})),
Body: ioutil.NopCloser(bytes.NewReader([]byte{})),
}, nil
})
config := defaultContentConfig()
@@ -302,7 +303,7 @@ func TestRequestBody(t *testing.T) {
}
// test error set when failing to read file
f, err := os.CreateTemp("", "test")
f, err := ioutil.TempFile("", "test")
if err != nil {
t.Fatalf("unable to create temp file")
}
@@ -337,206 +338,6 @@ func TestResultIntoWithNoBodyReturnsErr(t *testing.T) {
}
}
func TestURLTemplate(t *testing.T) {
uri, _ := url.Parse("http://localhost/some/base/url/path")
uriSingleSlash, _ := url.Parse("http://localhost/")
testCases := []struct {
Request *Request
ExpectedFullURL string
ExpectedFinalURL string
}{
{
// non dynamic client
Request: NewRequestWithClient(uri, "", ClientContentConfig{GroupVersion: schema.GroupVersion{Group: "test"}}, nil).Verb("POST").
Prefix("api", "v1").Resource("r1").Namespace("ns").Name("nm").Param("p0", "v0"),
ExpectedFullURL: "http://localhost/some/base/url/path/api/v1/namespaces/ns/r1/nm?p0=v0",
ExpectedFinalURL: "http://localhost/some/base/url/path/api/v1/namespaces/%7Bnamespace%7D/r1/%7Bname%7D?p0=%7Bvalue%7D",
},
{
// non dynamic client with wrong api group
Request: NewRequestWithClient(uri, "", ClientContentConfig{GroupVersion: schema.GroupVersion{Group: "test"}}, nil).Verb("POST").
Prefix("pre1", "v1").Resource("r1").Namespace("ns").Name("nm").Param("p0", "v0"),
ExpectedFullURL: "http://localhost/some/base/url/path/pre1/v1/namespaces/ns/r1/nm?p0=v0",
ExpectedFinalURL: "http://localhost/%7Bprefix%7D",
},
{
// dynamic client with core group + namespace + resourceResource (with name)
// /api/$RESOURCEVERSION/namespaces/$NAMESPACE/$RESOURCE/%NAME
Request: NewRequestWithClient(uri, "", ClientContentConfig{GroupVersion: schema.GroupVersion{Group: "test"}}, nil).Verb("DELETE").
Prefix("/api/v1/namespaces/ns/r1/name1"),
ExpectedFullURL: "http://localhost/some/base/url/path/api/v1/namespaces/ns/r1/name1",
ExpectedFinalURL: "http://localhost/some/base/url/path/api/v1/namespaces/%7Bnamespace%7D/r1/%7Bname%7D",
},
{
// dynamic client with named group + namespace + resourceResource (with name)
// /apis/$NAMEDGROUPNAME/$RESOURCEVERSION/namespaces/$NAMESPACE/$RESOURCE/%NAME
Request: NewRequestWithClient(uri, "", ClientContentConfig{GroupVersion: schema.GroupVersion{Group: "test"}}, nil).Verb("DELETE").
Prefix("/apis/g1/v1/namespaces/ns/r1/name1"),
ExpectedFullURL: "http://localhost/some/base/url/path/apis/g1/v1/namespaces/ns/r1/name1",
ExpectedFinalURL: "http://localhost/some/base/url/path/apis/g1/v1/namespaces/%7Bnamespace%7D/r1/%7Bname%7D",
},
{
// dynamic client with core group + namespace + resourceResource (with NO name)
// /api/$RESOURCEVERSION/namespaces/$NAMESPACE/$RESOURCE
Request: NewRequestWithClient(uri, "", ClientContentConfig{GroupVersion: schema.GroupVersion{Group: "test"}}, nil).Verb("DELETE").
Prefix("/api/v1/namespaces/ns/r1"),
ExpectedFullURL: "http://localhost/some/base/url/path/api/v1/namespaces/ns/r1",
ExpectedFinalURL: "http://localhost/some/base/url/path/api/v1/namespaces/%7Bnamespace%7D/r1",
},
{
// dynamic client with named group + namespace + resourceResource (with NO name)
// /apis/$NAMEDGROUPNAME/$RESOURCEVERSION/namespaces/$NAMESPACE/$RESOURCE
Request: NewRequestWithClient(uri, "", ClientContentConfig{GroupVersion: schema.GroupVersion{Group: "test"}}, nil).Verb("DELETE").
Prefix("/apis/g1/v1/namespaces/ns/r1"),
ExpectedFullURL: "http://localhost/some/base/url/path/apis/g1/v1/namespaces/ns/r1",
ExpectedFinalURL: "http://localhost/some/base/url/path/apis/g1/v1/namespaces/%7Bnamespace%7D/r1",
},
{
// dynamic client with core group + resourceResource (with name)
// /api/$RESOURCEVERSION/$RESOURCE/%NAME
Request: NewRequestWithClient(uri, "", ClientContentConfig{GroupVersion: schema.GroupVersion{Group: "test"}}, nil).Verb("DELETE").
Prefix("/api/v1/r1/name1"),
ExpectedFullURL: "http://localhost/some/base/url/path/api/v1/r1/name1",
ExpectedFinalURL: "http://localhost/some/base/url/path/api/v1/r1/%7Bname%7D",
},
{
// dynamic client with named group + resourceResource (with name)
// /apis/$NAMEDGROUPNAME/$RESOURCEVERSION/$RESOURCE/%NAME
Request: NewRequestWithClient(uri, "", ClientContentConfig{GroupVersion: schema.GroupVersion{Group: "test"}}, nil).Verb("DELETE").
Prefix("/apis/g1/v1/r1/name1"),
ExpectedFullURL: "http://localhost/some/base/url/path/apis/g1/v1/r1/name1",
ExpectedFinalURL: "http://localhost/some/base/url/path/apis/g1/v1/r1/%7Bname%7D",
},
{
// dynamic client with named group + namespace + resourceResource (with name) + subresource
// /apis/$NAMEDGROUPNAME/$RESOURCEVERSION/namespaces/$NAMESPACE/$RESOURCE/%NAME/$SUBRESOURCE
Request: NewRequestWithClient(uri, "", ClientContentConfig{GroupVersion: schema.GroupVersion{Group: "test"}}, nil).Verb("DELETE").
Prefix("/apis/namespaces/namespaces/namespaces/namespaces/namespaces/namespaces/finalize"),
ExpectedFullURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces/namespaces/namespaces/namespaces/finalize",
ExpectedFinalURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces/%7Bnamespace%7D/namespaces/%7Bname%7D/finalize",
},
{
// dynamic client with named group + namespace + resourceResource (with name)
// /apis/$NAMEDGROUPNAME/$RESOURCEVERSION/namespaces/$NAMESPACE/$RESOURCE/%NAME
Request: NewRequestWithClient(uri, "", ClientContentConfig{GroupVersion: schema.GroupVersion{Group: "test"}}, nil).Verb("DELETE").
Prefix("/apis/namespaces/namespaces/namespaces/namespaces/namespaces/namespaces"),
ExpectedFullURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces/namespaces/namespaces/namespaces",
ExpectedFinalURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces/%7Bnamespace%7D/namespaces/%7Bname%7D",
},
{
// dynamic client with named group + namespace + resourceResource (with NO name) + subresource
// /apis/$NAMEDGROUPNAME/$RESOURCEVERSION/namespaces/$NAMESPACE/$RESOURCE/%SUBRESOURCE
Request: NewRequestWithClient(uri, "", ClientContentConfig{GroupVersion: schema.GroupVersion{Group: "test"}}, nil).Verb("DELETE").
Prefix("/apis/namespaces/namespaces/namespaces/namespaces/namespaces/finalize"),
ExpectedFullURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces/namespaces/namespaces/finalize",
ExpectedFinalURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces/%7Bnamespace%7D/namespaces/finalize",
},
{
// dynamic client with named group + namespace + resourceResource (with NO name) + subresource
// /apis/$NAMEDGROUPNAME/$RESOURCEVERSION/namespaces/$NAMESPACE/$RESOURCE/%SUBRESOURCE
Request: NewRequestWithClient(uri, "", ClientContentConfig{GroupVersion: schema.GroupVersion{Group: "test"}}, nil).Verb("DELETE").
Prefix("/apis/namespaces/namespaces/namespaces/namespaces/namespaces/status"),
ExpectedFullURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces/namespaces/namespaces/status",
ExpectedFinalURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces/%7Bnamespace%7D/namespaces/status",
},
{
// dynamic client with named group + namespace + resourceResource (with no name)
// /apis/$NAMEDGROUPNAME/$RESOURCEVERSION/namespaces/$NAMESPACE/$RESOURCE/%NAME
Request: NewRequestWithClient(uri, "", ClientContentConfig{GroupVersion: schema.GroupVersion{Group: "test"}}, nil).Verb("DELETE").
Prefix("/apis/namespaces/namespaces/namespaces/namespaces/namespaces"),
ExpectedFullURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces/namespaces/namespaces",
ExpectedFinalURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces/%7Bnamespace%7D/namespaces",
},
{
// dynamic client with named group + resourceResource (with name) + subresource
// /apis/$NAMEDGROUPNAME/$RESOURCEVERSION/namespaces/$NAMESPACE/$RESOURCE/%NAME
Request: NewRequestWithClient(uri, "", ClientContentConfig{GroupVersion: schema.GroupVersion{Group: "test"}}, nil).Verb("DELETE").
Prefix("/apis/namespaces/namespaces/namespaces/namespaces/finalize"),
ExpectedFullURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces/namespaces/finalize",
ExpectedFinalURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces/%7Bname%7D/finalize",
},
{
// dynamic client with named group + resourceResource (with name) + subresource
// /apis/$NAMEDGROUPNAME/$RESOURCEVERSION/namespaces/$NAMESPACE/$RESOURCE/%NAME
Request: NewRequestWithClient(uri, "", ClientContentConfig{GroupVersion: schema.GroupVersion{Group: "test"}}, nil).Verb("DELETE").
Prefix("/apis/namespaces/namespaces/namespaces/namespaces/status"),
ExpectedFullURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces/namespaces/status",
ExpectedFinalURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces/%7Bname%7D/status",
},
{
// dynamic client with named group + resourceResource (with name)
// /apis/$NAMEDGROUPNAME/$RESOURCEVERSION/$RESOURCE/%NAME
Request: NewRequestWithClient(uri, "", ClientContentConfig{GroupVersion: schema.GroupVersion{Group: "test"}}, nil).Verb("DELETE").
Prefix("/apis/namespaces/namespaces/namespaces/namespaces"),
ExpectedFullURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces/namespaces",
ExpectedFinalURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces/%7Bname%7D",
},
{
// dynamic client with named group + resourceResource (with no name)
// /apis/$NAMEDGROUPNAME/$RESOURCEVERSION/$RESOURCE/%NAME
Request: NewRequestWithClient(uri, "", ClientContentConfig{GroupVersion: schema.GroupVersion{Group: "test"}}, nil).Verb("DELETE").
Prefix("/apis/namespaces/namespaces/namespaces"),
ExpectedFullURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces",
ExpectedFinalURL: "http://localhost/some/base/url/path/apis/namespaces/namespaces/namespaces",
},
{
// dynamic client with wrong api group + namespace + resourceResource (with name) + subresource
// /apis/$NAMEDGROUPNAME/$RESOURCEVERSION/namespaces/$NAMESPACE/$RESOURCE/%NAME/$SUBRESOURCE
Request: NewRequestWithClient(uri, "", ClientContentConfig{GroupVersion: schema.GroupVersion{Group: "test"}}, nil).Verb("DELETE").
Prefix("/pre1/namespaces/namespaces/namespaces/namespaces/namespaces/namespaces/finalize"),
ExpectedFullURL: "http://localhost/some/base/url/path/pre1/namespaces/namespaces/namespaces/namespaces/namespaces/namespaces/finalize",
ExpectedFinalURL: "http://localhost/%7Bprefix%7D",
},
{
// dynamic client with core group + namespace + resourceResource (with name) where baseURL is a single /
// /api/$RESOURCEVERSION/namespaces/$NAMESPACE/$RESOURCE/%NAME
Request: NewRequestWithClient(uriSingleSlash, "", ClientContentConfig{GroupVersion: schema.GroupVersion{Group: "test"}}, nil).Verb("DELETE").
Prefix("/api/v1/namespaces/ns/r2/name1"),
ExpectedFullURL: "http://localhost/api/v1/namespaces/ns/r2/name1",
ExpectedFinalURL: "http://localhost/api/v1/namespaces/%7Bnamespace%7D/r2/%7Bname%7D",
},
{
// dynamic client with core group + namespace + resourceResource (with name) where baseURL is 'some/base/url/path'
// /api/$RESOURCEVERSION/namespaces/$NAMESPACE/$RESOURCE/%NAME
Request: NewRequestWithClient(uri, "", ClientContentConfig{GroupVersion: schema.GroupVersion{Group: "test"}}, nil).Verb("DELETE").
Prefix("/api/v1/namespaces/ns/r3/name1"),
ExpectedFullURL: "http://localhost/some/base/url/path/api/v1/namespaces/ns/r3/name1",
ExpectedFinalURL: "http://localhost/some/base/url/path/api/v1/namespaces/%7Bnamespace%7D/r3/%7Bname%7D",
},
{
// dynamic client where baseURL is a single /
// /
Request: NewRequestWithClient(uriSingleSlash, "", ClientContentConfig{GroupVersion: schema.GroupVersion{Group: "test"}}, nil).Verb("DELETE").
Prefix("/"),
ExpectedFullURL: "http://localhost/",
ExpectedFinalURL: "http://localhost/",
},
{
// dynamic client where baseURL is a single /
// /version
Request: NewRequestWithClient(uriSingleSlash, "", ClientContentConfig{GroupVersion: schema.GroupVersion{Group: "test"}}, nil).Verb("DELETE").
Prefix("/version"),
ExpectedFullURL: "http://localhost/version",
ExpectedFinalURL: "http://localhost/version",
},
}
for i, testCase := range testCases {
r := testCase.Request
full := r.URL()
if full.String() != testCase.ExpectedFullURL {
t.Errorf("%d: unexpected initial URL: %s %s", i, full, testCase.ExpectedFullURL)
}
actualURL := r.finalURLTemplate()
actual := actualURL.String()
if actual != testCase.ExpectedFinalURL {
t.Errorf("%d: unexpected URL template: %s %s", i, actual, testCase.ExpectedFinalURL)
}
if r.URL().String() != full.String() {
t.Errorf("%d, creating URL template changed request: %s -> %s", i, full.String(), r.URL().String())
}
}
}
func TestTransformResponse(t *testing.T) {
invalid := []byte("aaaaa")
uri, _ := url.Parse("http://localhost")
@@ -559,7 +360,7 @@ func TestTransformResponse(t *testing.T) {
Response: &http.Response{
StatusCode: http.StatusUnauthorized,
Header: http.Header{"Content-Type": []string{"application/json"}},
Body: io.NopCloser(bytes.NewReader(invalid)),
Body: ioutil.NopCloser(bytes.NewReader(invalid)),
},
Error: true,
ErrFn: func(err error) bool {
@@ -570,7 +371,7 @@ func TestTransformResponse(t *testing.T) {
Response: &http.Response{
StatusCode: http.StatusUnauthorized,
Header: http.Header{"Content-Type": []string{"text/any"}},
Body: io.NopCloser(bytes.NewReader(invalid)),
Body: ioutil.NopCloser(bytes.NewReader(invalid)),
},
Error: true,
ErrFn: func(err error) bool {
@@ -578,13 +379,13 @@ func TestTransformResponse(t *testing.T) {
},
},
{Response: &http.Response{StatusCode: http.StatusForbidden}, Error: true},
{Response: &http.Response{StatusCode: http.StatusOK, Body: io.NopCloser(bytes.NewReader(invalid))}, Data: invalid},
{Response: &http.Response{StatusCode: http.StatusOK, Body: io.NopCloser(bytes.NewReader(invalid))}, Data: invalid},
{Response: &http.Response{StatusCode: http.StatusOK, Body: ioutil.NopCloser(bytes.NewReader(invalid))}, Data: invalid},
{Response: &http.Response{StatusCode: http.StatusOK, Body: ioutil.NopCloser(bytes.NewReader(invalid))}, Data: invalid},
}
for i, test := range testCases {
r := NewRequestWithClient(uri, "", defaultContentConfig(), nil)
if test.Response.Body == nil {
test.Response.Body = io.NopCloser(bytes.NewReader([]byte{}))
test.Response.Body = ioutil.NopCloser(bytes.NewReader([]byte{}))
}
result := r.transformResponse(test.Response, &http.Request{})
response, created, err := result.body, result.statusCode == http.StatusCreated, result.err
@@ -657,7 +458,7 @@ func TestTransformResponseNegotiate(t *testing.T) {
Response: &http.Response{
StatusCode: http.StatusUnauthorized,
Header: http.Header{"Content-Type": []string{"application/json"}},
Body: io.NopCloser(bytes.NewReader(invalid)),
Body: ioutil.NopCloser(bytes.NewReader(invalid)),
},
Called: true,
ExpectContentType: "application/json",
@@ -671,7 +472,7 @@ func TestTransformResponseNegotiate(t *testing.T) {
Response: &http.Response{
StatusCode: http.StatusUnauthorized,
Header: http.Header{"Content-Type": []string{"application/protobuf"}},
Body: io.NopCloser(bytes.NewReader(invalid)),
Body: ioutil.NopCloser(bytes.NewReader(invalid)),
},
Decoder: scheme.Codecs.LegacyCodec(v1.SchemeGroupVersion),
@@ -701,7 +502,7 @@ func TestTransformResponseNegotiate(t *testing.T) {
Response: &http.Response{
StatusCode: http.StatusOK,
Header: http.Header{"Content-Type": []string{"text/any"}},
Body: io.NopCloser(bytes.NewReader(invalid)),
Body: ioutil.NopCloser(bytes.NewReader(invalid)),
},
Decoder: scheme.Codecs.LegacyCodec(v1.SchemeGroupVersion),
Called: true,
@@ -712,7 +513,7 @@ func TestTransformResponseNegotiate(t *testing.T) {
ContentType: "text/any",
Response: &http.Response{
StatusCode: http.StatusOK,
Body: io.NopCloser(bytes.NewReader(invalid)),
Body: ioutil.NopCloser(bytes.NewReader(invalid)),
},
Decoder: scheme.Codecs.LegacyCodec(v1.SchemeGroupVersion),
Called: true,
@@ -724,7 +525,7 @@ func TestTransformResponseNegotiate(t *testing.T) {
Response: &http.Response{
StatusCode: http.StatusNotFound,
Header: http.Header{"Content-Type": []string{"application/unrecognized"}},
Body: io.NopCloser(bytes.NewReader(invalid)),
Body: ioutil.NopCloser(bytes.NewReader(invalid)),
},
Decoder: scheme.Codecs.LegacyCodec(v1.SchemeGroupVersion),
@@ -748,7 +549,7 @@ func TestTransformResponseNegotiate(t *testing.T) {
contentConfig.Negotiator = negotiator
r := NewRequestWithClient(uri, "", contentConfig, nil)
if test.Response.Body == nil {
test.Response.Body = io.NopCloser(bytes.NewReader([]byte{}))
test.Response.Body = ioutil.NopCloser(bytes.NewReader([]byte{}))
}
result := r.transformResponse(test.Response, &http.Request{})
_, err := result.body, result.err
@@ -800,7 +601,7 @@ func TestTransformUnstructuredError(t *testing.T) {
},
Res: &http.Response{
StatusCode: http.StatusConflict,
Body: io.NopCloser(bytes.NewReader(nil)),
Body: ioutil.NopCloser(bytes.NewReader(nil)),
},
ErrFn: apierrors.IsAlreadyExists,
},
@@ -812,7 +613,7 @@ func TestTransformUnstructuredError(t *testing.T) {
},
Res: &http.Response{
StatusCode: http.StatusConflict,
Body: io.NopCloser(bytes.NewReader(nil)),
Body: ioutil.NopCloser(bytes.NewReader(nil)),
},
ErrFn: apierrors.IsConflict,
},
@@ -822,7 +623,7 @@ func TestTransformUnstructuredError(t *testing.T) {
Req: &http.Request{},
Res: &http.Response{
StatusCode: http.StatusNotFound,
Body: io.NopCloser(bytes.NewReader(nil)),
Body: ioutil.NopCloser(bytes.NewReader(nil)),
},
ErrFn: apierrors.IsNotFound,
},
@@ -830,14 +631,14 @@ func TestTransformUnstructuredError(t *testing.T) {
Req: &http.Request{},
Res: &http.Response{
StatusCode: http.StatusBadRequest,
Body: io.NopCloser(bytes.NewReader(nil)),
Body: ioutil.NopCloser(bytes.NewReader(nil)),
},
ErrFn: apierrors.IsBadRequest,
},
{
// status in response overrides transformed result
Req: &http.Request{},
Res: &http.Response{StatusCode: http.StatusBadRequest, Body: io.NopCloser(bytes.NewReader([]byte(`{"kind":"Status","apiVersion":"v1","status":"Failure","code":404}`)))},
Res: &http.Response{StatusCode: http.StatusBadRequest, Body: ioutil.NopCloser(bytes.NewReader([]byte(`{"kind":"Status","apiVersion":"v1","status":"Failure","code":404}`)))},
ErrFn: apierrors.IsBadRequest,
Transformed: &apierrors.StatusError{
ErrStatus: metav1.Status{Status: metav1.StatusFailure, Code: http.StatusNotFound},
@@ -846,20 +647,20 @@ func TestTransformUnstructuredError(t *testing.T) {
{
// successful status is ignored
Req: &http.Request{},
Res: &http.Response{StatusCode: http.StatusBadRequest, Body: io.NopCloser(bytes.NewReader([]byte(`{"kind":"Status","apiVersion":"v1","status":"Success","code":404}`)))},
Res: &http.Response{StatusCode: http.StatusBadRequest, Body: ioutil.NopCloser(bytes.NewReader([]byte(`{"kind":"Status","apiVersion":"v1","status":"Success","code":404}`)))},
ErrFn: apierrors.IsBadRequest,
},
{
// empty object does not change result
Req: &http.Request{},
Res: &http.Response{StatusCode: http.StatusBadRequest, Body: io.NopCloser(bytes.NewReader([]byte(`{}`)))},
Res: &http.Response{StatusCode: http.StatusBadRequest, Body: ioutil.NopCloser(bytes.NewReader([]byte(`{}`)))},
ErrFn: apierrors.IsBadRequest,
},
{
// we default apiVersion for backwards compatibility with old clients
// TODO: potentially remove in 1.7
Req: &http.Request{},
Res: &http.Response{StatusCode: http.StatusBadRequest, Body: io.NopCloser(bytes.NewReader([]byte(`{"kind":"Status","status":"Failure","code":404}`)))},
Res: &http.Response{StatusCode: http.StatusBadRequest, Body: ioutil.NopCloser(bytes.NewReader([]byte(`{"kind":"Status","status":"Failure","code":404}`)))},
ErrFn: apierrors.IsBadRequest,
Transformed: &apierrors.StatusError{
ErrStatus: metav1.Status{Status: metav1.StatusFailure, Code: http.StatusNotFound},
@@ -868,7 +669,7 @@ func TestTransformUnstructuredError(t *testing.T) {
{
// we do not default kind
Req: &http.Request{},
Res: &http.Response{StatusCode: http.StatusBadRequest, Body: io.NopCloser(bytes.NewReader([]byte(`{"status":"Failure","code":404}`)))},
Res: &http.Response{StatusCode: http.StatusBadRequest, Body: ioutil.NopCloser(bytes.NewReader([]byte(`{"status":"Failure","code":404}`)))},
ErrFn: apierrors.IsBadRequest,
},
}
@@ -972,7 +773,7 @@ func TestRequestWatch(t *testing.T) {
serverReturns: []responseErr{
{response: &http.Response{
StatusCode: http.StatusForbidden,
Body: io.NopCloser(bytes.NewReader([]byte{})),
Body: ioutil.NopCloser(bytes.NewReader([]byte{})),
}, err: nil},
},
attemptsExpected: 1,
@@ -1015,7 +816,7 @@ func TestRequestWatch(t *testing.T) {
serverReturns: []responseErr{
{response: &http.Response{
StatusCode: http.StatusForbidden,
Body: io.NopCloser(bytes.NewReader([]byte{})),
Body: ioutil.NopCloser(bytes.NewReader([]byte{})),
}, err: nil},
},
attemptsExpected: 1,
@@ -1035,7 +836,7 @@ func TestRequestWatch(t *testing.T) {
serverReturns: []responseErr{
{response: &http.Response{
StatusCode: http.StatusUnauthorized,
Body: io.NopCloser(bytes.NewReader([]byte{})),
Body: ioutil.NopCloser(bytes.NewReader([]byte{})),
}, err: nil},
},
attemptsExpected: 1,
@@ -1055,7 +856,7 @@ func TestRequestWatch(t *testing.T) {
serverReturns: []responseErr{
{response: &http.Response{
StatusCode: http.StatusUnauthorized,
Body: io.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(scheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), &metav1.Status{
Body: ioutil.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(scheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), &metav1.Status{
Status: metav1.StatusFailure,
Reason: metav1.StatusReasonUnauthorized,
})))),
@@ -1287,7 +1088,7 @@ func TestRequestStream(t *testing.T) {
serverReturns: []responseErr{
{response: &http.Response{
StatusCode: http.StatusUnauthorized,
Body: io.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(scheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), &metav1.Status{
Body: ioutil.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(scheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), &metav1.Status{
Status: metav1.StatusFailure,
Reason: metav1.StatusReasonUnauthorized,
})))),
@@ -1306,7 +1107,7 @@ func TestRequestStream(t *testing.T) {
serverReturns: []responseErr{
{response: &http.Response{
StatusCode: http.StatusBadRequest,
Body: io.NopCloser(bytes.NewReader([]byte(`{"kind":"Status","apiVersion":"v1","metadata":{},"status":"Failure","message":"a container name must be specified for pod kube-dns-v20-mz5cv, choose one of: [kubedns dnsmasq healthz]","reason":"BadRequest","code":400}`))),
Body: ioutil.NopCloser(bytes.NewReader([]byte(`{"kind":"Status","apiVersion":"v1","metadata":{},"status":"Failure","message":"a container name must be specified for pod kube-dns-v20-mz5cv, choose one of: [kubedns dnsmasq healthz]","reason":"BadRequest","code":400}`))),
}, err: nil},
},
attemptsExpected: 1,
@@ -1387,7 +1188,7 @@ func TestRequestStream(t *testing.T) {
{response: retryAfterResponse(), err: nil},
{response: &http.Response{
StatusCode: http.StatusOK,
Body: io.NopCloser(bytes.NewReader([]byte{})),
Body: ioutil.NopCloser(bytes.NewReader([]byte{})),
}, err: nil},
},
},
@@ -1626,7 +1427,7 @@ func TestConnectionResetByPeerIsRetried(t *testing.T) {
if count >= 3 {
return &http.Response{
StatusCode: http.StatusOK,
Body: io.NopCloser(bytes.NewReader([]byte{})),
Body: ioutil.NopCloser(bytes.NewReader([]byte{})),
}, nil
}
return nil, &net.OpError{Err: syscall.ECONNRESET}
@@ -1654,7 +1455,7 @@ func TestCheckRetryHandles429And5xx(t *testing.T) {
count := 0
ch := make(chan struct{})
testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
data, err := io.ReadAll(req.Body)
data, err := ioutil.ReadAll(req.Body)
if err != nil {
t.Fatalf("unable to read request body: %v", err)
}
@@ -1806,7 +1607,7 @@ func TestDoRequestNewWayFile(t *testing.T) {
t.Errorf("unexpected error: %v", err)
}
file, err := os.CreateTemp("", "foo")
file, err := ioutil.TempFile("", "foo")
if err != nil {
t.Errorf("unexpected error: %v", err)
}
@@ -1978,7 +1779,7 @@ func TestBody(t *testing.T) {
obj := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}
bodyExpected, _ := runtime.Encode(scheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), obj)
f, err := os.CreateTemp("", "test_body")
f, err := ioutil.TempFile("", "test_body")
if err != nil {
t.Fatalf("TempFile error: %v", err)
}
@@ -2648,7 +2449,7 @@ func TestRequestWithRetry(t *testing.T) {
}{
{
name: "server returns retry-after response, request body is not io.Seeker, retry goes ahead",
body: io.NopCloser(bytes.NewReader([]byte{})),
body: ioutil.NopCloser(bytes.NewReader([]byte{})),
serverReturns: responseErr{response: retryAfterResponse(), err: nil},
errExpected: nil,
transformFuncInvokedExpected: 1,
@@ -2672,7 +2473,7 @@ func TestRequestWithRetry(t *testing.T) {
},
{
name: "server returns retryable err, request body is not io.Seek, retry goes ahead",
body: io.NopCloser(bytes.NewReader([]byte{})),
body: ioutil.NopCloser(bytes.NewReader([]byte{})),
serverReturns: responseErr{response: nil, err: io.ErrUnexpectedEOF},
errExpected: io.ErrUnexpectedEOF,
transformFuncInvokedExpected: 0,
@@ -2972,7 +2773,7 @@ func testRequestWithRetry(t *testing.T, key string, doFunc func(ctx context.Cont
resp := test.serverReturns[attempts].response
if resp != nil {
responseRecorder.delegated = io.NopCloser(bytes.NewReader([]byte{}))
responseRecorder.delegated = ioutil.NopCloser(bytes.NewReader([]byte{}))
resp.Body = responseRecorder
}
return resp, test.serverReturns[attempts].err
@@ -3253,7 +3054,7 @@ func testRetryWithRateLimiterBackoffAndMetrics(t *testing.T, key string, doFunc
interceptor.Do()
resp := test.serverReturns[attempts].response
if resp != nil {
resp.Body = io.NopCloser(bytes.NewReader([]byte{}))
resp.Body = ioutil.NopCloser(bytes.NewReader([]byte{}))
}
return resp, test.serverReturns[attempts].err
})
@@ -3389,7 +3190,7 @@ func testWithRetryInvokeOrder(t *testing.T, key string, doFunc func(ctx context.
interceptor.Do()
resp := test.serverReturns[attempts].response
if resp != nil {
resp.Body = io.NopCloser(bytes.NewReader([]byte{}))
resp.Body = ioutil.NopCloser(bytes.NewReader([]byte{}))
}
return resp, test.serverReturns[attempts].err
})
@@ -3564,7 +3365,7 @@ func testWithWrapPreviousError(t *testing.T, doFunc func(ctx context.Context, r
resp := test.serverReturns[attempts].response
if resp != nil {
resp.Body = io.NopCloser(bytes.NewReader([]byte{}))
resp.Body = ioutil.NopCloser(bytes.NewReader([]byte{}))
}
return resp, test.serverReturns[attempts].err
})
@@ -3864,12 +3665,12 @@ func TestRequestBodyResetOrder(t *testing.T) {
}()
// read the request body.
io.ReadAll(req.Body)
ioutil.ReadAll(req.Body)
// first attempt, we send a retry-after
if attempts == 0 {
resp := retryAfterResponse()
respBodyTracker.ReadCloser = io.NopCloser(bytes.NewReader([]byte{}))
respBodyTracker.ReadCloser = ioutil.NopCloser(bytes.NewReader([]byte{}))
resp.Body = respBodyTracker
return resp, nil
}

View File

@@ -108,13 +108,10 @@ func (c *Config) TransportConfig() (*transport.Config, error) {
Groups: c.Impersonate.Groups,
Extra: c.Impersonate.Extra,
},
Dial: c.Dial,
Proxy: c.Proxy,
}
if c.Dial != nil {
conf.DialHolder = &transport.DialHolder{Dial: c.Dial}
}
if c.ExecProvider != nil && c.AuthProvider != nil {
return nil, errors.New("execProvider and authProvider cannot be used in combination")
}

View File

@@ -1,149 +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 rest
import (
"context"
"net"
"strings"
"sync"
"testing"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
)
// TestTransportForThreadSafe is meant to be run with the race detector
// to detect that the Transport generation for client-go is safe to
// call from multiple goroutines.
func TestTransportForThreadSafe(t *testing.T) {
const (
rootCACert = `-----BEGIN CERTIFICATE-----
MIIC4DCCAcqgAwIBAgIBATALBgkqhkiG9w0BAQswIzEhMB8GA1UEAwwYMTAuMTMu
MTI5LjEwNkAxNDIxMzU5MDU4MB4XDTE1MDExNTIxNTczN1oXDTE2MDExNTIxNTcz
OFowIzEhMB8GA1UEAwwYMTAuMTMuMTI5LjEwNkAxNDIxMzU5MDU4MIIBIjANBgkq
hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAunDRXGwsiYWGFDlWH6kjGun+PshDGeZX
xtx9lUnL8pIRWH3wX6f13PO9sktaOWW0T0mlo6k2bMlSLlSZgG9H6og0W6gLS3vq
s4VavZ6DbXIwemZG2vbRwsvR+t4G6Nbwelm6F8RFnA1Fwt428pavmNQ/wgYzo+T1
1eS+HiN4ACnSoDSx3QRWcgBkB1g6VReofVjx63i0J+w8Q/41L9GUuLqquFxu6ZnH
60vTB55lHgFiDLjA1FkEz2dGvGh/wtnFlRvjaPC54JH2K1mPYAUXTreoeJtLJKX0
ycoiyB24+zGCniUmgIsmQWRPaOPircexCp1BOeze82BT1LCZNTVaxQIDAQABoyMw
ITAOBgNVHQ8BAf8EBAMCAKQwDwYDVR0TAQH/BAUwAwEB/zALBgkqhkiG9w0BAQsD
ggEBADMxsUuAFlsYDpF4fRCzXXwrhbtj4oQwcHpbu+rnOPHCZupiafzZpDu+rw4x
YGPnCb594bRTQn4pAu3Ac18NbLD5pV3uioAkv8oPkgr8aUhXqiv7KdDiaWm6sbAL
EHiXVBBAFvQws10HMqMoKtO8f1XDNAUkWduakR/U6yMgvOPwS7xl0eUTqyRB6zGb
K55q2dejiFWaFqB/y78txzvz6UlOZKE44g2JAVoJVM6kGaxh33q8/FmrL4kuN3ut
W+MmJCVDvd4eEqPwbp7146ZWTqpIJ8lvA6wuChtqV8lhAPka2hD/LMqY8iXNmfXD
uml0obOEy+ON91k+SWTJ3ggmF/U=
-----END CERTIFICATE-----`
authPluginName = "auth-plugin-tr"
)
err := RegisterAuthProviderPlugin(authPluginName, pluginAProvider)
// the plugins are stored in a global variable that fails if the test
// runs with the flag -count N, with N > 1
if err != nil && !strings.Contains(err.Error(), "was registered twice") {
t.Errorf("Unexpected error: failed to register pluginA: %v", err)
}
configurations := []struct {
name string
config *Config
}{
{
name: "simple config",
config: &Config{
Host: "localhost:8080",
Username: "gopher",
Password: "g0ph3r",
},
},
{
name: "not cacheable config",
config: &Config{
Host: "localhost:8080",
Username: "gopher",
Password: "g0ph3r",
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
return nil, nil
},
},
},
{
name: "with TLS config",
config: &Config{
Host: "localhost:8080",
Username: "gopher",
Password: "g0ph3r",
TLSClientConfig: TLSClientConfig{
CAData: []byte(rootCACert),
},
},
},
{
name: "with auth provider",
config: &Config{
Host: "localhost:8080",
Username: "gopher",
Password: "g0ph3r",
TLSClientConfig: TLSClientConfig{
CAData: []byte(rootCACert),
},
AuthProvider: &clientcmdapi.AuthProviderConfig{
Name: authPluginName,
Config: map[string]string{"secret": "s3cr3t"},
},
},
},
{
name: "with exec provider",
config: &Config{
Host: "localhost:8080",
Username: "gopher",
Password: "g0ph3r",
TLSClientConfig: TLSClientConfig{
CAData: []byte(rootCACert),
},
ExecProvider: &clientcmdapi.ExecConfig{
Args: []string{"secret"},
Env: []clientcmdapi.ExecEnvVar{{Name: "secret", Value: "s3cr3t"}},
APIVersion: "client.authentication.k8s.io/v1beta1",
},
},
},
}
var wg sync.WaitGroup
for _, tt := range configurations {
tt := tt
wg.Add(1)
go func() {
defer wg.Done()
for i := 1; i <= 50; i++ {
wg.Add(1)
go func() {
defer wg.Done()
_, err := TransportFor(tt.config)
if err != nil {
t.Errorf("Config: %s TransportFor() error = %v", tt.name, err)
}
}()
}
}()
}
wg.Wait()
}

View File

@@ -18,7 +18,7 @@ package versioned_test
import (
"bytes"
"io"
"io/ioutil"
"testing"
"k8s.io/api/core/v1"
@@ -70,7 +70,7 @@ func TestEncodeDecodeRoundTrip(t *testing.T) {
continue
}
rc := io.NopCloser(buf)
rc := ioutil.NopCloser(buf)
decoder := restclientwatch.NewDecoder(streaming.NewDecoder(rc, getDecoder()), getDecoder())
event, obj, err := decoder.Decode()
if err != nil {

View File

@@ -20,6 +20,7 @@ import (
"context"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"time"
@@ -344,7 +345,7 @@ func readAndCloseResponseBody(resp *http.Response) {
defer resp.Body.Close()
if resp.ContentLength <= maxBodySlurpSize {
io.Copy(io.Discard, &io.LimitedReader{R: resp.Body, N: maxBodySlurpSize})
io.Copy(ioutil.Discard, &io.LimitedReader{R: resp.Body, N: maxBodySlurpSize})
}
}

View File

@@ -22,6 +22,7 @@ import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"testing"
@@ -45,7 +46,7 @@ import (
)
func bytesBody(bodyBytes []byte) io.ReadCloser {
return io.NopCloser(bytes.NewReader(bodyBytes))
return ioutil.NopCloser(bytes.NewReader(bodyBytes))
}
func defaultHeaders() http.Header {
@@ -183,7 +184,7 @@ func fakeScaleClient(t *testing.T) (ScalesGetter, []schema.GroupResource) {
return &http.Response{StatusCode: http.StatusOK, Header: defaultHeaders(), Body: bytesBody(res)}, nil
case "PUT":
decoder := codecs.UniversalDeserializer()
body, err := io.ReadAll(req.Body)
body, err := ioutil.ReadAll(req.Body)
if err != nil {
return nil, err
}
@@ -200,7 +201,7 @@ func fakeScaleClient(t *testing.T) (ScalesGetter, []schema.GroupResource) {
}
return &http.Response{StatusCode: http.StatusOK, Header: defaultHeaders(), Body: bytesBody(res)}, nil
case "PATCH":
body, err := io.ReadAll(req.Body)
body, err := ioutil.ReadAll(req.Body)
if err != nil {
return nil, err
}

View File

@@ -65,6 +65,7 @@ package auth
// TODO: need a way to rotate Tokens. Therefore, need a way for client object to be reset when the authcfg is updated.
import (
"encoding/json"
"io/ioutil"
"os"
restclient "k8s.io/client-go/rest"
@@ -89,7 +90,7 @@ func LoadFromFile(path string) (*Info, error) {
if _, err := os.Stat(path); os.IsNotExist(err) {
return nil, err
}
data, err := os.ReadFile(path)
data, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}

View File

@@ -17,6 +17,7 @@ limitations under the License.
package auth_test
import (
"io/ioutil"
"os"
"reflect"
"testing"
@@ -41,7 +42,7 @@ func TestLoadFromFile(t *testing.T) {
}
for _, loadAuthInfoTest := range loadAuthInfoTests {
tt := loadAuthInfoTest
aifile, err := os.CreateTemp("", "testAuthInfo")
aifile, err := ioutil.TempFile("", "testAuthInfo")
if err != nil {
t.Errorf("Unexpected error: %v", err)
}

View File

@@ -135,9 +135,7 @@ type SharedInformer interface {
// AddEventHandler adds an event handler to the shared informer using the shared informer's resync
// period. Events to a single handler are delivered sequentially, but there is no coordination
// between different handlers.
// It returns a registration handle for the handler that can be used to remove
// the handler again.
AddEventHandler(handler ResourceEventHandler) (ResourceEventHandlerRegistration, error)
AddEventHandler(handler ResourceEventHandler)
// AddEventHandlerWithResyncPeriod adds an event handler to the
// shared informer with the requested resync period; zero means
// this handler does not care about resyncs. The resync operation
@@ -152,13 +150,7 @@ type SharedInformer interface {
// between any two resyncs may be longer than the nominal period
// because the implementation takes time to do work and there may
// be competing load and scheduling noise.
// It returns a registration handle for the handler that can be used to remove
// the handler again and an error if the handler cannot be added.
AddEventHandlerWithResyncPeriod(handler ResourceEventHandler, resyncPeriod time.Duration) (ResourceEventHandlerRegistration, error)
// RemoveEventHandler removes a formerly added event handler given by
// its registration handle.
// This function is guaranteed to be idempotent, and thread-safe.
RemoveEventHandler(handle ResourceEventHandlerRegistration) error
AddEventHandlerWithResyncPeriod(handler ResourceEventHandler, resyncPeriod time.Duration)
// GetStore returns the informer's local cache as a Store.
GetStore() Store
// GetController is deprecated, it does nothing useful
@@ -203,18 +195,8 @@ type SharedInformer interface {
// transform before mutating it at all and returning the copy to prevent
// data races.
SetTransform(handler TransformFunc) error
// IsStopped reports whether the informer has already been stopped.
// Adding event handlers to already stopped informers is not possible.
// An informer already stopped will never be started again.
IsStopped() bool
}
// Opaque interface representing the registration of ResourceEventHandler for
// a SharedInformer. Must be supplied back to the same SharedInformer's
// `RemoveEventHandler` to unregister the handlers.
type ResourceEventHandlerRegistration interface{}
// SharedIndexInformer provides add and get Indexers ability based on SharedInformer.
type SharedIndexInformer interface {
SharedInformer
@@ -510,8 +492,8 @@ func (s *sharedIndexInformer) GetController() Controller {
return &dummyController{informer: s}
}
func (s *sharedIndexInformer) AddEventHandler(handler ResourceEventHandler) (ResourceEventHandlerRegistration, error) {
return s.AddEventHandlerWithResyncPeriod(handler, s.defaultEventHandlerResyncPeriod)
func (s *sharedIndexInformer) AddEventHandler(handler ResourceEventHandler) {
s.AddEventHandlerWithResyncPeriod(handler, s.defaultEventHandlerResyncPeriod)
}
func determineResyncPeriod(desired, check time.Duration) time.Duration {
@@ -531,12 +513,13 @@ func determineResyncPeriod(desired, check time.Duration) time.Duration {
const minimumResyncPeriod = 1 * time.Second
func (s *sharedIndexInformer) AddEventHandlerWithResyncPeriod(handler ResourceEventHandler, resyncPeriod time.Duration) (ResourceEventHandlerRegistration, error) {
func (s *sharedIndexInformer) AddEventHandlerWithResyncPeriod(handler ResourceEventHandler, resyncPeriod time.Duration) {
s.startedLock.Lock()
defer s.startedLock.Unlock()
if s.stopped {
return nil, fmt.Errorf("handler %v was not added to shared informer because it has stopped already", handler)
klog.V(2).Infof("Handler %v was not added to shared informer because it has stopped already", handler)
return
}
if resyncPeriod > 0 {
@@ -562,7 +545,8 @@ func (s *sharedIndexInformer) AddEventHandlerWithResyncPeriod(handler ResourceEv
listener := newProcessListener(handler, resyncPeriod, determineResyncPeriod(resyncPeriod, s.resyncCheckPeriod), s.clock.Now(), initialBufferSize)
if !s.started {
return s.processor.addListener(listener), nil
s.processor.addListener(listener)
return
}
// in order to safely join, we have to
@@ -573,11 +557,10 @@ func (s *sharedIndexInformer) AddEventHandlerWithResyncPeriod(handler ResourceEv
s.blockDeltas.Lock()
defer s.blockDeltas.Unlock()
handle := s.processor.addListener(listener)
s.processor.addListener(listener)
for _, item := range s.indexer.List() {
listener.add(addNotification{newObj: item})
}
return handle, nil
}
func (s *sharedIndexInformer) HandleDeltas(obj interface{}) error {
@@ -627,26 +610,6 @@ func (s *sharedIndexInformer) OnDelete(old interface{}) {
s.processor.distribute(deleteNotification{oldObj: old}, false)
}
// IsStopped reports whether the informer has already been stopped
func (s *sharedIndexInformer) IsStopped() bool {
s.startedLock.Lock()
defer s.startedLock.Unlock()
return s.stopped
}
func (s *sharedIndexInformer) RemoveEventHandler(handle ResourceEventHandlerRegistration) error {
s.startedLock.Lock()
defer s.startedLock.Unlock()
// in order to safely remove, we have to
// 1. stop sending add/update/delete notifications
// 2. remove and stop listener
// 3. unblock
s.blockDeltas.Lock()
defer s.blockDeltas.Unlock()
return s.processor.removeListener(handle)
}
// sharedProcessor has a collection of processorListener and can
// distribute a notification object to its listeners. There are two
// kinds of distribute operations. The sync distributions go to a
@@ -656,85 +619,39 @@ func (s *sharedIndexInformer) RemoveEventHandler(handle ResourceEventHandlerRegi
type sharedProcessor struct {
listenersStarted bool
listenersLock sync.RWMutex
// Map from listeners to whether or not they are currently syncing
listeners map[*processorListener]bool
clock clock.Clock
wg wait.Group
listeners []*processorListener
syncingListeners []*processorListener
clock clock.Clock
wg wait.Group
}
func (p *sharedProcessor) getListener(registration ResourceEventHandlerRegistration) *processorListener {
p.listenersLock.RLock()
defer p.listenersLock.RUnlock()
if p.listeners == nil {
return nil
}
if result, ok := registration.(*processorListener); ok {
if _, exists := p.listeners[result]; exists {
return result
}
}
return nil
}
func (p *sharedProcessor) addListener(listener *processorListener) ResourceEventHandlerRegistration {
func (p *sharedProcessor) addListener(listener *processorListener) {
p.listenersLock.Lock()
defer p.listenersLock.Unlock()
if p.listeners == nil {
p.listeners = make(map[*processorListener]bool)
}
p.listeners[listener] = true
p.addListenerLocked(listener)
if p.listenersStarted {
p.wg.Start(listener.run)
p.wg.Start(listener.pop)
}
return listener
}
func (p *sharedProcessor) removeListener(handle ResourceEventHandlerRegistration) error {
p.listenersLock.Lock()
defer p.listenersLock.Unlock()
listener, ok := handle.(*processorListener)
if !ok {
return fmt.Errorf("invalid key type %t", handle)
} else if p.listeners == nil {
// No listeners are registered, do nothing
return nil
} else if _, exists := p.listeners[listener]; !exists {
// Listener is not registered, just do nothing
return nil
}
delete(p.listeners, listener)
if p.listenersStarted {
close(listener.addCh)
}
return nil
func (p *sharedProcessor) addListenerLocked(listener *processorListener) {
p.listeners = append(p.listeners, listener)
p.syncingListeners = append(p.syncingListeners, listener)
}
func (p *sharedProcessor) distribute(obj interface{}, sync bool) {
p.listenersLock.RLock()
defer p.listenersLock.RUnlock()
for listener, isSyncing := range p.listeners {
switch {
case !sync:
// non-sync messages are delivered to every listener
if sync {
for _, listener := range p.syncingListeners {
listener.add(obj)
case isSyncing:
// sync messages are delivered to every syncing listenter
}
} else {
for _, listener := range p.listeners {
listener.add(obj)
default:
// skipping a sync obj for a non-syncing listener
}
}
}
@@ -743,27 +660,18 @@ func (p *sharedProcessor) run(stopCh <-chan struct{}) {
func() {
p.listenersLock.RLock()
defer p.listenersLock.RUnlock()
for listener := range p.listeners {
for _, listener := range p.listeners {
p.wg.Start(listener.run)
p.wg.Start(listener.pop)
}
p.listenersStarted = true
}()
<-stopCh
p.listenersLock.Lock()
defer p.listenersLock.Unlock()
for listener := range p.listeners {
p.listenersLock.RLock()
defer p.listenersLock.RUnlock()
for _, listener := range p.listeners {
close(listener.addCh) // Tell .pop() to stop. .pop() will tell .run() to stop
}
// Wipe out list of listeners since they are now closed
// (processorListener cannot be re-used)
p.listeners = nil
// Reset to false since no listeners are running
p.listenersStarted = false
p.wg.Wait() // Wait for all .pop() and .run() to stop
}
@@ -773,16 +681,16 @@ func (p *sharedProcessor) shouldResync() bool {
p.listenersLock.Lock()
defer p.listenersLock.Unlock()
p.syncingListeners = []*processorListener{}
resyncNeeded := false
now := p.clock.Now()
for listener := range p.listeners {
for _, listener := range p.listeners {
// need to loop through all the listeners to see if they need to resync so we can prepare any
// listeners that are going to be resyncing.
shouldResync := listener.shouldResync(now)
p.listeners[listener] = shouldResync
if shouldResync {
if listener.shouldResync(now) {
resyncNeeded = true
p.syncingListeners = append(p.syncingListeners, listener)
listener.determineNextResync(now)
}
}
@@ -793,9 +701,8 @@ func (p *sharedProcessor) resyncCheckPeriodChanged(resyncCheckPeriod time.Durati
p.listenersLock.RLock()
defer p.listenersLock.RUnlock()
for listener := range p.listeners {
resyncPeriod := determineResyncPeriod(
listener.requestedResyncPeriod, resyncCheckPeriod)
for _, listener := range p.listeners {
resyncPeriod := determineResyncPeriod(listener.requestedResyncPeriod, resyncCheckPeriod)
listener.setResyncPeriod(resyncPeriod)
}
}

View File

@@ -17,10 +17,7 @@ limitations under the License.
package cache
import (
"context"
"fmt"
"math/rand"
"strconv"
"strings"
"sync"
"testing"
@@ -99,25 +96,6 @@ func (l *testListener) satisfiedExpectations() bool {
return sets.NewString(l.receivedItemNames...).Equal(l.expectedItemNames)
}
func eventHandlerCount(i SharedInformer) int {
s := i.(*sharedIndexInformer)
s.startedLock.Lock()
defer s.startedLock.Unlock()
return len(s.processor.listeners)
}
func isStarted(i SharedInformer) bool {
s := i.(*sharedIndexInformer)
s.startedLock.Lock()
defer s.startedLock.Unlock()
return s.started
}
func isRegistered(i SharedInformer, h ResourceEventHandlerRegistration) bool {
s := i.(*sharedIndexInformer)
return s.processor.getListener(h) != nil
}
func TestListenerResyncPeriods(t *testing.T) {
// source simulates an apiserver object endpoint.
source := fcache.NewFakeControllerSource()
@@ -211,7 +189,6 @@ func TestResyncCheckPeriod(t *testing.T) {
// create the shared informer and resync every 12 hours
informer := NewSharedInformer(source, &v1.Pod{}, 12*time.Hour).(*sharedIndexInformer)
gl := informer.processor.getListener
clock := testingclock.NewFakeClock(time.Now())
informer.clock = clock
@@ -219,60 +196,59 @@ func TestResyncCheckPeriod(t *testing.T) {
// listener 1, never resync
listener1 := newTestListener("listener1", 0)
handler1, _ := informer.AddEventHandlerWithResyncPeriod(listener1, listener1.resyncPeriod)
informer.AddEventHandlerWithResyncPeriod(listener1, listener1.resyncPeriod)
if e, a := 12*time.Hour, informer.resyncCheckPeriod; e != a {
t.Errorf("expected %d, got %d", e, a)
}
if e, a := time.Duration(0), gl(handler1).resyncPeriod; e != a {
if e, a := time.Duration(0), informer.processor.listeners[0].resyncPeriod; e != a {
t.Errorf("expected %d, got %d", e, a)
}
// listener 2, resync every minute
listener2 := newTestListener("listener2", 1*time.Minute)
handler2, _ := informer.AddEventHandlerWithResyncPeriod(listener2, listener2.resyncPeriod)
informer.AddEventHandlerWithResyncPeriod(listener2, listener2.resyncPeriod)
if e, a := 1*time.Minute, informer.resyncCheckPeriod; e != a {
t.Errorf("expected %d, got %d", e, a)
}
if e, a := time.Duration(0), gl(handler1).resyncPeriod; e != a {
if e, a := time.Duration(0), informer.processor.listeners[0].resyncPeriod; e != a {
t.Errorf("expected %d, got %d", e, a)
}
if e, a := 1*time.Minute, gl(handler2).resyncPeriod; e != a {
if e, a := 1*time.Minute, informer.processor.listeners[1].resyncPeriod; e != a {
t.Errorf("expected %d, got %d", e, a)
}
// listener 3, resync every 55 seconds
listener3 := newTestListener("listener3", 55*time.Second)
handler3, _ := informer.AddEventHandlerWithResyncPeriod(listener3, listener3.resyncPeriod)
informer.AddEventHandlerWithResyncPeriod(listener3, listener3.resyncPeriod)
if e, a := 55*time.Second, informer.resyncCheckPeriod; e != a {
t.Errorf("expected %d, got %d", e, a)
}
if e, a := time.Duration(0), gl(handler1).resyncPeriod; e != a {
if e, a := time.Duration(0), informer.processor.listeners[0].resyncPeriod; e != a {
t.Errorf("expected %d, got %d", e, a)
}
if e, a := 1*time.Minute, gl(handler2).resyncPeriod; e != a {
if e, a := 1*time.Minute, informer.processor.listeners[1].resyncPeriod; e != a {
t.Errorf("expected %d, got %d", e, a)
}
if e, a := 55*time.Second, gl(handler3).resyncPeriod; e != a {
if e, a := 55*time.Second, informer.processor.listeners[2].resyncPeriod; e != a {
t.Errorf("expected %d, got %d", e, a)
}
// listener 4, resync every 5 seconds
listener4 := newTestListener("listener4", 5*time.Second)
handler4, _ := informer.AddEventHandlerWithResyncPeriod(listener4, listener4.resyncPeriod)
informer.AddEventHandlerWithResyncPeriod(listener4, listener4.resyncPeriod)
if e, a := 5*time.Second, informer.resyncCheckPeriod; e != a {
t.Errorf("expected %d, got %d", e, a)
}
if e, a := time.Duration(0), gl(handler1).resyncPeriod; e != a {
if e, a := time.Duration(0), informer.processor.listeners[0].resyncPeriod; e != a {
t.Errorf("expected %d, got %d", e, a)
}
if e, a := 1*time.Minute, gl(handler2).resyncPeriod; e != a {
if e, a := 1*time.Minute, informer.processor.listeners[1].resyncPeriod; e != a {
t.Errorf("expected %d, got %d", e, a)
}
if e, a := 55*time.Second, gl(handler3).resyncPeriod; e != a {
if e, a := 55*time.Second, informer.processor.listeners[2].resyncPeriod; e != a {
t.Errorf("expected %d, got %d", e, a)
}
if e, a := 5*time.Second, gl(handler4).resyncPeriod; e != a {
if e, a := 5*time.Second, informer.processor.listeners[3].resyncPeriod; e != a {
t.Errorf("expected %d, got %d", e, a)
}
}
@@ -414,528 +390,3 @@ func TestSharedInformerTransformer(t *testing.T) {
t.Errorf("%s: expected %v, got %v", listenerTransformer.name, listenerTransformer.expectedItemNames, listenerTransformer.receivedItemNames)
}
}
func TestSharedInformerRemoveHandler(t *testing.T) {
source := fcache.NewFakeControllerSource()
source.Add(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod1"}})
informer := NewSharedInformer(source, &v1.Pod{}, 1*time.Second)
handler1 := &ResourceEventHandlerFuncs{}
handle1, err := informer.AddEventHandler(handler1)
if err != nil {
t.Errorf("informer did not add handler1: %s", err)
return
}
handler2 := &ResourceEventHandlerFuncs{}
handle2, err := informer.AddEventHandler(handler2)
if err != nil {
t.Errorf("informer did not add handler2: %s", err)
return
}
if eventHandlerCount(informer) != 2 {
t.Errorf("informer has %d registered handler, instead of 2", eventHandlerCount(informer))
}
if err := informer.RemoveEventHandler(handle2); err != nil {
t.Errorf("removing of second pointer handler failed: %s", err)
}
if eventHandlerCount(informer) != 1 {
t.Errorf("after removing handler informer has %d registered handler(s), instead of 1", eventHandlerCount(informer))
}
if err := informer.RemoveEventHandler(handle1); err != nil {
t.Errorf("removing of first pointer handler failed: %s", err)
}
if eventHandlerCount(informer) != 0 {
t.Errorf("informer still has registered handlers after removing both handlers")
}
}
func TestSharedInformerRemoveForeignHandler(t *testing.T) {
source := fcache.NewFakeControllerSource()
source.Add(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod1"}})
informer := NewSharedInformer(source, &v1.Pod{}, 1*time.Second).(*sharedIndexInformer)
source2 := fcache.NewFakeControllerSource()
source2.Add(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod1"}})
informer2 := NewSharedInformer(source2, &v1.Pod{}, 1*time.Second).(*sharedIndexInformer)
handler1 := &ResourceEventHandlerFuncs{}
handle1, err := informer.AddEventHandler(handler1)
if err != nil {
t.Errorf("informer did not add handler1: %s", err)
return
}
handler2 := &ResourceEventHandlerFuncs{}
handle2, err := informer.AddEventHandler(handler2)
if err != nil {
t.Errorf("informer did not add handler2: %s", err)
return
}
if eventHandlerCount(informer) != 2 {
t.Errorf("informer has %d registered handler, instead of 2", eventHandlerCount(informer))
}
if eventHandlerCount(informer2) != 0 {
t.Errorf("informer2 has %d registered handler, instead of 0", eventHandlerCount(informer2))
}
// remove handle at foreign informer
if isRegistered(informer2, handle1) {
t.Errorf("handle1 registered for informer2")
}
if isRegistered(informer2, handle2) {
t.Errorf("handle2 registered for informer2")
}
if err := informer2.RemoveEventHandler(handle1); err != nil {
t.Errorf("removing of second pointer handler failed: %s", err)
}
if eventHandlerCount(informer) != 2 {
t.Errorf("informer has %d registered handler, instead of 2", eventHandlerCount(informer))
}
if eventHandlerCount(informer2) != 0 {
t.Errorf("informer2 has %d registered handler, instead of 0", eventHandlerCount(informer2))
}
if !isRegistered(informer, handle1) {
t.Errorf("handle1 not registered anymore for informer")
}
if !isRegistered(informer, handle2) {
t.Errorf("handle2 not registered anymore for informer")
}
if eventHandlerCount(informer) != 2 {
t.Errorf("informer has %d registered handler, instead of 2", eventHandlerCount(informer))
}
if eventHandlerCount(informer2) != 0 {
t.Errorf("informer2 has %d registered handler, instead of 0", eventHandlerCount(informer2))
}
if !isRegistered(informer, handle1) {
t.Errorf("handle1 not registered anymore for informer")
}
if !isRegistered(informer, handle2) {
t.Errorf("handle2 not registered anymore for informer")
}
if err := informer.RemoveEventHandler(handle2); err != nil {
t.Errorf("removing of second pointer handler failed: %s", err)
}
if eventHandlerCount(informer) != 1 {
t.Errorf("after removing handler informer has %d registered handler(s), instead of 1", eventHandlerCount(informer))
}
if err := informer.RemoveEventHandler(handle1); err != nil {
t.Errorf("removing of first pointer handler failed: %s", err)
}
if eventHandlerCount(informer) != 0 {
t.Errorf("informer still has registered handlers after removing both handlers")
}
}
func TestSharedInformerMultipleRegistration(t *testing.T) {
source := fcache.NewFakeControllerSource()
source.Add(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod1"}})
informer := NewSharedInformer(source, &v1.Pod{}, 1*time.Second).(*sharedIndexInformer)
handler1 := &ResourceEventHandlerFuncs{}
reg1, err := informer.AddEventHandler(handler1)
if err != nil {
t.Errorf("informer did not add handler for the first time: %s", err)
return
}
if !isRegistered(informer, reg1) {
t.Errorf("handle1 is not active after successful registration")
return
}
reg2, err := informer.AddEventHandler(handler1)
if err != nil {
t.Errorf("informer did not add handler for the second: %s", err)
return
}
if !isRegistered(informer, reg2) {
t.Errorf("handle2 is not active after successful registration")
return
}
if eventHandlerCount(informer) != 2 {
t.Errorf("informer has %d registered handler(s), instead of 2", eventHandlerCount(informer))
}
if err := informer.RemoveEventHandler(reg1); err != nil {
t.Errorf("removing of duplicate handler registration failed: %s", err)
}
if isRegistered(informer, reg1) {
t.Errorf("handle1 is still active after successful remove")
return
}
if !isRegistered(informer, reg2) {
t.Errorf("handle2 is not active after removing handle1")
return
}
if eventHandlerCount(informer) != 1 {
if eventHandlerCount(informer) == 0 {
t.Errorf("informer has no registered handler anymore after removal of duplicate registrations")
} else {
t.Errorf("informer has unexpected number (%d) of handlers after removal of duplicate handler registration", eventHandlerCount(informer))
}
}
if err := informer.RemoveEventHandler(reg2); err != nil {
t.Errorf("removing of second handler registration failed: %s", err)
}
if isRegistered(informer, reg2) {
t.Errorf("handle2 is still active after successful remove")
return
}
if eventHandlerCount(informer) != 0 {
t.Errorf("informer has unexpected number (%d) of handlers after removal of second handler registrations", eventHandlerCount(informer))
}
}
func TestRemovingRemovedSharedInformer(t *testing.T) {
source := fcache.NewFakeControllerSource()
source.Add(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod1"}})
informer := NewSharedInformer(source, &v1.Pod{}, 1*time.Second).(*sharedIndexInformer)
handler := &ResourceEventHandlerFuncs{}
reg, err := informer.AddEventHandler(handler)
if err != nil {
t.Errorf("informer did not add handler for the first time: %s", err)
return
}
if err := informer.RemoveEventHandler(reg); err != nil {
t.Errorf("removing of handler registration failed: %s", err)
return
}
if isRegistered(informer, reg) {
t.Errorf("handle is still active after successful remove")
return
}
if err := informer.RemoveEventHandler(reg); err != nil {
t.Errorf("removing of already removed registration yields unexpected error: %s", err)
}
if isRegistered(informer, reg) {
t.Errorf("handle is still active after second remove")
return
}
}
// Shows that many concurrent goroutines can be manipulating shared informer
// listeners without tripping it up. There are not really many assertions in this
// test. Meant to be run with -race to find race conditions
func TestSharedInformerHandlerAbuse(t *testing.T) {
source := fcache.NewFakeControllerSource()
informer := NewSharedInformer(source, &v1.Pod{}, 1*time.Second).(*sharedIndexInformer)
ctx, cancel := context.WithCancel(context.Background())
informerCtx, informerCancel := context.WithCancel(context.Background())
go func() {
informer.Run(informerCtx.Done())
cancel()
}()
worker := func() {
// Keep adding and removing handler
// Make sure no duplicate events?
funcs := ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {},
UpdateFunc: func(oldObj, newObj interface{}) {},
DeleteFunc: func(obj interface{}) {},
}
handles := []ResourceEventHandlerRegistration{}
for {
select {
case <-ctx.Done():
return
default:
switch rand.Intn(2) {
case 0:
// Register handler again
reg, err := informer.AddEventHandlerWithResyncPeriod(funcs, 1*time.Second)
if err != nil {
if strings.Contains(err.Error(), "stopped already") {
// test is over
return
}
t.Errorf("failed to add handler: %v", err)
return
}
handles = append(handles, reg)
case 1:
// Remove a random handler
if len(handles) == 0 {
continue
}
idx := rand.Intn(len(handles))
err := informer.RemoveEventHandler(handles[idx])
if err != nil {
if strings.Contains(err.Error(), "stopped already") {
// test is over
return
}
t.Errorf("failed to remove handler: %v", err)
return
}
handles = append(handles[:idx], handles[idx+1:]...)
}
}
}
}
wg := sync.WaitGroup{}
for i := 0; i < 100; i++ {
wg.Add(1)
go func() {
worker()
wg.Done()
}()
}
objs := []*v1.Pod{}
// While workers run, randomly create events for the informer
for i := 0; i < 10000; i++ {
if len(objs) == 0 {
// Make sure there is always an object
obj := &v1.Pod{ObjectMeta: metav1.ObjectMeta{
Name: "pod" + strconv.Itoa(i),
}}
objs = append(objs, obj)
// deep copy before adding since the Modify function mutates the obj
source.Add(obj.DeepCopy())
}
switch rand.Intn(3) {
case 0:
// Add Object
obj := &v1.Pod{ObjectMeta: metav1.ObjectMeta{
Name: "pod" + strconv.Itoa(i),
}}
objs = append(objs, obj)
source.Add(obj.DeepCopy())
case 1:
// Update Object
idx := rand.Intn(len(objs))
source.Modify(objs[idx].DeepCopy())
case 2:
// Remove Object
idx := rand.Intn(len(objs))
source.Delete(objs[idx].DeepCopy())
objs = append(objs[:idx], objs[idx+1:]...)
}
}
// sotp informer which stops workers. stopping informer first to exercise
// contention for informer while it is closing
informerCancel()
// wait for workers to finish since they may throw errors
wg.Wait()
}
func TestStateSharedInformer(t *testing.T) {
source := fcache.NewFakeControllerSource()
source.Add(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod1"}})
informer := NewSharedInformer(source, &v1.Pod{}, 1*time.Second).(*sharedIndexInformer)
listener := newTestListener("listener", 0, "pod1")
informer.AddEventHandlerWithResyncPeriod(listener, listener.resyncPeriod)
if isStarted(informer) {
t.Errorf("informer already started after creation")
return
}
if informer.IsStopped() {
t.Errorf("informer already stopped after creation")
return
}
stop := make(chan struct{})
go informer.Run(stop)
if !listener.ok() {
t.Errorf("informer did not report initial objects")
close(stop)
return
}
if !isStarted(informer) {
t.Errorf("informer does not report to be started although handling events")
close(stop)
return
}
if informer.IsStopped() {
t.Errorf("informer reports to be stopped although stop channel not closed")
close(stop)
return
}
close(stop)
fmt.Println("sleeping")
time.Sleep(1 * time.Second)
if !informer.IsStopped() {
t.Errorf("informer reports not to be stopped although stop channel closed")
return
}
if !isStarted(informer) {
t.Errorf("informer reports not to be started after it has been started and stopped")
return
}
}
func TestAddOnStoppedSharedInformer(t *testing.T) {
source := fcache.NewFakeControllerSource()
source.Add(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod1"}})
informer := NewSharedInformer(source, &v1.Pod{}, 1*time.Second).(*sharedIndexInformer)
listener := newTestListener("listener", 0, "pod1")
stop := make(chan struct{})
go informer.Run(stop)
close(stop)
err := wait.PollImmediate(100*time.Millisecond, 2*time.Second, func() (bool, error) {
if informer.IsStopped() {
return true, nil
}
return false, nil
})
if err != nil {
t.Errorf("informer reports not to be stopped although stop channel closed")
return
}
_, err = informer.AddEventHandlerWithResyncPeriod(listener, listener.resyncPeriod)
if err == nil {
t.Errorf("stopped informer did not reject add handler")
return
}
if !strings.HasSuffix(err.Error(), "was not added to shared informer because it has stopped already") {
t.Errorf("adding handler to a stopped informer yields unexpected error: %s", err)
return
}
}
func TestRemoveOnStoppedSharedInformer(t *testing.T) {
source := fcache.NewFakeControllerSource()
source.Add(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod1"}})
informer := NewSharedInformer(source, &v1.Pod{}, 1*time.Second).(*sharedIndexInformer)
listener := newTestListener("listener", 0, "pod1")
handle, err := informer.AddEventHandlerWithResyncPeriod(listener, listener.resyncPeriod)
if err != nil {
t.Errorf("informer did not add handler: %s", err)
return
}
stop := make(chan struct{})
go informer.Run(stop)
close(stop)
fmt.Println("sleeping")
time.Sleep(1 * time.Second)
if !informer.IsStopped() {
t.Errorf("informer reports not to be stopped although stop channel closed")
return
}
err = informer.RemoveEventHandler(handle)
if err != nil {
t.Errorf("informer does not remove handler on stopped informer")
return
}
}
func TestRemoveWhileActive(t *testing.T) {
// source simulates an apiserver object endpoint.
source := fcache.NewFakeControllerSource()
// create the shared informer and resync every 12 hours
informer := NewSharedInformer(source, &v1.Pod{}, 0).(*sharedIndexInformer)
listener := newTestListener("listener", 0, "pod1")
handle, _ := informer.AddEventHandler(listener)
stop := make(chan struct{})
defer close(stop)
go informer.Run(stop)
source.Add(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod1"}})
if !listener.ok() {
t.Errorf("event did not occur")
return
}
informer.RemoveEventHandler(handle)
if isRegistered(informer, handle) {
t.Errorf("handle is still active after successful remove")
return
}
source.Add(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod2"}})
if !listener.ok() {
t.Errorf("unexpected event occurred")
return
}
}
func TestAddWhileActive(t *testing.T) {
// source simulates an apiserver object endpoint.
source := fcache.NewFakeControllerSource()
// create the shared informer and resync every 12 hours
informer := NewSharedInformer(source, &v1.Pod{}, 0).(*sharedIndexInformer)
listener1 := newTestListener("originalListener", 0, "pod1")
listener2 := newTestListener("originalListener", 0, "pod1", "pod2")
handle1, _ := informer.AddEventHandler(listener1)
stop := make(chan struct{})
defer close(stop)
go informer.Run(stop)
source.Add(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod1"}})
if !listener1.ok() {
t.Errorf("events on listener1 did not occur")
return
}
handle2, _ := informer.AddEventHandler(listener2)
source.Add(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod2"}})
if !listener2.ok() {
t.Errorf("event on listener2 did not occur")
return
}
if !isRegistered(informer, handle1) {
t.Errorf("handle1 is not active")
return
}
if !isRegistered(informer, handle2) {
t.Errorf("handle2 is not active")
return
}
listener1.expectedItemNames = listener2.expectedItemNames
if !listener1.ok() {
t.Errorf("events on listener1 did not occur")
return
}
}

View File

@@ -20,6 +20,7 @@ import (
"encoding/base64"
"errors"
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
@@ -151,7 +152,7 @@ func FlattenContent(path *string, contents *[]byte, baseDir string) error {
var err error
absPath := ResolvePath(*path, baseDir)
*contents, err = os.ReadFile(absPath)
*contents, err = ioutil.ReadFile(absPath)
if err != nil {
return err
}

View File

@@ -18,6 +18,7 @@ package api
import (
"fmt"
"io/ioutil"
"os"
"reflect"
"testing"
@@ -26,13 +27,13 @@ import (
)
func newMergedConfig(certFile, certContent, keyFile, keyContent, caFile, caContent string, t *testing.T) Config {
if err := os.WriteFile(certFile, []byte(certContent), 0644); err != nil {
if err := ioutil.WriteFile(certFile, []byte(certContent), 0644); err != nil {
t.Errorf("unexpected error: %v", err)
}
if err := os.WriteFile(keyFile, []byte(keyContent), 0600); err != nil {
if err := ioutil.WriteFile(keyFile, []byte(keyContent), 0600); err != nil {
t.Errorf("unexpected error: %v", err)
}
if err := os.WriteFile(caFile, []byte(caContent), 0644); err != nil {
if err := ioutil.WriteFile(caFile, []byte(caContent), 0644); err != nil {
t.Errorf("unexpected error: %v", err)
}
@@ -51,11 +52,11 @@ func newMergedConfig(certFile, certContent, keyFile, keyContent, caFile, caConte
}
func TestMinifySuccess(t *testing.T) {
certFile, _ := os.CreateTemp("", "")
certFile, _ := ioutil.TempFile("", "")
defer os.Remove(certFile.Name())
keyFile, _ := os.CreateTemp("", "")
keyFile, _ := ioutil.TempFile("", "")
defer os.Remove(keyFile.Name())
caFile, _ := os.CreateTemp("", "")
caFile, _ := ioutil.TempFile("", "")
defer os.Remove(caFile.Name())
mutatingConfig := newMergedConfig(certFile.Name(), "cert", keyFile.Name(), "key", caFile.Name(), "ca", t)
@@ -87,11 +88,11 @@ func TestMinifySuccess(t *testing.T) {
}
func TestMinifyMissingContext(t *testing.T) {
certFile, _ := os.CreateTemp("", "")
certFile, _ := ioutil.TempFile("", "")
defer os.Remove(certFile.Name())
keyFile, _ := os.CreateTemp("", "")
keyFile, _ := ioutil.TempFile("", "")
defer os.Remove(keyFile.Name())
caFile, _ := os.CreateTemp("", "")
caFile, _ := ioutil.TempFile("", "")
defer os.Remove(caFile.Name())
mutatingConfig := newMergedConfig(certFile.Name(), "cert", keyFile.Name(), "key", caFile.Name(), "ca", t)
@@ -105,11 +106,11 @@ func TestMinifyMissingContext(t *testing.T) {
}
func TestMinifyMissingCluster(t *testing.T) {
certFile, _ := os.CreateTemp("", "")
certFile, _ := ioutil.TempFile("", "")
defer os.Remove(certFile.Name())
keyFile, _ := os.CreateTemp("", "")
keyFile, _ := ioutil.TempFile("", "")
defer os.Remove(keyFile.Name())
caFile, _ := os.CreateTemp("", "")
caFile, _ := ioutil.TempFile("", "")
defer os.Remove(caFile.Name())
mutatingConfig := newMergedConfig(certFile.Name(), "cert", keyFile.Name(), "key", caFile.Name(), "ca", t)
@@ -123,11 +124,11 @@ func TestMinifyMissingCluster(t *testing.T) {
}
func TestMinifyMissingAuthInfo(t *testing.T) {
certFile, _ := os.CreateTemp("", "")
certFile, _ := ioutil.TempFile("", "")
defer os.Remove(certFile.Name())
keyFile, _ := os.CreateTemp("", "")
keyFile, _ := ioutil.TempFile("", "")
defer os.Remove(keyFile.Name())
caFile, _ := os.CreateTemp("", "")
caFile, _ := ioutil.TempFile("", "")
defer os.Remove(caFile.Name())
mutatingConfig := newMergedConfig(certFile.Name(), "cert", keyFile.Name(), "key", caFile.Name(), "ca", t)
@@ -141,11 +142,11 @@ func TestMinifyMissingAuthInfo(t *testing.T) {
}
func TestFlattenSuccess(t *testing.T) {
certFile, _ := os.CreateTemp("", "")
certFile, _ := ioutil.TempFile("", "")
defer os.Remove(certFile.Name())
keyFile, _ := os.CreateTemp("", "")
keyFile, _ := ioutil.TempFile("", "")
defer os.Remove(keyFile.Name())
caFile, _ := os.CreateTemp("", "")
caFile, _ := ioutil.TempFile("", "")
defer os.Remove(caFile.Name())
certData := "cert"
@@ -206,11 +207,11 @@ func TestFlattenSuccess(t *testing.T) {
}
func Example_minifyAndShorten() {
certFile, _ := os.CreateTemp("", "")
certFile, _ := ioutil.TempFile("", "")
defer os.Remove(certFile.Name())
keyFile, _ := os.CreateTemp("", "")
keyFile, _ := ioutil.TempFile("", "")
defer os.Remove(keyFile.Name())
caFile, _ := os.CreateTemp("", "")
caFile, _ := ioutil.TempFile("", "")
defer os.Remove(caFile.Name())
certData := "cert"
@@ -246,11 +247,11 @@ func Example_minifyAndShorten() {
}
func TestShortenSuccess(t *testing.T) {
certFile, _ := os.CreateTemp("", "")
certFile, _ := ioutil.TempFile("", "")
defer os.Remove(certFile.Name())
keyFile, _ := os.CreateTemp("", "")
keyFile, _ := ioutil.TempFile("", "")
defer os.Remove(keyFile.Name())
caFile, _ := os.CreateTemp("", "")
caFile, _ := ioutil.TempFile("", "")
defer os.Remove(caFile.Name())
certData := "cert"

View File

@@ -93,11 +93,6 @@ type Cluster struct {
// attach, port forward).
// +optional
ProxyURL string `json:"proxy-url,omitempty"`
// DisableCompression allows client to opt-out of response compression for all requests to the server. This is useful
// to speed up requests (specifically lists) when client-server network bandwidth is ample, by saving time on
// compression (server-side) and decompression (client-side): https://github.com/kubernetes/kubernetes/issues/112296.
// +optional
DisableCompression bool `json:"disable-compression,omitempty"`
// Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields
// +optional
Extensions map[string]runtime.Object `json:"extensions,omitempty"`

View File

@@ -46,12 +46,10 @@ func Example_ofOptionsConfig() {
Server: "https://alfa.org:8080",
InsecureSkipTLSVerify: true,
CertificateAuthority: "path/to/my/cert-ca-filename",
DisableCompression: true,
}
defaultConfig.Clusters["bravo"] = &Cluster{
Server: "https://bravo.org:8080",
InsecureSkipTLSVerify: false,
DisableCompression: false,
}
defaultConfig.AuthInfos["white-mage-via-cert"] = &AuthInfo{
ClientCertificate: "path/to/my/client-cert-filename",
@@ -96,7 +94,6 @@ func Example_ofOptionsConfig() {
// alfa:
// LocationOfOrigin: ""
// certificate-authority: path/to/my/cert-ca-filename
// disable-compression: true
// insecure-skip-tls-verify: true
// server: https://alfa.org:8080
// bravo:

View File

@@ -86,11 +86,6 @@ type Cluster struct {
// attach, port forward).
// +optional
ProxyURL string `json:"proxy-url,omitempty"`
// DisableCompression allows client to opt-out of response compression for all requests to the server. This is useful
// to speed up requests (specifically lists) when client-server network bandwidth is ample, by saving time on
// compression (server-side) and decompression (client-side): https://github.com/kubernetes/kubernetes/issues/112296.
// +optional
DisableCompression bool `json:"disable-compression,omitempty"`
// Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields
// +optional
Extensions []NamedExtension `json:"extensions,omitempty"`

View File

@@ -257,7 +257,6 @@ func autoConvert_v1_Cluster_To_api_Cluster(in *Cluster, out *api.Cluster, s conv
out.CertificateAuthority = in.CertificateAuthority
out.CertificateAuthorityData = *(*[]byte)(unsafe.Pointer(&in.CertificateAuthorityData))
out.ProxyURL = in.ProxyURL
out.DisableCompression = in.DisableCompression
if err := Convert_Slice_v1_NamedExtension_To_Map_string_To_runtime_Object(&in.Extensions, &out.Extensions, s); err != nil {
return err
}
@@ -277,7 +276,6 @@ func autoConvert_api_Cluster_To_v1_Cluster(in *api.Cluster, out *Cluster, s conv
out.CertificateAuthority = in.CertificateAuthority
out.CertificateAuthorityData = *(*[]byte)(unsafe.Pointer(&in.CertificateAuthorityData))
out.ProxyURL = in.ProxyURL
out.DisableCompression = in.DisableCompression
if err := Convert_Map_string_To_runtime_Object_To_Slice_v1_NamedExtension(&in.Extensions, &out.Extensions, s); err != nil {
return err
}

View File

@@ -20,6 +20,7 @@ import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"os"
"golang.org/x/term"
@@ -58,7 +59,7 @@ func (a *PromptingAuthLoader) LoadAuth(path string) (*clientauth.Info, error) {
if err != nil {
return &auth, err
}
err = os.WriteFile(path, data, 0600)
err = ioutil.WriteFile(path, data, 0600)
return &auth, err
}
authPtr, err := clientauth.LoadFromFile(path)

View File

@@ -19,6 +19,7 @@ package clientcmd
import (
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"os"
@@ -164,8 +165,6 @@ func (config *DirectClientConfig) ClientConfig() (*restclient.Config, error) {
clientConfig.Proxy = http.ProxyURL(u)
}
clientConfig.DisableCompression = configClusterInfo.DisableCompression
if config.overrides != nil && len(config.overrides.Timeout) > 0 {
timeout, err := ParseTimeout(config.overrides.Timeout)
if err != nil {
@@ -247,7 +246,7 @@ func (config *DirectClientConfig) getUserIdentificationPartialConfig(configAuthI
mergedConfig.BearerToken = configAuthInfo.Token
mergedConfig.BearerTokenFile = configAuthInfo.TokenFile
} else if len(configAuthInfo.TokenFile) > 0 {
tokenBytes, err := os.ReadFile(configAuthInfo.TokenFile)
tokenBytes, err := ioutil.ReadFile(configAuthInfo.TokenFile)
if err != nil {
return nil, err
}
@@ -587,7 +586,7 @@ func (config *inClusterClientConfig) Namespace() (string, bool, error) {
}
// Fall back to the namespace associated with the service account token, if available
if data, err := os.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/namespace"); err == nil {
if data, err := ioutil.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/namespace"); err == nil {
if ns := strings.TrimSpace(string(data)); len(ns) > 0 {
return ns, false, nil
}

View File

@@ -17,6 +17,7 @@ limitations under the License.
package clientcmd
import (
"io/ioutil"
"os"
"reflect"
"strings"
@@ -138,22 +139,6 @@ func createCAValidTestConfig() *clientcmdapi.Config {
return config
}
func TestDisableCompression(t *testing.T) {
config := createValidTestConfig()
clientBuilder := NewNonInteractiveClientConfig(*config, "clean", &ConfigOverrides{
ClusterInfo: clientcmdapi.Cluster{
DisableCompression: true,
},
}, nil)
actualCfg, err := clientBuilder.ClientConfig()
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
matchBoolArg(true, actualCfg.DisableCompression, t)
}
func TestInsecureOverridesCA(t *testing.T) {
config := createCAValidTestConfig()
clientBuilder := NewNonInteractiveClientConfig(*config, "clean", &ConfigOverrides{
@@ -173,7 +158,7 @@ func TestInsecureOverridesCA(t *testing.T) {
}
func TestCAOverridesCAData(t *testing.T) {
file, err := os.CreateTemp("", "my.ca")
file, err := ioutil.TempFile("", "my.ca")
if err != nil {
t.Fatalf("could not create tempfile: %v", err)
}
@@ -308,7 +293,7 @@ func TestModifyContext(t *testing.T) {
"clean": true,
}
tempPath, err := os.CreateTemp("", "testclientcmd-")
tempPath, err := ioutil.TempFile("", "testclientcmd-")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
@@ -493,13 +478,13 @@ func TestBasicAuthData(t *testing.T) {
func TestBasicTokenFile(t *testing.T) {
token := "exampletoken"
f, err := os.CreateTemp("", "tokenfile")
f, err := ioutil.TempFile("", "tokenfile")
if err != nil {
t.Errorf("Unexpected error: %v", err)
return
}
defer os.Remove(f.Name())
if err := os.WriteFile(f.Name(), []byte(token), 0644); err != nil {
if err := ioutil.WriteFile(f.Name(), []byte(token), 0644); err != nil {
t.Errorf("Unexpected error: %v", err)
return
}
@@ -529,13 +514,13 @@ func TestBasicTokenFile(t *testing.T) {
func TestPrecedenceTokenFile(t *testing.T) {
token := "exampletoken"
f, err := os.CreateTemp("", "tokenfile")
f, err := ioutil.TempFile("", "tokenfile")
if err != nil {
t.Errorf("Unexpected error: %v", err)
return
}
defer os.Remove(f.Name())
if err := os.WriteFile(f.Name(), []byte(token), 0644); err != nil {
if err := ioutil.WriteFile(f.Name(), []byte(token), 0644); err != nil {
t.Errorf("Unexpected error: %v", err)
return
}
@@ -919,12 +904,12 @@ users:
command: foo-command
provideClusterInfo: true
`
tmpfile, err := os.CreateTemp("", "kubeconfig")
tmpfile, err := ioutil.TempFile("", "kubeconfig")
if err != nil {
t.Error(err)
}
defer os.Remove(tmpfile.Name())
if err := os.WriteFile(tmpfile.Name(), []byte(content), 0666); err != nil {
if err := ioutil.WriteFile(tmpfile.Name(), []byte(content), 0666); err != nil {
t.Error(err)
}
config, err := BuildConfigFromFlags("", tmpfile.Name())

View File

@@ -18,6 +18,7 @@ package clientcmd
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"reflect"
@@ -282,12 +283,12 @@ func (rules *ClientConfigLoadingRules) Migrate() error {
return fmt.Errorf("cannot migrate %v to %v because it is a directory", source, destination)
}
data, err := os.ReadFile(source)
data, err := ioutil.ReadFile(source)
if err != nil {
return err
}
// destination is created with mode 0666 before umask
err = os.WriteFile(destination, data, 0666)
err = ioutil.WriteFile(destination, data, 0666)
if err != nil {
return err
}
@@ -362,7 +363,7 @@ func (rules *ClientConfigLoadingRules) IsDefaultConfig(config *restclient.Config
// LoadFromFile takes a filename and deserializes the contents into Config object
func LoadFromFile(filename string) (*clientcmdapi.Config, error) {
kubeconfigBytes, err := os.ReadFile(filename)
kubeconfigBytes, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
@@ -428,7 +429,7 @@ func WriteToFile(config clientcmdapi.Config, filename string) error {
}
}
if err := os.WriteFile(filename, content, 0600); err != nil {
if err := ioutil.WriteFile(filename, content, 0600); err != nil {
return err
}
return nil

View File

@@ -19,6 +19,7 @@ package clientcmd
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
@@ -73,8 +74,8 @@ var (
"red-user": {Token: "a-different-red-token"},
"yellow-user": {Token: "yellow-token"}},
Clusters: map[string]*clientcmdapi.Cluster{
"cow-cluster": {Server: "http://a-different-cow.org:8080", InsecureSkipTLSVerify: true, DisableCompression: true},
"donkey-cluster": {Server: "http://donkey.org:8080", InsecureSkipTLSVerify: true, DisableCompression: true}},
"cow-cluster": {Server: "http://a-different-cow.org:8080", InsecureSkipTLSVerify: true},
"donkey-cluster": {Server: "http://donkey.org:8080", InsecureSkipTLSVerify: true}},
CurrentContext: "federal-context",
}
)
@@ -131,10 +132,10 @@ func TestToleratingMissingFiles(t *testing.T) {
}
func TestErrorReadingFile(t *testing.T) {
commandLineFile, _ := os.CreateTemp("", "")
commandLineFile, _ := ioutil.TempFile("", "")
defer os.Remove(commandLineFile.Name())
if err := os.WriteFile(commandLineFile.Name(), []byte("bogus value"), 0644); err != nil {
if err := ioutil.WriteFile(commandLineFile.Name(), []byte("bogus value"), 0644); err != nil {
t.Fatalf("Error creating tempfile: %v", err)
}
@@ -152,7 +153,7 @@ func TestErrorReadingFile(t *testing.T) {
}
func TestErrorReadingNonFile(t *testing.T) {
tmpdir, err := os.MkdirTemp("", "")
tmpdir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatalf("Couldn't create tmpdir")
}
@@ -172,9 +173,9 @@ func TestErrorReadingNonFile(t *testing.T) {
}
func TestConflictingCurrentContext(t *testing.T) {
commandLineFile, _ := os.CreateTemp("", "")
commandLineFile, _ := ioutil.TempFile("", "")
defer os.Remove(commandLineFile.Name())
envVarFile, _ := os.CreateTemp("", "")
envVarFile, _ := ioutil.TempFile("", "")
defer os.Remove(envVarFile.Name())
mockCommandLineConfig := clientcmdapi.Config{
@@ -253,7 +254,7 @@ users: null
}
func TestLoadingEmptyMaps(t *testing.T) {
configFile, _ := os.CreateTemp("", "")
configFile, _ := ioutil.TempFile("", "")
defer os.Remove(configFile.Name())
mockConfig := clientcmdapi.Config{
@@ -279,10 +280,10 @@ func TestLoadingEmptyMaps(t *testing.T) {
}
func TestDuplicateClusterName(t *testing.T) {
configFile, _ := os.CreateTemp("", "")
configFile, _ := ioutil.TempFile("", "")
defer os.Remove(configFile.Name())
err := os.WriteFile(configFile.Name(), []byte(`
err := ioutil.WriteFile(configFile.Name(), []byte(`
kind: Config
apiVersion: v1
clusters:
@@ -321,10 +322,10 @@ users:
}
func TestDuplicateContextName(t *testing.T) {
configFile, _ := os.CreateTemp("", "")
configFile, _ := ioutil.TempFile("", "")
defer os.Remove(configFile.Name())
err := os.WriteFile(configFile.Name(), []byte(`
err := ioutil.WriteFile(configFile.Name(), []byte(`
kind: Config
apiVersion: v1
clusters:
@@ -363,10 +364,10 @@ users:
}
func TestDuplicateUserName(t *testing.T) {
configFile, _ := os.CreateTemp("", "")
configFile, _ := ioutil.TempFile("", "")
defer os.Remove(configFile.Name())
err := os.WriteFile(configFile.Name(), []byte(`
err := ioutil.WriteFile(configFile.Name(), []byte(`
kind: Config
apiVersion: v1
clusters:
@@ -403,10 +404,10 @@ users:
}
func TestDuplicateExtensionName(t *testing.T) {
configFile, _ := os.CreateTemp("", "")
configFile, _ := ioutil.TempFile("", "")
defer os.Remove(configFile.Name())
err := os.WriteFile(configFile.Name(), []byte(`
err := ioutil.WriteFile(configFile.Name(), []byte(`
kind: Config
apiVersion: v1
clusters:
@@ -471,14 +472,14 @@ func TestResolveRelativePaths(t *testing.T) {
},
}
configDir1, _ := os.MkdirTemp("", "")
configDir1, _ := ioutil.TempDir("", "")
defer os.RemoveAll(configDir1)
configFile1 := path.Join(configDir1, ".kubeconfig")
configDir1, _ = filepath.Abs(configDir1)
configDir2, _ := os.MkdirTemp("", "")
configDir2, _ := ioutil.TempDir("", "")
defer os.RemoveAll(configDir2)
configDir2, _ = os.MkdirTemp(configDir2, "")
configDir2, _ = ioutil.TempDir(configDir2, "")
configFile2 := path.Join(configDir2, ".kubeconfig")
configDir2, _ = filepath.Abs(configDir2)
@@ -559,9 +560,9 @@ func TestResolveRelativePaths(t *testing.T) {
}
func TestMigratingFile(t *testing.T) {
sourceFile, _ := os.CreateTemp("", "")
sourceFile, _ := ioutil.TempFile("", "")
defer os.Remove(sourceFile.Name())
destinationFile, _ := os.CreateTemp("", "")
destinationFile, _ := ioutil.TempFile("", "")
// delete the file so that we'll write to it
os.Remove(destinationFile.Name())
@@ -578,11 +579,11 @@ func TestMigratingFile(t *testing.T) {
// the load should have recreated this file
defer os.Remove(destinationFile.Name())
sourceContent, err := os.ReadFile(sourceFile.Name())
sourceContent, err := ioutil.ReadFile(sourceFile.Name())
if err != nil {
t.Errorf("unexpected error %v", err)
}
destinationContent, err := os.ReadFile(destinationFile.Name())
destinationContent, err := ioutil.ReadFile(destinationFile.Name())
if err != nil {
t.Errorf("unexpected error %v", err)
}
@@ -593,9 +594,9 @@ func TestMigratingFile(t *testing.T) {
}
func TestMigratingFileLeaveExistingFileAlone(t *testing.T) {
sourceFile, _ := os.CreateTemp("", "")
sourceFile, _ := ioutil.TempFile("", "")
defer os.Remove(sourceFile.Name())
destinationFile, _ := os.CreateTemp("", "")
destinationFile, _ := ioutil.TempFile("", "")
defer os.Remove(destinationFile.Name())
WriteToFile(testConfigAlfa, sourceFile.Name())
@@ -608,7 +609,7 @@ func TestMigratingFileLeaveExistingFileAlone(t *testing.T) {
t.Errorf("unexpected error %v", err)
}
destinationContent, err := os.ReadFile(destinationFile.Name())
destinationContent, err := ioutil.ReadFile(destinationFile.Name())
if err != nil {
t.Errorf("unexpected error %v", err)
}
@@ -620,7 +621,7 @@ func TestMigratingFileLeaveExistingFileAlone(t *testing.T) {
func TestMigratingFileSourceMissingSkip(t *testing.T) {
sourceFilename := "some-missing-file"
destinationFile, _ := os.CreateTemp("", "")
destinationFile, _ := ioutil.TempFile("", "")
// delete the file so that we'll write to it
os.Remove(destinationFile.Name())
@@ -638,7 +639,7 @@ func TestMigratingFileSourceMissingSkip(t *testing.T) {
}
func TestFileLocking(t *testing.T) {
f, _ := os.CreateTemp("", "")
f, _ := ioutil.TempFile("", "")
defer os.Remove(f.Name())
err := lockFile(f.Name())
@@ -654,9 +655,9 @@ func TestFileLocking(t *testing.T) {
}
func Example_noMergingOnExplicitPaths() {
commandLineFile, _ := os.CreateTemp("", "")
commandLineFile, _ := ioutil.TempFile("", "")
defer os.Remove(commandLineFile.Name())
envVarFile, _ := os.CreateTemp("", "")
envVarFile, _ := ioutil.TempFile("", "")
defer os.Remove(envVarFile.Name())
WriteToFile(testConfigAlfa, commandLineFile.Name())
@@ -703,9 +704,9 @@ func Example_noMergingOnExplicitPaths() {
}
func Example_mergingSomeWithConflict() {
commandLineFile, _ := os.CreateTemp("", "")
commandLineFile, _ := ioutil.TempFile("", "")
defer os.Remove(commandLineFile.Name())
envVarFile, _ := os.CreateTemp("", "")
envVarFile, _ := ioutil.TempFile("", "")
defer os.Remove(envVarFile.Name())
WriteToFile(testConfigAlfa, commandLineFile.Name())
@@ -736,7 +737,6 @@ func Example_mergingSomeWithConflict() {
// server: http://cow.org:8080
// name: cow-cluster
// - cluster:
// disable-compression: true
// insecure-skip-tls-verify: true
// server: http://donkey.org:8080
// name: donkey-cluster
@@ -759,13 +759,13 @@ func Example_mergingSomeWithConflict() {
}
func Example_mergingEverythingNoConflicts() {
commandLineFile, _ := os.CreateTemp("", "")
commandLineFile, _ := ioutil.TempFile("", "")
defer os.Remove(commandLineFile.Name())
envVarFile, _ := os.CreateTemp("", "")
envVarFile, _ := ioutil.TempFile("", "")
defer os.Remove(envVarFile.Name())
currentDirFile, _ := os.CreateTemp("", "")
currentDirFile, _ := ioutil.TempFile("", "")
defer os.Remove(currentDirFile.Name())
homeDirFile, _ := os.CreateTemp("", "")
homeDirFile, _ := ioutil.TempFile("", "")
defer os.Remove(homeDirFile.Name())
WriteToFile(testConfigAlfa, commandLineFile.Name())

View File

@@ -17,13 +17,14 @@ limitations under the License.
package clientcmd
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
)
func TestMain(m *testing.M) {
tmp, err := os.MkdirTemp("", "testkubeconfig")
tmp, err := ioutil.TempDir("", "testkubeconfig")
if err != nil {
panic(err)
}

View File

@@ -74,7 +74,6 @@ type ClusterOverrideFlags struct {
InsecureSkipTLSVerify FlagInfo
TLSServerName FlagInfo
ProxyURL FlagInfo
DisableCompression FlagInfo
}
// FlagInfo contains information about how to register a flag. This struct is useful if you want to provide a way for an extender to
@@ -144,26 +143,25 @@ func (f FlagInfo) BindBoolFlag(flags *pflag.FlagSet, target *bool) FlagInfo {
}
const (
FlagClusterName = "cluster"
FlagAuthInfoName = "user"
FlagContext = "context"
FlagNamespace = "namespace"
FlagAPIServer = "server"
FlagTLSServerName = "tls-server-name"
FlagInsecure = "insecure-skip-tls-verify"
FlagCertFile = "client-certificate"
FlagKeyFile = "client-key"
FlagCAFile = "certificate-authority"
FlagEmbedCerts = "embed-certs"
FlagBearerToken = "token"
FlagImpersonate = "as"
FlagImpersonateUID = "as-uid"
FlagImpersonateGroup = "as-group"
FlagUsername = "username"
FlagPassword = "password"
FlagTimeout = "request-timeout"
FlagProxyURL = "proxy-url"
FlagDisableCompression = "disable-compression"
FlagClusterName = "cluster"
FlagAuthInfoName = "user"
FlagContext = "context"
FlagNamespace = "namespace"
FlagAPIServer = "server"
FlagTLSServerName = "tls-server-name"
FlagInsecure = "insecure-skip-tls-verify"
FlagCertFile = "client-certificate"
FlagKeyFile = "client-key"
FlagCAFile = "certificate-authority"
FlagEmbedCerts = "embed-certs"
FlagBearerToken = "token"
FlagImpersonate = "as"
FlagImpersonateUID = "as-uid"
FlagImpersonateGroup = "as-group"
FlagUsername = "username"
FlagPassword = "password"
FlagTimeout = "request-timeout"
FlagProxyURL = "proxy-url"
)
// RecommendedConfigOverrideFlags is a convenience method to return recommended flag names prefixed with a string of your choosing
@@ -200,7 +198,6 @@ func RecommendedClusterOverrideFlags(prefix string) ClusterOverrideFlags {
InsecureSkipTLSVerify: FlagInfo{prefix + FlagInsecure, "", "false", "If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure"},
TLSServerName: FlagInfo{prefix + FlagTLSServerName, "", "", "If provided, this name will be used to validate server certificate. If this is not provided, hostname used to contact the server is used."},
ProxyURL: FlagInfo{prefix + FlagProxyURL, "", "", "If provided, this URL will be used to connect via proxy"},
DisableCompression: FlagInfo{prefix + FlagDisableCompression, "", "", "If true, opt-out of response compression for all requests to the server"},
}
}
@@ -241,7 +238,6 @@ func BindClusterFlags(clusterInfo *clientcmdapi.Cluster, flags *pflag.FlagSet, f
flagNames.InsecureSkipTLSVerify.BindBoolFlag(flags, &clusterInfo.InsecureSkipTLSVerify)
flagNames.TLSServerName.BindStringFlag(flags, &clusterInfo.TLSServerName)
flagNames.ProxyURL.BindStringFlag(flags, &clusterInfo.ProxyURL)
flagNames.DisableCompression.BindBoolFlag(flags, &clusterInfo.DisableCompression)
}
// BindFlags is a convenience method to bind the specified flags to their associated variables

View File

@@ -19,6 +19,7 @@ package clientcmd
import (
"errors"
"fmt"
"io/ioutil"
"os"
"strings"
"testing"
@@ -295,7 +296,7 @@ func TestValidateCleanClusterInfo(t *testing.T) {
}
func TestValidateCleanWithCAClusterInfo(t *testing.T) {
tempFile, _ := os.CreateTemp("", "")
tempFile, _ := ioutil.TempFile("", "")
defer os.Remove(tempFile.Name())
config := clientcmdapi.NewConfig()
@@ -338,7 +339,7 @@ func TestValidateCertFilesNotFoundAuthInfo(t *testing.T) {
}
func TestValidateCertDataOverridesFiles(t *testing.T) {
tempFile, _ := os.CreateTemp("", "")
tempFile, _ := ioutil.TempFile("", "")
defer os.Remove(tempFile.Name())
config := clientcmdapi.NewConfig()
@@ -358,7 +359,7 @@ func TestValidateCertDataOverridesFiles(t *testing.T) {
}
func TestValidateCleanCertFilesAuthInfo(t *testing.T) {
tempFile, _ := os.CreateTemp("", "")
tempFile, _ := ioutil.TempFile("", "")
defer os.Remove(tempFile.Name())
config := clientcmdapi.NewConfig()

View File

@@ -1019,21 +1019,58 @@ func testReleaseOnCancellation(t *testing.T, objectType string) {
onRelease = make(chan struct{})
lockObj runtime.Object
gets int
updates int
wg sync.WaitGroup
)
resetVars := func() {
onNewLeader = make(chan struct{})
onRenewCalled = make(chan struct{})
onRenewResume = make(chan struct{})
onRelease = make(chan struct{})
lockObj = nil
gets = 0
updates = 0
resourceLockConfig := rl.ResourceLockConfig{
Identity: "baz",
EventRecorder: &record.FakeRecorder{},
}
c := &fake.Clientset{}
c.AddReactor("get", objectType, func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) {
if lockObj != nil {
return true, lockObj, nil
}
return true, nil, errors.NewNotFound(action.(fakeclient.GetAction).GetResource().GroupResource(), action.(fakeclient.GetAction).GetName())
})
// create lock
c.AddReactor("create", objectType, func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) {
lockObj = action.(fakeclient.CreateAction).GetObject()
return true, lockObj, nil
})
c.AddReactor("update", objectType, func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) {
updates++
// Second update (first renew) should return our canceled error
// FakeClient doesn't do anything with the context so we're doing this ourselves
if updates == 2 {
close(onRenewCalled)
<-onRenewResume
return true, nil, context.Canceled
} else if updates == 3 {
close(onRelease)
}
lockObj = action.(fakeclient.UpdateAction).GetObject()
return true, lockObj, nil
})
c.AddReactor("*", "*", func(action fakeclient.Action) (bool, runtime.Object, error) {
t.Errorf("unreachable action. testclient called too many times: %+v", action)
return true, nil, fmt.Errorf("unreachable action")
})
lock, err := rl.New(objectType, "foo", "bar", c.CoreV1(), c.CoordinationV1(), resourceLockConfig)
if err != nil {
t.Fatal("resourcelock.New() = ", err)
}
lec := LeaderElectionConfig{
Lock: lock,
LeaseDuration: 15 * time.Second,
RenewDeadline: 2 * time.Second,
RetryPeriod: 1 * time.Second,
@@ -1050,168 +1087,40 @@ func testReleaseOnCancellation(t *testing.T, objectType string) {
},
}
tests := []struct {
name string
reactors []Reactor
}{
{
name: "release acquired lock on cancellation of update",
reactors: []Reactor{
{
verb: "get",
objectType: objectType,
reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) {
gets++
if lockObj != nil {
return true, lockObj, nil
}
return true, nil, errors.NewNotFound(action.(fakeclient.GetAction).GetResource().GroupResource(), action.(fakeclient.GetAction).GetName())
},
},
{
verb: "create",
objectType: objectType,
reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) {
lockObj = action.(fakeclient.CreateAction).GetObject()
return true, lockObj, nil
},
},
{
verb: "update",
objectType: objectType,
reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) {
updates++
// Second update (first renew) should return our canceled error
// FakeClient doesn't do anything with the context so we're doing this ourselves
if updates == 2 {
close(onRenewCalled)
<-onRenewResume
return true, nil, context.Canceled
} else if updates == 3 {
// We update the lock after the cancellation to release it
// This wg is to avoid the data race on lockObj
defer wg.Done()
close(onRelease)
}
lockObj = action.(fakeclient.UpdateAction).GetObject()
return true, lockObj, nil
},
},
},
},
{
name: "release acquired lock on cancellation of get",
reactors: []Reactor{
{
verb: "get",
objectType: objectType,
reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) {
gets++
if lockObj != nil {
// Third and more get (first create, second renew) should return our canceled error
// FakeClient doesn't do anything with the context so we're doing this ourselves
if gets >= 3 {
close(onRenewCalled)
<-onRenewResume
return true, nil, context.Canceled
}
return true, lockObj, nil
}
return true, nil, errors.NewNotFound(action.(fakeclient.GetAction).GetResource().GroupResource(), action.(fakeclient.GetAction).GetName())
},
},
{
verb: "create",
objectType: objectType,
reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) {
lockObj = action.(fakeclient.CreateAction).GetObject()
return true, lockObj, nil
},
},
{
verb: "update",
objectType: objectType,
reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) {
updates++
// Second update (first renew) should release the lock
if updates == 2 {
// We update the lock after the cancellation to release it
// This wg is to avoid the data race on lockObj
defer wg.Done()
close(onRelease)
}
lockObj = action.(fakeclient.UpdateAction).GetObject()
return true, lockObj, nil
},
},
},
},
elector, err := NewLeaderElector(lec)
if err != nil {
t.Fatal("Failed to create leader elector: ", err)
}
for i := range tests {
test := &tests[i]
t.Run(test.name, func(t *testing.T) {
wg.Add(1)
resetVars()
ctx, cancel := context.WithCancel(context.Background())
resourceLockConfig := rl.ResourceLockConfig{
Identity: "baz",
EventRecorder: &record.FakeRecorder{},
}
c := &fake.Clientset{}
for _, reactor := range test.reactors {
c.AddReactor(reactor.verb, objectType, reactor.reaction)
}
c.AddReactor("*", "*", func(action fakeclient.Action) (bool, runtime.Object, error) {
t.Errorf("unreachable action. testclient called too many times: %+v", action)
return true, nil, fmt.Errorf("unreachable action")
})
lock, err := rl.New(objectType, "foo", "bar", c.CoreV1(), c.CoordinationV1(), resourceLockConfig)
if err != nil {
t.Fatal("resourcelock.New() = ", err)
}
go elector.Run(ctx)
lec.Lock = lock
elector, err := NewLeaderElector(lec)
if err != nil {
t.Fatal("Failed to create leader elector: ", err)
}
// Wait for us to become the leader
select {
case <-onNewLeader:
case <-time.After(10 * time.Second):
t.Fatal("failed to become the leader")
}
ctx, cancel := context.WithCancel(context.Background())
// Wait for renew (update) to be invoked
select {
case <-onRenewCalled:
case <-time.After(10 * time.Second):
t.Fatal("the elector failed to renew the lock")
}
go elector.Run(ctx)
// Cancel the context - stopping the elector while
// it's running
cancel()
// Wait for us to become the leader
select {
case <-onNewLeader:
case <-time.After(10 * time.Second):
t.Fatal("failed to become the leader")
}
// Resume the update call to return the cancellation
// which should trigger the release flow
close(onRenewResume)
// Wait for renew (update) to be invoked
select {
case <-onRenewCalled:
case <-time.After(10 * time.Second):
t.Fatal("the elector failed to renew the lock")
}
// Cancel the context - stopping the elector while
// it's running
cancel()
// Resume the tryAcquireOrRenew call to return the cancellation
// which should trigger the release flow
close(onRenewResume)
select {
case <-onRelease:
case <-time.After(10 * time.Second):
t.Fatal("the lock was not released")
}
wg.Wait()
})
select {
case <-onRelease:
case <-time.After(10 * time.Second):
t.Fatal("the lock was not released")
}
}

View File

@@ -44,11 +44,11 @@ type configMapLock struct {
// Get returns the election record from a ConfigMap Annotation
func (cml *configMapLock) Get(ctx context.Context) (*LeaderElectionRecord, []byte, error) {
var record LeaderElectionRecord
cm, err := cml.Client.ConfigMaps(cml.ConfigMapMeta.Namespace).Get(ctx, cml.ConfigMapMeta.Name, metav1.GetOptions{})
var err error
cml.cm, err = cml.Client.ConfigMaps(cml.ConfigMapMeta.Namespace).Get(ctx, cml.ConfigMapMeta.Name, metav1.GetOptions{})
if err != nil {
return nil, nil, err
}
cml.cm = cm
if cml.cm.Annotations == nil {
cml.cm.Annotations = make(map[string]string)
}

View File

@@ -39,11 +39,11 @@ type endpointsLock struct {
// Get returns the election record from a Endpoints Annotation
func (el *endpointsLock) Get(ctx context.Context) (*LeaderElectionRecord, []byte, error) {
var record LeaderElectionRecord
ep, err := el.Client.Endpoints(el.EndpointsMeta.Namespace).Get(ctx, el.EndpointsMeta.Name, metav1.GetOptions{})
var err error
el.e, err = el.Client.Endpoints(el.EndpointsMeta.Namespace).Get(ctx, el.EndpointsMeta.Name, metav1.GetOptions{})
if err != nil {
return nil, nil, err
}
el.e = ep
if el.e.Annotations == nil {
el.e.Annotations = make(map[string]string)
}

View File

@@ -39,11 +39,11 @@ type LeaseLock struct {
// Get returns the election record from a Lease spec
func (ll *LeaseLock) Get(ctx context.Context) (*LeaderElectionRecord, []byte, error) {
lease, err := ll.Client.Leases(ll.LeaseMeta.Namespace).Get(ctx, ll.LeaseMeta.Name, metav1.GetOptions{})
var err error
ll.lease, err = ll.Client.Leases(ll.LeaseMeta.Namespace).Get(ctx, ll.LeaseMeta.Name, metav1.GetOptions{})
if err != nil {
return nil, nil, err
}
ll.lease = lease
record := LeaseSpecToLeaderElectionRecord(&ll.lease.Spec)
recordByte, err := json.Marshal(*record)
if err != nil {

View File

@@ -203,11 +203,6 @@ func (p *ListPager) eachListChunkBuffered(ctx context.Context, options metav1.Li
}()
for o := range chunkC {
select {
case <-ctx.Done():
return ctx.Err()
default:
}
err := fn(o)
if err != nil {
return err // any fn error should be returned immediately

View File

@@ -301,10 +301,16 @@ func TestListPager_EachListItem(t *testing.T) {
{
name: "cancel context while processing",
fields: fields{PageSize: 10, PageFn: (&testPager{t: t, expectPage: 10, remaining: 51, rv: "rv:20"}).PagedList},
want: list(10, "rv:20"), // The whole PageSize worth of items got returned.
want: list(3, "rv:20"), // all the items <= the one the processor returned an error on should have been visited
wantErr: true,
cancelContextOnItem: 3,
},
{
name: "panic processing item",
fields: fields{PageSize: 10, PageFn: (&testPager{t: t, expectPage: 10, remaining: 51, rv: "rv:20"}).PagedList},
want: list(3, "rv:20"), // all the items <= the one the processor returned an error on should have been visited
wantPanic: true,
},
}
processorErr := fmt.Errorf("processor error")
@@ -339,7 +345,8 @@ func TestListPager_EachListItem(t *testing.T) {
err = p.EachListItem(ctx, metav1.ListOptions{}, fn)
}()
if (panic != nil) && !tt.wantPanic {
t.Errorf(".EachListItem() panic = %v, wantPanic %v", panic, tt.wantPanic)
t.Fatalf(".EachListItem() panic = %v, wantPanic %v", panic, tt.wantPanic)
} else {
return
}
if (err != nil) != tt.wantErr {

View File

@@ -20,6 +20,7 @@ import (
"errors"
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
"sort"
@@ -347,11 +348,10 @@ func (pf *PortForwarder) handleConnection(conn net.Conn, port ForwardedPort) {
}
// we're not writing to this stream
errorStream.Close()
defer pf.streamConn.RemoveStreams(errorStream)
errorChan := make(chan error)
go func() {
message, err := io.ReadAll(errorStream)
message, err := ioutil.ReadAll(errorStream)
switch {
case err != nil:
errorChan <- fmt.Errorf("error reading from error stream for port %d -> %d: %v", port.Local, port.Remote, err)
@@ -368,7 +368,6 @@ func (pf *PortForwarder) handleConnection(conn net.Conn, port ForwardedPort) {
runtime.HandleError(fmt.Errorf("error creating forwarding stream for port %d -> %d: %v", port.Local, port.Remote, err))
return
}
defer pf.streamConn.RemoveStreams(dataStream)
localError := make(chan struct{})
remoteDone := make(chan struct{})

View File

@@ -51,7 +51,6 @@ type fakeConnection struct {
closeChan chan bool
dataStream *fakeStream
errorStream *fakeStream
streamCount int
}
func newFakeConnection() *fakeConnection {
@@ -65,10 +64,8 @@ func newFakeConnection() *fakeConnection {
func (c *fakeConnection) CreateStream(headers http.Header) (httpstream.Stream, error) {
switch headers.Get(v1.StreamType) {
case v1.StreamTypeData:
c.streamCount++
return c.dataStream, nil
case v1.StreamTypeError:
c.streamCount++
return c.errorStream, nil
default:
return nil, fmt.Errorf("fakeStream creation not supported for stream type %s", headers.Get(v1.StreamType))
@@ -87,10 +84,7 @@ func (c *fakeConnection) CloseChan() <-chan bool {
return c.closeChan
}
func (c *fakeConnection) RemoveStreams(streams ...httpstream.Stream) {
for range streams {
c.streamCount--
}
func (c *fakeConnection) RemoveStreams(_ ...httpstream.Stream) {
}
func (c *fakeConnection) SetIdleTimeout(timeout time.Duration) {
@@ -510,7 +504,7 @@ func TestHandleConnection(t *testing.T) {
// Test handleConnection
pf.handleConnection(localConnection, ForwardedPort{Local: 1111, Remote: 2222})
assert.Equal(t, 0, remoteConnection.streamCount, "stream count should be zero")
assert.Equal(t, "test data from local", remoteDataReceived.String())
assert.Equal(t, "test data from remote", localConnection.receiveBuffer.String())
assert.Equal(t, "Handling connection for 1111\n", out.String())
@@ -544,7 +538,6 @@ func TestHandleConnectionSendsRemoteError(t *testing.T) {
// Test handleConnection, using go-routine because it needs to be able to write to unbuffered pf.errorChan
pf.handleConnection(localConnection, ForwardedPort{Local: 1111, Remote: 2222})
assert.Equal(t, 0, remoteConnection.streamCount, "stream count should be zero")
assert.Equal(t, "", remoteDataReceived.String())
assert.Equal(t, "", localConnection.receiveBuffer.String())
assert.Equal(t, "Handling connection for 1111\n", out.String())

View File

@@ -19,6 +19,7 @@ package remotecommand
import (
"fmt"
"io"
"io/ioutil"
"k8s.io/apimachinery/pkg/util/runtime"
)
@@ -38,7 +39,7 @@ func watchErrorStream(errorStream io.Reader, d errorStreamDecoder) chan error {
go func() {
defer runtime.HandleCrash()
message, err := io.ReadAll(errorStream)
message, err := ioutil.ReadAll(errorStream)
switch {
case err != nil && err != io.EOF:
errorChan <- fmt.Errorf("error reading from error stream: %s", err)

View File

@@ -20,6 +20,7 @@ import (
"encoding/json"
"errors"
"io"
"io/ioutil"
v1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -71,7 +72,7 @@ func fakeMassiveDataAttacher(stdin io.Reader, stdout, stderr io.WriteCloser, tty
}
go func() {
io.Copy(io.Discard, stdin)
io.Copy(ioutil.Discard, stdin)
copyDone <- struct{}{}
}()

View File

@@ -19,6 +19,7 @@ package remotecommand
import (
"fmt"
"io"
"io/ioutil"
"net/http"
"k8s.io/api/core/v1"
@@ -110,7 +111,7 @@ func (p *streamProtocolV1) stream(conn streamCreator) error {
// always read from errorStream
go func() {
message, err := io.ReadAll(p.errorStream)
message, err := ioutil.ReadAll(p.errorStream)
if err != nil && err != io.EOF {
errorChan <- fmt.Errorf("Error reading from error stream: %s", err)
return

View File

@@ -19,6 +19,7 @@ package remotecommand
import (
"fmt"
"io"
"io/ioutil"
"net/http"
"sync"
@@ -125,7 +126,7 @@ func (p *streamProtocolV2) copyStdin() {
// this "copy" doesn't actually read anything - it's just here to wait for
// the server to close remoteStdin.
if _, err := io.Copy(io.Discard, p.remoteStdin); err != nil {
if _, err := io.Copy(ioutil.Discard, p.remoteStdin); err != nil {
runtime.HandleError(err)
}
}()
@@ -144,7 +145,7 @@ func (p *streamProtocolV2) copyStdout(wg *sync.WaitGroup) {
// make sure, packet in queue can be consumed.
// block in queue may lead to deadlock in conn.server
// issue: https://github.com/kubernetes/kubernetes/issues/96339
defer io.Copy(io.Discard, p.remoteStdout)
defer io.Copy(ioutil.Discard, p.remoteStdout)
if _, err := io.Copy(p.Stdout, p.remoteStdout); err != nil {
runtime.HandleError(err)
@@ -161,7 +162,7 @@ func (p *streamProtocolV2) copyStderr(wg *sync.WaitGroup) {
go func() {
defer runtime.HandleCrash()
defer wg.Done()
defer io.Copy(io.Discard, p.remoteStderr)
defer io.Copy(ioutil.Discard, p.remoteStderr)
if _, err := io.Copy(p.Stderr, p.remoteStderr); err != nil {
runtime.HandleError(err)

View File

@@ -17,7 +17,6 @@ limitations under the License.
package transport
import (
"context"
"fmt"
"net"
"net/http"
@@ -56,9 +55,6 @@ type tlsCacheKey struct {
serverName string
nextProtos string
disableCompression bool
// these functions are wrapped to allow them to be used as map keys
getCert *GetCertHolder
dial *DialHolder
}
func (t tlsCacheKey) String() string {
@@ -66,8 +62,7 @@ func (t tlsCacheKey) String() string {
if len(t.keyData) > 0 {
keyText = "<redacted>"
}
return fmt.Sprintf("insecure:%v, caData:%#v, certData:%#v, keyData:%s, serverName:%s, disableCompression:%t, getCert:%p, dial:%p",
t.insecure, t.caData, t.certData, keyText, t.serverName, t.disableCompression, t.getCert, t.dial)
return fmt.Sprintf("insecure:%v, caData:%#v, certData:%#v, keyData:%s, serverName:%s, disableCompression:%t", t.insecure, t.caData, t.certData, keyText, t.serverName, t.disableCompression)
}
func (c *tlsTransportCache) get(config *Config) (http.RoundTripper, error) {
@@ -93,14 +88,12 @@ func (c *tlsTransportCache) get(config *Config) (http.RoundTripper, error) {
return nil, err
}
// The options didn't require a custom TLS config
if tlsConfig == nil && config.DialHolder == nil && config.Proxy == nil {
if tlsConfig == nil && config.Dial == nil && config.Proxy == nil {
return http.DefaultTransport, nil
}
var dial func(ctx context.Context, network, address string) (net.Conn, error)
if config.DialHolder != nil {
dial = config.DialHolder.Dial
} else {
dial := config.Dial
if dial == nil {
dial = (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
@@ -145,7 +138,7 @@ func tlsConfigKey(c *Config) (tlsCacheKey, bool, error) {
return tlsCacheKey{}, false, err
}
if c.Proxy != nil {
if c.TLS.GetCert != nil || c.Dial != nil || c.Proxy != nil {
// cannot determine equality for functions
return tlsCacheKey{}, false, nil
}
@@ -156,8 +149,6 @@ func tlsConfigKey(c *Config) (tlsCacheKey, bool, error) {
serverName: c.TLS.ServerName,
nextProtos: strings.Join(c.TLS.NextProtos, ","),
disableCompression: c.DisableCompression,
getCert: c.TLS.GetCertHolder,
dial: c.DialHolder,
}
if c.TLS.ReloadTLSFiles {

View File

@@ -21,8 +21,6 @@ import (
"crypto/tls"
"net"
"net/http"
"net/url"
"reflect"
"testing"
)
@@ -60,21 +58,16 @@ func TestTLSConfigKey(t *testing.T) {
t.Errorf("Expected identical cache keys for %q and %q, got:\n\t%s\n\t%s", nameA, nameB, keyA, keyB)
continue
}
if keyA != (tlsCacheKey{}) {
t.Errorf("Expected empty cache keys for %q and %q, got:\n\t%s\n\t%s", nameA, nameB, keyA, keyB)
continue
}
}
}
// Make sure config fields that affect the tls config affect the cache key
dialer := net.Dialer{}
getCert := &GetCertHolder{GetCert: func() (*tls.Certificate, error) { return nil, nil }}
getCert := func() (*tls.Certificate, error) { return nil, nil }
uniqueConfigurations := map[string]*Config{
"proxy": {Proxy: func(request *http.Request) (*url.URL, error) { return nil, nil }},
"no tls": {},
"dialer": {DialHolder: &DialHolder{Dial: dialer.DialContext}},
"dialer2": {DialHolder: &DialHolder{Dial: func(ctx context.Context, network, address string) (net.Conn, error) { return nil, nil }}},
"dialer": {Dial: dialer.DialContext},
"dialer2": {Dial: func(ctx context.Context, network, address string) (net.Conn, error) { return nil, nil }},
"insecure": {TLS: TLSConfig{Insecure: true}},
"cadata 1": {TLS: TLSConfig{CAData: []byte{1}}},
"cadata 2": {TLS: TLSConfig{CAData: []byte{2}}},
@@ -125,20 +118,20 @@ func TestTLSConfigKey(t *testing.T) {
},
"getCert1": {
TLS: TLSConfig{
KeyData: []byte{1},
GetCertHolder: getCert,
KeyData: []byte{1},
GetCert: getCert,
},
},
"getCert2": {
TLS: TLSConfig{
KeyData: []byte{1},
GetCertHolder: &GetCertHolder{GetCert: func() (*tls.Certificate, error) { return nil, nil }},
KeyData: []byte{1},
GetCert: func() (*tls.Certificate, error) { return nil, nil },
},
},
"getCert1, key 2": {
TLS: TLSConfig{
KeyData: []byte{2},
GetCertHolder: getCert,
KeyData: []byte{2},
GetCert: getCert,
},
},
"http2, http1.1": {TLS: TLSConfig{NextProtos: []string{"h2", "http/1.1"}}},
@@ -157,17 +150,6 @@ func TestTLSConfigKey(t *testing.T) {
continue
}
shouldCacheA := valueA.Proxy == nil
if shouldCacheA != canCacheA {
t.Errorf("Unexpected canCache=false for " + nameA)
}
configIsNotEmpty := !reflect.DeepEqual(*valueA, Config{})
if keyA == (tlsCacheKey{}) && shouldCacheA && configIsNotEmpty {
t.Errorf("Expected non-empty cache keys for %q and %q, got:\n\t%s\n\t%s", nameA, nameB, keyA, keyB)
continue
}
// Make sure we get the same key on the same config
if nameA == nameB {
if keyA != keyB {

View File

@@ -67,9 +67,8 @@ type Config struct {
// instead of setting this value directly.
WrapTransport WrapperFunc
// DialHolder specifies the dial function for creating unencrypted TCP connections.
// This struct indirection is used to make transport configs cacheable.
DialHolder *DialHolder
// Dial specifies the dial function for creating unencrypted TCP connections.
Dial func(ctx context.Context, network, address string) (net.Conn, error)
// Proxy is the proxy func to be used for all requests made by this
// transport. If Proxy is nil, http.ProxyFromEnvironment is used. If Proxy
@@ -79,11 +78,6 @@ type Config struct {
Proxy func(*http.Request) (*url.URL, error)
}
// DialHolder is used to make the wrapped function comparable so that it can be used as a map key.
type DialHolder struct {
Dial func(ctx context.Context, network, address string) (net.Conn, error)
}
// ImpersonationConfig has all the available impersonation options
type ImpersonationConfig struct {
// UserName matches user.Info.GetName()
@@ -118,7 +112,7 @@ func (c *Config) HasCertAuth() bool {
// HasCertCallback returns whether the configuration has certificate callback or not.
func (c *Config) HasCertCallback() bool {
return c.TLS.GetCertHolder != nil
return c.TLS.GetCert != nil
}
// Wrap adds a transport middleware function that will give the caller
@@ -149,12 +143,5 @@ type TLSConfig struct {
// To use only http/1.1, set to ["http/1.1"].
NextProtos []string
// Callback that returns a TLS client certificate. CertData, CertFile, KeyData and KeyFile supercede this field.
// This struct indirection is used to make transport configs cacheable.
GetCertHolder *GetCertHolder
}
// GetCertHolder is used to make the wrapped function comparable so that it can be used as a map key.
type GetCertHolder struct {
GetCert func() (*tls.Certificate, error)
GetCert func() (*tls.Certificate, error) // Callback that returns a TLS client certificate. CertData, CertFile, KeyData and KeyFile supercede this field.
}

View File

@@ -18,8 +18,8 @@ package transport
import (
"fmt"
"io/ioutil"
"net/http"
"os"
"strings"
"sync"
"time"
@@ -132,7 +132,7 @@ type fileTokenSource struct {
var _ = oauth2.TokenSource(&fileTokenSource{})
func (ts *fileTokenSource) Token() (*oauth2.Token, error) {
tokb, err := os.ReadFile(ts.path)
tokb, err := ioutil.ReadFile(ts.path)
if err != nil {
return nil, fmt.Errorf("failed to read token file %q: %v", ts.path, err)
}

View File

@@ -22,8 +22,8 @@ import (
"crypto/x509"
"encoding/pem"
"fmt"
"io/ioutil"
"net/http"
"os"
"sync"
"time"
@@ -39,10 +39,6 @@ func New(config *Config) (http.RoundTripper, error) {
return nil, fmt.Errorf("using a custom transport with TLS certificate options or the insecure flag is not allowed")
}
if !isValidHolders(config) {
return nil, fmt.Errorf("misconfigured holder for dialer or cert callback")
}
var (
rt http.RoundTripper
err error
@@ -60,18 +56,6 @@ func New(config *Config) (http.RoundTripper, error) {
return HTTPWrappersForConfig(config, rt)
}
func isValidHolders(config *Config) bool {
if config.TLS.GetCertHolder != nil && config.TLS.GetCertHolder.GetCert == nil {
return false
}
if config.DialHolder != nil && config.DialHolder.Dial == nil {
return false
}
return true
}
// TLSConfigFor returns a tls.Config that will provide the transport level security defined
// by the provided Config. Will return nil if no transport level security is requested.
func TLSConfigFor(c *Config) (*tls.Config, error) {
@@ -132,7 +116,7 @@ func TLSConfigFor(c *Config) (*tls.Config, error) {
return dynamicCertLoader()
}
if c.HasCertCallback() {
cert, err := c.TLS.GetCertHolder.GetCert()
cert, err := c.TLS.GetCert()
if err != nil {
return nil, err
}
@@ -173,7 +157,10 @@ func loadTLSFiles(c *Config) error {
}
c.TLS.KeyData, err = dataFromSliceOrFile(c.TLS.KeyData, c.TLS.KeyFile)
return err
if err != nil {
return err
}
return nil
}
// dataFromSliceOrFile returns data from the slice (if non-empty), or from the file,
@@ -183,7 +170,7 @@ func dataFromSliceOrFile(data []byte, file string) ([]byte, error) {
return data, nil
}
if len(file) > 0 {
fileData, err := os.ReadFile(file)
fileData, err := ioutil.ReadFile(file)
if err != nil {
return []byte{}, err
}

View File

@@ -21,7 +21,6 @@ import (
"crypto/tls"
"errors"
"fmt"
"net"
"net/http"
"testing"
)
@@ -95,13 +94,6 @@ stR0Yiw0buV6DL/moUO0HIM9Bjh96HJp+LxiIS6UCdIhMPp5HoQa
)
func TestNew(t *testing.T) {
globalGetCert := &GetCertHolder{
GetCert: func() (*tls.Certificate, error) { return nil, nil },
}
globalDial := &DialHolder{
Dial: func(ctx context.Context, network, address string) (net.Conn, error) { return nil, nil },
}
testCases := map[string]struct {
Config *Config
Err bool
@@ -217,11 +209,9 @@ func TestNew(t *testing.T) {
Config: &Config{
TLS: TLSConfig{
CAData: []byte(rootCACert),
GetCertHolder: &GetCertHolder{
GetCert: func() (*tls.Certificate, error) {
crt, err := tls.X509KeyPair([]byte(certData), []byte(keyData))
return &crt, err
},
GetCert: func() (*tls.Certificate, error) {
crt, err := tls.X509KeyPair([]byte(certData), []byte(keyData))
return &crt, err
},
},
},
@@ -233,10 +223,8 @@ func TestNew(t *testing.T) {
Config: &Config{
TLS: TLSConfig{
CAData: []byte(rootCACert),
GetCertHolder: &GetCertHolder{
GetCert: func() (*tls.Certificate, error) {
return nil, errors.New("GetCert failure")
},
GetCert: func() (*tls.Certificate, error) {
return nil, errors.New("GetCert failure")
},
},
},
@@ -247,10 +235,8 @@ func TestNew(t *testing.T) {
Config: &Config{
TLS: TLSConfig{
CAData: []byte(rootCACert),
GetCertHolder: &GetCertHolder{
GetCert: func() (*tls.Certificate, error) {
return nil, nil
},
GetCert: func() (*tls.Certificate, error) {
return nil, nil
},
CertData: []byte(certData),
KeyData: []byte(keyData),
@@ -263,96 +249,12 @@ func TestNew(t *testing.T) {
Config: &Config{
TLS: TLSConfig{
CAData: []byte(rootCACert),
GetCertHolder: &GetCertHolder{
GetCert: func() (*tls.Certificate, error) {
return nil, nil
},
GetCert: func() (*tls.Certificate, error) {
return nil, nil
},
},
},
},
"nil holders": {
Config: &Config{
TLS: TLSConfig{
GetCertHolder: nil,
},
DialHolder: nil,
},
Err: false,
TLS: false,
TLSCert: false,
TLSErr: false,
Default: true,
Insecure: false,
DefaultRoots: false,
},
"non-nil dial holder and nil internal": {
Config: &Config{
TLS: TLSConfig{
GetCertHolder: nil,
},
DialHolder: &DialHolder{},
},
Err: true,
},
"non-nil cert holder and nil internal": {
Config: &Config{
TLS: TLSConfig{
GetCertHolder: &GetCertHolder{},
},
DialHolder: nil,
},
Err: true,
},
"non-nil dial holder+internal": {
Config: &Config{
TLS: TLSConfig{
GetCertHolder: nil,
},
DialHolder: &DialHolder{
Dial: func(ctx context.Context, network, address string) (net.Conn, error) { return nil, nil },
},
},
Err: false,
TLS: true,
TLSCert: false,
TLSErr: false,
Default: false,
Insecure: false,
DefaultRoots: true,
},
"non-nil cert holder+internal": {
Config: &Config{
TLS: TLSConfig{
GetCertHolder: &GetCertHolder{
GetCert: func() (*tls.Certificate, error) { return nil, nil },
},
},
DialHolder: nil,
},
Err: false,
TLS: true,
TLSCert: true,
TLSErr: false,
Default: false,
Insecure: false,
DefaultRoots: true,
},
"non-nil holders+internal with global address": {
Config: &Config{
TLS: TLSConfig{
GetCertHolder: globalGetCert,
},
DialHolder: globalDial,
},
Err: false,
TLS: true,
TLSCert: true,
TLSErr: false,
Default: false,
Insecure: false,
DefaultRoots: true,
},
}
for k, testCase := range testCases {
t.Run(k, func(t *testing.T) {

View File

@@ -25,9 +25,9 @@ import (
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"io/ioutil"
"math/big"
"net"
"os"
"path/filepath"
"strings"
"time"
@@ -101,9 +101,9 @@ func GenerateSelfSignedCertKeyWithFixtures(host string, alternateIPs []net.IP, a
certFixturePath := filepath.Join(fixtureDirectory, baseName+".crt")
keyFixturePath := filepath.Join(fixtureDirectory, baseName+".key")
if len(fixtureDirectory) > 0 {
cert, err := os.ReadFile(certFixturePath)
cert, err := ioutil.ReadFile(certFixturePath)
if err == nil {
key, err := os.ReadFile(keyFixturePath)
key, err := ioutil.ReadFile(keyFixturePath)
if err == nil {
return cert, key, nil
}
@@ -188,10 +188,10 @@ func GenerateSelfSignedCertKeyWithFixtures(host string, alternateIPs []net.IP, a
}
if len(fixtureDirectory) > 0 {
if err := os.WriteFile(certFixturePath, certBuffer.Bytes(), 0644); err != nil {
if err := ioutil.WriteFile(certFixturePath, certBuffer.Bytes(), 0644); err != nil {
return nil, nil, fmt.Errorf("failed to write cert fixture to %s: %v", certFixturePath, err)
}
if err := os.WriteFile(keyFixturePath, keyBuffer.Bytes(), 0644); err != nil {
if err := ioutil.WriteFile(keyFixturePath, keyBuffer.Bytes(), 0644); err != nil {
return nil, nil, fmt.Errorf("failed to write key fixture to %s: %v", certFixturePath, err)
}
}

View File

@@ -20,8 +20,8 @@ import (
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"io/ioutil"
"net"
"os"
"testing"
"k8s.io/client-go/util/keyutil"
@@ -36,7 +36,7 @@ func TestMakeCSR(t *testing.T) {
dnsSANs := []string{"localhost"}
ipSANs := []net.IP{netutils.ParseIPSloppy("127.0.0.1")}
keyData, err := os.ReadFile(keyFile)
keyData, err := ioutil.ReadFile(keyFile)
if err != nil {
t.Fatal(err)
}

View File

@@ -19,6 +19,7 @@ package cert
import (
"crypto/x509"
"fmt"
"io/ioutil"
"os"
"path/filepath"
)
@@ -65,13 +66,13 @@ func WriteCert(certPath string, data []byte) error {
if err := os.MkdirAll(filepath.Dir(certPath), os.FileMode(0755)); err != nil {
return err
}
return os.WriteFile(certPath, data, os.FileMode(0644))
return ioutil.WriteFile(certPath, data, os.FileMode(0644))
}
// NewPool returns an x509.CertPool containing the certificates in the given PEM-encoded file.
// Returns an error if the file could not be read, a certificate could not be parsed, or if the file does not contain any certificates
func NewPool(filename string) (*x509.CertPool, error) {
pemBlock, err := os.ReadFile(filename)
pemBlock, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
@@ -100,7 +101,7 @@ func NewPoolFromBytes(pemBlock []byte) (*x509.CertPool, error) {
// CertsFromFile returns the x509.Certificates contained in the given PEM-encoded file.
// Returns an error if the file could not be read, a certificate could not be parsed, or if the file does not contain any certificates
func CertsFromFile(file string) ([]*x509.Certificate, error) {
pemBlock, err := os.ReadFile(file)
pemBlock, err := ioutil.ReadFile(file)
if err != nil {
return nil, err
}

View File

@@ -18,13 +18,14 @@ package certificate
import (
"bytes"
"io/ioutil"
"os"
"path/filepath"
"testing"
)
func TestUpdateSymlinkExistingFileError(t *testing.T) {
dir, err := os.MkdirTemp("", "k8s-test-update-symlink")
dir, err := ioutil.TempDir("", "k8s-test-update-symlink")
if err != nil {
t.Fatalf("Unable to create the test directory %q: %v", dir, err)
}
@@ -34,7 +35,7 @@ func TestUpdateSymlinkExistingFileError(t *testing.T) {
}
}()
pairFile := filepath.Join(dir, "kubelet-current.pem")
if err := os.WriteFile(pairFile, nil, 0600); err != nil {
if err := ioutil.WriteFile(pairFile, nil, 0600); err != nil {
t.Fatalf("Unable to create the file %q: %v", pairFile, err)
}
@@ -48,7 +49,7 @@ func TestUpdateSymlinkExistingFileError(t *testing.T) {
}
func TestUpdateSymlinkNewFileNotExist(t *testing.T) {
dir, err := os.MkdirTemp("", "k8s-test-update-symlink")
dir, err := ioutil.TempDir("", "k8s-test-update-symlink")
if err != nil {
t.Fatalf("Unable to create the test directory %q: %v", dir, err)
}
@@ -58,7 +59,7 @@ func TestUpdateSymlinkNewFileNotExist(t *testing.T) {
}
}()
oldPairFile := filepath.Join(dir, "kubelet-oldpair.pem")
if err := os.WriteFile(oldPairFile, nil, 0600); err != nil {
if err := ioutil.WriteFile(oldPairFile, nil, 0600); err != nil {
t.Fatalf("Unable to create the file %q: %v", oldPairFile, err)
}
@@ -88,7 +89,7 @@ func TestUpdateSymlinkNewFileNotExist(t *testing.T) {
}
func TestUpdateSymlinkNoSymlink(t *testing.T) {
dir, err := os.MkdirTemp("", "k8s-test-update-symlink")
dir, err := ioutil.TempDir("", "k8s-test-update-symlink")
if err != nil {
t.Fatalf("Unable to create the test directory %q: %v", dir, err)
}
@@ -98,7 +99,7 @@ func TestUpdateSymlinkNoSymlink(t *testing.T) {
}
}()
pairFile := filepath.Join(dir, "kubelet-newfile.pem")
if err := os.WriteFile(pairFile, nil, 0600); err != nil {
if err := ioutil.WriteFile(pairFile, nil, 0600); err != nil {
t.Fatalf("Unable to create the file %q: %v", pairFile, err)
}
@@ -123,7 +124,7 @@ func TestUpdateSymlinkNoSymlink(t *testing.T) {
func TestUpdateSymlinkReplaceExistingSymlink(t *testing.T) {
prefix := "kubelet"
dir, err := os.MkdirTemp("", "k8s-test-update-symlink")
dir, err := ioutil.TempDir("", "k8s-test-update-symlink")
if err != nil {
t.Fatalf("Unable to create the test directory %q: %v", dir, err)
}
@@ -133,11 +134,11 @@ func TestUpdateSymlinkReplaceExistingSymlink(t *testing.T) {
}
}()
oldPairFile := filepath.Join(dir, prefix+"-oldfile.pem")
if err := os.WriteFile(oldPairFile, nil, 0600); err != nil {
if err := ioutil.WriteFile(oldPairFile, nil, 0600); err != nil {
t.Fatalf("Unable to create the file %q: %v", oldPairFile, err)
}
newPairFile := filepath.Join(dir, prefix+"-newfile.pem")
if err := os.WriteFile(newPairFile, nil, 0600); err != nil {
if err := ioutil.WriteFile(newPairFile, nil, 0600); err != nil {
t.Fatalf("Unable to create the file %q: %v", newPairFile, err)
}
currentPairFile := filepath.Join(dir, prefix+"-current.pem")
@@ -177,7 +178,7 @@ func TestUpdateSymlinkReplaceExistingSymlink(t *testing.T) {
}
func TestLoadFile(t *testing.T) {
dir, err := os.MkdirTemp("", "k8s-test-load-cert-key-blocks")
dir, err := ioutil.TempDir("", "k8s-test-load-cert-key-blocks")
if err != nil {
t.Fatalf("Unable to create the test directory %q: %v", dir, err)
}
@@ -198,7 +199,7 @@ func TestLoadFile(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
if err := os.WriteFile(pairFile, tt.data, 0600); err != nil {
if err := ioutil.WriteFile(pairFile, tt.data, 0600); err != nil {
t.Fatalf("Unable to create the file %q: %v", pairFile, err)
}
cert, err := loadFile(pairFile)
@@ -217,7 +218,7 @@ func TestLoadFile(t *testing.T) {
func TestUpdateNoRotation(t *testing.T) {
prefix := "kubelet-server"
dir, err := os.MkdirTemp("", "k8s-test-certstore-current")
dir, err := ioutil.TempDir("", "k8s-test-certstore-current")
if err != nil {
t.Fatalf("Unable to create the test directory %q: %v", dir, err)
}
@@ -227,11 +228,11 @@ func TestUpdateNoRotation(t *testing.T) {
}
}()
keyFile := filepath.Join(dir, "kubelet.key")
if err := os.WriteFile(keyFile, storeCertData.keyPEM, 0600); err != nil {
if err := ioutil.WriteFile(keyFile, storeCertData.keyPEM, 0600); err != nil {
t.Fatalf("Unable to create the file %q: %v", keyFile, err)
}
certFile := filepath.Join(dir, "kubelet.crt")
if err := os.WriteFile(certFile, storeCertData.certificatePEM, 0600); err != nil {
if err := ioutil.WriteFile(certFile, storeCertData.certificatePEM, 0600); err != nil {
t.Fatalf("Unable to create the file %q: %v", certFile, err)
}
@@ -251,7 +252,7 @@ func TestUpdateNoRotation(t *testing.T) {
func TestUpdateRotation(t *testing.T) {
prefix := "kubelet-server"
dir, err := os.MkdirTemp("", "k8s-test-certstore-current")
dir, err := ioutil.TempDir("", "k8s-test-certstore-current")
if err != nil {
t.Fatalf("Unable to create the test directory %q: %v", dir, err)
}
@@ -261,11 +262,11 @@ func TestUpdateRotation(t *testing.T) {
}
}()
keyFile := filepath.Join(dir, "kubelet.key")
if err := os.WriteFile(keyFile, storeCertData.keyPEM, 0600); err != nil {
if err := ioutil.WriteFile(keyFile, storeCertData.keyPEM, 0600); err != nil {
t.Fatalf("Unable to create the file %q: %v", keyFile, err)
}
certFile := filepath.Join(dir, "kubelet.crt")
if err := os.WriteFile(certFile, storeCertData.certificatePEM, 0600); err != nil {
if err := ioutil.WriteFile(certFile, storeCertData.certificatePEM, 0600); err != nil {
t.Fatalf("Unable to create the file %q: %v", certFile, err)
}
@@ -285,7 +286,7 @@ func TestUpdateRotation(t *testing.T) {
func TestUpdateTwoCerts(t *testing.T) {
prefix := "kubelet-server"
dir, err := os.MkdirTemp("", "k8s-test-certstore-current")
dir, err := ioutil.TempDir("", "k8s-test-certstore-current")
if err != nil {
t.Fatalf("Unable to create the test directory %q: %v", dir, err)
}
@@ -295,11 +296,11 @@ func TestUpdateTwoCerts(t *testing.T) {
}
}()
keyFile := filepath.Join(dir, "kubelet.key")
if err := os.WriteFile(keyFile, storeTwoCertsData.keyPEM, 0600); err != nil {
if err := ioutil.WriteFile(keyFile, storeTwoCertsData.keyPEM, 0600); err != nil {
t.Fatalf("Unable to create the file %q: %v", keyFile, err)
}
certFile := filepath.Join(dir, "kubelet.crt")
if err := os.WriteFile(certFile, storeTwoCertsData.certificatePEM, 0600); err != nil {
if err := ioutil.WriteFile(certFile, storeTwoCertsData.certificatePEM, 0600); err != nil {
t.Fatalf("Unable to create the file %q: %v", certFile, err)
}
@@ -322,7 +323,7 @@ func TestUpdateTwoCerts(t *testing.T) {
func TestUpdateWithBadCertKeyData(t *testing.T) {
prefix := "kubelet-server"
dir, err := os.MkdirTemp("", "k8s-test-certstore-current")
dir, err := ioutil.TempDir("", "k8s-test-certstore-current")
if err != nil {
t.Fatalf("Unable to create the test directory %q: %v", dir, err)
}
@@ -332,11 +333,11 @@ func TestUpdateWithBadCertKeyData(t *testing.T) {
}
}()
keyFile := filepath.Join(dir, "kubelet.key")
if err := os.WriteFile(keyFile, storeCertData.keyPEM, 0600); err != nil {
if err := ioutil.WriteFile(keyFile, storeCertData.keyPEM, 0600); err != nil {
t.Fatalf("Unable to create the file %q: %v", keyFile, err)
}
certFile := filepath.Join(dir, "kubelet.crt")
if err := os.WriteFile(certFile, storeCertData.certificatePEM, 0600); err != nil {
if err := ioutil.WriteFile(certFile, storeCertData.certificatePEM, 0600); err != nil {
t.Fatalf("Unable to create the file %q: %v", certFile, err)
}
@@ -356,7 +357,7 @@ func TestUpdateWithBadCertKeyData(t *testing.T) {
func TestCurrentPairFile(t *testing.T) {
prefix := "kubelet-server"
dir, err := os.MkdirTemp("", "k8s-test-certstore-current")
dir, err := ioutil.TempDir("", "k8s-test-certstore-current")
if err != nil {
t.Fatalf("Unable to create the test directory %q: %v", dir, err)
}
@@ -368,7 +369,7 @@ func TestCurrentPairFile(t *testing.T) {
pairFile := filepath.Join(dir, prefix+"-pair.pem")
data := append(storeCertData.certificatePEM, []byte("\n")...)
data = append(data, storeCertData.keyPEM...)
if err := os.WriteFile(pairFile, data, 0600); err != nil {
if err := ioutil.WriteFile(pairFile, data, 0600); err != nil {
t.Fatalf("Unable to create the file %q: %v", pairFile, err)
}
currentFile := filepath.Join(dir, prefix+"-current.pem")
@@ -395,7 +396,7 @@ func TestCurrentPairFile(t *testing.T) {
func TestCurrentCertKeyFiles(t *testing.T) {
prefix := "kubelet-server"
dir, err := os.MkdirTemp("", "k8s-test-certstore-current")
dir, err := ioutil.TempDir("", "k8s-test-certstore-current")
if err != nil {
t.Fatalf("Unable to create the test directory %q: %v", dir, err)
}
@@ -405,11 +406,11 @@ func TestCurrentCertKeyFiles(t *testing.T) {
}
}()
certFile := filepath.Join(dir, "kubelet.crt")
if err := os.WriteFile(certFile, storeCertData.certificatePEM, 0600); err != nil {
if err := ioutil.WriteFile(certFile, storeCertData.certificatePEM, 0600); err != nil {
t.Fatalf("Unable to create the file %q: %v", certFile, err)
}
keyFile := filepath.Join(dir, "kubelet.key")
if err := os.WriteFile(keyFile, storeCertData.keyPEM, 0600); err != nil {
if err := ioutil.WriteFile(keyFile, storeCertData.keyPEM, 0600); err != nil {
t.Fatalf("Unable to create the file %q: %v", keyFile, err)
}
@@ -432,7 +433,7 @@ func TestCurrentCertKeyFiles(t *testing.T) {
func TestCurrentTwoCerts(t *testing.T) {
prefix := "kubelet-server"
dir, err := os.MkdirTemp("", "k8s-test-certstore-current")
dir, err := ioutil.TempDir("", "k8s-test-certstore-current")
if err != nil {
t.Fatalf("Unable to create the test directory %q: %v", dir, err)
}
@@ -442,11 +443,11 @@ func TestCurrentTwoCerts(t *testing.T) {
}
}()
certFile := filepath.Join(dir, "kubelet.crt")
if err := os.WriteFile(certFile, storeTwoCertsData.certificatePEM, 0600); err != nil {
if err := ioutil.WriteFile(certFile, storeTwoCertsData.certificatePEM, 0600); err != nil {
t.Fatalf("Unable to create the file %q: %v", certFile, err)
}
keyFile := filepath.Join(dir, "kubelet.key")
if err := os.WriteFile(keyFile, storeTwoCertsData.keyPEM, 0600); err != nil {
if err := ioutil.WriteFile(keyFile, storeTwoCertsData.keyPEM, 0600); err != nil {
t.Fatalf("Unable to create the file %q: %v", keyFile, err)
}
@@ -471,7 +472,7 @@ func TestCurrentTwoCerts(t *testing.T) {
}
func TestCurrentNoFiles(t *testing.T) {
dir, err := os.MkdirTemp("", "k8s-test-certstore-current")
dir, err := ioutil.TempDir("", "k8s-test-certstore-current")
if err != nil {
t.Fatalf("Unable to create the test directory %q: %v", dir, err)
}

View File

@@ -1,213 +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 csaupgrade
import (
"bytes"
"fmt"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
)
const csaAnnotationName = "kubectl.kubernetes.io/last-applied-configuration"
var csaAnnotationFieldSet = fieldpath.NewSet(fieldpath.MakePathOrDie("metadata", "annotations", csaAnnotationName))
// Upgrades the Manager information for fields managed with client-side-apply (CSA)
// Prepares fields owned by `csaManager` for 'Update' operations for use now
// with the given `ssaManager` for `Apply` operations.
//
// This transformation should be performed on an object if it has been previously
// managed using client-side-apply to prepare it for future use with
// server-side-apply.
//
// Caveats:
// 1. This operation is not reversible. Information about which fields the client
// owned will be lost in this operation.
// 2. Supports being performed either before or after initial server-side apply.
// 3. Client-side apply tends to own more fields (including fields that are defaulted),
// this will possibly remove this defaults, they will be re-defaulted, that's fine.
// 4. Care must be taken to not overwrite the managed fields on the server if they
// have changed before sending a patch.
//
// obj - Target of the operation which has been managed with CSA in the past
// csaManagerName - Name of FieldManager formerly used for `Update` operations
// ssaManagerName - Name of FieldManager formerly used for `Apply` operations
func UpgradeManagedFields(
obj runtime.Object,
csaManagerName string,
ssaManagerName string,
) error {
accessor, err := meta.Accessor(obj)
if err != nil {
return fmt.Errorf("error accessing object metadata: %w", err)
}
// Create managed fields clone since we modify the values
var managedFields []metav1.ManagedFieldsEntry
managedFields = append(managedFields, accessor.GetManagedFields()...)
// Locate SSA manager
replaceIndex, managerExists := findFirstIndex(managedFields,
func(entry metav1.ManagedFieldsEntry) bool {
return entry.Manager == ssaManagerName &&
entry.Operation == metav1.ManagedFieldsOperationApply &&
entry.Subresource == ""
})
if !managerExists {
// SSA manager does not exist. Find the most recent matching CSA manager,
// convert it to an SSA manager.
//
// (find first index, since managed fields are sorted so that most recent is
// first in the list)
replaceIndex, managerExists = findFirstIndex(managedFields,
func(entry metav1.ManagedFieldsEntry) bool {
return entry.Manager == csaManagerName &&
entry.Operation == metav1.ManagedFieldsOperationUpdate &&
entry.Subresource == ""
})
if !managerExists {
// There are no CSA managers that need to be converted. Nothing to do
// Return early
return nil
}
// Convert CSA manager into SSA manager
managedFields[replaceIndex].Operation = metav1.ManagedFieldsOperationApply
managedFields[replaceIndex].Manager = ssaManagerName
}
err = unionManagerIntoIndex(managedFields, replaceIndex, csaManagerName)
if err != nil {
return err
}
// Create version of managed fields which has no CSA managers with the given name
filteredManagers := filter(managedFields, func(entry metav1.ManagedFieldsEntry) bool {
return !(entry.Manager == csaManagerName &&
entry.Operation == metav1.ManagedFieldsOperationUpdate &&
entry.Subresource == "")
})
// Wipe out last-applied-configuration annotation if it exists
annotations := accessor.GetAnnotations()
delete(annotations, csaAnnotationName)
// Commit changes to object
accessor.SetAnnotations(annotations)
accessor.SetManagedFields(filteredManagers)
return nil
}
// Locates an Update manager entry named `csaManagerName` with the same APIVersion
// as the manager at the targetIndex. Unions both manager's fields together
// into the manager specified by `targetIndex`. No other managers are modified.
func unionManagerIntoIndex(entries []metav1.ManagedFieldsEntry, targetIndex int, csaManagerName string) error {
ssaManager := entries[targetIndex]
// find Update manager of same APIVersion, union ssa fields with it.
// discard all other Update managers of the same name
csaManagerIndex, csaManagerExists := findFirstIndex(entries,
func(entry metav1.ManagedFieldsEntry) bool {
return entry.Manager == csaManagerName &&
entry.Operation == metav1.ManagedFieldsOperationUpdate &&
entry.Subresource == "" &&
entry.APIVersion == ssaManager.APIVersion
})
targetFieldSet, err := decodeManagedFieldsEntrySet(ssaManager)
if err != nil {
return fmt.Errorf("failed to convert fields to set: %w", err)
}
combinedFieldSet := &targetFieldSet
// Union the csa manager with the existing SSA manager. Do nothing if
// there was no good candidate found
if csaManagerExists {
csaManager := entries[csaManagerIndex]
csaFieldSet, err := decodeManagedFieldsEntrySet(csaManager)
if err != nil {
return fmt.Errorf("failed to convert fields to set: %w", err)
}
combinedFieldSet = combinedFieldSet.Union(&csaFieldSet)
}
// Ensure that the resultant fieldset does not include the
// last applied annotation
combinedFieldSet = combinedFieldSet.Difference(csaAnnotationFieldSet)
// Encode the fields back to the serialized format
err = encodeManagedFieldsEntrySet(&entries[targetIndex], *combinedFieldSet)
if err != nil {
return fmt.Errorf("failed to encode field set: %w", err)
}
return nil
}
func findFirstIndex[T any](
collection []T,
predicate func(T) bool,
) (int, bool) {
for idx, entry := range collection {
if predicate(entry) {
return idx, true
}
}
return -1, false
}
func filter[T any](
collection []T,
predicate func(T) bool,
) []T {
result := make([]T, 0, len(collection))
for _, value := range collection {
if predicate(value) {
result = append(result, value)
}
}
if len(result) == 0 {
return nil
}
return result
}
// Included from fieldmanager.internal to avoid dependency cycle
// FieldsToSet creates a set paths from an input trie of fields
func decodeManagedFieldsEntrySet(f metav1.ManagedFieldsEntry) (s fieldpath.Set, err error) {
err = s.FromJSON(bytes.NewReader(f.FieldsV1.Raw))
return s, err
}
// SetToFields creates a trie of fields from an input set of paths
func encodeManagedFieldsEntrySet(f *metav1.ManagedFieldsEntry, s fieldpath.Set) (err error) {
f.FieldsV1.Raw, err = s.ToJSON()
return err
}

View File

@@ -1,482 +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 csaupgrade_test
import (
"reflect"
"testing"
"github.com/google/go-cmp/cmp"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/util/yaml"
"k8s.io/client-go/util/csaupgrade"
)
func TestUpgradeCSA(t *testing.T) {
cases := []struct {
Name string
CSAManager string
SSAManager string
OriginalObject []byte
ExpectedObject []byte
}{
{
// Case where there is a CSA entry with the given name, but no SSA entry
// is found. Expect that the CSA entry is converted to an SSA entry
// and renamed.
Name: "csa-basic-direct-conversion",
CSAManager: "kubectl-client-side-apply",
SSAManager: "kubectl",
OriginalObject: []byte(`
apiVersion: v1
data: {}
kind: ConfigMap
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","data":{"key":"value","legacy":"unused"},"kind":"ConfigMap","metadata":{"annotations":{},"name":"test","namespace":"default"}}
creationTimestamp: "2022-08-22T23:08:23Z"
managedFields:
- apiVersion: v1
fieldsType: FieldsV1
fieldsV1:
f:data:
.: {}
f:key: {}
f:legacy: {}
f:metadata:
f:annotations:
.: {}
f:kubectl.kubernetes.io/last-applied-configuration: {}
manager: kubectl-client-side-apply
operation: Update
time: "2022-08-22T23:08:23Z"
name: test
namespace: default
`),
ExpectedObject: []byte(`
apiVersion: v1
data: {}
kind: ConfigMap
metadata:
annotations: {}
creationTimestamp: "2022-08-22T23:08:23Z"
managedFields:
- apiVersion: v1
fieldsType: FieldsV1
fieldsV1:
f:data:
.: {}
f:key: {}
f:legacy: {}
f:metadata:
f:annotations: {}
manager: kubectl
operation: Apply
time: "2022-08-22T23:08:23Z"
name: test
namespace: default
`),
},
{
// This is the case when kubectl --server-side is used for the first time
// Server creates duplicate managed fields entry - one for Update and another
// for Apply. Expect entries to be merged into one entry, which is unchanged
// from initial SSA.
Name: "csa-combine-with-ssa-duplicate-keys",
CSAManager: "kubectl-client-side-apply",
SSAManager: "kubectl",
OriginalObject: []byte(`
apiVersion: v1
data: {}
kind: ConfigMap
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","data":{"key":"value","legacy":"unused"},"kind":"ConfigMap","metadata":{"annotations":{},"name":"test","namespace":"default"}}
creationTimestamp: "2022-08-22T23:08:23Z"
managedFields:
- apiVersion: v1
fieldsType: FieldsV1
fieldsV1:
f:data:
.: {}
f:key: {}
f:legacy: {}
f:metadata:
f:annotations:
.: {}
f:kubectl.kubernetes.io/last-applied-configuration: {}
manager: kubectl
operation: Apply
time: "2022-08-23T23:08:23Z"
- apiVersion: v1
fieldsType: FieldsV1
fieldsV1:
f:data:
.: {}
f:key: {}
f:legacy: {}
f:metadata:
f:annotations:
.: {}
f:kubectl.kubernetes.io/last-applied-configuration: {}
manager: kubectl-client-side-apply
operation: Update
time: "2022-08-22T23:08:23Z"
name: test
namespace: default
`),
ExpectedObject: []byte(`
apiVersion: v1
data: {}
kind: ConfigMap
metadata:
annotations: {}
creationTimestamp: "2022-08-22T23:08:23Z"
managedFields:
- apiVersion: v1
fieldsType: FieldsV1
fieldsV1:
f:data:
.: {}
f:key: {}
f:legacy: {}
f:metadata:
f:annotations: {}
manager: kubectl
operation: Apply
time: "2022-08-23T23:08:23Z"
name: test
namespace: default
`),
},
{
// This is the case when kubectl --server-side is used for the first time,
// but then a key is removed. A bug would take place where key is left in
// CSA entry but no longer present in SSA entry, so it would not be pruned.
// This shows that upgrading such an object results in correct behavior next
// time SSA applier
// Expect final object to have unioned keys from both entries
Name: "csa-combine-with-ssa-additional-keys",
CSAManager: "kubectl-client-side-apply",
SSAManager: "kubectl",
OriginalObject: []byte(`
apiVersion: v1
data: {}
kind: ConfigMap
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","data":{"key":"value","legacy":"unused"},"kind":"ConfigMap","metadata":{"annotations":{},"name":"test","namespace":"default"}}
creationTimestamp: "2022-08-22T23:08:23Z"
managedFields:
- apiVersion: v1
fieldsType: FieldsV1
fieldsV1:
f:data:
.: {}
f:key: {}
f:metadata:
f:annotations:
.: {}
f:kubectl.kubernetes.io/last-applied-configuration: {}
manager: kubectl
operation: Apply
time: "2022-08-23T23:08:23Z"
- apiVersion: v1
fieldsType: FieldsV1
fieldsV1:
f:data:
.: {}
f:key: {}
f:legacy: {}
f:metadata:
f:annotations:
.: {}
f:kubectl.kubernetes.io/last-applied-configuration: {}
manager: kubectl-client-side-apply
operation: Update
time: "2022-08-22T23:08:23Z"
name: test
namespace: default
`),
ExpectedObject: []byte(`
apiVersion: v1
data: {}
kind: ConfigMap
metadata:
annotations: {}
creationTimestamp: "2022-08-22T23:08:23Z"
managedFields:
- apiVersion: v1
fieldsType: FieldsV1
fieldsV1:
f:data:
.: {}
f:key: {}
f:legacy: {}
f:metadata:
f:annotations: {}
manager: kubectl
operation: Apply
time: "2022-08-23T23:08:23Z"
name: test
namespace: default
`),
},
{
// Case when there are multiple CSA versions on the object which do not
// match the version from the apply entry. Shows they are tossed away
// without being merged.
Name: "csa-no-applicable-version",
CSAManager: "kubectl-client-side-apply",
SSAManager: "kubectl",
OriginalObject: []byte(`
apiVersion: v1
data: {}
kind: ConfigMap
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","data":{"key":"value","legacy":"unused"},"kind":"ConfigMap","metadata":{"annotations":{},"name":"test","namespace":"default"}}
creationTimestamp: "2022-08-22T23:08:23Z"
managedFields:
- apiVersion: v5
fieldsType: FieldsV1
fieldsV1:
f:data:
.: {}
f:key: {}
f:legacy: {}
f:metadata:
f:annotations:
.: {}
f:kubectl.kubernetes.io/last-applied-configuration: {}
manager: kubectl
operation: Apply
time: "2022-08-23T23:08:23Z"
- apiVersion: v1
fieldsType: FieldsV1
fieldsV1:
f:data:
f:key2: {}
f:metadata:
f:annotations:
f:hello2: {}
manager: kubectl-client-side-apply
operation: Update
time: "2022-08-22T23:08:23Z"
- apiVersion: v2
fieldsType: FieldsV1
fieldsV1:
f:data:
f:key3: {}
f:metadata:
f:annotations:
f:hello3: {}
manager: kubectl-client-side-apply
operation: Update
time: "2022-08-22T23:08:23Z"
- apiVersion: v3
fieldsType: FieldsV1
fieldsV1:
f:data:
f:key4: {}
f:metadata:
f:annotations:
f:hello3: {}
manager: kubectl-client-side-apply
operation: Update
time: "2022-08-22T23:08:23Z"
- apiVersion: v4
fieldsType: FieldsV1
fieldsV1:
f:data:
f:key5: {}
f:metadata:
f:annotations:
f:hello4: {}
manager: kubectl-client-side-apply
operation: Update
time: "2022-08-22T23:08:23Z"
name: test
namespace: default
`),
ExpectedObject: []byte(`
apiVersion: v1
data: {}
kind: ConfigMap
metadata:
annotations: {}
creationTimestamp: "2022-08-22T23:08:23Z"
managedFields:
- apiVersion: v5
fieldsType: FieldsV1
fieldsV1:
f:data:
.: {}
f:key: {}
f:legacy: {}
f:metadata:
f:annotations: {}
manager: kubectl
operation: Apply
time: "2022-08-23T23:08:23Z"
name: test
namespace: default
`),
},
{
// Case when there are multiple CSA versions on the object which do not
// match the version from the apply entry, and one which does.
// Shows that CSA entry with matching version is unioned into the SSA entry.
Name: "csa-single-applicable-version",
CSAManager: "kubectl-client-side-apply",
SSAManager: "kubectl",
OriginalObject: []byte(`
apiVersion: v1
data: {}
kind: ConfigMap
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","data":{"key":"value","legacy":"unused"},"kind":"ConfigMap","metadata":{"annotations":{},"name":"test","namespace":"default"}}
creationTimestamp: "2022-08-22T23:08:23Z"
managedFields:
- apiVersion: v5
fieldsType: FieldsV1
fieldsV1:
f:data:
.: {}
f:key: {}
f:legacy: {}
f:metadata:
f:annotations:
.: {}
f:kubectl.kubernetes.io/last-applied-configuration: {}
manager: kubectl
operation: Apply
time: "2022-08-23T23:08:23Z"
- apiVersion: v5
fieldsType: FieldsV1
fieldsV1:
f:data:
f:key2: {}
f:metadata:
f:annotations:
f:hello2: {}
manager: kubectl-client-side-apply
operation: Update
time: "2022-08-22T23:08:23Z"
- apiVersion: v2
fieldsType: FieldsV1
fieldsV1:
f:data:
f:key3: {}
f:metadata:
f:annotations:
f:hello3: {}
manager: kubectl-client-side-apply
operation: Update
time: "2022-08-22T23:08:23Z"
- apiVersion: v3
fieldsType: FieldsV1
fieldsV1:
f:data:
f:key4: {}
f:metadata:
f:annotations:
f:hello4: {}
manager: kubectl-client-side-apply
operation: Update
time: "2022-08-22T23:08:23Z"
- apiVersion: v4
fieldsType: FieldsV1
fieldsV1:
f:data:
f:key5: {}
f:metadata:
f:annotations:
f:hello5: {}
manager: kubectl-client-side-apply
operation: Update
time: "2022-08-22T23:08:23Z"
name: test
namespace: default
`),
ExpectedObject: []byte(`
apiVersion: v1
data: {}
kind: ConfigMap
metadata:
annotations: {}
creationTimestamp: "2022-08-22T23:08:23Z"
managedFields:
- apiVersion: v5
fieldsType: FieldsV1
fieldsV1:
f:data:
.: {}
f:key: {}
f:key2: {}
f:legacy: {}
f:metadata:
f:annotations:
.: {}
f:hello2: {}
manager: kubectl
operation: Apply
time: "2022-08-23T23:08:23Z"
name: test
namespace: default
`),
},
}
for _, testCase := range cases {
t.Run(testCase.Name, func(t *testing.T) {
initialObject := unstructured.Unstructured{}
err := yaml.Unmarshal(testCase.OriginalObject, &initialObject)
if err != nil {
t.Fatal(err)
}
upgraded := initialObject.DeepCopy()
err = csaupgrade.UpgradeManagedFields(
upgraded,
testCase.CSAManager,
testCase.SSAManager,
)
if err != nil {
t.Fatal(err)
}
expectedObject := unstructured.Unstructured{}
err = yaml.Unmarshal(testCase.ExpectedObject, &expectedObject)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(&expectedObject, upgraded) {
t.Fatal(cmp.Diff(&expectedObject, upgraded))
}
})
}
}

View File

@@ -26,6 +26,7 @@ import (
"crypto/x509"
"encoding/pem"
"fmt"
"io/ioutil"
"os"
"path/filepath"
)
@@ -68,13 +69,13 @@ func WriteKey(keyPath string, data []byte) error {
if err := os.MkdirAll(filepath.Dir(keyPath), os.FileMode(0755)); err != nil {
return err
}
return os.WriteFile(keyPath, data, os.FileMode(0600))
return ioutil.WriteFile(keyPath, data, os.FileMode(0600))
}
// LoadOrGenerateKeyFile looks for a key in the file at the given path. If it
// can't find one, it will generate a new key and store it there.
func LoadOrGenerateKeyFile(keyPath string) (data []byte, wasGenerated bool, err error) {
loadedData, err := os.ReadFile(keyPath)
loadedData, err := ioutil.ReadFile(keyPath)
// Call verifyKeyData to ensure the file wasn't empty/corrupt.
if err == nil && verifyKeyData(loadedData) {
return loadedData, false, err
@@ -121,7 +122,7 @@ func MarshalPrivateKeyToPEM(privateKey crypto.PrivateKey) ([]byte, error) {
// PrivateKeyFromFile returns the private key in rsa.PrivateKey or ecdsa.PrivateKey format from a given PEM-encoded file.
// Returns an error if the file could not be read or if the private key could not be parsed.
func PrivateKeyFromFile(file string) (interface{}, error) {
data, err := os.ReadFile(file)
data, err := ioutil.ReadFile(file)
if err != nil {
return nil, err
}
@@ -135,7 +136,7 @@ func PrivateKeyFromFile(file string) (interface{}, error) {
// PublicKeysFromFile returns the public keys in rsa.PublicKey or ecdsa.PublicKey format from a given PEM-encoded file.
// Reads public keys from both public and private key files.
func PublicKeysFromFile(file string) ([]interface{}, error) {
data, err := os.ReadFile(file)
data, err := ioutil.ReadFile(file)
if err != nil {
return nil, err
}

View File

@@ -17,6 +17,7 @@ limitations under the License.
package keyutil
import (
"io/ioutil"
"os"
"testing"
)
@@ -114,7 +115,7 @@ q2bbtE7r1JMK+/sQA5sNAp+7Vdc3psr1OaNzyTyuhTECyRdFKXm63cMnGg==
)
func TestReadPrivateKey(t *testing.T) {
f, err := os.CreateTemp("", "")
f, err := ioutil.TempFile("", "")
if err != nil {
t.Fatalf("error creating tmpfile: %v", err)
}
@@ -124,21 +125,21 @@ func TestReadPrivateKey(t *testing.T) {
t.Fatalf("Expected error reading key from empty file, got none")
}
if err := os.WriteFile(f.Name(), []byte(rsaPrivateKey), os.FileMode(0600)); err != nil {
if err := ioutil.WriteFile(f.Name(), []byte(rsaPrivateKey), os.FileMode(0600)); err != nil {
t.Fatalf("error writing private key to tmpfile: %v", err)
}
if _, err := PrivateKeyFromFile(f.Name()); err != nil {
t.Fatalf("error reading private RSA key: %v", err)
}
if err := os.WriteFile(f.Name(), []byte(ecdsaPrivateKey), os.FileMode(0600)); err != nil {
if err := ioutil.WriteFile(f.Name(), []byte(ecdsaPrivateKey), os.FileMode(0600)); err != nil {
t.Fatalf("error writing private key to tmpfile: %v", err)
}
if _, err := PrivateKeyFromFile(f.Name()); err != nil {
t.Fatalf("error reading private ECDSA key: %v", err)
}
if err := os.WriteFile(f.Name(), []byte(ecdsaPrivateKeyWithParams), os.FileMode(0600)); err != nil {
if err := ioutil.WriteFile(f.Name(), []byte(ecdsaPrivateKeyWithParams), os.FileMode(0600)); err != nil {
t.Fatalf("error writing private key to tmpfile: %v", err)
}
if _, err := PrivateKeyFromFile(f.Name()); err != nil {
@@ -147,7 +148,7 @@ func TestReadPrivateKey(t *testing.T) {
}
func TestReadPublicKeys(t *testing.T) {
f, err := os.CreateTemp("", "")
f, err := ioutil.TempFile("", "")
if err != nil {
t.Fatalf("error creating tmpfile: %v", err)
}
@@ -157,7 +158,7 @@ func TestReadPublicKeys(t *testing.T) {
t.Fatalf("Expected error reading keys from empty file, got none")
}
if err := os.WriteFile(f.Name(), []byte(rsaPublicKey), os.FileMode(0600)); err != nil {
if err := ioutil.WriteFile(f.Name(), []byte(rsaPublicKey), os.FileMode(0600)); err != nil {
t.Fatalf("error writing public key to tmpfile: %v", err)
}
if keys, err := PublicKeysFromFile(f.Name()); err != nil {
@@ -166,7 +167,7 @@ func TestReadPublicKeys(t *testing.T) {
t.Fatalf("expected 1 key, got %d", len(keys))
}
if err := os.WriteFile(f.Name(), []byte(ecdsaPublicKey), os.FileMode(0600)); err != nil {
if err := ioutil.WriteFile(f.Name(), []byte(ecdsaPublicKey), os.FileMode(0600)); err != nil {
t.Fatalf("error writing public key to tmpfile: %v", err)
}
if keys, err := PublicKeysFromFile(f.Name()); err != nil {
@@ -175,7 +176,7 @@ func TestReadPublicKeys(t *testing.T) {
t.Fatalf("expected 1 key, got %d", len(keys))
}
if err := os.WriteFile(f.Name(), []byte(rsaPublicKey+"\n"+ecdsaPublicKey), os.FileMode(0600)); err != nil {
if err := ioutil.WriteFile(f.Name(), []byte(rsaPublicKey+"\n"+ecdsaPublicKey), os.FileMode(0600)); err != nil {
t.Fatalf("error writing public key to tmpfile: %v", err)
}
if keys, err := PublicKeysFromFile(f.Name()); err != nil {
@@ -184,7 +185,7 @@ func TestReadPublicKeys(t *testing.T) {
t.Fatalf("expected 2 keys, got %d", len(keys))
}
if err := os.WriteFile(f.Name(), []byte(certificate), os.FileMode(0600)); err != nil {
if err := ioutil.WriteFile(f.Name(), []byte(certificate), os.FileMode(0600)); err != nil {
t.Fatalf("error writing certificate to tmpfile: %v", err)
}
if keys, err := PublicKeysFromFile(f.Name()); err != nil {

View File

@@ -17,7 +17,7 @@ limitations under the License.
package testing
import (
"io"
"io/ioutil"
"net/http"
"net/url"
"reflect"
@@ -83,7 +83,7 @@ func (f *FakeHandler) ServeHTTP(response http.ResponseWriter, request *http.Requ
response.WriteHeader(f.StatusCode)
response.Write([]byte(f.ResponseBody))
bodyReceived, err := io.ReadAll(request.Body)
bodyReceived, err := ioutil.ReadAll(request.Body)
if err != nil && f.T != nil {
f.T.Logf("Received read error: %v", err)
}

View File

@@ -18,8 +18,8 @@ package testing
import (
"encoding/json"
"io"
"io/fs"
"io/ioutil"
"net/http"
"net/http/httptest"
"os"
@@ -71,7 +71,7 @@ func NewFakeOpenAPIV3Server(specsPath string) (*FakeOpenAPIServer, error) {
}
defer file.Close()
vals, err := io.ReadAll(file)
vals, err := ioutil.ReadAll(file)
if err != nil {
panic(err)
}

View File

@@ -17,6 +17,7 @@ limitations under the License.
package testing
import (
"io/ioutil"
"os"
)
@@ -25,7 +26,7 @@ import (
// deleted with a call to "os.RemoveAll(...)".
// In case of error, it'll return an empty string and the error.
func MkTmpdir(prefix string) (string, error) {
tmpDir, err := os.MkdirTemp(os.TempDir(), prefix)
tmpDir, err := ioutil.TempDir(os.TempDir(), prefix)
if err != nil {
return "", err
}