Update go dependencies
This commit is contained in:
parent
15ffb51394
commit
bb4d483837
1621 changed files with 86368 additions and 284392 deletions
864
vendor/k8s.io/client-go/discovery/discovery_client_test.go
generated
vendored
864
vendor/k8s.io/client-go/discovery/discovery_client_test.go
generated
vendored
|
|
@ -1,864 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 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 discovery_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"mime"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/googleapis/gnostic/OpenAPIv2"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/version"
|
||||
. "k8s.io/client-go/discovery"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
)
|
||||
|
||||
func TestGetServerVersion(t *testing.T) {
|
||||
expect := version.Info{
|
||||
Major: "foo",
|
||||
Minor: "bar",
|
||||
GitCommit: "baz",
|
||||
}
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
output, err := json.Marshal(expect)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected encoding error: %v", err)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write(output)
|
||||
}))
|
||||
defer server.Close()
|
||||
client := NewDiscoveryClientForConfigOrDie(&restclient.Config{Host: server.URL})
|
||||
|
||||
got, err := client.ServerVersion()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected encoding error: %v", err)
|
||||
}
|
||||
if e, a := expect, *got; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetServerGroupsWithV1Server(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
var obj interface{}
|
||||
switch req.URL.Path {
|
||||
case "/api":
|
||||
obj = &metav1.APIVersions{
|
||||
Versions: []string{
|
||||
"v1",
|
||||
},
|
||||
}
|
||||
case "/apis":
|
||||
obj = &metav1.APIGroupList{
|
||||
Groups: []metav1.APIGroup{
|
||||
{
|
||||
Name: "extensions",
|
||||
Versions: []metav1.GroupVersionForDiscovery{
|
||||
{GroupVersion: "extensions/v1beta1"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
default:
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
output, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected encoding error: %v", err)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write(output)
|
||||
}))
|
||||
defer server.Close()
|
||||
client := NewDiscoveryClientForConfigOrDie(&restclient.Config{Host: server.URL})
|
||||
// ServerGroups should not return an error even if server returns error at /api and /apis
|
||||
apiGroupList, err := client.ServerGroups()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
groupVersions := metav1.ExtractGroupVersions(apiGroupList)
|
||||
if !reflect.DeepEqual(groupVersions, []string{"v1", "extensions/v1beta1"}) {
|
||||
t.Errorf("expected: %q, got: %q", []string{"v1", "extensions/v1beta1"}, groupVersions)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetServerGroupsWithBrokenServer(t *testing.T) {
|
||||
for _, statusCode := range []int{http.StatusNotFound, http.StatusForbidden} {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
w.WriteHeader(statusCode)
|
||||
}))
|
||||
defer server.Close()
|
||||
client := NewDiscoveryClientForConfigOrDie(&restclient.Config{Host: server.URL})
|
||||
// ServerGroups should not return an error even if server returns Not Found or Forbidden error at all end points
|
||||
apiGroupList, err := client.ServerGroups()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
groupVersions := metav1.ExtractGroupVersions(apiGroupList)
|
||||
if len(groupVersions) != 0 {
|
||||
t.Errorf("expected empty list, got: %q", groupVersions)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetServerResourcesWithV1Server(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
var obj interface{}
|
||||
switch req.URL.Path {
|
||||
case "/api":
|
||||
obj = &metav1.APIVersions{
|
||||
Versions: []string{
|
||||
"v1",
|
||||
},
|
||||
}
|
||||
default:
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
output, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected encoding error: %v", err)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write(output)
|
||||
}))
|
||||
defer server.Close()
|
||||
client := NewDiscoveryClientForConfigOrDie(&restclient.Config{Host: server.URL})
|
||||
// ServerResources should not return an error even if server returns error at /api/v1.
|
||||
serverResources, err := client.ServerResources()
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
gvs := groupVersions(serverResources)
|
||||
if !sets.NewString(gvs...).Has("v1") {
|
||||
t.Errorf("missing v1 in resource list: %v", serverResources)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetServerResources(t *testing.T) {
|
||||
stable := metav1.APIResourceList{
|
||||
GroupVersion: "v1",
|
||||
APIResources: []metav1.APIResource{
|
||||
{Name: "pods", Namespaced: true, Kind: "Pod"},
|
||||
{Name: "services", Namespaced: true, Kind: "Service"},
|
||||
{Name: "namespaces", Namespaced: false, Kind: "Namespace"},
|
||||
},
|
||||
}
|
||||
beta := metav1.APIResourceList{
|
||||
GroupVersion: "extensions/v1beta1",
|
||||
APIResources: []metav1.APIResource{
|
||||
{Name: "deployments", Namespaced: true, Kind: "Deployment"},
|
||||
{Name: "ingresses", Namespaced: true, Kind: "Ingress"},
|
||||
{Name: "jobs", Namespaced: true, Kind: "Job"},
|
||||
},
|
||||
}
|
||||
tests := []struct {
|
||||
resourcesList *metav1.APIResourceList
|
||||
path string
|
||||
request string
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
resourcesList: &stable,
|
||||
path: "/api/v1",
|
||||
request: "v1",
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
resourcesList: &beta,
|
||||
path: "/apis/extensions/v1beta1",
|
||||
request: "extensions/v1beta1",
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
resourcesList: &stable,
|
||||
path: "/api/v1",
|
||||
request: "foobar",
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
var list interface{}
|
||||
switch req.URL.Path {
|
||||
case "/api/v1":
|
||||
list = &stable
|
||||
case "/apis/extensions/v1beta1":
|
||||
list = &beta
|
||||
case "/api":
|
||||
list = &metav1.APIVersions{
|
||||
Versions: []string{
|
||||
"v1",
|
||||
},
|
||||
}
|
||||
case "/apis":
|
||||
list = &metav1.APIGroupList{
|
||||
Groups: []metav1.APIGroup{
|
||||
{
|
||||
Versions: []metav1.GroupVersionForDiscovery{
|
||||
{GroupVersion: "extensions/v1beta1"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
default:
|
||||
t.Logf("unexpected request: %s", req.URL.Path)
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
output, err := json.Marshal(list)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected encoding error: %v", err)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write(output)
|
||||
}))
|
||||
defer server.Close()
|
||||
client := NewDiscoveryClientForConfigOrDie(&restclient.Config{Host: server.URL})
|
||||
for _, test := range tests {
|
||||
got, err := client.ServerResourcesForGroupVersion(test.request)
|
||||
if test.expectErr {
|
||||
if err == nil {
|
||||
t.Error("unexpected non-error")
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(got, test.resourcesList) {
|
||||
t.Errorf("expected:\n%v\ngot:\n%v\n", test.resourcesList, got)
|
||||
}
|
||||
}
|
||||
|
||||
serverResources, err := client.ServerResources()
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
serverGroupVersions := sets.NewString(groupVersions(serverResources)...)
|
||||
for _, api := range []string{"v1", "extensions/v1beta1"} {
|
||||
if !serverGroupVersions.Has(api) {
|
||||
t.Errorf("missing expected api %q in %v", api, serverResources)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var returnedOpenAPI = openapi_v2.Document{
|
||||
Definitions: &openapi_v2.Definitions{
|
||||
AdditionalProperties: []*openapi_v2.NamedSchema{
|
||||
{
|
||||
Name: "fake.type.1",
|
||||
Value: &openapi_v2.Schema{
|
||||
Properties: &openapi_v2.Properties{
|
||||
AdditionalProperties: []*openapi_v2.NamedSchema{
|
||||
{
|
||||
Name: "count",
|
||||
Value: &openapi_v2.Schema{
|
||||
Type: &openapi_v2.TypeItem{
|
||||
Value: []string{"integer"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "fake.type.2",
|
||||
Value: &openapi_v2.Schema{
|
||||
Properties: &openapi_v2.Properties{
|
||||
AdditionalProperties: []*openapi_v2.NamedSchema{
|
||||
{
|
||||
Name: "count",
|
||||
Value: &openapi_v2.Schema{
|
||||
Type: &openapi_v2.TypeItem{
|
||||
Value: []string{"array"},
|
||||
},
|
||||
Items: &openapi_v2.ItemsItem{
|
||||
Schema: []*openapi_v2.Schema{
|
||||
{
|
||||
Type: &openapi_v2.TypeItem{
|
||||
Value: []string{"string"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func openapiSchemaDeprecatedFakeServer(status int) (*httptest.Server, error) {
|
||||
var sErr error
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
if req.URL.Path == "/openapi/v2" {
|
||||
// write the error status for the new endpoint request
|
||||
w.WriteHeader(status)
|
||||
return
|
||||
}
|
||||
if req.URL.Path != "/swagger-2.0.0.pb-v1" {
|
||||
sErr = fmt.Errorf("Unexpected url %v", req.URL)
|
||||
}
|
||||
if req.Method != "GET" {
|
||||
sErr = fmt.Errorf("Unexpected method %v", req.Method)
|
||||
}
|
||||
|
||||
mime.AddExtensionType(".pb-v1", "application/com.github.googleapis.gnostic.OpenAPIv2@68f4ded+protobuf")
|
||||
|
||||
output, err := proto.Marshal(&returnedOpenAPI)
|
||||
if err != nil {
|
||||
sErr = err
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write(output)
|
||||
}))
|
||||
return server, sErr
|
||||
}
|
||||
|
||||
func openapiSchemaFakeServer() (*httptest.Server, error) {
|
||||
var sErr error
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
if req.URL.Path != "/openapi/v2" {
|
||||
sErr = fmt.Errorf("Unexpected url %v", req.URL)
|
||||
}
|
||||
if req.Method != "GET" {
|
||||
sErr = fmt.Errorf("Unexpected method %v", req.Method)
|
||||
}
|
||||
decipherableFormat := req.Header.Get("Accept")
|
||||
if decipherableFormat != "application/com.github.proto-openapi.spec.v2@v1.0+protobuf" {
|
||||
sErr = fmt.Errorf("Unexpected accept mime type %v", decipherableFormat)
|
||||
}
|
||||
|
||||
mime.AddExtensionType(".pb-v1", "application/com.github.googleapis.gnostic.OpenAPIv2@68f4ded+protobuf")
|
||||
|
||||
output, err := proto.Marshal(&returnedOpenAPI)
|
||||
if err != nil {
|
||||
sErr = err
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write(output)
|
||||
}))
|
||||
return server, sErr
|
||||
}
|
||||
|
||||
func TestGetOpenAPISchema(t *testing.T) {
|
||||
server, err := openapiSchemaFakeServer()
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error starting fake server: %v", err)
|
||||
}
|
||||
defer server.Close()
|
||||
|
||||
client := NewDiscoveryClientForConfigOrDie(&restclient.Config{Host: server.URL})
|
||||
got, err := client.OpenAPISchema()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error getting openapi: %v", err)
|
||||
}
|
||||
if e, a := returnedOpenAPI, *got; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOpenAPISchemaForbiddenFallback(t *testing.T) {
|
||||
server, err := openapiSchemaDeprecatedFakeServer(http.StatusForbidden)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error starting fake server: %v", err)
|
||||
}
|
||||
defer server.Close()
|
||||
|
||||
client := NewDiscoveryClientForConfigOrDie(&restclient.Config{Host: server.URL})
|
||||
got, err := client.OpenAPISchema()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error getting openapi: %v", err)
|
||||
}
|
||||
if e, a := returnedOpenAPI, *got; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOpenAPISchemaNotFoundFallback(t *testing.T) {
|
||||
server, err := openapiSchemaDeprecatedFakeServer(http.StatusNotFound)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error starting fake server: %v", err)
|
||||
}
|
||||
defer server.Close()
|
||||
|
||||
client := NewDiscoveryClientForConfigOrDie(&restclient.Config{Host: server.URL})
|
||||
got, err := client.OpenAPISchema()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error getting openapi: %v", err)
|
||||
}
|
||||
if e, a := returnedOpenAPI, *got; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOpenAPISchemaNotAcceptableFallback(t *testing.T) {
|
||||
server, err := openapiSchemaDeprecatedFakeServer(http.StatusNotAcceptable)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error starting fake server: %v", err)
|
||||
}
|
||||
defer server.Close()
|
||||
|
||||
client := NewDiscoveryClientForConfigOrDie(&restclient.Config{Host: server.URL})
|
||||
got, err := client.OpenAPISchema()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error getting openapi: %v", err)
|
||||
}
|
||||
if e, a := returnedOpenAPI, *got; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestServerPreferredResources(t *testing.T) {
|
||||
stable := metav1.APIResourceList{
|
||||
GroupVersion: "v1",
|
||||
APIResources: []metav1.APIResource{
|
||||
{Name: "pods", Namespaced: true, Kind: "Pod"},
|
||||
{Name: "services", Namespaced: true, Kind: "Service"},
|
||||
{Name: "namespaces", Namespaced: false, Kind: "Namespace"},
|
||||
},
|
||||
}
|
||||
tests := []struct {
|
||||
resourcesList []*metav1.APIResourceList
|
||||
response func(w http.ResponseWriter, req *http.Request)
|
||||
expectErr func(err error) bool
|
||||
}{
|
||||
{
|
||||
resourcesList: []*metav1.APIResourceList{&stable},
|
||||
expectErr: IsGroupDiscoveryFailedError,
|
||||
response: func(w http.ResponseWriter, req *http.Request) {
|
||||
var list interface{}
|
||||
switch req.URL.Path {
|
||||
case "/apis/extensions/v1beta1":
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
case "/api/v1":
|
||||
list = &stable
|
||||
case "/api":
|
||||
list = &metav1.APIVersions{
|
||||
Versions: []string{
|
||||
"v1",
|
||||
},
|
||||
}
|
||||
case "/apis":
|
||||
list = &metav1.APIGroupList{
|
||||
Groups: []metav1.APIGroup{
|
||||
{
|
||||
Versions: []metav1.GroupVersionForDiscovery{
|
||||
{GroupVersion: "extensions/v1beta1"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
default:
|
||||
t.Logf("unexpected request: %s", req.URL.Path)
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
output, err := json.Marshal(list)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected encoding error: %v", err)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write(output)
|
||||
},
|
||||
},
|
||||
{
|
||||
resourcesList: nil,
|
||||
expectErr: IsGroupDiscoveryFailedError,
|
||||
response: func(w http.ResponseWriter, req *http.Request) {
|
||||
var list interface{}
|
||||
switch req.URL.Path {
|
||||
case "/apis/extensions/v1beta1":
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
case "/api/v1":
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
case "/api":
|
||||
list = &metav1.APIVersions{
|
||||
Versions: []string{
|
||||
"v1",
|
||||
},
|
||||
}
|
||||
case "/apis":
|
||||
list = &metav1.APIGroupList{
|
||||
Groups: []metav1.APIGroup{
|
||||
{
|
||||
Versions: []metav1.GroupVersionForDiscovery{
|
||||
{GroupVersion: "extensions/v1beta1"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
default:
|
||||
t.Logf("unexpected request: %s", req.URL.Path)
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
output, err := json.Marshal(list)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected encoding error: %v", err)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write(output)
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
server := httptest.NewServer(http.HandlerFunc(test.response))
|
||||
defer server.Close()
|
||||
|
||||
client := NewDiscoveryClientForConfigOrDie(&restclient.Config{Host: server.URL})
|
||||
resources, err := client.ServerPreferredResources()
|
||||
if test.expectErr != nil {
|
||||
if err == nil {
|
||||
t.Error("unexpected non-error")
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
continue
|
||||
}
|
||||
got, err := GroupVersionResources(resources)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
continue
|
||||
}
|
||||
expected, _ := GroupVersionResources(test.resourcesList)
|
||||
if !reflect.DeepEqual(got, expected) {
|
||||
t.Errorf("expected:\n%v\ngot:\n%v\n", test.resourcesList, got)
|
||||
}
|
||||
server.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func TestServerPreferredResourcesRetries(t *testing.T) {
|
||||
stable := metav1.APIResourceList{
|
||||
GroupVersion: "v1",
|
||||
APIResources: []metav1.APIResource{
|
||||
{Name: "pods", Namespaced: true, Kind: "Pod"},
|
||||
},
|
||||
}
|
||||
beta := metav1.APIResourceList{
|
||||
GroupVersion: "extensions/v1",
|
||||
APIResources: []metav1.APIResource{
|
||||
{Name: "deployments", Namespaced: true, Kind: "Deployment"},
|
||||
},
|
||||
}
|
||||
|
||||
response := func(numErrors int) http.HandlerFunc {
|
||||
var i = 0
|
||||
return func(w http.ResponseWriter, req *http.Request) {
|
||||
var list interface{}
|
||||
switch req.URL.Path {
|
||||
case "/apis/extensions/v1beta1":
|
||||
if i < numErrors {
|
||||
i++
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
list = &beta
|
||||
case "/api/v1":
|
||||
list = &stable
|
||||
case "/api":
|
||||
list = &metav1.APIVersions{
|
||||
Versions: []string{
|
||||
"v1",
|
||||
},
|
||||
}
|
||||
case "/apis":
|
||||
list = &metav1.APIGroupList{
|
||||
Groups: []metav1.APIGroup{
|
||||
{
|
||||
Name: "extensions",
|
||||
Versions: []metav1.GroupVersionForDiscovery{
|
||||
{GroupVersion: "extensions/v1beta1"},
|
||||
},
|
||||
PreferredVersion: metav1.GroupVersionForDiscovery{
|
||||
GroupVersion: "extensions/v1beta1",
|
||||
Version: "v1beta1",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
default:
|
||||
t.Logf("unexpected request: %s", req.URL.Path)
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
output, err := json.Marshal(list)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected encoding error: %v", err)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write(output)
|
||||
}
|
||||
}
|
||||
tests := []struct {
|
||||
responseErrors int
|
||||
expectResources int
|
||||
expectedError func(err error) bool
|
||||
}{
|
||||
{
|
||||
responseErrors: 1,
|
||||
expectResources: 2,
|
||||
expectedError: func(err error) bool {
|
||||
return err == nil
|
||||
},
|
||||
},
|
||||
{
|
||||
responseErrors: 2,
|
||||
expectResources: 1,
|
||||
expectedError: IsGroupDiscoveryFailedError,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range tests {
|
||||
server := httptest.NewServer(http.HandlerFunc(response(tc.responseErrors)))
|
||||
defer server.Close()
|
||||
|
||||
client := NewDiscoveryClientForConfigOrDie(&restclient.Config{Host: server.URL})
|
||||
resources, err := client.ServerPreferredResources()
|
||||
if !tc.expectedError(err) {
|
||||
t.Errorf("case %d: unexpected error: %v", i, err)
|
||||
}
|
||||
got, err := GroupVersionResources(resources)
|
||||
if err != nil {
|
||||
t.Errorf("case %d: unexpected error: %v", i, err)
|
||||
}
|
||||
if len(got) != tc.expectResources {
|
||||
t.Errorf("case %d: expect %d resources, got %#v", i, tc.expectResources, got)
|
||||
}
|
||||
server.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func TestServerPreferredNamespacedResources(t *testing.T) {
|
||||
stable := metav1.APIResourceList{
|
||||
GroupVersion: "v1",
|
||||
APIResources: []metav1.APIResource{
|
||||
{Name: "pods", Namespaced: true, Kind: "Pod"},
|
||||
{Name: "services", Namespaced: true, Kind: "Service"},
|
||||
{Name: "namespaces", Namespaced: false, Kind: "Namespace"},
|
||||
},
|
||||
}
|
||||
batchv1 := metav1.APIResourceList{
|
||||
GroupVersion: "batch/v1",
|
||||
APIResources: []metav1.APIResource{
|
||||
{Name: "jobs", Namespaced: true, Kind: "Job"},
|
||||
},
|
||||
}
|
||||
batchv2alpha1 := metav1.APIResourceList{
|
||||
GroupVersion: "batch/v2alpha1",
|
||||
APIResources: []metav1.APIResource{
|
||||
{Name: "jobs", Namespaced: true, Kind: "Job"},
|
||||
{Name: "cronjobs", Namespaced: true, Kind: "CronJob"},
|
||||
},
|
||||
}
|
||||
batchv3alpha1 := metav1.APIResourceList{
|
||||
GroupVersion: "batch/v3alpha1",
|
||||
APIResources: []metav1.APIResource{
|
||||
{Name: "jobs", Namespaced: true, Kind: "Job"},
|
||||
{Name: "cronjobs", Namespaced: true, Kind: "CronJob"},
|
||||
},
|
||||
}
|
||||
tests := []struct {
|
||||
response func(w http.ResponseWriter, req *http.Request)
|
||||
expected map[schema.GroupVersionResource]struct{}
|
||||
}{
|
||||
{
|
||||
response: func(w http.ResponseWriter, req *http.Request) {
|
||||
var list interface{}
|
||||
switch req.URL.Path {
|
||||
case "/api/v1":
|
||||
list = &stable
|
||||
case "/api":
|
||||
list = &metav1.APIVersions{
|
||||
Versions: []string{
|
||||
"v1",
|
||||
},
|
||||
}
|
||||
default:
|
||||
t.Logf("unexpected request: %s", req.URL.Path)
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
output, err := json.Marshal(list)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected encoding error: %v", err)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write(output)
|
||||
},
|
||||
expected: map[schema.GroupVersionResource]struct{}{
|
||||
{Group: "", Version: "v1", Resource: "pods"}: {},
|
||||
{Group: "", Version: "v1", Resource: "services"}: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
response: func(w http.ResponseWriter, req *http.Request) {
|
||||
var list interface{}
|
||||
switch req.URL.Path {
|
||||
case "/apis":
|
||||
list = &metav1.APIGroupList{
|
||||
Groups: []metav1.APIGroup{
|
||||
{
|
||||
Name: "batch",
|
||||
Versions: []metav1.GroupVersionForDiscovery{
|
||||
{GroupVersion: "batch/v1", Version: "v1"},
|
||||
{GroupVersion: "batch/v2alpha1", Version: "v2alpha1"},
|
||||
{GroupVersion: "batch/v3alpha1", Version: "v3alpha1"},
|
||||
},
|
||||
PreferredVersion: metav1.GroupVersionForDiscovery{GroupVersion: "batch/v1", Version: "v1"},
|
||||
},
|
||||
},
|
||||
}
|
||||
case "/apis/batch/v1":
|
||||
list = &batchv1
|
||||
case "/apis/batch/v2alpha1":
|
||||
list = &batchv2alpha1
|
||||
case "/apis/batch/v3alpha1":
|
||||
list = &batchv3alpha1
|
||||
default:
|
||||
t.Logf("unexpected request: %s", req.URL.Path)
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
output, err := json.Marshal(list)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected encoding error: %v", err)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write(output)
|
||||
},
|
||||
expected: map[schema.GroupVersionResource]struct{}{
|
||||
{Group: "batch", Version: "v1", Resource: "jobs"}: {},
|
||||
{Group: "batch", Version: "v2alpha1", Resource: "cronjobs"}: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
response: func(w http.ResponseWriter, req *http.Request) {
|
||||
var list interface{}
|
||||
switch req.URL.Path {
|
||||
case "/apis":
|
||||
list = &metav1.APIGroupList{
|
||||
Groups: []metav1.APIGroup{
|
||||
{
|
||||
Name: "batch",
|
||||
Versions: []metav1.GroupVersionForDiscovery{
|
||||
{GroupVersion: "batch/v1", Version: "v1"},
|
||||
{GroupVersion: "batch/v2alpha1", Version: "v2alpha1"},
|
||||
{GroupVersion: "batch/v3alpha1", Version: "v3alpha1"},
|
||||
},
|
||||
PreferredVersion: metav1.GroupVersionForDiscovery{GroupVersion: "batch/v2alpha", Version: "v2alpha1"},
|
||||
},
|
||||
},
|
||||
}
|
||||
case "/apis/batch/v1":
|
||||
list = &batchv1
|
||||
case "/apis/batch/v2alpha1":
|
||||
list = &batchv2alpha1
|
||||
case "/apis/batch/v3alpha1":
|
||||
list = &batchv3alpha1
|
||||
default:
|
||||
t.Logf("unexpected request: %s", req.URL.Path)
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
output, err := json.Marshal(list)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected encoding error: %v", err)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write(output)
|
||||
},
|
||||
expected: map[schema.GroupVersionResource]struct{}{
|
||||
{Group: "batch", Version: "v2alpha1", Resource: "jobs"}: {},
|
||||
{Group: "batch", Version: "v2alpha1", Resource: "cronjobs"}: {},
|
||||
},
|
||||
},
|
||||
}
|
||||
for i, test := range tests {
|
||||
server := httptest.NewServer(http.HandlerFunc(test.response))
|
||||
defer server.Close()
|
||||
|
||||
client := NewDiscoveryClientForConfigOrDie(&restclient.Config{Host: server.URL})
|
||||
resources, err := client.ServerPreferredNamespacedResources()
|
||||
if err != nil {
|
||||
t.Errorf("[%d] unexpected error: %v", i, err)
|
||||
continue
|
||||
}
|
||||
got, err := GroupVersionResources(resources)
|
||||
if err != nil {
|
||||
t.Errorf("[%d] unexpected error: %v", i, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(got, test.expected) {
|
||||
t.Errorf("[%d] expected:\n%v\ngot:\n%v\n", i, test.expected, got)
|
||||
}
|
||||
server.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func groupVersions(resources []*metav1.APIResourceList) []string {
|
||||
result := []string{}
|
||||
for _, resourceList := range resources {
|
||||
result = append(result, resourceList.GroupVersion)
|
||||
}
|
||||
return result
|
||||
}
|
||||
46
vendor/k8s.io/client-go/discovery/fake/discovery_test.go
generated
vendored
46
vendor/k8s.io/client-go/discovery/fake/discovery_test.go
generated
vendored
|
|
@ -1,46 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package fake_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/version"
|
||||
fakediscovery "k8s.io/client-go/discovery/fake"
|
||||
fakeclientset "k8s.io/client-go/kubernetes/fake"
|
||||
)
|
||||
|
||||
func TestFakingServerVersion(t *testing.T) {
|
||||
client := fakeclientset.NewSimpleClientset()
|
||||
fakeDiscovery, ok := client.Discovery().(*fakediscovery.FakeDiscovery)
|
||||
if !ok {
|
||||
t.Fatalf("couldn't convert Discovery() to *FakeDiscovery")
|
||||
}
|
||||
|
||||
testGitCommit := "v1.0.0"
|
||||
fakeDiscovery.FakedServerVersion = &version.Info{
|
||||
GitCommit: testGitCommit,
|
||||
}
|
||||
|
||||
sv, err := client.Discovery().ServerVersion()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if sv.GitCommit != testGitCommit {
|
||||
t.Fatalf("unexpected faked discovery return value: %q", sv.GitCommit)
|
||||
}
|
||||
}
|
||||
184
vendor/k8s.io/client-go/discovery/helper_blackbox_test.go
generated
vendored
184
vendor/k8s.io/client-go/discovery/helper_blackbox_test.go
generated
vendored
|
|
@ -1,184 +0,0 @@
|
|||
/*
|
||||
Copyright 2015 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package discovery_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/client-go/discovery"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/rest/fake"
|
||||
)
|
||||
|
||||
func objBody(object interface{}) io.ReadCloser {
|
||||
output, err := json.MarshalIndent(object, "", "")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return ioutil.NopCloser(bytes.NewReader([]byte(output)))
|
||||
}
|
||||
|
||||
func TestServerSupportsVersion(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
requiredVersion schema.GroupVersion
|
||||
serverVersions []string
|
||||
expectErr func(err error) bool
|
||||
sendErr error
|
||||
statusCode int
|
||||
}{
|
||||
{
|
||||
name: "explicit version supported",
|
||||
requiredVersion: schema.GroupVersion{Version: "v1"},
|
||||
serverVersions: []string{"/version1", v1.SchemeGroupVersion.String()},
|
||||
statusCode: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "explicit version not supported on server",
|
||||
requiredVersion: schema.GroupVersion{Version: "v1"},
|
||||
serverVersions: []string{"version1"},
|
||||
expectErr: func(err error) bool { return strings.Contains(err.Error(), `server does not support API version "v1"`) },
|
||||
statusCode: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "connection refused error",
|
||||
serverVersions: []string{"version1"},
|
||||
sendErr: errors.New("connection refused"),
|
||||
expectErr: func(err error) bool { return strings.Contains(err.Error(), "connection refused") },
|
||||
statusCode: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "discovery fails due to 404 Not Found errors and thus serverVersions is empty, use requested GroupVersion",
|
||||
requiredVersion: schema.GroupVersion{Version: "version1"},
|
||||
statusCode: http.StatusNotFound,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
fakeClient := &fake.RESTClient{
|
||||
NegotiatedSerializer: scheme.Codecs,
|
||||
Resp: &http.Response{
|
||||
StatusCode: test.statusCode,
|
||||
Body: objBody(&metav1.APIVersions{Versions: test.serverVersions}),
|
||||
},
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
if test.sendErr != nil {
|
||||
return nil, test.sendErr
|
||||
}
|
||||
header := http.Header{}
|
||||
header.Set("Content-Type", runtime.ContentTypeJSON)
|
||||
return &http.Response{StatusCode: test.statusCode, Header: header, Body: objBody(&metav1.APIVersions{Versions: test.serverVersions})}, nil
|
||||
}),
|
||||
}
|
||||
c := discovery.NewDiscoveryClientForConfigOrDie(&restclient.Config{})
|
||||
c.RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
|
||||
err := discovery.ServerSupportsVersion(c, test.requiredVersion)
|
||||
if err == nil && test.expectErr != nil {
|
||||
t.Errorf("expected error, got nil for [%s].", test.name)
|
||||
}
|
||||
if err != nil {
|
||||
if test.expectErr == nil || !test.expectErr(err) {
|
||||
t.Errorf("unexpected error for [%s]: %v.", test.name, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilteredBy(t *testing.T) {
|
||||
all := discovery.ResourcePredicateFunc(func(gv string, r *metav1.APIResource) bool {
|
||||
return true
|
||||
})
|
||||
none := discovery.ResourcePredicateFunc(func(gv string, r *metav1.APIResource) bool {
|
||||
return false
|
||||
})
|
||||
onlyV2 := discovery.ResourcePredicateFunc(func(gv string, r *metav1.APIResource) bool {
|
||||
return strings.HasSuffix(gv, "/v2") || gv == "v2"
|
||||
})
|
||||
onlyBar := discovery.ResourcePredicateFunc(func(gv string, r *metav1.APIResource) bool {
|
||||
return r.Kind == "Bar"
|
||||
})
|
||||
|
||||
foo := []*metav1.APIResourceList{
|
||||
{
|
||||
GroupVersion: "foo/v1",
|
||||
APIResources: []metav1.APIResource{
|
||||
{Name: "bar", Kind: "Bar"},
|
||||
{Name: "test", Kind: "Test"},
|
||||
},
|
||||
},
|
||||
{
|
||||
GroupVersion: "foo/v2",
|
||||
APIResources: []metav1.APIResource{
|
||||
{Name: "bar", Kind: "Bar"},
|
||||
{Name: "test", Kind: "Test"},
|
||||
},
|
||||
},
|
||||
{
|
||||
GroupVersion: "foo/v3",
|
||||
APIResources: []metav1.APIResource{},
|
||||
},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
input []*metav1.APIResourceList
|
||||
pred discovery.ResourcePredicate
|
||||
expectedResources []string
|
||||
}{
|
||||
{nil, all, []string{}},
|
||||
{[]*metav1.APIResourceList{
|
||||
{GroupVersion: "foo/v1"},
|
||||
}, all, []string{}},
|
||||
{foo, all, []string{"foo/v1.bar", "foo/v1.test", "foo/v2.bar", "foo/v2.test"}},
|
||||
{foo, onlyV2, []string{"foo/v2.bar", "foo/v2.test"}},
|
||||
{foo, onlyBar, []string{"foo/v1.bar", "foo/v2.bar"}},
|
||||
{foo, none, []string{}},
|
||||
}
|
||||
for i, test := range tests {
|
||||
filtered := discovery.FilteredBy(test.pred, test.input)
|
||||
|
||||
if expected, got := sets.NewString(test.expectedResources...), sets.NewString(stringify(filtered)...); !expected.Equal(got) {
|
||||
t.Errorf("[%d] unexpected group versions: expected=%v, got=%v", i, test.expectedResources, stringify(filtered))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func stringify(rls []*metav1.APIResourceList) []string {
|
||||
result := []string{}
|
||||
for _, rl := range rls {
|
||||
for _, r := range rl.APIResources {
|
||||
result = append(result, rl.GroupVersion+"."+r.Name)
|
||||
}
|
||||
if len(rl.APIResources) == 0 {
|
||||
result = append(result, rl.GroupVersion)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
384
vendor/k8s.io/client-go/discovery/restmapper_test.go
generated
vendored
384
vendor/k8s.io/client-go/discovery/restmapper_test.go
generated
vendored
|
|
@ -1,384 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package discovery_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/version"
|
||||
. "k8s.io/client-go/discovery"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/rest/fake"
|
||||
|
||||
"github.com/googleapis/gnostic/OpenAPIv2"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestRESTMapper(t *testing.T) {
|
||||
resources := []*APIGroupResources{
|
||||
{
|
||||
Group: metav1.APIGroup{
|
||||
Name: "extensions",
|
||||
Versions: []metav1.GroupVersionForDiscovery{
|
||||
{Version: "v1beta"},
|
||||
},
|
||||
PreferredVersion: metav1.GroupVersionForDiscovery{Version: "v1beta"},
|
||||
},
|
||||
VersionedResources: map[string][]metav1.APIResource{
|
||||
"v1beta": {
|
||||
{Name: "jobs", Namespaced: true, Kind: "Job"},
|
||||
{Name: "pods", Namespaced: true, Kind: "Pod"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Group: metav1.APIGroup{
|
||||
Versions: []metav1.GroupVersionForDiscovery{
|
||||
{Version: "v1"},
|
||||
{Version: "v2"},
|
||||
},
|
||||
PreferredVersion: metav1.GroupVersionForDiscovery{Version: "v1"},
|
||||
},
|
||||
VersionedResources: map[string][]metav1.APIResource{
|
||||
"v1": {
|
||||
{Name: "pods", Namespaced: true, Kind: "Pod"},
|
||||
},
|
||||
"v2": {
|
||||
{Name: "pods", Namespaced: true, Kind: "Pod"},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// This group tests finding and prioritizing resources that only exist in non-preferred versions
|
||||
{
|
||||
Group: metav1.APIGroup{
|
||||
Name: "unpreferred",
|
||||
Versions: []metav1.GroupVersionForDiscovery{
|
||||
{Version: "v1"},
|
||||
{Version: "v2beta1"},
|
||||
{Version: "v2alpha1"},
|
||||
},
|
||||
PreferredVersion: metav1.GroupVersionForDiscovery{Version: "v1"},
|
||||
},
|
||||
VersionedResources: map[string][]metav1.APIResource{
|
||||
"v1": {
|
||||
{Name: "broccoli", Namespaced: true, Kind: "Broccoli"},
|
||||
},
|
||||
"v2beta1": {
|
||||
{Name: "broccoli", Namespaced: true, Kind: "Broccoli"},
|
||||
{Name: "peas", Namespaced: true, Kind: "Pea"},
|
||||
},
|
||||
"v2alpha1": {
|
||||
{Name: "broccoli", Namespaced: true, Kind: "Broccoli"},
|
||||
{Name: "peas", Namespaced: true, Kind: "Pea"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
restMapper := NewRESTMapper(resources, nil)
|
||||
|
||||
kindTCs := []struct {
|
||||
input schema.GroupVersionResource
|
||||
want schema.GroupVersionKind
|
||||
}{
|
||||
{
|
||||
input: schema.GroupVersionResource{
|
||||
Resource: "pods",
|
||||
},
|
||||
want: schema.GroupVersionKind{
|
||||
Version: "v1",
|
||||
Kind: "Pod",
|
||||
},
|
||||
},
|
||||
{
|
||||
input: schema.GroupVersionResource{
|
||||
Version: "v1",
|
||||
Resource: "pods",
|
||||
},
|
||||
want: schema.GroupVersionKind{
|
||||
Version: "v1",
|
||||
Kind: "Pod",
|
||||
},
|
||||
},
|
||||
{
|
||||
input: schema.GroupVersionResource{
|
||||
Version: "v2",
|
||||
Resource: "pods",
|
||||
},
|
||||
want: schema.GroupVersionKind{
|
||||
Version: "v2",
|
||||
Kind: "Pod",
|
||||
},
|
||||
},
|
||||
{
|
||||
input: schema.GroupVersionResource{
|
||||
Resource: "pods",
|
||||
},
|
||||
want: schema.GroupVersionKind{
|
||||
Version: "v1",
|
||||
Kind: "Pod",
|
||||
},
|
||||
},
|
||||
{
|
||||
input: schema.GroupVersionResource{
|
||||
Resource: "jobs",
|
||||
},
|
||||
want: schema.GroupVersionKind{
|
||||
Group: "extensions",
|
||||
Version: "v1beta",
|
||||
Kind: "Job",
|
||||
},
|
||||
},
|
||||
{
|
||||
input: schema.GroupVersionResource{
|
||||
Resource: "peas",
|
||||
},
|
||||
want: schema.GroupVersionKind{
|
||||
Group: "unpreferred",
|
||||
Version: "v2beta1",
|
||||
Kind: "Pea",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range kindTCs {
|
||||
got, err := restMapper.KindFor(tc.input)
|
||||
if err != nil {
|
||||
t.Errorf("KindFor(%#v) unexpected error: %v", tc.input, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(got, tc.want) {
|
||||
t.Errorf("KindFor(%#v) = %#v, want %#v", tc.input, got, tc.want)
|
||||
}
|
||||
}
|
||||
|
||||
resourceTCs := []struct {
|
||||
input schema.GroupVersionResource
|
||||
want schema.GroupVersionResource
|
||||
}{
|
||||
{
|
||||
input: schema.GroupVersionResource{
|
||||
Resource: "pods",
|
||||
},
|
||||
want: schema.GroupVersionResource{
|
||||
Version: "v1",
|
||||
Resource: "pods",
|
||||
},
|
||||
},
|
||||
{
|
||||
input: schema.GroupVersionResource{
|
||||
Version: "v1",
|
||||
Resource: "pods",
|
||||
},
|
||||
want: schema.GroupVersionResource{
|
||||
Version: "v1",
|
||||
Resource: "pods",
|
||||
},
|
||||
},
|
||||
{
|
||||
input: schema.GroupVersionResource{
|
||||
Version: "v2",
|
||||
Resource: "pods",
|
||||
},
|
||||
want: schema.GroupVersionResource{
|
||||
Version: "v2",
|
||||
Resource: "pods",
|
||||
},
|
||||
},
|
||||
{
|
||||
input: schema.GroupVersionResource{
|
||||
Resource: "pods",
|
||||
},
|
||||
want: schema.GroupVersionResource{
|
||||
Version: "v1",
|
||||
Resource: "pods",
|
||||
},
|
||||
},
|
||||
{
|
||||
input: schema.GroupVersionResource{
|
||||
Resource: "jobs",
|
||||
},
|
||||
want: schema.GroupVersionResource{
|
||||
Group: "extensions",
|
||||
Version: "v1beta",
|
||||
Resource: "jobs",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range resourceTCs {
|
||||
got, err := restMapper.ResourceFor(tc.input)
|
||||
if err != nil {
|
||||
t.Errorf("ResourceFor(%#v) unexpected error: %v", tc.input, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(got, tc.want) {
|
||||
t.Errorf("ResourceFor(%#v) = %#v, want %#v", tc.input, got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeferredDiscoveryRESTMapper_CacheMiss(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
cdc := fakeCachedDiscoveryInterface{fresh: false}
|
||||
m := NewDeferredDiscoveryRESTMapper(&cdc, nil)
|
||||
assert.False(cdc.fresh, "should NOT be fresh after instantiation")
|
||||
assert.Zero(cdc.invalidateCalls, "should not have called Invalidate()")
|
||||
|
||||
gvk, err := m.KindFor(schema.GroupVersionResource{
|
||||
Group: "a",
|
||||
Version: "v1",
|
||||
Resource: "foo",
|
||||
})
|
||||
assert.NoError(err)
|
||||
assert.True(cdc.fresh, "should be fresh after a cache-miss")
|
||||
assert.Equal(cdc.invalidateCalls, 1, "should have called Invalidate() once")
|
||||
assert.Equal(gvk.Kind, "Foo")
|
||||
|
||||
gvk, err = m.KindFor(schema.GroupVersionResource{
|
||||
Group: "a",
|
||||
Version: "v1",
|
||||
Resource: "foo",
|
||||
})
|
||||
assert.NoError(err)
|
||||
assert.Equal(cdc.invalidateCalls, 1, "should NOT have called Invalidate() again")
|
||||
|
||||
gvk, err = m.KindFor(schema.GroupVersionResource{
|
||||
Group: "a",
|
||||
Version: "v1",
|
||||
Resource: "bar",
|
||||
})
|
||||
assert.Error(err)
|
||||
assert.Equal(cdc.invalidateCalls, 1, "should NOT have called Invalidate() again after another cache-miss, but with fresh==true")
|
||||
|
||||
cdc.fresh = false
|
||||
gvk, err = m.KindFor(schema.GroupVersionResource{
|
||||
Group: "a",
|
||||
Version: "v1",
|
||||
Resource: "bar",
|
||||
})
|
||||
assert.Error(err)
|
||||
assert.Equal(cdc.invalidateCalls, 2, "should HAVE called Invalidate() again after another cache-miss, but with fresh==false")
|
||||
}
|
||||
|
||||
type fakeCachedDiscoveryInterface struct {
|
||||
invalidateCalls int
|
||||
fresh bool
|
||||
enabledA bool
|
||||
}
|
||||
|
||||
var _ CachedDiscoveryInterface = &fakeCachedDiscoveryInterface{}
|
||||
|
||||
func (c *fakeCachedDiscoveryInterface) Fresh() bool {
|
||||
return c.fresh
|
||||
}
|
||||
|
||||
func (c *fakeCachedDiscoveryInterface) Invalidate() {
|
||||
c.invalidateCalls = c.invalidateCalls + 1
|
||||
c.fresh = true
|
||||
c.enabledA = true
|
||||
}
|
||||
|
||||
func (c *fakeCachedDiscoveryInterface) RESTClient() restclient.Interface {
|
||||
return &fake.RESTClient{}
|
||||
}
|
||||
|
||||
func (c *fakeCachedDiscoveryInterface) ServerGroups() (*metav1.APIGroupList, error) {
|
||||
if c.enabledA {
|
||||
return &metav1.APIGroupList{
|
||||
Groups: []metav1.APIGroup{
|
||||
{
|
||||
Name: "a",
|
||||
Versions: []metav1.GroupVersionForDiscovery{
|
||||
{
|
||||
GroupVersion: "a/v1",
|
||||
Version: "v1",
|
||||
},
|
||||
},
|
||||
PreferredVersion: metav1.GroupVersionForDiscovery{
|
||||
GroupVersion: "a/v1",
|
||||
Version: "v1",
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
return &metav1.APIGroupList{}, nil
|
||||
}
|
||||
|
||||
func (c *fakeCachedDiscoveryInterface) ServerResourcesForGroupVersion(groupVersion string) (*metav1.APIResourceList, error) {
|
||||
if c.enabledA && groupVersion == "a/v1" {
|
||||
return &metav1.APIResourceList{
|
||||
GroupVersion: "a/v1",
|
||||
APIResources: []metav1.APIResource{
|
||||
{
|
||||
Name: "foo",
|
||||
Kind: "Foo",
|
||||
Namespaced: false,
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
return nil, errors.NewNotFound(schema.GroupResource{}, "")
|
||||
}
|
||||
|
||||
func (c *fakeCachedDiscoveryInterface) ServerResources() ([]*metav1.APIResourceList, error) {
|
||||
if c.enabledA {
|
||||
av1, _ := c.ServerResourcesForGroupVersion("a/v1")
|
||||
return []*metav1.APIResourceList{av1}, nil
|
||||
}
|
||||
return []*metav1.APIResourceList{}, nil
|
||||
}
|
||||
|
||||
func (c *fakeCachedDiscoveryInterface) ServerPreferredResources() ([]*metav1.APIResourceList, error) {
|
||||
if c.enabledA {
|
||||
return []*metav1.APIResourceList{
|
||||
{
|
||||
GroupVersion: "a/v1",
|
||||
APIResources: []metav1.APIResource{
|
||||
{
|
||||
Name: "foo",
|
||||
Kind: "Foo",
|
||||
Verbs: []string{},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (c *fakeCachedDiscoveryInterface) ServerPreferredNamespacedResources() ([]*metav1.APIResourceList, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (c *fakeCachedDiscoveryInterface) ServerVersion() (*version.Info, error) {
|
||||
return &version.Info{}, nil
|
||||
}
|
||||
|
||||
func (c *fakeCachedDiscoveryInterface) OpenAPISchema() (*openapi_v2.Document, error) {
|
||||
return &openapi_v2.Document{}, nil
|
||||
}
|
||||
152
vendor/k8s.io/client-go/listers/extensions/v1beta1/daemonset_expansion_test.go
generated
vendored
152
vendor/k8s.io/client-go/listers/extensions/v1beta1/daemonset_expansion_test.go
generated
vendored
|
|
@ -1,152 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 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 v1beta1
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
extensions "k8s.io/api/extensions/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
|
||||
func TestDaemonSetLister(t *testing.T) {
|
||||
store := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{"namespace": cache.MetaNamespaceIndexFunc})
|
||||
lister := NewDaemonSetLister(store)
|
||||
testCases := []struct {
|
||||
inDSs []*extensions.DaemonSet
|
||||
list func() ([]*extensions.DaemonSet, error)
|
||||
outDaemonSetNames sets.String
|
||||
expectErr bool
|
||||
}{
|
||||
// Basic listing
|
||||
{
|
||||
inDSs: []*extensions.DaemonSet{
|
||||
{ObjectMeta: metav1.ObjectMeta{Name: "basic"}},
|
||||
},
|
||||
list: func() ([]*extensions.DaemonSet, error) {
|
||||
return lister.List(labels.Everything())
|
||||
},
|
||||
outDaemonSetNames: sets.NewString("basic"),
|
||||
},
|
||||
// Listing multiple daemon sets
|
||||
{
|
||||
inDSs: []*extensions.DaemonSet{
|
||||
{ObjectMeta: metav1.ObjectMeta{Name: "basic"}},
|
||||
{ObjectMeta: metav1.ObjectMeta{Name: "complex"}},
|
||||
{ObjectMeta: metav1.ObjectMeta{Name: "complex2"}},
|
||||
},
|
||||
list: func() ([]*extensions.DaemonSet, error) {
|
||||
return lister.List(labels.Everything())
|
||||
},
|
||||
outDaemonSetNames: sets.NewString("basic", "complex", "complex2"),
|
||||
},
|
||||
// No pod labels
|
||||
{
|
||||
inDSs: []*extensions.DaemonSet{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "basic", Namespace: "ns"},
|
||||
Spec: extensions.DaemonSetSpec{
|
||||
Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "baz"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
list: func() ([]*extensions.DaemonSet, error) {
|
||||
pod := &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "pod1", Namespace: "ns"},
|
||||
}
|
||||
return lister.GetPodDaemonSets(pod)
|
||||
},
|
||||
outDaemonSetNames: sets.NewString(),
|
||||
expectErr: true,
|
||||
},
|
||||
// No DS selectors
|
||||
{
|
||||
inDSs: []*extensions.DaemonSet{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "basic", Namespace: "ns"},
|
||||
},
|
||||
},
|
||||
list: func() ([]*extensions.DaemonSet, error) {
|
||||
pod := &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pod1",
|
||||
Namespace: "ns",
|
||||
Labels: map[string]string{"foo": "bar"},
|
||||
},
|
||||
}
|
||||
return lister.GetPodDaemonSets(pod)
|
||||
},
|
||||
outDaemonSetNames: sets.NewString(),
|
||||
expectErr: true,
|
||||
},
|
||||
// Matching labels to selectors and namespace
|
||||
{
|
||||
inDSs: []*extensions.DaemonSet{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "foo"},
|
||||
Spec: extensions.DaemonSetSpec{
|
||||
Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "bar", Namespace: "ns"},
|
||||
Spec: extensions.DaemonSetSpec{
|
||||
Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
list: func() ([]*extensions.DaemonSet, error) {
|
||||
pod := &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pod1",
|
||||
Labels: map[string]string{"foo": "bar"},
|
||||
Namespace: "ns",
|
||||
},
|
||||
}
|
||||
return lister.GetPodDaemonSets(pod)
|
||||
},
|
||||
outDaemonSetNames: sets.NewString("bar"),
|
||||
},
|
||||
}
|
||||
for _, c := range testCases {
|
||||
for _, r := range c.inDSs {
|
||||
store.Add(r)
|
||||
}
|
||||
|
||||
daemonSets, err := c.list()
|
||||
if err != nil && c.expectErr {
|
||||
continue
|
||||
} else if c.expectErr {
|
||||
t.Error("Expected error, got none")
|
||||
continue
|
||||
} else if err != nil {
|
||||
t.Errorf("Unexpected error %#v", err)
|
||||
continue
|
||||
}
|
||||
daemonSetNames := make([]string, len(daemonSets))
|
||||
for ix := range daemonSets {
|
||||
daemonSetNames[ix] = daemonSets[ix].Name
|
||||
}
|
||||
if !c.outDaemonSetNames.HasAll(daemonSetNames...) || len(daemonSetNames) != len(c.outDaemonSetNames) {
|
||||
t.Errorf("Unexpected got controllers %+v expected %+v", daemonSetNames, c.outDaemonSetNames)
|
||||
}
|
||||
}
|
||||
}
|
||||
133
vendor/k8s.io/client-go/plugin/pkg/client/auth/azure/azure_test.go
generated
vendored
133
vendor/k8s.io/client-go/plugin/pkg/client/auth/azure/azure_test.go
generated
vendored
|
|
@ -1,133 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 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 azure
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/Azure/go-autorest/autorest/adal"
|
||||
)
|
||||
|
||||
func TestAzureTokenSource(t *testing.T) {
|
||||
fakeAccessToken := "fake token 1"
|
||||
fakeSource := fakeTokenSource{
|
||||
accessToken: fakeAccessToken,
|
||||
expiresOn: strconv.FormatInt(time.Now().Add(3600*time.Second).Unix(), 10),
|
||||
}
|
||||
cfg := make(map[string]string)
|
||||
persiter := &fakePersister{cache: make(map[string]string)}
|
||||
tokenCache := newAzureTokenCache()
|
||||
tokenSource := newAzureTokenSource(&fakeSource, tokenCache, cfg, persiter)
|
||||
token, err := tokenSource.Token()
|
||||
if err != nil {
|
||||
t.Errorf("failed to retrieve the token form cache: %v", err)
|
||||
}
|
||||
|
||||
wantCacheLen := 1
|
||||
if len(tokenCache.cache) != wantCacheLen {
|
||||
t.Errorf("Token() cache length error: got %v, want %v", len(tokenCache.cache), wantCacheLen)
|
||||
}
|
||||
|
||||
if token != tokenCache.cache[azureTokenKey] {
|
||||
t.Error("Token() returned token != cached token")
|
||||
}
|
||||
|
||||
wantCfg := token2Cfg(token)
|
||||
persistedCfg := persiter.Cache()
|
||||
for k, v := range persistedCfg {
|
||||
if strings.Compare(v, wantCfg[k]) != 0 {
|
||||
t.Errorf("Token() persisted cfg %s: got %v, want %v", k, v, wantCfg[k])
|
||||
}
|
||||
}
|
||||
|
||||
fakeSource.accessToken = "fake token 2"
|
||||
token, err = tokenSource.Token()
|
||||
if err != nil {
|
||||
t.Errorf("failed to retrieve the cached token: %v", err)
|
||||
}
|
||||
|
||||
if token.token.AccessToken != fakeAccessToken {
|
||||
t.Errorf("Token() didn't return the cached token")
|
||||
}
|
||||
}
|
||||
|
||||
type fakePersister struct {
|
||||
lock sync.Mutex
|
||||
cache map[string]string
|
||||
}
|
||||
|
||||
func (p *fakePersister) Persist(cache map[string]string) error {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
p.cache = map[string]string{}
|
||||
for k, v := range cache {
|
||||
p.cache[k] = v
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *fakePersister) Cache() map[string]string {
|
||||
ret := map[string]string{}
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
for k, v := range p.cache {
|
||||
ret[k] = v
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
type fakeTokenSource struct {
|
||||
expiresOn string
|
||||
accessToken string
|
||||
}
|
||||
|
||||
func (ts *fakeTokenSource) Token() (*azureToken, error) {
|
||||
return &azureToken{
|
||||
token: newFackeAzureToken(ts.accessToken, ts.expiresOn),
|
||||
clientID: "fake",
|
||||
tenantID: "fake",
|
||||
apiserverID: "fake",
|
||||
}, nil
|
||||
}
|
||||
|
||||
func token2Cfg(token *azureToken) map[string]string {
|
||||
cfg := make(map[string]string)
|
||||
cfg[cfgAccessToken] = token.token.AccessToken
|
||||
cfg[cfgRefreshToken] = token.token.RefreshToken
|
||||
cfg[cfgClientID] = token.clientID
|
||||
cfg[cfgTenantID] = token.tenantID
|
||||
cfg[cfgApiserverID] = token.apiserverID
|
||||
cfg[cfgExpiresIn] = token.token.ExpiresIn
|
||||
cfg[cfgExpiresOn] = token.token.ExpiresOn
|
||||
return cfg
|
||||
}
|
||||
|
||||
func newFackeAzureToken(accessToken string, expiresOn string) adal.Token {
|
||||
return adal.Token{
|
||||
AccessToken: accessToken,
|
||||
RefreshToken: "fake",
|
||||
ExpiresIn: "3600",
|
||||
ExpiresOn: expiresOn,
|
||||
NotBefore: expiresOn,
|
||||
Resource: "fake",
|
||||
Type: "fake",
|
||||
}
|
||||
}
|
||||
413
vendor/k8s.io/client-go/plugin/pkg/client/auth/exec/exec_test.go
generated
vendored
413
vendor/k8s.io/client-go/plugin/pkg/client/auth/exec/exec_test.go
generated
vendored
|
|
@ -1,413 +0,0 @@
|
|||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package exec
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/client-go/pkg/apis/clientauthentication"
|
||||
"k8s.io/client-go/tools/clientcmd/api"
|
||||
)
|
||||
|
||||
func TestCacheKey(t *testing.T) {
|
||||
c1 := &api.ExecConfig{
|
||||
Command: "foo-bar",
|
||||
Args: []string{"1", "2"},
|
||||
Env: []api.ExecEnvVar{
|
||||
{Name: "3", Value: "4"},
|
||||
{Name: "5", Value: "6"},
|
||||
{Name: "7", Value: "8"},
|
||||
},
|
||||
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||
}
|
||||
c2 := &api.ExecConfig{
|
||||
Command: "foo-bar",
|
||||
Args: []string{"1", "2"},
|
||||
Env: []api.ExecEnvVar{
|
||||
{Name: "3", Value: "4"},
|
||||
{Name: "5", Value: "6"},
|
||||
{Name: "7", Value: "8"},
|
||||
},
|
||||
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||
}
|
||||
c3 := &api.ExecConfig{
|
||||
Command: "foo-bar",
|
||||
Args: []string{"1", "2"},
|
||||
Env: []api.ExecEnvVar{
|
||||
{Name: "3", Value: "4"},
|
||||
{Name: "5", Value: "6"},
|
||||
},
|
||||
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||
}
|
||||
key1 := cacheKey(c1)
|
||||
key2 := cacheKey(c2)
|
||||
key3 := cacheKey(c3)
|
||||
if key1 != key2 {
|
||||
t.Error("key1 and key2 didn't match")
|
||||
}
|
||||
if key1 == key3 {
|
||||
t.Error("key1 and key3 matched")
|
||||
}
|
||||
if key2 == key3 {
|
||||
t.Error("key2 and key3 matched")
|
||||
}
|
||||
}
|
||||
|
||||
func compJSON(t *testing.T, got, want []byte) {
|
||||
t.Helper()
|
||||
gotJSON := &bytes.Buffer{}
|
||||
wantJSON := &bytes.Buffer{}
|
||||
|
||||
if err := json.Indent(gotJSON, got, "", " "); err != nil {
|
||||
t.Errorf("got invalid JSON: %v", err)
|
||||
}
|
||||
if err := json.Indent(wantJSON, want, "", " "); err != nil {
|
||||
t.Errorf("want invalid JSON: %v", err)
|
||||
}
|
||||
g := strings.TrimSpace(gotJSON.String())
|
||||
w := strings.TrimSpace(wantJSON.String())
|
||||
if g != w {
|
||||
t.Errorf("wanted %q, got %q", w, g)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetToken(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
config api.ExecConfig
|
||||
output string
|
||||
interactive bool
|
||||
response *clientauthentication.Response
|
||||
wantInput string
|
||||
wantToken string
|
||||
wantExpiry time.Time
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "basic-request",
|
||||
config: api.ExecConfig{
|
||||
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||
},
|
||||
wantInput: `{
|
||||
"kind":"ExecCredential",
|
||||
"apiVersion":"client.authentication.k8s.io/v1alpha1",
|
||||
"spec": {}
|
||||
}`,
|
||||
output: `{
|
||||
"kind": "ExecCredential",
|
||||
"apiVersion": "client.authentication.k8s.io/v1alpha1",
|
||||
"status": {
|
||||
"token": "foo-bar"
|
||||
}
|
||||
}`,
|
||||
wantToken: "foo-bar",
|
||||
},
|
||||
{
|
||||
name: "interactive",
|
||||
config: api.ExecConfig{
|
||||
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||
},
|
||||
interactive: true,
|
||||
wantInput: `{
|
||||
"kind":"ExecCredential",
|
||||
"apiVersion":"client.authentication.k8s.io/v1alpha1",
|
||||
"spec": {
|
||||
"interactive": true
|
||||
}
|
||||
}`,
|
||||
output: `{
|
||||
"kind": "ExecCredential",
|
||||
"apiVersion": "client.authentication.k8s.io/v1alpha1",
|
||||
"status": {
|
||||
"token": "foo-bar"
|
||||
}
|
||||
}`,
|
||||
wantToken: "foo-bar",
|
||||
},
|
||||
{
|
||||
name: "response",
|
||||
config: api.ExecConfig{
|
||||
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||
},
|
||||
response: &clientauthentication.Response{
|
||||
Header: map[string][]string{
|
||||
"WWW-Authenticate": {`Basic realm="Access to the staging site", charset="UTF-8"`},
|
||||
},
|
||||
Code: 401,
|
||||
},
|
||||
wantInput: `{
|
||||
"kind":"ExecCredential",
|
||||
"apiVersion":"client.authentication.k8s.io/v1alpha1",
|
||||
"spec": {
|
||||
"response": {
|
||||
"header": {
|
||||
"WWW-Authenticate": [
|
||||
"Basic realm=\"Access to the staging site\", charset=\"UTF-8\""
|
||||
]
|
||||
},
|
||||
"code": 401
|
||||
}
|
||||
}
|
||||
}`,
|
||||
output: `{
|
||||
"kind": "ExecCredential",
|
||||
"apiVersion": "client.authentication.k8s.io/v1alpha1",
|
||||
"status": {
|
||||
"token": "foo-bar"
|
||||
}
|
||||
}`,
|
||||
wantToken: "foo-bar",
|
||||
},
|
||||
{
|
||||
name: "expiry",
|
||||
config: api.ExecConfig{
|
||||
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||
},
|
||||
wantInput: `{
|
||||
"kind":"ExecCredential",
|
||||
"apiVersion":"client.authentication.k8s.io/v1alpha1",
|
||||
"spec": {}
|
||||
}`,
|
||||
output: `{
|
||||
"kind": "ExecCredential",
|
||||
"apiVersion": "client.authentication.k8s.io/v1alpha1",
|
||||
"status": {
|
||||
"token": "foo-bar",
|
||||
"expirationTimestamp": "2006-01-02T15:04:05Z"
|
||||
}
|
||||
}`,
|
||||
wantExpiry: time.Date(2006, 01, 02, 15, 04, 05, 0, time.UTC),
|
||||
wantToken: "foo-bar",
|
||||
},
|
||||
{
|
||||
name: "no-group-version",
|
||||
config: api.ExecConfig{
|
||||
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||
},
|
||||
wantInput: `{
|
||||
"kind":"ExecCredential",
|
||||
"apiVersion":"client.authentication.k8s.io/v1alpha1",
|
||||
"spec": {}
|
||||
}`,
|
||||
output: `{
|
||||
"kind": "ExecCredential",
|
||||
"status": {
|
||||
"token": "foo-bar"
|
||||
}
|
||||
}`,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "no-status",
|
||||
config: api.ExecConfig{
|
||||
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||
},
|
||||
wantInput: `{
|
||||
"kind":"ExecCredential",
|
||||
"apiVersion":"client.authentication.k8s.io/v1alpha1",
|
||||
"spec": {}
|
||||
}`,
|
||||
output: `{
|
||||
"kind": "ExecCredential",
|
||||
"apiVersion":"client.authentication.k8s.io/v1alpha1"
|
||||
}`,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "no-token",
|
||||
config: api.ExecConfig{
|
||||
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||
},
|
||||
wantInput: `{
|
||||
"kind":"ExecCredential",
|
||||
"apiVersion":"client.authentication.k8s.io/v1alpha1",
|
||||
"spec": {}
|
||||
}`,
|
||||
output: `{
|
||||
"kind": "ExecCredential",
|
||||
"apiVersion":"client.authentication.k8s.io/v1alpha1",
|
||||
"status": {}
|
||||
}`,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
c := test.config
|
||||
|
||||
c.Command = "./testdata/test-plugin.sh"
|
||||
c.Env = append(c.Env, api.ExecEnvVar{
|
||||
Name: "TEST_OUTPUT",
|
||||
Value: test.output,
|
||||
})
|
||||
|
||||
a, err := newAuthenticator(newCache(), &c)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
stderr := &bytes.Buffer{}
|
||||
a.stderr = stderr
|
||||
a.interactive = test.interactive
|
||||
a.environ = func() []string { return nil }
|
||||
|
||||
token, err := a.getToken(test.response)
|
||||
if err != nil {
|
||||
if !test.wantErr {
|
||||
t.Errorf("get token %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if test.wantErr {
|
||||
t.Fatal("expected error getting token")
|
||||
}
|
||||
|
||||
if token != test.wantToken {
|
||||
t.Errorf("expected token %q got %q", test.wantToken, token)
|
||||
}
|
||||
|
||||
if !a.exp.Equal(test.wantExpiry) {
|
||||
t.Errorf("expected expiry %v got %v", test.wantExpiry, a.exp)
|
||||
}
|
||||
|
||||
compJSON(t, stderr.Bytes(), []byte(test.wantInput))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoundTripper(t *testing.T) {
|
||||
wantToken := ""
|
||||
|
||||
n := time.Now()
|
||||
now := func() time.Time { return n }
|
||||
|
||||
env := []string{""}
|
||||
environ := func() []string {
|
||||
s := make([]string, len(env))
|
||||
copy(s, env)
|
||||
return s
|
||||
}
|
||||
|
||||
setOutput := func(s string) {
|
||||
env[0] = "TEST_OUTPUT=" + s
|
||||
}
|
||||
|
||||
handler := func(w http.ResponseWriter, r *http.Request) {
|
||||
gotToken := ""
|
||||
parts := strings.Split(r.Header.Get("Authorization"), " ")
|
||||
if len(parts) > 1 && strings.EqualFold(parts[0], "bearer") {
|
||||
gotToken = parts[1]
|
||||
}
|
||||
|
||||
if wantToken != gotToken {
|
||||
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
fmt.Fprintln(w, "ok")
|
||||
}
|
||||
server := httptest.NewServer(http.HandlerFunc(handler))
|
||||
|
||||
c := api.ExecConfig{
|
||||
Command: "./testdata/test-plugin.sh",
|
||||
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||
}
|
||||
a, err := newAuthenticator(newCache(), &c)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
a.environ = environ
|
||||
a.now = now
|
||||
a.stderr = ioutil.Discard
|
||||
|
||||
client := http.Client{
|
||||
Transport: a.WrapTransport(http.DefaultTransport),
|
||||
}
|
||||
|
||||
get := func(t *testing.T, statusCode int) {
|
||||
t.Helper()
|
||||
resp, err := client.Get(server.URL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != statusCode {
|
||||
t.Errorf("wanted status %d got %d", statusCode, resp.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
setOutput(`{
|
||||
"kind": "ExecCredential",
|
||||
"apiVersion": "client.authentication.k8s.io/v1alpha1",
|
||||
"status": {
|
||||
"token": "token1"
|
||||
}
|
||||
}`)
|
||||
wantToken = "token1"
|
||||
get(t, http.StatusOK)
|
||||
|
||||
setOutput(`{
|
||||
"kind": "ExecCredential",
|
||||
"apiVersion": "client.authentication.k8s.io/v1alpha1",
|
||||
"status": {
|
||||
"token": "token2"
|
||||
}
|
||||
}`)
|
||||
// Previous token should be cached
|
||||
get(t, http.StatusOK)
|
||||
|
||||
wantToken = "token2"
|
||||
// Token is still cached, hits unauthorized but causes token to rotate.
|
||||
get(t, http.StatusUnauthorized)
|
||||
// Follow up request uses the rotated token.
|
||||
get(t, http.StatusOK)
|
||||
|
||||
setOutput(`{
|
||||
"kind": "ExecCredential",
|
||||
"apiVersion": "client.authentication.k8s.io/v1alpha1",
|
||||
"status": {
|
||||
"token": "token3",
|
||||
"expirationTimestamp": "` + now().Add(time.Hour).Format(time.RFC3339Nano) + `"
|
||||
}
|
||||
}`)
|
||||
wantToken = "token3"
|
||||
// Token is still cached, hit's unauthorized but causes rotation to token with an expiry.
|
||||
get(t, http.StatusUnauthorized)
|
||||
get(t, http.StatusOK)
|
||||
|
||||
// Move time forward 2 hours, "token3" is now expired.
|
||||
n = n.Add(time.Hour * 2)
|
||||
setOutput(`{
|
||||
"kind": "ExecCredential",
|
||||
"apiVersion": "client.authentication.k8s.io/v1alpha1",
|
||||
"status": {
|
||||
"token": "token4",
|
||||
"expirationTimestamp": "` + now().Add(time.Hour).Format(time.RFC3339Nano) + `"
|
||||
}
|
||||
}`)
|
||||
wantToken = "token4"
|
||||
// Old token is expired, should refresh automatically without hitting a 401.
|
||||
get(t, http.StatusOK)
|
||||
}
|
||||
497
vendor/k8s.io/client-go/plugin/pkg/client/auth/gcp/gcp_test.go
generated
vendored
497
vendor/k8s.io/client-go/plugin/pkg/client/auth/gcp/gcp_test.go
generated
vendored
|
|
@ -1,497 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package gcp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
type fakeOutput struct {
|
||||
args []string
|
||||
output string
|
||||
}
|
||||
|
||||
var (
|
||||
wantCmd []string
|
||||
// Output for fakeExec, keyed by command
|
||||
execOutputs = map[string]fakeOutput{
|
||||
"/default/no/args": {
|
||||
args: []string{},
|
||||
output: `{
|
||||
"access_token": "faketoken",
|
||||
"token_expiry": "2016-10-31T22:31:09.123000000Z"
|
||||
}`},
|
||||
"/default/legacy/args": {
|
||||
args: []string{"arg1", "arg2", "arg3"},
|
||||
output: `{
|
||||
"access_token": "faketoken",
|
||||
"token_expiry": "2016-10-31T22:31:09.123000000Z"
|
||||
}`},
|
||||
"/space in path/customkeys": {
|
||||
args: []string{"can", "haz", "auth"},
|
||||
output: `{
|
||||
"token": "faketoken",
|
||||
"token_expiry": {
|
||||
"datetime": "2016-10-31 22:31:09.123"
|
||||
}
|
||||
}`},
|
||||
"missing/tokenkey/noargs": {
|
||||
args: []string{},
|
||||
output: `{
|
||||
"broken": "faketoken",
|
||||
"token_expiry": {
|
||||
"datetime": "2016-10-31 22:31:09.123000000Z"
|
||||
}
|
||||
}`},
|
||||
"missing/expirykey/legacyargs": {
|
||||
args: []string{"split", "on", "whitespace"},
|
||||
output: `{
|
||||
"access_token": "faketoken",
|
||||
"expires": "2016-10-31T22:31:09.123000000Z"
|
||||
}`},
|
||||
"invalid expiry/timestamp": {
|
||||
args: []string{"foo", "--bar", "--baz=abc,def"},
|
||||
output: `{
|
||||
"access_token": "faketoken",
|
||||
"token_expiry": "sometime soon, idk"
|
||||
}`},
|
||||
"badjson": {
|
||||
args: []string{},
|
||||
output: `{
|
||||
"access_token": "faketoken",
|
||||
"token_expiry": "sometime soon, idk"
|
||||
------
|
||||
`},
|
||||
}
|
||||
)
|
||||
|
||||
func fakeExec(command string, args ...string) *exec.Cmd {
|
||||
cs := []string{"-test.run=TestHelperProcess", "--", command}
|
||||
cs = append(cs, args...)
|
||||
cmd := exec.Command(os.Args[0], cs...)
|
||||
cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}
|
||||
return cmd
|
||||
}
|
||||
|
||||
func TestHelperProcess(t *testing.T) {
|
||||
if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
|
||||
return
|
||||
}
|
||||
// Strip out the leading args used to exec into this function.
|
||||
gotCmd := os.Args[3]
|
||||
gotArgs := os.Args[4:]
|
||||
output, ok := execOutputs[gotCmd]
|
||||
if !ok {
|
||||
fmt.Fprintf(os.Stdout, "unexpected call cmd=%q args=%v\n", gotCmd, gotArgs)
|
||||
os.Exit(1)
|
||||
} else if !reflect.DeepEqual(output.args, gotArgs) {
|
||||
fmt.Fprintf(os.Stdout, "call cmd=%q got args %v, want: %v\n", gotCmd, gotArgs, output.args)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Fprintf(os.Stdout, output.output)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
func Test_isCmdTokenSource(t *testing.T) {
|
||||
c1 := map[string]string{"cmd-path": "foo"}
|
||||
if v := isCmdTokenSource(c1); !v {
|
||||
t.Fatalf("cmd-path present in config (%+v), but got %v", c1, v)
|
||||
}
|
||||
|
||||
c2 := map[string]string{"cmd-args": "foo bar"}
|
||||
if v := isCmdTokenSource(c2); v {
|
||||
t.Fatalf("cmd-path not present in config (%+v), but got %v", c2, v)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_tokenSource_cmd(t *testing.T) {
|
||||
if _, err := tokenSource(true, map[string]string{}); err == nil {
|
||||
t.Fatalf("expected error, cmd-args not present in config")
|
||||
}
|
||||
|
||||
c := map[string]string{
|
||||
"cmd-path": "foo",
|
||||
"cmd-args": "bar"}
|
||||
ts, err := tokenSource(true, c)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to return cmd token source: %+v", err)
|
||||
}
|
||||
if ts == nil {
|
||||
t.Fatal("returned nil token source")
|
||||
}
|
||||
if _, ok := ts.(*commandTokenSource); !ok {
|
||||
t.Fatalf("returned token source type:(%T) expected:(*commandTokenSource)", ts)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_tokenSource_cmdCannotBeUsedWithScopes(t *testing.T) {
|
||||
c := map[string]string{
|
||||
"cmd-path": "foo",
|
||||
"scopes": "A,B"}
|
||||
if _, err := tokenSource(true, c); err == nil {
|
||||
t.Fatal("expected error when scopes is used with cmd-path")
|
||||
}
|
||||
}
|
||||
|
||||
func Test_tokenSource_applicationDefaultCredentials_fails(t *testing.T) {
|
||||
// try to use empty ADC file
|
||||
fakeTokenFile, err := ioutil.TempFile("", "adctoken")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create fake token file: +%v", err)
|
||||
}
|
||||
fakeTokenFile.Close()
|
||||
defer os.Remove(fakeTokenFile.Name())
|
||||
|
||||
os.Setenv("GOOGLE_APPLICATION_CREDENTIALS", fakeTokenFile.Name())
|
||||
defer os.Unsetenv("GOOGLE_APPLICATION_CREDENTIALS")
|
||||
if _, err := tokenSource(false, map[string]string{}); err == nil {
|
||||
t.Fatalf("expected error because specified ADC token file is not a JSON")
|
||||
}
|
||||
}
|
||||
|
||||
func Test_tokenSource_applicationDefaultCredentials(t *testing.T) {
|
||||
fakeTokenFile, err := ioutil.TempFile("", "adctoken")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create fake token file: +%v", err)
|
||||
}
|
||||
fakeTokenFile.Close()
|
||||
defer os.Remove(fakeTokenFile.Name())
|
||||
if err := ioutil.WriteFile(fakeTokenFile.Name(), []byte(`{"type":"service_account"}`), 0600); err != nil {
|
||||
t.Fatalf("failed to write to fake token file: %+v", err)
|
||||
}
|
||||
|
||||
os.Setenv("GOOGLE_APPLICATION_CREDENTIALS", fakeTokenFile.Name())
|
||||
defer os.Unsetenv("GOOGLE_APPLICATION_CREDENTIALS")
|
||||
ts, err := tokenSource(false, map[string]string{})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get a token source: %+v", err)
|
||||
}
|
||||
if ts == nil {
|
||||
t.Fatal("returned nil token source")
|
||||
}
|
||||
}
|
||||
|
||||
func Test_parseScopes(t *testing.T) {
|
||||
cases := []struct {
|
||||
in map[string]string
|
||||
out []string
|
||||
}{
|
||||
{
|
||||
map[string]string{},
|
||||
[]string{
|
||||
"https://www.googleapis.com/auth/cloud-platform",
|
||||
"https://www.googleapis.com/auth/userinfo.email"},
|
||||
},
|
||||
{
|
||||
map[string]string{"scopes": ""},
|
||||
[]string{},
|
||||
},
|
||||
{
|
||||
map[string]string{"scopes": "A,B,C"},
|
||||
[]string{"A", "B", "C"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
got := parseScopes(c.in)
|
||||
if !reflect.DeepEqual(got, c.out) {
|
||||
t.Errorf("expected=%v, got=%v", c.out, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func errEquiv(got, want error) bool {
|
||||
if got == want {
|
||||
return true
|
||||
}
|
||||
if got != nil && want != nil {
|
||||
return strings.Contains(got.Error(), want.Error())
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func TestCmdTokenSource(t *testing.T) {
|
||||
execCommand = fakeExec
|
||||
fakeExpiry := time.Date(2016, 10, 31, 22, 31, 9, 123000000, time.UTC)
|
||||
customFmt := "2006-01-02 15:04:05.999999999"
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
gcpConfig map[string]string
|
||||
tok *oauth2.Token
|
||||
newErr, tokenErr error
|
||||
}{
|
||||
{
|
||||
"default",
|
||||
map[string]string{
|
||||
"cmd-path": "/default/no/args",
|
||||
},
|
||||
&oauth2.Token{
|
||||
AccessToken: "faketoken",
|
||||
TokenType: "Bearer",
|
||||
Expiry: fakeExpiry,
|
||||
},
|
||||
nil,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"default legacy args",
|
||||
map[string]string{
|
||||
"cmd-path": "/default/legacy/args arg1 arg2 arg3",
|
||||
},
|
||||
&oauth2.Token{
|
||||
AccessToken: "faketoken",
|
||||
TokenType: "Bearer",
|
||||
Expiry: fakeExpiry,
|
||||
},
|
||||
nil,
|
||||
nil,
|
||||
},
|
||||
|
||||
{
|
||||
"custom keys",
|
||||
map[string]string{
|
||||
"cmd-path": "/space in path/customkeys",
|
||||
"cmd-args": "can haz auth",
|
||||
"token-key": "{.token}",
|
||||
"expiry-key": "{.token_expiry.datetime}",
|
||||
"time-fmt": customFmt,
|
||||
},
|
||||
&oauth2.Token{
|
||||
AccessToken: "faketoken",
|
||||
TokenType: "Bearer",
|
||||
Expiry: fakeExpiry,
|
||||
},
|
||||
nil,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"missing cmd",
|
||||
map[string]string{
|
||||
"cmd-path": "",
|
||||
},
|
||||
nil,
|
||||
fmt.Errorf("missing access token cmd"),
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"missing token-key",
|
||||
map[string]string{
|
||||
"cmd-path": "missing/tokenkey/noargs",
|
||||
"token-key": "{.token}",
|
||||
},
|
||||
nil,
|
||||
nil,
|
||||
fmt.Errorf("error parsing token-key %q", "{.token}"),
|
||||
},
|
||||
|
||||
{
|
||||
"missing expiry-key",
|
||||
map[string]string{
|
||||
"cmd-path": "missing/expirykey/legacyargs split on whitespace",
|
||||
"expiry-key": "{.expiry}",
|
||||
},
|
||||
nil,
|
||||
nil,
|
||||
fmt.Errorf("error parsing expiry-key %q", "{.expiry}"),
|
||||
},
|
||||
{
|
||||
"invalid expiry timestamp",
|
||||
map[string]string{
|
||||
"cmd-path": "invalid expiry/timestamp",
|
||||
"cmd-args": "foo --bar --baz=abc,def",
|
||||
},
|
||||
&oauth2.Token{
|
||||
AccessToken: "faketoken",
|
||||
TokenType: "Bearer",
|
||||
Expiry: time.Time{},
|
||||
},
|
||||
nil,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"bad JSON",
|
||||
map[string]string{
|
||||
"cmd-path": "badjson",
|
||||
},
|
||||
nil,
|
||||
nil,
|
||||
fmt.Errorf("invalid character '-' after object key:value pair"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
provider, err := newGCPAuthProvider("", tc.gcpConfig, nil /* persister */)
|
||||
if !errEquiv(err, tc.newErr) {
|
||||
t.Errorf("%q newGCPAuthProvider error: got %v, want %v", tc.name, err, tc.newErr)
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ts := provider.(*gcpAuthProvider).tokenSource.(*cachedTokenSource).source.(*commandTokenSource)
|
||||
wantCmd = append([]string{ts.cmd}, ts.args...)
|
||||
tok, err := ts.Token()
|
||||
if !errEquiv(err, tc.tokenErr) {
|
||||
t.Errorf("%q Token() error: got %v, want %v", tc.name, err, tc.tokenErr)
|
||||
}
|
||||
if !reflect.DeepEqual(tok, tc.tok) {
|
||||
t.Errorf("%q Token() got %v, want %v", tc.name, tok, tc.tok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type fakePersister struct {
|
||||
lk sync.Mutex
|
||||
cache map[string]string
|
||||
}
|
||||
|
||||
func (f *fakePersister) Persist(cache map[string]string) error {
|
||||
f.lk.Lock()
|
||||
defer f.lk.Unlock()
|
||||
f.cache = map[string]string{}
|
||||
for k, v := range cache {
|
||||
f.cache[k] = v
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *fakePersister) read() map[string]string {
|
||||
ret := map[string]string{}
|
||||
f.lk.Lock()
|
||||
defer f.lk.Unlock()
|
||||
for k, v := range f.cache {
|
||||
ret[k] = v
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
type fakeTokenSource struct {
|
||||
token *oauth2.Token
|
||||
err error
|
||||
}
|
||||
|
||||
func (f *fakeTokenSource) Token() (*oauth2.Token, error) {
|
||||
return f.token, f.err
|
||||
}
|
||||
|
||||
func TestCachedTokenSource(t *testing.T) {
|
||||
tok := &oauth2.Token{AccessToken: "fakeaccesstoken"}
|
||||
persister := &fakePersister{}
|
||||
source := &fakeTokenSource{
|
||||
token: tok,
|
||||
err: nil,
|
||||
}
|
||||
cache := map[string]string{
|
||||
"foo": "bar",
|
||||
"baz": "bazinga",
|
||||
}
|
||||
ts, err := newCachedTokenSource("fakeaccesstoken", "", persister, source, cache)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(10)
|
||||
for i := 0; i < 10; i++ {
|
||||
go func() {
|
||||
_, err := ts.Token()
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %s", err)
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
cache["access-token"] = "fakeaccesstoken"
|
||||
cache["expiry"] = tok.Expiry.Format(time.RFC3339Nano)
|
||||
if got := persister.read(); !reflect.DeepEqual(got, cache) {
|
||||
t.Errorf("got cache %v, want %v", got, cache)
|
||||
}
|
||||
}
|
||||
|
||||
type MockTransport struct {
|
||||
res *http.Response
|
||||
}
|
||||
|
||||
func (t *MockTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
return t.res, nil
|
||||
}
|
||||
|
||||
func TestClearingCredentials(t *testing.T) {
|
||||
|
||||
fakeExpiry := time.Now().Add(time.Hour)
|
||||
|
||||
cache := map[string]string{
|
||||
"access-token": "fakeToken",
|
||||
"expiry": fakeExpiry.String(),
|
||||
}
|
||||
|
||||
cts := cachedTokenSource{
|
||||
source: nil,
|
||||
accessToken: cache["access-token"],
|
||||
expiry: fakeExpiry,
|
||||
persister: nil,
|
||||
cache: nil,
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
res http.Response
|
||||
cache map[string]string
|
||||
}{
|
||||
{
|
||||
"Unauthorized",
|
||||
http.Response{StatusCode: 401},
|
||||
make(map[string]string),
|
||||
},
|
||||
{
|
||||
"Authorized",
|
||||
http.Response{StatusCode: 200},
|
||||
cache,
|
||||
},
|
||||
}
|
||||
|
||||
persister := &fakePersister{}
|
||||
req := http.Request{Header: http.Header{}}
|
||||
|
||||
for _, tc := range tests {
|
||||
authProvider := gcpAuthProvider{&cts, persister}
|
||||
|
||||
fakeTransport := MockTransport{&tc.res}
|
||||
|
||||
transport := (authProvider.WrapTransport(&fakeTransport))
|
||||
persister.Persist(cache)
|
||||
|
||||
transport.RoundTrip(&req)
|
||||
|
||||
if got := persister.read(); !reflect.DeepEqual(got, tc.cache) {
|
||||
t.Errorf("got cache %v, want %v", got, tc.cache)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
138
vendor/k8s.io/client-go/plugin/pkg/client/auth/oidc/oidc_test.go
generated
vendored
138
vendor/k8s.io/client-go/plugin/pkg/client/auth/oidc/oidc_test.go
generated
vendored
|
|
@ -1,138 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package oidc
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestJSONTime(t *testing.T) {
|
||||
data := `{
|
||||
"t1": 1493851263,
|
||||
"t2": 1.493851263e9
|
||||
}`
|
||||
|
||||
var v struct {
|
||||
T1 jsonTime `json:"t1"`
|
||||
T2 jsonTime `json:"t2"`
|
||||
}
|
||||
if err := json.Unmarshal([]byte(data), &v); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
wantT1 := time.Unix(1493851263, 0)
|
||||
wantT2 := time.Unix(1493851263, 0)
|
||||
gotT1 := time.Time(v.T1)
|
||||
gotT2 := time.Time(v.T2)
|
||||
|
||||
if !wantT1.Equal(gotT1) {
|
||||
t.Errorf("t1 value: wanted %s got %s", wantT1, gotT1)
|
||||
}
|
||||
if !wantT2.Equal(gotT2) {
|
||||
t.Errorf("t2 value: wanted %s got %s", wantT2, gotT2)
|
||||
}
|
||||
}
|
||||
|
||||
func encodeJWT(header, payload, sig string) string {
|
||||
e := func(s string) string {
|
||||
return base64.RawURLEncoding.EncodeToString([]byte(s))
|
||||
}
|
||||
return e(header) + "." + e(payload) + "." + e(sig)
|
||||
}
|
||||
|
||||
func TestExpired(t *testing.T) {
|
||||
now := time.Now()
|
||||
|
||||
nowFunc := func() time.Time { return now }
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
idToken string
|
||||
wantErr bool
|
||||
wantExpired bool
|
||||
}{
|
||||
{
|
||||
name: "valid",
|
||||
idToken: encodeJWT(
|
||||
"{}",
|
||||
fmt.Sprintf(`{"exp":%d}`, now.Add(time.Hour).Unix()),
|
||||
"blah", // signature isn't veified.
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "expired",
|
||||
idToken: encodeJWT(
|
||||
"{}",
|
||||
fmt.Sprintf(`{"exp":%d}`, now.Add(-time.Hour).Unix()),
|
||||
"blah", // signature isn't veified.
|
||||
),
|
||||
wantExpired: true,
|
||||
},
|
||||
{
|
||||
name: "bad exp claim",
|
||||
idToken: encodeJWT(
|
||||
"{}",
|
||||
`{"exp":"foobar"}`,
|
||||
"blah", // signature isn't veified.
|
||||
),
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "not an id token",
|
||||
idToken: "notanidtoken",
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
valid, err := idTokenExpired(nowFunc, test.idToken)
|
||||
if err != nil {
|
||||
if !test.wantErr {
|
||||
t.Errorf("parse error: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if test.wantExpired == valid {
|
||||
t.Errorf("wanted expired %t, got %t", test.wantExpired, !valid)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientCache(t *testing.T) {
|
||||
cache := newClientCache()
|
||||
|
||||
if _, ok := cache.getClient("issuer1", "id1"); ok {
|
||||
t.Fatalf("got client before putting one in the cache")
|
||||
}
|
||||
|
||||
cli1 := new(oidcAuthProvider)
|
||||
cli2 := new(oidcAuthProvider)
|
||||
|
||||
gotcli := cache.setClient("issuer1", "id1", cli1)
|
||||
if cli1 != gotcli {
|
||||
t.Fatalf("set first client and got a different one")
|
||||
}
|
||||
|
||||
gotcli = cache.setClient("issuer1", "id1", cli2)
|
||||
if cli1 != gotcli {
|
||||
t.Fatalf("set a second client and didn't get the first")
|
||||
}
|
||||
}
|
||||
116
vendor/k8s.io/client-go/plugin/pkg/client/auth/openstack/openstack_test.go
generated
vendored
116
vendor/k8s.io/client-go/plugin/pkg/client/auth/openstack/openstack_test.go
generated
vendored
|
|
@ -1,116 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 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 (
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// testTokenGetter is a simple random token getter.
|
||||
type testTokenGetter struct{}
|
||||
|
||||
const LetterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
|
||||
func RandStringBytes(n int) string {
|
||||
b := make([]byte, n)
|
||||
for i := range b {
|
||||
b[i] = LetterBytes[rand.Intn(len(LetterBytes))]
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func (*testTokenGetter) Token() (string, error) {
|
||||
return RandStringBytes(32), nil
|
||||
}
|
||||
|
||||
// testRoundTripper is mocked roundtripper which responds with unauthorized when
|
||||
// there is no authorization header, otherwise returns status ok.
|
||||
type testRoundTripper struct{}
|
||||
|
||||
func (trt *testRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
authHeader := req.Header.Get("Authorization")
|
||||
if authHeader == "" || authHeader == "Bearer " {
|
||||
return &http.Response{
|
||||
StatusCode: http.StatusUnauthorized,
|
||||
}, nil
|
||||
}
|
||||
return &http.Response{StatusCode: http.StatusOK}, nil
|
||||
}
|
||||
|
||||
func TestOpenstackAuthProvider(t *testing.T) {
|
||||
trt := &tokenRoundTripper{
|
||||
RoundTripper: &testRoundTripper{},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
ttl time.Duration
|
||||
interval time.Duration
|
||||
same bool
|
||||
}{
|
||||
{
|
||||
name: "normal",
|
||||
ttl: 2 * time.Second,
|
||||
interval: 1 * time.Second,
|
||||
same: true,
|
||||
},
|
||||
{
|
||||
name: "expire",
|
||||
ttl: 1 * time.Second,
|
||||
interval: 2 * time.Second,
|
||||
same: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
trt.tokenGetter = &cachedGetter{
|
||||
tokenGetter: &testTokenGetter{},
|
||||
ttl: test.ttl,
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(http.MethodPost, "https://test-api-server.com", nil)
|
||||
if err != nil {
|
||||
t.Errorf("failed to new request: %s", err)
|
||||
}
|
||||
trt.RoundTrip(req)
|
||||
header := req.Header.Get("Authorization")
|
||||
if header == "" {
|
||||
t.Errorf("expect to see token in header, but is absent")
|
||||
}
|
||||
|
||||
time.Sleep(test.interval)
|
||||
|
||||
req, err = http.NewRequest(http.MethodPost, "https://test-api-server.com", nil)
|
||||
if err != nil {
|
||||
t.Errorf("failed to new request: %s", err)
|
||||
}
|
||||
trt.RoundTrip(req)
|
||||
newHeader := req.Header.Get("Authorization")
|
||||
if newHeader == "" {
|
||||
t.Errorf("expect to see token in header, but is absent")
|
||||
}
|
||||
|
||||
same := newHeader == header
|
||||
if same != test.same {
|
||||
t.Errorf("expect to get %t when compare header, but saw %t", test.same, same)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
343
vendor/k8s.io/client-go/rest/client_test.go
generated
vendored
343
vendor/k8s.io/client-go/rest/client_test.go
generated
vendored
|
|
@ -1,343 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 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 (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"fmt"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
v1beta1 "k8s.io/api/extensions/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/diff"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
utiltesting "k8s.io/client-go/util/testing"
|
||||
)
|
||||
|
||||
type TestParam struct {
|
||||
actualError error
|
||||
expectingError bool
|
||||
actualCreated bool
|
||||
expCreated bool
|
||||
expStatus *metav1.Status
|
||||
testBody bool
|
||||
testBodyErrorIsNotNil bool
|
||||
}
|
||||
|
||||
// TestSerializer makes sure that you're always able to decode metav1.Status
|
||||
func TestSerializer(t *testing.T) {
|
||||
gv := v1beta1.SchemeGroupVersion
|
||||
contentConfig := ContentConfig{
|
||||
ContentType: "application/json",
|
||||
GroupVersion: &gv,
|
||||
NegotiatedSerializer: serializer.DirectCodecFactory{CodecFactory: scheme.Codecs},
|
||||
}
|
||||
|
||||
serializer, err := createSerializers(contentConfig)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// bytes based on actual return from API server when encoding an "unversioned" object
|
||||
obj, err := runtime.Decode(serializer.Decoder, []byte(`{"kind":"Status","apiVersion":"v1","metadata":{},"status":"Success"}`))
|
||||
t.Log(obj)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDoRequestSuccess(t *testing.T) {
|
||||
testServer, fakeHandler, status := testServerEnv(t, 200)
|
||||
defer testServer.Close()
|
||||
|
||||
c, err := restClient(testServer)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
body, err := c.Get().Prefix("test").Do().Raw()
|
||||
|
||||
testParam := TestParam{actualError: err, expectingError: false, expCreated: true,
|
||||
expStatus: status, testBody: true, testBodyErrorIsNotNil: false}
|
||||
validate(testParam, t, body, fakeHandler)
|
||||
}
|
||||
|
||||
func TestDoRequestFailed(t *testing.T) {
|
||||
status := &metav1.Status{
|
||||
Code: http.StatusNotFound,
|
||||
Status: metav1.StatusFailure,
|
||||
Reason: metav1.StatusReasonNotFound,
|
||||
Message: " \"\" not found",
|
||||
Details: &metav1.StatusDetails{},
|
||||
}
|
||||
expectedBody, _ := runtime.Encode(scheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), status)
|
||||
fakeHandler := utiltesting.FakeHandler{
|
||||
StatusCode: 404,
|
||||
ResponseBody: string(expectedBody),
|
||||
T: t,
|
||||
}
|
||||
testServer := httptest.NewServer(&fakeHandler)
|
||||
defer testServer.Close()
|
||||
|
||||
c, err := restClient(testServer)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
err = c.Get().Do().Error()
|
||||
if err == nil {
|
||||
t.Errorf("unexpected non-error")
|
||||
}
|
||||
ss, ok := err.(errors.APIStatus)
|
||||
if !ok {
|
||||
t.Errorf("unexpected error type %v", err)
|
||||
}
|
||||
actual := ss.Status()
|
||||
if !reflect.DeepEqual(status, &actual) {
|
||||
t.Errorf("Unexpected mis-match: %s", diff.ObjectReflectDiff(status, &actual))
|
||||
}
|
||||
}
|
||||
|
||||
func TestDoRawRequestFailed(t *testing.T) {
|
||||
status := &metav1.Status{
|
||||
Code: http.StatusNotFound,
|
||||
Status: metav1.StatusFailure,
|
||||
Reason: metav1.StatusReasonNotFound,
|
||||
Message: "the server could not find the requested resource",
|
||||
Details: &metav1.StatusDetails{
|
||||
Causes: []metav1.StatusCause{
|
||||
{Type: metav1.CauseTypeUnexpectedServerResponse, Message: "unknown"},
|
||||
},
|
||||
},
|
||||
}
|
||||
expectedBody, _ := runtime.Encode(scheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), status)
|
||||
fakeHandler := utiltesting.FakeHandler{
|
||||
StatusCode: 404,
|
||||
ResponseBody: string(expectedBody),
|
||||
T: t,
|
||||
}
|
||||
testServer := httptest.NewServer(&fakeHandler)
|
||||
defer testServer.Close()
|
||||
|
||||
c, err := restClient(testServer)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
body, err := c.Get().Do().Raw()
|
||||
|
||||
if err == nil || body == nil {
|
||||
t.Errorf("unexpected non-error: %#v", body)
|
||||
}
|
||||
ss, ok := err.(errors.APIStatus)
|
||||
if !ok {
|
||||
t.Errorf("unexpected error type %v", err)
|
||||
}
|
||||
actual := ss.Status()
|
||||
if !reflect.DeepEqual(status, &actual) {
|
||||
t.Errorf("Unexpected mis-match: %s", diff.ObjectReflectDiff(status, &actual))
|
||||
}
|
||||
}
|
||||
|
||||
func TestDoRequestCreated(t *testing.T) {
|
||||
testServer, fakeHandler, status := testServerEnv(t, 201)
|
||||
defer testServer.Close()
|
||||
|
||||
c, err := restClient(testServer)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
created := false
|
||||
body, err := c.Get().Prefix("test").Do().WasCreated(&created).Raw()
|
||||
|
||||
testParam := TestParam{actualError: err, expectingError: false, expCreated: true,
|
||||
expStatus: status, testBody: false}
|
||||
validate(testParam, t, body, fakeHandler)
|
||||
}
|
||||
|
||||
func TestDoRequestNotCreated(t *testing.T) {
|
||||
testServer, fakeHandler, expectedStatus := testServerEnv(t, 202)
|
||||
defer testServer.Close()
|
||||
c, err := restClient(testServer)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
created := false
|
||||
body, err := c.Get().Prefix("test").Do().WasCreated(&created).Raw()
|
||||
testParam := TestParam{actualError: err, expectingError: false, expCreated: false,
|
||||
expStatus: expectedStatus, testBody: false}
|
||||
validate(testParam, t, body, fakeHandler)
|
||||
}
|
||||
|
||||
func TestDoRequestAcceptedNoContentReturned(t *testing.T) {
|
||||
testServer, fakeHandler, _ := testServerEnv(t, 204)
|
||||
defer testServer.Close()
|
||||
|
||||
c, err := restClient(testServer)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
created := false
|
||||
body, err := c.Get().Prefix("test").Do().WasCreated(&created).Raw()
|
||||
testParam := TestParam{actualError: err, expectingError: false, expCreated: false,
|
||||
testBody: false}
|
||||
validate(testParam, t, body, fakeHandler)
|
||||
}
|
||||
|
||||
func TestBadRequest(t *testing.T) {
|
||||
testServer, fakeHandler, _ := testServerEnv(t, 400)
|
||||
defer testServer.Close()
|
||||
c, err := restClient(testServer)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
created := false
|
||||
body, err := c.Get().Prefix("test").Do().WasCreated(&created).Raw()
|
||||
testParam := TestParam{actualError: err, expectingError: true, expCreated: false,
|
||||
testBody: true}
|
||||
validate(testParam, t, body, fakeHandler)
|
||||
}
|
||||
|
||||
func validate(testParam TestParam, t *testing.T, body []byte, fakeHandler *utiltesting.FakeHandler) {
|
||||
switch {
|
||||
case testParam.expectingError && testParam.actualError == nil:
|
||||
t.Errorf("Expected error")
|
||||
case !testParam.expectingError && testParam.actualError != nil:
|
||||
t.Error(testParam.actualError)
|
||||
}
|
||||
if !testParam.expCreated {
|
||||
if testParam.actualCreated {
|
||||
t.Errorf("Expected object not to be created")
|
||||
}
|
||||
}
|
||||
statusOut, err := runtime.Decode(scheme.Codecs.UniversalDeserializer(), body)
|
||||
if testParam.testBody {
|
||||
if testParam.testBodyErrorIsNotNil && err == nil {
|
||||
t.Errorf("Expected Error")
|
||||
}
|
||||
if !testParam.testBodyErrorIsNotNil && err != nil {
|
||||
t.Errorf("Unexpected Error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if testParam.expStatus != nil {
|
||||
if !reflect.DeepEqual(testParam.expStatus, statusOut) {
|
||||
t.Errorf("Unexpected mis-match. Expected %#v. Saw %#v", testParam.expStatus, statusOut)
|
||||
}
|
||||
}
|
||||
fakeHandler.ValidateRequest(t, "/"+v1.SchemeGroupVersion.String()+"/test", "GET", nil)
|
||||
|
||||
}
|
||||
|
||||
func TestHttpMethods(t *testing.T) {
|
||||
testServer, _, _ := testServerEnv(t, 200)
|
||||
defer testServer.Close()
|
||||
c, _ := restClient(testServer)
|
||||
|
||||
request := c.Post()
|
||||
if request == nil {
|
||||
t.Errorf("Post : Object returned should not be nil")
|
||||
}
|
||||
|
||||
request = c.Get()
|
||||
if request == nil {
|
||||
t.Errorf("Get: Object returned should not be nil")
|
||||
}
|
||||
|
||||
request = c.Put()
|
||||
if request == nil {
|
||||
t.Errorf("Put : Object returned should not be nil")
|
||||
}
|
||||
|
||||
request = c.Delete()
|
||||
if request == nil {
|
||||
t.Errorf("Delete : Object returned should not be nil")
|
||||
}
|
||||
|
||||
request = c.Patch(types.JSONPatchType)
|
||||
if request == nil {
|
||||
t.Errorf("Patch : Object returned should not be nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateBackoffManager(t *testing.T) {
|
||||
|
||||
theUrl, _ := url.Parse("http://localhost")
|
||||
|
||||
// 1 second base backoff + duration of 2 seconds -> exponential backoff for requests.
|
||||
os.Setenv(envBackoffBase, "1")
|
||||
os.Setenv(envBackoffDuration, "2")
|
||||
backoff := readExpBackoffConfig()
|
||||
backoff.UpdateBackoff(theUrl, nil, 500)
|
||||
backoff.UpdateBackoff(theUrl, nil, 500)
|
||||
if backoff.CalculateBackoff(theUrl)/time.Second != 2 {
|
||||
t.Errorf("Backoff env not working.")
|
||||
}
|
||||
|
||||
// 0 duration -> no backoff.
|
||||
os.Setenv(envBackoffBase, "1")
|
||||
os.Setenv(envBackoffDuration, "0")
|
||||
backoff.UpdateBackoff(theUrl, nil, 500)
|
||||
backoff.UpdateBackoff(theUrl, nil, 500)
|
||||
backoff = readExpBackoffConfig()
|
||||
if backoff.CalculateBackoff(theUrl)/time.Second != 0 {
|
||||
t.Errorf("Zero backoff duration, but backoff still occurring.")
|
||||
}
|
||||
|
||||
// No env -> No backoff.
|
||||
os.Setenv(envBackoffBase, "")
|
||||
os.Setenv(envBackoffDuration, "")
|
||||
backoff = readExpBackoffConfig()
|
||||
backoff.UpdateBackoff(theUrl, nil, 500)
|
||||
backoff.UpdateBackoff(theUrl, nil, 500)
|
||||
if backoff.CalculateBackoff(theUrl)/time.Second != 0 {
|
||||
t.Errorf("Backoff should have been 0.")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func testServerEnv(t *testing.T, statusCode int) (*httptest.Server, *utiltesting.FakeHandler, *metav1.Status) {
|
||||
status := &metav1.Status{TypeMeta: metav1.TypeMeta{APIVersion: "v1", Kind: "Status"}, Status: fmt.Sprintf("%s", metav1.StatusSuccess)}
|
||||
expectedBody, _ := runtime.Encode(scheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), status)
|
||||
fakeHandler := utiltesting.FakeHandler{
|
||||
StatusCode: statusCode,
|
||||
ResponseBody: string(expectedBody),
|
||||
T: t,
|
||||
}
|
||||
testServer := httptest.NewServer(&fakeHandler)
|
||||
return testServer, &fakeHandler, status
|
||||
}
|
||||
|
||||
func restClient(testServer *httptest.Server) (*RESTClient, error) {
|
||||
c, err := RESTClientFor(&Config{
|
||||
Host: testServer.URL,
|
||||
ContentConfig: ContentConfig{
|
||||
GroupVersion: &v1.SchemeGroupVersion,
|
||||
NegotiatedSerializer: serializer.DirectCodecFactory{CodecFactory: scheme.Codecs},
|
||||
},
|
||||
Username: "user",
|
||||
Password: "pass",
|
||||
})
|
||||
return c, err
|
||||
}
|
||||
376
vendor/k8s.io/client-go/rest/config_test.go
generated
vendored
376
vendor/k8s.io/client-go/rest/config_test.go
generated
vendored
|
|
@ -1,376 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package rest
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
fuzz "github.com/google/gofuzz"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/diff"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
"k8s.io/client-go/util/flowcontrol"
|
||||
|
||||
"errors"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestIsConfigTransportTLS(t *testing.T) {
|
||||
testCases := []struct {
|
||||
Config *Config
|
||||
TransportTLS bool
|
||||
}{
|
||||
{
|
||||
Config: &Config{},
|
||||
TransportTLS: false,
|
||||
},
|
||||
{
|
||||
Config: &Config{
|
||||
Host: "https://localhost",
|
||||
},
|
||||
TransportTLS: true,
|
||||
},
|
||||
{
|
||||
Config: &Config{
|
||||
Host: "localhost",
|
||||
TLSClientConfig: TLSClientConfig{
|
||||
CertFile: "foo",
|
||||
},
|
||||
},
|
||||
TransportTLS: true,
|
||||
},
|
||||
{
|
||||
Config: &Config{
|
||||
Host: "///:://localhost",
|
||||
TLSClientConfig: TLSClientConfig{
|
||||
CertFile: "foo",
|
||||
},
|
||||
},
|
||||
TransportTLS: false,
|
||||
},
|
||||
{
|
||||
Config: &Config{
|
||||
Host: "1.2.3.4:567",
|
||||
TLSClientConfig: TLSClientConfig{
|
||||
Insecure: true,
|
||||
},
|
||||
},
|
||||
TransportTLS: true,
|
||||
},
|
||||
}
|
||||
for _, testCase := range testCases {
|
||||
if err := SetKubernetesDefaults(testCase.Config); err != nil {
|
||||
t.Errorf("setting defaults failed for %#v: %v", testCase.Config, err)
|
||||
continue
|
||||
}
|
||||
useTLS := IsConfigTransportTLS(*testCase.Config)
|
||||
if testCase.TransportTLS != useTLS {
|
||||
t.Errorf("expected %v for %#v", testCase.TransportTLS, testCase.Config)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetKubernetesDefaultsUserAgent(t *testing.T) {
|
||||
config := &Config{}
|
||||
if err := SetKubernetesDefaults(config); err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
if !strings.Contains(config.UserAgent, "kubernetes/") {
|
||||
t.Errorf("no user agent set: %#v", config)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdjustVersion(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
assert.Equal("1.2.3", adjustVersion("1.2.3-alpha4"))
|
||||
assert.Equal("1.2.3", adjustVersion("1.2.3-alpha"))
|
||||
assert.Equal("1.2.3", adjustVersion("1.2.3"))
|
||||
assert.Equal("unknown", adjustVersion(""))
|
||||
}
|
||||
|
||||
func TestAdjustCommit(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
assert.Equal("1234567", adjustCommit("1234567890"))
|
||||
assert.Equal("123456", adjustCommit("123456"))
|
||||
assert.Equal("unknown", adjustCommit(""))
|
||||
}
|
||||
|
||||
func TestAdjustCommand(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
assert.Equal("beans", adjustCommand(filepath.Join("home", "bob", "Downloads", "beans")))
|
||||
assert.Equal("beans", adjustCommand(filepath.Join(".", "beans")))
|
||||
assert.Equal("beans", adjustCommand("beans"))
|
||||
assert.Equal("unknown", adjustCommand(""))
|
||||
}
|
||||
|
||||
func TestBuildUserAgent(t *testing.T) {
|
||||
assert.New(t).Equal(
|
||||
"lynx/nicest (beos/itanium) kubernetes/baaaaaaaaad",
|
||||
buildUserAgent(
|
||||
"lynx", "nicest",
|
||||
"beos", "itanium", "baaaaaaaaad"))
|
||||
}
|
||||
|
||||
// This function untestable since it doesn't accept arguments.
|
||||
func TestDefaultKubernetesUserAgent(t *testing.T) {
|
||||
assert.New(t).Contains(DefaultKubernetesUserAgent(), "kubernetes")
|
||||
}
|
||||
|
||||
func TestRESTClientRequires(t *testing.T) {
|
||||
if _, err := RESTClientFor(&Config{Host: "127.0.0.1", ContentConfig: ContentConfig{NegotiatedSerializer: scheme.Codecs}}); err == nil {
|
||||
t.Errorf("unexpected non-error")
|
||||
}
|
||||
if _, err := RESTClientFor(&Config{Host: "127.0.0.1", ContentConfig: ContentConfig{GroupVersion: &v1.SchemeGroupVersion}}); err == nil {
|
||||
t.Errorf("unexpected non-error")
|
||||
}
|
||||
if _, err := RESTClientFor(&Config{Host: "127.0.0.1", ContentConfig: ContentConfig{GroupVersion: &v1.SchemeGroupVersion, NegotiatedSerializer: scheme.Codecs}}); err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
type fakeLimiter struct {
|
||||
FakeSaturation float64
|
||||
FakeQPS float32
|
||||
}
|
||||
|
||||
func (t *fakeLimiter) TryAccept() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (t *fakeLimiter) Saturation() float64 {
|
||||
return t.FakeSaturation
|
||||
}
|
||||
|
||||
func (t *fakeLimiter) QPS() float32 {
|
||||
return t.FakeQPS
|
||||
}
|
||||
|
||||
func (t *fakeLimiter) Stop() {}
|
||||
|
||||
func (t *fakeLimiter) Accept() {}
|
||||
|
||||
type fakeCodec struct{}
|
||||
|
||||
func (c *fakeCodec) Decode([]byte, *schema.GroupVersionKind, runtime.Object) (runtime.Object, *schema.GroupVersionKind, error) {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
func (c *fakeCodec) Encode(obj runtime.Object, stream io.Writer) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type fakeRoundTripper struct{}
|
||||
|
||||
func (r *fakeRoundTripper) RoundTrip(*http.Request) (*http.Response, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var fakeWrapperFunc = func(http.RoundTripper) http.RoundTripper {
|
||||
return &fakeRoundTripper{}
|
||||
}
|
||||
|
||||
type fakeNegotiatedSerializer struct{}
|
||||
|
||||
func (n *fakeNegotiatedSerializer) SupportedMediaTypes() []runtime.SerializerInfo {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *fakeNegotiatedSerializer) EncoderForVersion(serializer runtime.Encoder, gv runtime.GroupVersioner) runtime.Encoder {
|
||||
return &fakeCodec{}
|
||||
}
|
||||
|
||||
func (n *fakeNegotiatedSerializer) DecoderToVersion(serializer runtime.Decoder, gv runtime.GroupVersioner) runtime.Decoder {
|
||||
return &fakeCodec{}
|
||||
}
|
||||
|
||||
var fakeDialFunc = func(network, addr string) (net.Conn, error) {
|
||||
return nil, fakeDialerError
|
||||
}
|
||||
var fakeDialerError = errors.New("fakedialer")
|
||||
|
||||
type fakeAuthProviderConfigPersister struct{}
|
||||
|
||||
func (fakeAuthProviderConfigPersister) Persist(map[string]string) error {
|
||||
return fakeAuthProviderConfigPersisterError
|
||||
}
|
||||
|
||||
var fakeAuthProviderConfigPersisterError = errors.New("fakeAuthProviderConfigPersisterError")
|
||||
|
||||
func TestAnonymousConfig(t *testing.T) {
|
||||
f := fuzz.New().NilChance(0.0).NumElements(1, 1)
|
||||
f.Funcs(
|
||||
func(r *runtime.Codec, f fuzz.Continue) {
|
||||
codec := &fakeCodec{}
|
||||
f.Fuzz(codec)
|
||||
*r = codec
|
||||
},
|
||||
func(r *http.RoundTripper, f fuzz.Continue) {
|
||||
roundTripper := &fakeRoundTripper{}
|
||||
f.Fuzz(roundTripper)
|
||||
*r = roundTripper
|
||||
},
|
||||
func(fn *func(http.RoundTripper) http.RoundTripper, f fuzz.Continue) {
|
||||
*fn = fakeWrapperFunc
|
||||
},
|
||||
func(r *runtime.NegotiatedSerializer, f fuzz.Continue) {
|
||||
serializer := &fakeNegotiatedSerializer{}
|
||||
f.Fuzz(serializer)
|
||||
*r = serializer
|
||||
},
|
||||
func(r *flowcontrol.RateLimiter, f fuzz.Continue) {
|
||||
limiter := &fakeLimiter{}
|
||||
f.Fuzz(limiter)
|
||||
*r = limiter
|
||||
},
|
||||
// Authentication does not require fuzzer
|
||||
func(r *AuthProviderConfigPersister, f fuzz.Continue) {},
|
||||
func(r *clientcmdapi.AuthProviderConfig, f fuzz.Continue) {
|
||||
r.Config = map[string]string{}
|
||||
},
|
||||
// Dial does not require fuzzer
|
||||
func(r *func(network, addr string) (net.Conn, error), f fuzz.Continue) {},
|
||||
)
|
||||
for i := 0; i < 20; i++ {
|
||||
original := &Config{}
|
||||
f.Fuzz(original)
|
||||
actual := AnonymousClientConfig(original)
|
||||
expected := *original
|
||||
|
||||
// this is the list of known security related fields, add to this list if a new field
|
||||
// is added to Config, update AnonymousClientConfig to preserve the field otherwise.
|
||||
expected.Impersonate = ImpersonationConfig{}
|
||||
expected.BearerToken = ""
|
||||
expected.Username = ""
|
||||
expected.Password = ""
|
||||
expected.AuthProvider = nil
|
||||
expected.AuthConfigPersister = nil
|
||||
expected.ExecProvider = nil
|
||||
expected.TLSClientConfig.CertData = nil
|
||||
expected.TLSClientConfig.CertFile = ""
|
||||
expected.TLSClientConfig.KeyData = nil
|
||||
expected.TLSClientConfig.KeyFile = ""
|
||||
|
||||
// The DeepEqual cannot handle the func comparison, so we just verify if the
|
||||
// function return the expected object.
|
||||
if actual.WrapTransport == nil || !reflect.DeepEqual(expected.WrapTransport(nil), &fakeRoundTripper{}) {
|
||||
t.Fatalf("AnonymousClientConfig dropped the WrapTransport field")
|
||||
} else {
|
||||
actual.WrapTransport = nil
|
||||
expected.WrapTransport = nil
|
||||
}
|
||||
if actual.Dial != nil {
|
||||
_, actualError := actual.Dial("", "")
|
||||
_, expectedError := actual.Dial("", "")
|
||||
if !reflect.DeepEqual(expectedError, actualError) {
|
||||
t.Fatalf("CopyConfig dropped the Dial field")
|
||||
}
|
||||
} else {
|
||||
actual.Dial = nil
|
||||
expected.Dial = nil
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(*actual, expected) {
|
||||
t.Fatalf("AnonymousClientConfig dropped unexpected fields, identify whether they are security related or not: %s", diff.ObjectGoPrintDiff(expected, actual))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCopyConfig(t *testing.T) {
|
||||
f := fuzz.New().NilChance(0.0).NumElements(1, 1)
|
||||
f.Funcs(
|
||||
func(r *runtime.Codec, f fuzz.Continue) {
|
||||
codec := &fakeCodec{}
|
||||
f.Fuzz(codec)
|
||||
*r = codec
|
||||
},
|
||||
func(r *http.RoundTripper, f fuzz.Continue) {
|
||||
roundTripper := &fakeRoundTripper{}
|
||||
f.Fuzz(roundTripper)
|
||||
*r = roundTripper
|
||||
},
|
||||
func(fn *func(http.RoundTripper) http.RoundTripper, f fuzz.Continue) {
|
||||
*fn = fakeWrapperFunc
|
||||
},
|
||||
func(r *runtime.NegotiatedSerializer, f fuzz.Continue) {
|
||||
serializer := &fakeNegotiatedSerializer{}
|
||||
f.Fuzz(serializer)
|
||||
*r = serializer
|
||||
},
|
||||
func(r *flowcontrol.RateLimiter, f fuzz.Continue) {
|
||||
limiter := &fakeLimiter{}
|
||||
f.Fuzz(limiter)
|
||||
*r = limiter
|
||||
},
|
||||
func(r *AuthProviderConfigPersister, f fuzz.Continue) {
|
||||
*r = fakeAuthProviderConfigPersister{}
|
||||
},
|
||||
func(r *func(network, addr string) (net.Conn, error), f fuzz.Continue) {
|
||||
*r = fakeDialFunc
|
||||
},
|
||||
)
|
||||
for i := 0; i < 20; i++ {
|
||||
original := &Config{}
|
||||
f.Fuzz(original)
|
||||
actual := CopyConfig(original)
|
||||
expected := *original
|
||||
|
||||
// this is the list of known risky fields, add to this list if a new field
|
||||
// is added to Config, update CopyConfig to preserve the field otherwise.
|
||||
|
||||
// The DeepEqual cannot handle the func comparison, so we just verify if the
|
||||
// function return the expected object.
|
||||
if actual.WrapTransport == nil || !reflect.DeepEqual(expected.WrapTransport(nil), &fakeRoundTripper{}) {
|
||||
t.Fatalf("CopyConfig dropped the WrapTransport field")
|
||||
} else {
|
||||
actual.WrapTransport = nil
|
||||
expected.WrapTransport = nil
|
||||
}
|
||||
if actual.Dial != nil {
|
||||
_, actualError := actual.Dial("", "")
|
||||
_, expectedError := actual.Dial("", "")
|
||||
if !reflect.DeepEqual(expectedError, actualError) {
|
||||
t.Fatalf("CopyConfig dropped the Dial field")
|
||||
}
|
||||
}
|
||||
actual.Dial = nil
|
||||
expected.Dial = nil
|
||||
if actual.AuthConfigPersister != nil {
|
||||
actualError := actual.AuthConfigPersister.Persist(nil)
|
||||
expectedError := actual.AuthConfigPersister.Persist(nil)
|
||||
if !reflect.DeepEqual(expectedError, actualError) {
|
||||
t.Fatalf("CopyConfig dropped the Dial field")
|
||||
}
|
||||
}
|
||||
actual.AuthConfigPersister = nil
|
||||
expected.AuthConfigPersister = nil
|
||||
|
||||
if !reflect.DeepEqual(*actual, expected) {
|
||||
t.Fatalf("CopyConfig dropped unexpected fields, identify whether they are security related or not: %s", diff.ObjectReflectDiff(expected, *actual))
|
||||
}
|
||||
}
|
||||
}
|
||||
311
vendor/k8s.io/client-go/rest/plugin_test.go
generated
vendored
311
vendor/k8s.io/client-go/rest/plugin_test.go
generated
vendored
|
|
@ -1,311 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package rest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
)
|
||||
|
||||
func TestAuthPluginWrapTransport(t *testing.T) {
|
||||
if err := RegisterAuthProviderPlugin("pluginA", pluginAProvider); err != nil {
|
||||
t.Errorf("Unexpected error: failed to register pluginA: %v", err)
|
||||
}
|
||||
if err := RegisterAuthProviderPlugin("pluginB", pluginBProvider); err != nil {
|
||||
t.Errorf("Unexpected error: failed to register pluginB: %v", err)
|
||||
}
|
||||
if err := RegisterAuthProviderPlugin("pluginFail", pluginFailProvider); err != nil {
|
||||
t.Errorf("Unexpected error: failed to register pluginFail: %v", err)
|
||||
}
|
||||
testCases := []struct {
|
||||
useWrapTransport bool
|
||||
plugin string
|
||||
expectErr bool
|
||||
expectPluginA bool
|
||||
expectPluginB bool
|
||||
}{
|
||||
{false, "", false, false, false},
|
||||
{false, "pluginA", false, true, false},
|
||||
{false, "pluginB", false, false, true},
|
||||
{false, "pluginFail", true, false, false},
|
||||
{false, "pluginUnknown", true, false, false},
|
||||
}
|
||||
for i, tc := range testCases {
|
||||
c := Config{}
|
||||
if tc.useWrapTransport {
|
||||
// Specify an existing WrapTransport in the config to make sure that
|
||||
// plugins play nicely.
|
||||
c.WrapTransport = func(rt http.RoundTripper) http.RoundTripper {
|
||||
return &wrapTransport{rt}
|
||||
}
|
||||
}
|
||||
if len(tc.plugin) != 0 {
|
||||
c.AuthProvider = &clientcmdapi.AuthProviderConfig{Name: tc.plugin}
|
||||
}
|
||||
tConfig, err := c.TransportConfig()
|
||||
if err != nil {
|
||||
// Unknown/bad plugins are expected to fail here.
|
||||
if !tc.expectErr {
|
||||
t.Errorf("%d. Did not expect errors loading Auth Plugin: %q. Got: %v", i, tc.plugin, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
var fullyWrappedTransport http.RoundTripper
|
||||
fullyWrappedTransport = &emptyTransport{}
|
||||
if tConfig.WrapTransport != nil {
|
||||
fullyWrappedTransport = tConfig.WrapTransport(&emptyTransport{})
|
||||
}
|
||||
res, err := fullyWrappedTransport.RoundTrip(&http.Request{})
|
||||
if err != nil {
|
||||
t.Errorf("%d. Unexpected error in RoundTrip: %v", i, err)
|
||||
continue
|
||||
}
|
||||
hasWrapTransport := res.Header.Get("wrapTransport") == "Y"
|
||||
hasPluginA := res.Header.Get("pluginA") == "Y"
|
||||
hasPluginB := res.Header.Get("pluginB") == "Y"
|
||||
if hasWrapTransport != tc.useWrapTransport {
|
||||
t.Errorf("%d. Expected Existing config.WrapTransport: %t; Got: %t", i, tc.useWrapTransport, hasWrapTransport)
|
||||
}
|
||||
if hasPluginA != tc.expectPluginA {
|
||||
t.Errorf("%d. Expected Plugin A: %t; Got: %t", i, tc.expectPluginA, hasPluginA)
|
||||
}
|
||||
if hasPluginB != tc.expectPluginB {
|
||||
t.Errorf("%d. Expected Plugin B: %t; Got: %t", i, tc.expectPluginB, hasPluginB)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuthPluginPersist(t *testing.T) {
|
||||
// register pluginA by a different name so we don't collide across tests.
|
||||
if err := RegisterAuthProviderPlugin("pluginA2", pluginAProvider); err != nil {
|
||||
t.Errorf("Unexpected error: failed to register pluginA: %v", err)
|
||||
}
|
||||
if err := RegisterAuthProviderPlugin("pluginPersist", pluginPersistProvider); err != nil {
|
||||
t.Errorf("Unexpected error: failed to register pluginPersist: %v", err)
|
||||
}
|
||||
fooBarConfig := map[string]string{"foo": "bar"}
|
||||
testCases := []struct {
|
||||
plugin string
|
||||
startingConfig map[string]string
|
||||
expectedConfigAfterLogin map[string]string
|
||||
expectedConfigAfterRoundTrip map[string]string
|
||||
}{
|
||||
// non-persisting plugins should work fine without modifying config.
|
||||
{"pluginA2", map[string]string{}, map[string]string{}, map[string]string{}},
|
||||
{"pluginA2", fooBarConfig, fooBarConfig, fooBarConfig},
|
||||
// plugins that persist config should be able to persist when they want.
|
||||
{
|
||||
"pluginPersist",
|
||||
map[string]string{},
|
||||
map[string]string{
|
||||
"login": "Y",
|
||||
},
|
||||
map[string]string{
|
||||
"login": "Y",
|
||||
"roundTrips": "1",
|
||||
},
|
||||
},
|
||||
{
|
||||
"pluginPersist",
|
||||
map[string]string{
|
||||
"login": "Y",
|
||||
"roundTrips": "123",
|
||||
},
|
||||
map[string]string{
|
||||
"login": "Y",
|
||||
"roundTrips": "123",
|
||||
},
|
||||
map[string]string{
|
||||
"login": "Y",
|
||||
"roundTrips": "124",
|
||||
},
|
||||
},
|
||||
}
|
||||
for i, tc := range testCases {
|
||||
cfg := &clientcmdapi.AuthProviderConfig{
|
||||
Name: tc.plugin,
|
||||
Config: tc.startingConfig,
|
||||
}
|
||||
persister := &inMemoryPersister{make(map[string]string)}
|
||||
persister.Persist(tc.startingConfig)
|
||||
plugin, err := GetAuthProvider("127.0.0.1", cfg, persister)
|
||||
if err != nil {
|
||||
t.Errorf("%d. Unexpected error: failed to get plugin %q: %v", i, tc.plugin, err)
|
||||
}
|
||||
if err := plugin.Login(); err != nil {
|
||||
t.Errorf("%d. Unexpected error calling Login() w/ plugin %q: %v", i, tc.plugin, err)
|
||||
}
|
||||
// Make sure the plugin persisted what we expect after Login().
|
||||
if !reflect.DeepEqual(persister.savedConfig, tc.expectedConfigAfterLogin) {
|
||||
t.Errorf("%d. Unexpected persisted config after calling %s.Login(): \nGot:\n%v\nExpected:\n%v",
|
||||
i, tc.plugin, persister.savedConfig, tc.expectedConfigAfterLogin)
|
||||
}
|
||||
if _, err := plugin.WrapTransport(&emptyTransport{}).RoundTrip(&http.Request{}); err != nil {
|
||||
t.Errorf("%d. Unexpected error round-tripping w/ plugin %q: %v", i, tc.plugin, err)
|
||||
}
|
||||
// Make sure the plugin persisted what we expect after RoundTrip().
|
||||
if !reflect.DeepEqual(persister.savedConfig, tc.expectedConfigAfterRoundTrip) {
|
||||
t.Errorf("%d. Unexpected persisted config after calling %s.WrapTransport.RoundTrip(): \nGot:\n%v\nExpected:\n%v",
|
||||
i, tc.plugin, persister.savedConfig, tc.expectedConfigAfterLogin)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// emptyTransport provides an empty http.Response with an initialized header
|
||||
// to allow wrapping RoundTrippers to set header values.
|
||||
type emptyTransport struct{}
|
||||
|
||||
func (*emptyTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
res := &http.Response{
|
||||
Header: make(map[string][]string),
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// wrapTransport sets "wrapTransport" = "Y" on the response.
|
||||
type wrapTransport struct {
|
||||
rt http.RoundTripper
|
||||
}
|
||||
|
||||
func (w *wrapTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
res, err := w.rt.RoundTrip(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res.Header.Add("wrapTransport", "Y")
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// wrapTransportA sets "pluginA" = "Y" on the response.
|
||||
type wrapTransportA struct {
|
||||
rt http.RoundTripper
|
||||
}
|
||||
|
||||
func (w *wrapTransportA) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
res, err := w.rt.RoundTrip(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res.Header.Add("pluginA", "Y")
|
||||
return res, nil
|
||||
}
|
||||
|
||||
type pluginA struct{}
|
||||
|
||||
func (*pluginA) WrapTransport(rt http.RoundTripper) http.RoundTripper {
|
||||
return &wrapTransportA{rt}
|
||||
}
|
||||
|
||||
func (*pluginA) Login() error { return nil }
|
||||
|
||||
func pluginAProvider(string, map[string]string, AuthProviderConfigPersister) (AuthProvider, error) {
|
||||
return &pluginA{}, nil
|
||||
}
|
||||
|
||||
// wrapTransportB sets "pluginB" = "Y" on the response.
|
||||
type wrapTransportB struct {
|
||||
rt http.RoundTripper
|
||||
}
|
||||
|
||||
func (w *wrapTransportB) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
res, err := w.rt.RoundTrip(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res.Header.Add("pluginB", "Y")
|
||||
return res, nil
|
||||
}
|
||||
|
||||
type pluginB struct{}
|
||||
|
||||
func (*pluginB) WrapTransport(rt http.RoundTripper) http.RoundTripper {
|
||||
return &wrapTransportB{rt}
|
||||
}
|
||||
|
||||
func (*pluginB) Login() error { return nil }
|
||||
|
||||
func pluginBProvider(string, map[string]string, AuthProviderConfigPersister) (AuthProvider, error) {
|
||||
return &pluginB{}, nil
|
||||
}
|
||||
|
||||
// pluginFailProvider simulates a registered AuthPlugin that fails to load.
|
||||
func pluginFailProvider(string, map[string]string, AuthProviderConfigPersister) (AuthProvider, error) {
|
||||
return nil, fmt.Errorf("Failed to load AuthProvider")
|
||||
}
|
||||
|
||||
type inMemoryPersister struct {
|
||||
savedConfig map[string]string
|
||||
}
|
||||
|
||||
func (i *inMemoryPersister) Persist(config map[string]string) error {
|
||||
i.savedConfig = make(map[string]string)
|
||||
for k, v := range config {
|
||||
i.savedConfig[k] = v
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// wrapTransportPersist increments the "roundTrips" entry from the config when
|
||||
// roundTrip is called.
|
||||
type wrapTransportPersist struct {
|
||||
rt http.RoundTripper
|
||||
config map[string]string
|
||||
persister AuthProviderConfigPersister
|
||||
}
|
||||
|
||||
func (w *wrapTransportPersist) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
roundTrips := 0
|
||||
if rtVal, ok := w.config["roundTrips"]; ok {
|
||||
var err error
|
||||
roundTrips, err = strconv.Atoi(rtVal)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
roundTrips++
|
||||
w.config["roundTrips"] = fmt.Sprintf("%d", roundTrips)
|
||||
if err := w.persister.Persist(w.config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return w.rt.RoundTrip(req)
|
||||
}
|
||||
|
||||
type pluginPersist struct {
|
||||
config map[string]string
|
||||
persister AuthProviderConfigPersister
|
||||
}
|
||||
|
||||
func (p *pluginPersist) WrapTransport(rt http.RoundTripper) http.RoundTripper {
|
||||
return &wrapTransportPersist{rt, p.config, p.persister}
|
||||
}
|
||||
|
||||
// Login sets the config entry "login" to "Y".
|
||||
func (p *pluginPersist) Login() error {
|
||||
p.config["login"] = "Y"
|
||||
p.persister.Persist(p.config)
|
||||
return nil
|
||||
}
|
||||
|
||||
func pluginPersistProvider(_ string, config map[string]string, persister AuthProviderConfigPersister) (AuthProvider, error) {
|
||||
return &pluginPersist{config, persister}, nil
|
||||
}
|
||||
1789
vendor/k8s.io/client-go/rest/request_test.go
generated
vendored
1789
vendor/k8s.io/client-go/rest/request_test.go
generated
vendored
File diff suppressed because it is too large
Load diff
61
vendor/k8s.io/client-go/rest/url_utils_test.go
generated
vendored
61
vendor/k8s.io/client-go/rest/url_utils_test.go
generated
vendored
|
|
@ -1,61 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package rest
|
||||
|
||||
import (
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
func TestValidatesHostParameter(t *testing.T) {
|
||||
testCases := []struct {
|
||||
Host string
|
||||
APIPath string
|
||||
|
||||
URL string
|
||||
Err bool
|
||||
}{
|
||||
{"127.0.0.1", "", "http://127.0.0.1/" + v1.SchemeGroupVersion.Version, false},
|
||||
{"127.0.0.1:8080", "", "http://127.0.0.1:8080/" + v1.SchemeGroupVersion.Version, false},
|
||||
{"foo.bar.com", "", "http://foo.bar.com/" + v1.SchemeGroupVersion.Version, false},
|
||||
{"http://host/prefix", "", "http://host/prefix/" + v1.SchemeGroupVersion.Version, false},
|
||||
{"http://host", "", "http://host/" + v1.SchemeGroupVersion.Version, false},
|
||||
{"http://host", "/", "http://host/" + v1.SchemeGroupVersion.Version, false},
|
||||
{"http://host", "/other", "http://host/other/" + v1.SchemeGroupVersion.Version, false},
|
||||
{"host/server", "", "", true},
|
||||
}
|
||||
for i, testCase := range testCases {
|
||||
u, versionedAPIPath, err := DefaultServerURL(testCase.Host, testCase.APIPath, v1.SchemeGroupVersion, false)
|
||||
switch {
|
||||
case err == nil && testCase.Err:
|
||||
t.Errorf("expected error but was nil")
|
||||
continue
|
||||
case err != nil && !testCase.Err:
|
||||
t.Errorf("unexpected error %v", err)
|
||||
continue
|
||||
case err != nil:
|
||||
continue
|
||||
}
|
||||
u.Path = path.Join(u.Path, versionedAPIPath)
|
||||
if e, a := testCase.URL, u.String(); e != a {
|
||||
t.Errorf("%d: expected host %s, got %s", i, e, a)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
79
vendor/k8s.io/client-go/rest/urlbackoff_test.go
generated
vendored
79
vendor/k8s.io/client-go/rest/urlbackoff_test.go
generated
vendored
|
|
@ -1,79 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 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 (
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/client-go/util/flowcontrol"
|
||||
)
|
||||
|
||||
func parse(raw string) *url.URL {
|
||||
theUrl, _ := url.Parse(raw)
|
||||
return theUrl
|
||||
}
|
||||
|
||||
func TestURLBackoffFunctionalityCollisions(t *testing.T) {
|
||||
myBackoff := &URLBackoff{
|
||||
Backoff: flowcontrol.NewBackOff(1*time.Second, 60*time.Second),
|
||||
}
|
||||
|
||||
// Add some noise and make sure backoff for a clean URL is zero.
|
||||
myBackoff.UpdateBackoff(parse("http://100.200.300.400:8080"), nil, 500)
|
||||
|
||||
myBackoff.UpdateBackoff(parse("http://1.2.3.4:8080"), nil, 500)
|
||||
|
||||
if myBackoff.CalculateBackoff(parse("http://1.2.3.4:100")) > 0 {
|
||||
t.Errorf("URLs are colliding in the backoff map!")
|
||||
}
|
||||
}
|
||||
|
||||
// TestURLBackoffFunctionality generally tests the URLBackoff wrapper. We avoid duplicating tests from backoff and request.
|
||||
func TestURLBackoffFunctionality(t *testing.T) {
|
||||
myBackoff := &URLBackoff{
|
||||
Backoff: flowcontrol.NewBackOff(1*time.Second, 60*time.Second),
|
||||
}
|
||||
|
||||
// Now test that backoff increases, then recovers.
|
||||
// 200 and 300 should both result in clearing the backoff.
|
||||
// all others like 429 should result in increased backoff.
|
||||
seconds := []int{0,
|
||||
1, 2, 4, 8, 0,
|
||||
1, 2}
|
||||
returnCodes := []int{
|
||||
429, 500, 501, 502, 300,
|
||||
500, 501, 502,
|
||||
}
|
||||
|
||||
if len(seconds) != len(returnCodes) {
|
||||
t.Fatalf("responseCode to backoff arrays should be the same length... sanity check failed.")
|
||||
}
|
||||
|
||||
for i, sec := range seconds {
|
||||
backoffSec := myBackoff.CalculateBackoff(parse("http://1.2.3.4:100"))
|
||||
if backoffSec < time.Duration(sec)*time.Second || backoffSec > time.Duration(sec+5)*time.Second {
|
||||
t.Errorf("Backoff out of range %v: %v %v", i, sec, backoffSec)
|
||||
}
|
||||
myBackoff.UpdateBackoff(parse("http://1.2.3.4:100/responseCodeForFuncTest"), nil, returnCodes[i])
|
||||
}
|
||||
|
||||
if myBackoff.CalculateBackoff(parse("http://1.2.3.4:100")) == 0 {
|
||||
t.Errorf("The final return code %v should have resulted in a backoff ! ", returnCodes[7])
|
||||
}
|
||||
}
|
||||
123
vendor/k8s.io/client-go/rest/watch/decoder_test.go
generated
vendored
123
vendor/k8s.io/client-go/rest/watch/decoder_test.go
generated
vendored
|
|
@ -1,123 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 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 versioned_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
runtimejson "k8s.io/apimachinery/pkg/runtime/serializer/json"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer/streaming"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
restclientwatch "k8s.io/client-go/rest/watch"
|
||||
)
|
||||
|
||||
// getDecoder mimics how k8s.io/client-go/rest.createSerializers creates a decoder
|
||||
func getDecoder() runtime.Decoder {
|
||||
jsonSerializer := runtimejson.NewSerializer(runtimejson.DefaultMetaFactory, scheme.Scheme, scheme.Scheme, false)
|
||||
directCodecFactory := serializer.DirectCodecFactory{CodecFactory: scheme.Codecs}
|
||||
return directCodecFactory.DecoderToVersion(jsonSerializer, v1.SchemeGroupVersion)
|
||||
}
|
||||
|
||||
func TestDecoder(t *testing.T) {
|
||||
table := []watch.EventType{watch.Added, watch.Deleted, watch.Modified, watch.Error}
|
||||
|
||||
for _, eventType := range table {
|
||||
out, in := io.Pipe()
|
||||
|
||||
decoder := restclientwatch.NewDecoder(streaming.NewDecoder(out, getDecoder()), getDecoder())
|
||||
|
||||
expect := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}
|
||||
encoder := json.NewEncoder(in)
|
||||
go func() {
|
||||
data, err := runtime.Encode(scheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), expect)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error %v", err)
|
||||
}
|
||||
event := metav1.WatchEvent{
|
||||
Type: string(eventType),
|
||||
Object: runtime.RawExtension{Raw: json.RawMessage(data)},
|
||||
}
|
||||
if err := encoder.Encode(&event); err != nil {
|
||||
t.Errorf("Unexpected error %v", err)
|
||||
}
|
||||
in.Close()
|
||||
}()
|
||||
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
action, got, err := decoder.Decode()
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error %v", err)
|
||||
}
|
||||
if e, a := eventType, action; e != a {
|
||||
t.Errorf("Expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := expect, got; !apiequality.Semantic.DeepDerivative(e, a) {
|
||||
t.Errorf("Expected %v, got %v", e, a)
|
||||
}
|
||||
t.Logf("Exited read")
|
||||
close(done)
|
||||
}()
|
||||
<-done
|
||||
|
||||
done = make(chan struct{})
|
||||
go func() {
|
||||
_, _, err := decoder.Decode()
|
||||
if err == nil {
|
||||
t.Errorf("Unexpected nil error")
|
||||
}
|
||||
close(done)
|
||||
}()
|
||||
<-done
|
||||
|
||||
decoder.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecoder_SourceClose(t *testing.T) {
|
||||
out, in := io.Pipe()
|
||||
decoder := restclientwatch.NewDecoder(streaming.NewDecoder(out, getDecoder()), getDecoder())
|
||||
|
||||
done := make(chan struct{})
|
||||
|
||||
go func() {
|
||||
_, _, err := decoder.Decode()
|
||||
if err == nil {
|
||||
t.Errorf("Unexpected nil error")
|
||||
}
|
||||
close(done)
|
||||
}()
|
||||
|
||||
in.Close()
|
||||
|
||||
select {
|
||||
case <-done:
|
||||
break
|
||||
case <-time.After(wait.ForeverTestTimeout):
|
||||
t.Error("Timeout")
|
||||
}
|
||||
}
|
||||
84
vendor/k8s.io/client-go/rest/watch/encoder_test.go
generated
vendored
84
vendor/k8s.io/client-go/rest/watch/encoder_test.go
generated
vendored
|
|
@ -1,84 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 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 versioned_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
runtimejson "k8s.io/apimachinery/pkg/runtime/serializer/json"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer/streaming"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
restclientwatch "k8s.io/client-go/rest/watch"
|
||||
)
|
||||
|
||||
// getEncoder mimics how k8s.io/client-go/rest.createSerializers creates a encoder
|
||||
func getEncoder() runtime.Encoder {
|
||||
jsonSerializer := runtimejson.NewSerializer(runtimejson.DefaultMetaFactory, scheme.Scheme, scheme.Scheme, false)
|
||||
directCodecFactory := serializer.DirectCodecFactory{CodecFactory: scheme.Codecs}
|
||||
return directCodecFactory.EncoderForVersion(jsonSerializer, v1.SchemeGroupVersion)
|
||||
}
|
||||
|
||||
func TestEncodeDecodeRoundTrip(t *testing.T) {
|
||||
testCases := []struct {
|
||||
Type watch.EventType
|
||||
Object runtime.Object
|
||||
}{
|
||||
{
|
||||
watch.Added,
|
||||
&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}},
|
||||
},
|
||||
{
|
||||
watch.Modified,
|
||||
&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}},
|
||||
},
|
||||
{
|
||||
watch.Deleted,
|
||||
&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}},
|
||||
},
|
||||
}
|
||||
for i, testCase := range testCases {
|
||||
buf := &bytes.Buffer{}
|
||||
|
||||
encoder := restclientwatch.NewEncoder(streaming.NewEncoder(buf, getEncoder()), getEncoder())
|
||||
if err := encoder.Encode(&watch.Event{Type: testCase.Type, Object: testCase.Object}); err != nil {
|
||||
t.Errorf("%d: unexpected error: %v", i, err)
|
||||
continue
|
||||
}
|
||||
|
||||
rc := ioutil.NopCloser(buf)
|
||||
decoder := restclientwatch.NewDecoder(streaming.NewDecoder(rc, getDecoder()), getDecoder())
|
||||
event, obj, err := decoder.Decode()
|
||||
if err != nil {
|
||||
t.Errorf("%d: unexpected error: %v", i, err)
|
||||
continue
|
||||
}
|
||||
if !apiequality.Semantic.DeepDerivative(testCase.Object, obj) {
|
||||
t.Errorf("%d: expected %#v, got %#v", i, testCase.Object, obj)
|
||||
}
|
||||
if event != testCase.Type {
|
||||
t.Errorf("%d: unexpected type: %#v", i, event)
|
||||
}
|
||||
}
|
||||
}
|
||||
192
vendor/k8s.io/client-go/testing/fixture_test.go
generated
vendored
192
vendor/k8s.io/client-go/testing/fixture_test.go
generated
vendored
|
|
@ -1,192 +0,0 @@
|
|||
/*
|
||||
Copyright 2015 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package testing
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
serializer "k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
)
|
||||
|
||||
func getArbitraryResource(s schema.GroupVersionResource, name, namespace string) *unstructured.Unstructured {
|
||||
return &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"kind": s.Resource,
|
||||
"apiVersion": s.Version,
|
||||
"metadata": map[string]interface{}{
|
||||
"name": name,
|
||||
"namespace": namespace,
|
||||
"generateName": "test_generateName",
|
||||
"uid": "test_uid",
|
||||
"resourceVersion": "test_resourceVersion",
|
||||
"selfLink": "test_selfLink",
|
||||
},
|
||||
"data": strconv.Itoa(rand.Int()),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestWatchCallNonNamespace(t *testing.T) {
|
||||
testResource := schema.GroupVersionResource{Group: "", Version: "test_version", Resource: "test_kind"}
|
||||
testObj := getArbitraryResource(testResource, "test_name", "test_namespace")
|
||||
accessor, err := meta.Accessor(testObj)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
ns := accessor.GetNamespace()
|
||||
scheme := runtime.NewScheme()
|
||||
codecs := serializer.NewCodecFactory(scheme)
|
||||
o := NewObjectTracker(scheme, codecs.UniversalDecoder())
|
||||
watch, err := o.Watch(testResource, ns)
|
||||
go func() {
|
||||
err := o.Create(testResource, testObj, ns)
|
||||
if err != nil {
|
||||
t.Errorf("test resource creation failed: %v", err)
|
||||
}
|
||||
}()
|
||||
out := <-watch.ResultChan()
|
||||
assert.Equal(t, testObj, out.Object, "watched object mismatch")
|
||||
}
|
||||
|
||||
func TestWatchCallAllNamespace(t *testing.T) {
|
||||
testResource := schema.GroupVersionResource{Group: "", Version: "test_version", Resource: "test_kind"}
|
||||
testObj := getArbitraryResource(testResource, "test_name", "test_namespace")
|
||||
accessor, err := meta.Accessor(testObj)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
ns := accessor.GetNamespace()
|
||||
scheme := runtime.NewScheme()
|
||||
codecs := serializer.NewCodecFactory(scheme)
|
||||
o := NewObjectTracker(scheme, codecs.UniversalDecoder())
|
||||
w, err := o.Watch(testResource, "test_namespace")
|
||||
wAll, err := o.Watch(testResource, "")
|
||||
go func() {
|
||||
err := o.Create(testResource, testObj, ns)
|
||||
assert.NoError(t, err, "test resource creation failed")
|
||||
}()
|
||||
out := <-w.ResultChan()
|
||||
outAll := <-wAll.ResultChan()
|
||||
assert.Equal(t, watch.Added, out.Type, "watch event mismatch")
|
||||
assert.Equal(t, watch.Added, outAll.Type, "watch event mismatch")
|
||||
assert.Equal(t, testObj, out.Object, "watched created object mismatch")
|
||||
assert.Equal(t, testObj, outAll.Object, "watched created object mismatch")
|
||||
go func() {
|
||||
err := o.Update(testResource, testObj, ns)
|
||||
assert.NoError(t, err, "test resource updating failed")
|
||||
}()
|
||||
out = <-w.ResultChan()
|
||||
outAll = <-wAll.ResultChan()
|
||||
assert.Equal(t, watch.Modified, out.Type, "watch event mismatch")
|
||||
assert.Equal(t, watch.Modified, outAll.Type, "watch event mismatch")
|
||||
assert.Equal(t, testObj, out.Object, "watched updated object mismatch")
|
||||
assert.Equal(t, testObj, outAll.Object, "watched updated object mismatch")
|
||||
go func() {
|
||||
err := o.Delete(testResource, "test_namespace", "test_name")
|
||||
assert.NoError(t, err, "test resource deletion failed")
|
||||
}()
|
||||
out = <-w.ResultChan()
|
||||
outAll = <-wAll.ResultChan()
|
||||
assert.Equal(t, watch.Deleted, out.Type, "watch event mismatch")
|
||||
assert.Equal(t, watch.Deleted, outAll.Type, "watch event mismatch")
|
||||
assert.Equal(t, testObj, out.Object, "watched deleted object mismatch")
|
||||
assert.Equal(t, testObj, outAll.Object, "watched deleted object mismatch")
|
||||
}
|
||||
|
||||
func TestWatchCallMultipleInvocation(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
op watch.EventType
|
||||
}{
|
||||
{
|
||||
"foo",
|
||||
watch.Added,
|
||||
},
|
||||
{
|
||||
"bar",
|
||||
watch.Added,
|
||||
},
|
||||
{
|
||||
"bar",
|
||||
watch.Modified,
|
||||
},
|
||||
{
|
||||
"foo",
|
||||
watch.Deleted,
|
||||
},
|
||||
{
|
||||
"bar",
|
||||
watch.Deleted,
|
||||
},
|
||||
}
|
||||
|
||||
scheme := runtime.NewScheme()
|
||||
codecs := serializer.NewCodecFactory(scheme)
|
||||
testResource := schema.GroupVersionResource{Group: "", Version: "test_version", Resource: "test_kind"}
|
||||
|
||||
o := NewObjectTracker(scheme, codecs.UniversalDecoder())
|
||||
watchNamespaces := []string{
|
||||
"",
|
||||
"",
|
||||
"test_namespace",
|
||||
"test_namespace",
|
||||
}
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(watchNamespaces))
|
||||
for idx, watchNamespace := range watchNamespaces {
|
||||
i := idx
|
||||
w, err := o.Watch(testResource, watchNamespace)
|
||||
go func() {
|
||||
assert.NoError(t, err, "watch invocation failed")
|
||||
for _, c := range cases {
|
||||
fmt.Printf("%#v %#v\n", c, i)
|
||||
event := <-w.ResultChan()
|
||||
accessor, err := meta.Accessor(event.Object)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
assert.Equal(t, c.op, event.Type, "watch event mismatched")
|
||||
assert.Equal(t, c.name, accessor.GetName(), "watched object mismatch")
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
for _, c := range cases {
|
||||
switch c.op {
|
||||
case watch.Added:
|
||||
obj := getArbitraryResource(testResource, c.name, "test_namespace")
|
||||
o.Create(testResource, obj, "test_namespace")
|
||||
case watch.Modified:
|
||||
obj := getArbitraryResource(testResource, c.name, "test_namespace")
|
||||
o.Update(testResource, obj, "test_namespace")
|
||||
case watch.Deleted:
|
||||
o.Delete(testResource, "test_namespace", c.name)
|
||||
}
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
69
vendor/k8s.io/client-go/tools/auth/clientauth_test.go
generated
vendored
69
vendor/k8s.io/client-go/tools/auth/clientauth_test.go
generated
vendored
|
|
@ -1,69 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 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 auth_test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
clientauth "k8s.io/client-go/tools/auth"
|
||||
)
|
||||
|
||||
func TestLoadFromFile(t *testing.T) {
|
||||
loadAuthInfoTests := []struct {
|
||||
authData string
|
||||
authInfo *clientauth.Info
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
`{"user": "user", "password": "pass"}`,
|
||||
&clientauth.Info{User: "user", Password: "pass"},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"", nil, true,
|
||||
},
|
||||
}
|
||||
for _, loadAuthInfoTest := range loadAuthInfoTests {
|
||||
tt := loadAuthInfoTest
|
||||
aifile, err := ioutil.TempFile("", "testAuthInfo")
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
if tt.authData != "missing" {
|
||||
defer os.Remove(aifile.Name())
|
||||
defer aifile.Close()
|
||||
_, err = aifile.WriteString(tt.authData)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
} else {
|
||||
aifile.Close()
|
||||
os.Remove(aifile.Name())
|
||||
}
|
||||
authInfo, err := clientauth.LoadFromFile(aifile.Name())
|
||||
gotErr := err != nil
|
||||
if gotErr != tt.expectErr {
|
||||
t.Errorf("expected errorness: %v, actual errorness: %v", tt.expectErr, gotErr)
|
||||
}
|
||||
if !reflect.DeepEqual(authInfo, tt.authInfo) {
|
||||
t.Errorf("Expected %v, got %v", tt.authInfo, authInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
405
vendor/k8s.io/client-go/tools/cache/controller_test.go
generated
vendored
405
vendor/k8s.io/client-go/tools/cache/controller_test.go
generated
vendored
|
|
@ -1,405 +0,0 @@
|
|||
/*
|
||||
Copyright 2015 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cache
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
fcache "k8s.io/client-go/tools/cache/testing"
|
||||
|
||||
"github.com/google/gofuzz"
|
||||
)
|
||||
|
||||
func Example() {
|
||||
// source simulates an apiserver object endpoint.
|
||||
source := fcache.NewFakeControllerSource()
|
||||
|
||||
// This will hold the downstream state, as we know it.
|
||||
downstream := NewStore(DeletionHandlingMetaNamespaceKeyFunc)
|
||||
|
||||
// This will hold incoming changes. Note how we pass downstream in as a
|
||||
// KeyLister, that way resync operations will result in the correct set
|
||||
// of update/delete deltas.
|
||||
fifo := NewDeltaFIFO(MetaNamespaceKeyFunc, downstream)
|
||||
|
||||
// Let's do threadsafe output to get predictable test results.
|
||||
deletionCounter := make(chan string, 1000)
|
||||
|
||||
cfg := &Config{
|
||||
Queue: fifo,
|
||||
ListerWatcher: source,
|
||||
ObjectType: &v1.Pod{},
|
||||
FullResyncPeriod: time.Millisecond * 100,
|
||||
RetryOnError: false,
|
||||
|
||||
// Let's implement a simple controller that just deletes
|
||||
// everything that comes in.
|
||||
Process: func(obj interface{}) error {
|
||||
// Obj is from the Pop method of the Queue we make above.
|
||||
newest := obj.(Deltas).Newest()
|
||||
|
||||
if newest.Type != Deleted {
|
||||
// Update our downstream store.
|
||||
err := downstream.Add(newest.Object)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete this object.
|
||||
source.Delete(newest.Object.(runtime.Object))
|
||||
} else {
|
||||
// Update our downstream store.
|
||||
err := downstream.Delete(newest.Object)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// fifo's KeyOf is easiest, because it handles
|
||||
// DeletedFinalStateUnknown markers.
|
||||
key, err := fifo.KeyOf(newest.Object)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Report this deletion.
|
||||
deletionCounter <- key
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
// Create the controller and run it until we close stop.
|
||||
stop := make(chan struct{})
|
||||
defer close(stop)
|
||||
go New(cfg).Run(stop)
|
||||
|
||||
// Let's add a few objects to the source.
|
||||
testIDs := []string{"a-hello", "b-controller", "c-framework"}
|
||||
for _, name := range testIDs {
|
||||
// Note that these pods are not valid-- the fake source doesn't
|
||||
// call validation or anything.
|
||||
source.Add(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: name}})
|
||||
}
|
||||
|
||||
// Let's wait for the controller to process the things we just added.
|
||||
outputSet := sets.String{}
|
||||
for i := 0; i < len(testIDs); i++ {
|
||||
outputSet.Insert(<-deletionCounter)
|
||||
}
|
||||
|
||||
for _, key := range outputSet.List() {
|
||||
fmt.Println(key)
|
||||
}
|
||||
// Output:
|
||||
// a-hello
|
||||
// b-controller
|
||||
// c-framework
|
||||
}
|
||||
|
||||
func ExampleNewInformer() {
|
||||
// source simulates an apiserver object endpoint.
|
||||
source := fcache.NewFakeControllerSource()
|
||||
|
||||
// Let's do threadsafe output to get predictable test results.
|
||||
deletionCounter := make(chan string, 1000)
|
||||
|
||||
// Make a controller that immediately deletes anything added to it, and
|
||||
// logs anything deleted.
|
||||
_, controller := NewInformer(
|
||||
source,
|
||||
&v1.Pod{},
|
||||
time.Millisecond*100,
|
||||
ResourceEventHandlerFuncs{
|
||||
AddFunc: func(obj interface{}) {
|
||||
source.Delete(obj.(runtime.Object))
|
||||
},
|
||||
DeleteFunc: func(obj interface{}) {
|
||||
key, err := DeletionHandlingMetaNamespaceKeyFunc(obj)
|
||||
if err != nil {
|
||||
key = "oops something went wrong with the key"
|
||||
}
|
||||
|
||||
// Report this deletion.
|
||||
deletionCounter <- key
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
// Run the controller and run it until we close stop.
|
||||
stop := make(chan struct{})
|
||||
defer close(stop)
|
||||
go controller.Run(stop)
|
||||
|
||||
// Let's add a few objects to the source.
|
||||
testIDs := []string{"a-hello", "b-controller", "c-framework"}
|
||||
for _, name := range testIDs {
|
||||
// Note that these pods are not valid-- the fake source doesn't
|
||||
// call validation or anything.
|
||||
source.Add(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: name}})
|
||||
}
|
||||
|
||||
// Let's wait for the controller to process the things we just added.
|
||||
outputSet := sets.String{}
|
||||
for i := 0; i < len(testIDs); i++ {
|
||||
outputSet.Insert(<-deletionCounter)
|
||||
}
|
||||
|
||||
for _, key := range outputSet.List() {
|
||||
fmt.Println(key)
|
||||
}
|
||||
// Output:
|
||||
// a-hello
|
||||
// b-controller
|
||||
// c-framework
|
||||
}
|
||||
|
||||
func TestHammerController(t *testing.T) {
|
||||
// This test executes a bunch of requests through the fake source and
|
||||
// controller framework to make sure there's no locking/threading
|
||||
// errors. If an error happens, it should hang forever or trigger the
|
||||
// race detector.
|
||||
|
||||
// source simulates an apiserver object endpoint.
|
||||
source := fcache.NewFakeControllerSource()
|
||||
|
||||
// Let's do threadsafe output to get predictable test results.
|
||||
outputSetLock := sync.Mutex{}
|
||||
// map of key to operations done on the key
|
||||
outputSet := map[string][]string{}
|
||||
|
||||
recordFunc := func(eventType string, obj interface{}) {
|
||||
key, err := DeletionHandlingMetaNamespaceKeyFunc(obj)
|
||||
if err != nil {
|
||||
t.Errorf("something wrong with key: %v", err)
|
||||
key = "oops something went wrong with the key"
|
||||
}
|
||||
|
||||
// Record some output when items are deleted.
|
||||
outputSetLock.Lock()
|
||||
defer outputSetLock.Unlock()
|
||||
outputSet[key] = append(outputSet[key], eventType)
|
||||
}
|
||||
|
||||
// Make a controller which just logs all the changes it gets.
|
||||
_, controller := NewInformer(
|
||||
source,
|
||||
&v1.Pod{},
|
||||
time.Millisecond*100,
|
||||
ResourceEventHandlerFuncs{
|
||||
AddFunc: func(obj interface{}) { recordFunc("add", obj) },
|
||||
UpdateFunc: func(oldObj, newObj interface{}) { recordFunc("update", newObj) },
|
||||
DeleteFunc: func(obj interface{}) { recordFunc("delete", obj) },
|
||||
},
|
||||
)
|
||||
|
||||
if controller.HasSynced() {
|
||||
t.Errorf("Expected HasSynced() to return false before we started the controller")
|
||||
}
|
||||
|
||||
// Run the controller and run it until we close stop.
|
||||
stop := make(chan struct{})
|
||||
go controller.Run(stop)
|
||||
|
||||
// Let's wait for the controller to do its initial sync
|
||||
wait.Poll(100*time.Millisecond, wait.ForeverTestTimeout, func() (bool, error) {
|
||||
return controller.HasSynced(), nil
|
||||
})
|
||||
if !controller.HasSynced() {
|
||||
t.Errorf("Expected HasSynced() to return true after the initial sync")
|
||||
}
|
||||
|
||||
wg := sync.WaitGroup{}
|
||||
const threads = 3
|
||||
wg.Add(threads)
|
||||
for i := 0; i < threads; i++ {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
// Let's add a few objects to the source.
|
||||
currentNames := sets.String{}
|
||||
rs := rand.NewSource(rand.Int63())
|
||||
f := fuzz.New().NilChance(.5).NumElements(0, 2).RandSource(rs)
|
||||
r := rand.New(rs) // Mustn't use r and f concurrently!
|
||||
for i := 0; i < 100; i++ {
|
||||
var name string
|
||||
var isNew bool
|
||||
if currentNames.Len() == 0 || r.Intn(3) == 1 {
|
||||
f.Fuzz(&name)
|
||||
isNew = true
|
||||
} else {
|
||||
l := currentNames.List()
|
||||
name = l[r.Intn(len(l))]
|
||||
}
|
||||
|
||||
pod := &v1.Pod{}
|
||||
f.Fuzz(pod)
|
||||
pod.ObjectMeta.Name = name
|
||||
pod.ObjectMeta.Namespace = "default"
|
||||
// Add, update, or delete randomly.
|
||||
// Note that these pods are not valid-- the fake source doesn't
|
||||
// call validation or perform any other checking.
|
||||
if isNew {
|
||||
currentNames.Insert(name)
|
||||
source.Add(pod)
|
||||
continue
|
||||
}
|
||||
switch r.Intn(2) {
|
||||
case 0:
|
||||
currentNames.Insert(name)
|
||||
source.Modify(pod)
|
||||
case 1:
|
||||
currentNames.Delete(name)
|
||||
source.Delete(pod)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
// Let's wait for the controller to finish processing the things we just added.
|
||||
// TODO: look in the queue to see how many items need to be processed.
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
close(stop)
|
||||
|
||||
// TODO: Verify that no goroutines were leaked here and that everything shut
|
||||
// down cleanly.
|
||||
|
||||
outputSetLock.Lock()
|
||||
t.Logf("got: %#v", outputSet)
|
||||
}
|
||||
|
||||
func TestUpdate(t *testing.T) {
|
||||
// This test is going to exercise the various paths that result in a
|
||||
// call to update.
|
||||
|
||||
// source simulates an apiserver object endpoint.
|
||||
source := fcache.NewFakeControllerSource()
|
||||
|
||||
const (
|
||||
FROM = "from"
|
||||
TO = "to"
|
||||
)
|
||||
|
||||
// These are the transitions we expect to see; because this is
|
||||
// asynchronous, there are a lot of valid possibilities.
|
||||
type pair struct{ from, to string }
|
||||
allowedTransitions := map[pair]bool{
|
||||
{FROM, TO}: true,
|
||||
|
||||
// Because a resync can happen when we've already observed one
|
||||
// of the above but before the item is deleted.
|
||||
{TO, TO}: true,
|
||||
// Because a resync could happen before we observe an update.
|
||||
{FROM, FROM}: true,
|
||||
}
|
||||
|
||||
pod := func(name, check string, final bool) *v1.Pod {
|
||||
p := &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Labels: map[string]string{"check": check},
|
||||
},
|
||||
}
|
||||
if final {
|
||||
p.Labels["final"] = "true"
|
||||
}
|
||||
return p
|
||||
}
|
||||
deletePod := func(p *v1.Pod) bool {
|
||||
return p.Labels["final"] == "true"
|
||||
}
|
||||
|
||||
tests := []func(string){
|
||||
func(name string) {
|
||||
name = "a-" + name
|
||||
source.Add(pod(name, FROM, false))
|
||||
source.Modify(pod(name, TO, true))
|
||||
},
|
||||
}
|
||||
|
||||
const threads = 3
|
||||
|
||||
var testDoneWG sync.WaitGroup
|
||||
testDoneWG.Add(threads * len(tests))
|
||||
|
||||
// Make a controller that deletes things once it observes an update.
|
||||
// It calls Done() on the wait group on deletions so we can tell when
|
||||
// everything we've added has been deleted.
|
||||
watchCh := make(chan struct{})
|
||||
_, controller := NewInformer(
|
||||
&testLW{
|
||||
WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
|
||||
watch, err := source.Watch(options)
|
||||
close(watchCh)
|
||||
return watch, err
|
||||
},
|
||||
ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
|
||||
return source.List(options)
|
||||
},
|
||||
},
|
||||
&v1.Pod{},
|
||||
0,
|
||||
ResourceEventHandlerFuncs{
|
||||
UpdateFunc: func(oldObj, newObj interface{}) {
|
||||
o, n := oldObj.(*v1.Pod), newObj.(*v1.Pod)
|
||||
from, to := o.Labels["check"], n.Labels["check"]
|
||||
if !allowedTransitions[pair{from, to}] {
|
||||
t.Errorf("observed transition %q -> %q for %v", from, to, n.Name)
|
||||
}
|
||||
if deletePod(n) {
|
||||
source.Delete(n)
|
||||
}
|
||||
},
|
||||
DeleteFunc: func(obj interface{}) {
|
||||
testDoneWG.Done()
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
// Run the controller and run it until we close stop.
|
||||
// Once Run() is called, calls to testDoneWG.Done() might start, so
|
||||
// all testDoneWG.Add() calls must happen before this point
|
||||
stop := make(chan struct{})
|
||||
go controller.Run(stop)
|
||||
<-watchCh
|
||||
|
||||
// run every test a few times, in parallel
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(threads * len(tests))
|
||||
for i := 0; i < threads; i++ {
|
||||
for j, f := range tests {
|
||||
go func(name string, f func(string)) {
|
||||
defer wg.Done()
|
||||
f(name)
|
||||
}(fmt.Sprintf("%v-%v", i, j), f)
|
||||
}
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
// Let's wait for the controller to process the things we just added.
|
||||
testDoneWG.Wait()
|
||||
close(stop)
|
||||
}
|
||||
492
vendor/k8s.io/client-go/tools/cache/delta_fifo_test.go
generated
vendored
492
vendor/k8s.io/client-go/tools/cache/delta_fifo_test.go
generated
vendored
|
|
@ -1,492 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cache
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// helper function to reduce stuttering
|
||||
func testPop(f *DeltaFIFO) testFifoObject {
|
||||
return Pop(f).(Deltas).Newest().Object.(testFifoObject)
|
||||
}
|
||||
|
||||
// keyLookupFunc adapts a raw function to be a KeyLookup.
|
||||
type keyLookupFunc func() []testFifoObject
|
||||
|
||||
// ListKeys just calls kl.
|
||||
func (kl keyLookupFunc) ListKeys() []string {
|
||||
result := []string{}
|
||||
for _, fifoObj := range kl() {
|
||||
result = append(result, fifoObj.name)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// GetByKey returns the key if it exists in the list returned by kl.
|
||||
func (kl keyLookupFunc) GetByKey(key string) (interface{}, bool, error) {
|
||||
for _, v := range kl() {
|
||||
if v.name == key {
|
||||
return v, true, nil
|
||||
}
|
||||
}
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
func TestDeltaFIFO_basic(t *testing.T) {
|
||||
f := NewDeltaFIFO(testFifoObjectKeyFunc, nil)
|
||||
const amount = 500
|
||||
go func() {
|
||||
for i := 0; i < amount; i++ {
|
||||
f.Add(mkFifoObj(string([]rune{'a', rune(i)}), i+1))
|
||||
}
|
||||
}()
|
||||
go func() {
|
||||
for u := uint64(0); u < amount; u++ {
|
||||
f.Add(mkFifoObj(string([]rune{'b', rune(u)}), u+1))
|
||||
}
|
||||
}()
|
||||
|
||||
lastInt := int(0)
|
||||
lastUint := uint64(0)
|
||||
for i := 0; i < amount*2; i++ {
|
||||
switch obj := testPop(f).val.(type) {
|
||||
case int:
|
||||
if obj <= lastInt {
|
||||
t.Errorf("got %v (int) out of order, last was %v", obj, lastInt)
|
||||
}
|
||||
lastInt = obj
|
||||
case uint64:
|
||||
if obj <= lastUint {
|
||||
t.Errorf("got %v (uint) out of order, last was %v", obj, lastUint)
|
||||
} else {
|
||||
lastUint = obj
|
||||
}
|
||||
default:
|
||||
t.Fatalf("unexpected type %#v", obj)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeltaFIFO_requeueOnPop(t *testing.T) {
|
||||
f := NewDeltaFIFO(testFifoObjectKeyFunc, nil)
|
||||
|
||||
f.Add(mkFifoObj("foo", 10))
|
||||
_, err := f.Pop(func(obj interface{}) error {
|
||||
if obj.(Deltas)[0].Object.(testFifoObject).name != "foo" {
|
||||
t.Fatalf("unexpected object: %#v", obj)
|
||||
}
|
||||
return ErrRequeue{Err: nil}
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if _, ok, err := f.GetByKey("foo"); !ok || err != nil {
|
||||
t.Fatalf("object should have been requeued: %t %v", ok, err)
|
||||
}
|
||||
|
||||
_, err = f.Pop(func(obj interface{}) error {
|
||||
if obj.(Deltas)[0].Object.(testFifoObject).name != "foo" {
|
||||
t.Fatalf("unexpected object: %#v", obj)
|
||||
}
|
||||
return ErrRequeue{Err: fmt.Errorf("test error")}
|
||||
})
|
||||
if err == nil || err.Error() != "test error" {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if _, ok, err := f.GetByKey("foo"); !ok || err != nil {
|
||||
t.Fatalf("object should have been requeued: %t %v", ok, err)
|
||||
}
|
||||
|
||||
_, err = f.Pop(func(obj interface{}) error {
|
||||
if obj.(Deltas)[0].Object.(testFifoObject).name != "foo" {
|
||||
t.Fatalf("unexpected object: %#v", obj)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if _, ok, err := f.GetByKey("foo"); ok || err != nil {
|
||||
t.Fatalf("object should have been removed: %t %v", ok, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeltaFIFO_addUpdate(t *testing.T) {
|
||||
f := NewDeltaFIFO(testFifoObjectKeyFunc, nil)
|
||||
f.Add(mkFifoObj("foo", 10))
|
||||
f.Update(mkFifoObj("foo", 12))
|
||||
f.Delete(mkFifoObj("foo", 15))
|
||||
|
||||
if e, a := []interface{}{mkFifoObj("foo", 15)}, f.List(); !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("Expected %+v, got %+v", e, a)
|
||||
}
|
||||
if e, a := []string{"foo"}, f.ListKeys(); !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("Expected %+v, got %+v", e, a)
|
||||
}
|
||||
|
||||
got := make(chan testFifoObject, 2)
|
||||
go func() {
|
||||
for {
|
||||
obj := testPop(f)
|
||||
t.Logf("got a thing %#v", obj)
|
||||
t.Logf("D len: %v", len(f.queue))
|
||||
got <- obj
|
||||
}
|
||||
}()
|
||||
|
||||
first := <-got
|
||||
if e, a := 15, first.val; e != a {
|
||||
t.Errorf("Didn't get updated value (%v), got %v", e, a)
|
||||
}
|
||||
select {
|
||||
case unexpected := <-got:
|
||||
t.Errorf("Got second value %v", unexpected.val)
|
||||
case <-time.After(50 * time.Millisecond):
|
||||
}
|
||||
_, exists, _ := f.Get(mkFifoObj("foo", ""))
|
||||
if exists {
|
||||
t.Errorf("item did not get removed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeltaFIFO_enqueueingNoLister(t *testing.T) {
|
||||
f := NewDeltaFIFO(testFifoObjectKeyFunc, nil)
|
||||
f.Add(mkFifoObj("foo", 10))
|
||||
f.Update(mkFifoObj("bar", 15))
|
||||
f.Add(mkFifoObj("qux", 17))
|
||||
f.Delete(mkFifoObj("qux", 18))
|
||||
|
||||
// This delete does not enqueue anything because baz doesn't exist.
|
||||
f.Delete(mkFifoObj("baz", 20))
|
||||
|
||||
expectList := []int{10, 15, 18}
|
||||
for _, expect := range expectList {
|
||||
if e, a := expect, testPop(f).val; e != a {
|
||||
t.Errorf("Didn't get updated value (%v), got %v", e, a)
|
||||
}
|
||||
}
|
||||
if e, a := 0, len(f.items); e != a {
|
||||
t.Errorf("queue unexpectedly not empty: %v != %v\n%#v", e, a, f.items)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeltaFIFO_enqueueingWithLister(t *testing.T) {
|
||||
f := NewDeltaFIFO(
|
||||
testFifoObjectKeyFunc,
|
||||
keyLookupFunc(func() []testFifoObject {
|
||||
return []testFifoObject{mkFifoObj("foo", 5), mkFifoObj("bar", 6), mkFifoObj("baz", 7)}
|
||||
}),
|
||||
)
|
||||
f.Add(mkFifoObj("foo", 10))
|
||||
f.Update(mkFifoObj("bar", 15))
|
||||
|
||||
// This delete does enqueue the deletion, because "baz" is in the key lister.
|
||||
f.Delete(mkFifoObj("baz", 20))
|
||||
|
||||
expectList := []int{10, 15, 20}
|
||||
for _, expect := range expectList {
|
||||
if e, a := expect, testPop(f).val; e != a {
|
||||
t.Errorf("Didn't get updated value (%v), got %v", e, a)
|
||||
}
|
||||
}
|
||||
if e, a := 0, len(f.items); e != a {
|
||||
t.Errorf("queue unexpectedly not empty: %v != %v", e, a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeltaFIFO_addReplace(t *testing.T) {
|
||||
f := NewDeltaFIFO(testFifoObjectKeyFunc, nil)
|
||||
f.Add(mkFifoObj("foo", 10))
|
||||
f.Replace([]interface{}{mkFifoObj("foo", 15)}, "0")
|
||||
got := make(chan testFifoObject, 2)
|
||||
go func() {
|
||||
for {
|
||||
got <- testPop(f)
|
||||
}
|
||||
}()
|
||||
|
||||
first := <-got
|
||||
if e, a := 15, first.val; e != a {
|
||||
t.Errorf("Didn't get updated value (%v), got %v", e, a)
|
||||
}
|
||||
select {
|
||||
case unexpected := <-got:
|
||||
t.Errorf("Got second value %v", unexpected.val)
|
||||
case <-time.After(50 * time.Millisecond):
|
||||
}
|
||||
_, exists, _ := f.Get(mkFifoObj("foo", ""))
|
||||
if exists {
|
||||
t.Errorf("item did not get removed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeltaFIFO_ResyncNonExisting(t *testing.T) {
|
||||
f := NewDeltaFIFO(
|
||||
testFifoObjectKeyFunc,
|
||||
keyLookupFunc(func() []testFifoObject {
|
||||
return []testFifoObject{mkFifoObj("foo", 5)}
|
||||
}),
|
||||
)
|
||||
f.Delete(mkFifoObj("foo", 10))
|
||||
f.Resync()
|
||||
|
||||
deltas := f.items["foo"]
|
||||
if len(deltas) != 1 {
|
||||
t.Fatalf("unexpected deltas length: %v", deltas)
|
||||
}
|
||||
if deltas[0].Type != Deleted {
|
||||
t.Errorf("unexpected delta: %v", deltas[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeltaFIFO_DeleteExistingNonPropagated(t *testing.T) {
|
||||
f := NewDeltaFIFO(
|
||||
testFifoObjectKeyFunc,
|
||||
keyLookupFunc(func() []testFifoObject {
|
||||
return []testFifoObject{}
|
||||
}),
|
||||
)
|
||||
f.Add(mkFifoObj("foo", 5))
|
||||
f.Delete(mkFifoObj("foo", 6))
|
||||
|
||||
deltas := f.items["foo"]
|
||||
if len(deltas) != 2 {
|
||||
t.Fatalf("unexpected deltas length: %v", deltas)
|
||||
}
|
||||
if deltas[len(deltas)-1].Type != Deleted {
|
||||
t.Errorf("unexpected delta: %v", deltas[len(deltas)-1])
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeltaFIFO_ReplaceMakesDeletions(t *testing.T) {
|
||||
f := NewDeltaFIFO(
|
||||
testFifoObjectKeyFunc,
|
||||
keyLookupFunc(func() []testFifoObject {
|
||||
return []testFifoObject{mkFifoObj("foo", 5), mkFifoObj("bar", 6), mkFifoObj("baz", 7)}
|
||||
}),
|
||||
)
|
||||
f.Delete(mkFifoObj("baz", 10))
|
||||
f.Replace([]interface{}{mkFifoObj("foo", 5)}, "0")
|
||||
|
||||
expectedList := []Deltas{
|
||||
{{Deleted, mkFifoObj("baz", 10)}},
|
||||
{{Sync, mkFifoObj("foo", 5)}},
|
||||
// Since "bar" didn't have a delete event and wasn't in the Replace list
|
||||
// it should get a tombstone key with the right Obj.
|
||||
{{Deleted, DeletedFinalStateUnknown{Key: "bar", Obj: mkFifoObj("bar", 6)}}},
|
||||
}
|
||||
|
||||
for _, expected := range expectedList {
|
||||
cur := Pop(f).(Deltas)
|
||||
if e, a := expected, cur; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("Expected %#v, got %#v", e, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeltaFIFO_UpdateResyncRace(t *testing.T) {
|
||||
f := NewDeltaFIFO(
|
||||
testFifoObjectKeyFunc,
|
||||
keyLookupFunc(func() []testFifoObject {
|
||||
return []testFifoObject{mkFifoObj("foo", 5)}
|
||||
}),
|
||||
)
|
||||
f.Update(mkFifoObj("foo", 6))
|
||||
f.Resync()
|
||||
|
||||
expectedList := []Deltas{
|
||||
{{Updated, mkFifoObj("foo", 6)}},
|
||||
}
|
||||
|
||||
for _, expected := range expectedList {
|
||||
cur := Pop(f).(Deltas)
|
||||
if e, a := expected, cur; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("Expected %#v, got %#v", e, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeltaFIFO_HasSyncedCorrectOnDeletion(t *testing.T) {
|
||||
f := NewDeltaFIFO(
|
||||
testFifoObjectKeyFunc,
|
||||
keyLookupFunc(func() []testFifoObject {
|
||||
return []testFifoObject{mkFifoObj("foo", 5), mkFifoObj("bar", 6), mkFifoObj("baz", 7)}
|
||||
}),
|
||||
)
|
||||
f.Replace([]interface{}{mkFifoObj("foo", 5)}, "0")
|
||||
|
||||
expectedList := []Deltas{
|
||||
{{Sync, mkFifoObj("foo", 5)}},
|
||||
// Since "bar" didn't have a delete event and wasn't in the Replace list
|
||||
// it should get a tombstone key with the right Obj.
|
||||
{{Deleted, DeletedFinalStateUnknown{Key: "bar", Obj: mkFifoObj("bar", 6)}}},
|
||||
}
|
||||
|
||||
for _, expected := range expectedList {
|
||||
if f.HasSynced() {
|
||||
t.Errorf("Expected HasSynced to be false")
|
||||
}
|
||||
cur := Pop(f).(Deltas)
|
||||
if e, a := expected, cur; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("Expected %#v, got %#v", e, a)
|
||||
}
|
||||
}
|
||||
if f.HasSynced() {
|
||||
t.Errorf("Expected HasSynced to be true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeltaFIFO_detectLineJumpers(t *testing.T) {
|
||||
f := NewDeltaFIFO(testFifoObjectKeyFunc, nil)
|
||||
|
||||
f.Add(mkFifoObj("foo", 10))
|
||||
f.Add(mkFifoObj("bar", 1))
|
||||
f.Add(mkFifoObj("foo", 11))
|
||||
f.Add(mkFifoObj("foo", 13))
|
||||
f.Add(mkFifoObj("zab", 30))
|
||||
|
||||
if e, a := 13, testPop(f).val; a != e {
|
||||
t.Fatalf("expected %d, got %d", e, a)
|
||||
}
|
||||
|
||||
f.Add(mkFifoObj("foo", 14)) // ensure foo doesn't jump back in line
|
||||
|
||||
if e, a := 1, testPop(f).val; a != e {
|
||||
t.Fatalf("expected %d, got %d", e, a)
|
||||
}
|
||||
|
||||
if e, a := 30, testPop(f).val; a != e {
|
||||
t.Fatalf("expected %d, got %d", e, a)
|
||||
}
|
||||
|
||||
if e, a := 14, testPop(f).val; a != e {
|
||||
t.Fatalf("expected %d, got %d", e, a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeltaFIFO_addIfNotPresent(t *testing.T) {
|
||||
f := NewDeltaFIFO(testFifoObjectKeyFunc, nil)
|
||||
|
||||
f.Add(mkFifoObj("b", 3))
|
||||
b3 := Pop(f)
|
||||
f.Add(mkFifoObj("c", 4))
|
||||
c4 := Pop(f)
|
||||
if e, a := 0, len(f.items); e != a {
|
||||
t.Fatalf("Expected %v, got %v items in queue", e, a)
|
||||
}
|
||||
|
||||
f.Add(mkFifoObj("a", 1))
|
||||
f.Add(mkFifoObj("b", 2))
|
||||
f.AddIfNotPresent(b3)
|
||||
f.AddIfNotPresent(c4)
|
||||
|
||||
if e, a := 3, len(f.items); a != e {
|
||||
t.Fatalf("expected queue length %d, got %d", e, a)
|
||||
}
|
||||
|
||||
expectedValues := []int{1, 2, 4}
|
||||
for _, expected := range expectedValues {
|
||||
if actual := testPop(f).val; actual != expected {
|
||||
t.Fatalf("expected value %d, got %d", expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeltaFIFO_KeyOf(t *testing.T) {
|
||||
f := DeltaFIFO{keyFunc: testFifoObjectKeyFunc}
|
||||
|
||||
table := []struct {
|
||||
obj interface{}
|
||||
key string
|
||||
}{
|
||||
{obj: testFifoObject{name: "A"}, key: "A"},
|
||||
{obj: DeletedFinalStateUnknown{Key: "B", Obj: nil}, key: "B"},
|
||||
{obj: Deltas{{Object: testFifoObject{name: "C"}}}, key: "C"},
|
||||
{obj: Deltas{{Object: DeletedFinalStateUnknown{Key: "D", Obj: nil}}}, key: "D"},
|
||||
}
|
||||
|
||||
for _, item := range table {
|
||||
got, err := f.KeyOf(item.obj)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error for %q: %v", item.obj, err)
|
||||
continue
|
||||
}
|
||||
if e, a := item.key, got; e != a {
|
||||
t.Errorf("Expected %v, got %v", e, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeltaFIFO_HasSynced(t *testing.T) {
|
||||
tests := []struct {
|
||||
actions []func(f *DeltaFIFO)
|
||||
expectedSynced bool
|
||||
}{
|
||||
{
|
||||
actions: []func(f *DeltaFIFO){},
|
||||
expectedSynced: false,
|
||||
},
|
||||
{
|
||||
actions: []func(f *DeltaFIFO){
|
||||
func(f *DeltaFIFO) { f.Add(mkFifoObj("a", 1)) },
|
||||
},
|
||||
expectedSynced: true,
|
||||
},
|
||||
{
|
||||
actions: []func(f *DeltaFIFO){
|
||||
func(f *DeltaFIFO) { f.Replace([]interface{}{}, "0") },
|
||||
},
|
||||
expectedSynced: true,
|
||||
},
|
||||
{
|
||||
actions: []func(f *DeltaFIFO){
|
||||
func(f *DeltaFIFO) { f.Replace([]interface{}{mkFifoObj("a", 1), mkFifoObj("b", 2)}, "0") },
|
||||
},
|
||||
expectedSynced: false,
|
||||
},
|
||||
{
|
||||
actions: []func(f *DeltaFIFO){
|
||||
func(f *DeltaFIFO) { f.Replace([]interface{}{mkFifoObj("a", 1), mkFifoObj("b", 2)}, "0") },
|
||||
func(f *DeltaFIFO) { Pop(f) },
|
||||
},
|
||||
expectedSynced: false,
|
||||
},
|
||||
{
|
||||
actions: []func(f *DeltaFIFO){
|
||||
func(f *DeltaFIFO) { f.Replace([]interface{}{mkFifoObj("a", 1), mkFifoObj("b", 2)}, "0") },
|
||||
func(f *DeltaFIFO) { Pop(f) },
|
||||
func(f *DeltaFIFO) { Pop(f) },
|
||||
},
|
||||
expectedSynced: true,
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
f := NewDeltaFIFO(testFifoObjectKeyFunc, nil)
|
||||
|
||||
for _, action := range test.actions {
|
||||
action(f)
|
||||
}
|
||||
if e, a := test.expectedSynced, f.HasSynced(); a != e {
|
||||
t.Errorf("test case %v failed, expected: %v , got %v", i, e, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
189
vendor/k8s.io/client-go/tools/cache/expiration_cache_test.go
generated
vendored
189
vendor/k8s.io/client-go/tools/cache/expiration_cache_test.go
generated
vendored
|
|
@ -1,189 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cache
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/clock"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
)
|
||||
|
||||
func TestTTLExpirationBasic(t *testing.T) {
|
||||
testObj := testStoreObject{id: "foo", val: "bar"}
|
||||
deleteChan := make(chan string, 1)
|
||||
ttlStore := NewFakeExpirationStore(
|
||||
testStoreKeyFunc, deleteChan,
|
||||
&FakeExpirationPolicy{
|
||||
NeverExpire: sets.NewString(),
|
||||
RetrieveKeyFunc: func(obj interface{}) (string, error) {
|
||||
return obj.(*timestampedEntry).obj.(testStoreObject).id, nil
|
||||
},
|
||||
},
|
||||
clock.RealClock{},
|
||||
)
|
||||
err := ttlStore.Add(testObj)
|
||||
if err != nil {
|
||||
t.Errorf("Unable to add obj %#v", testObj)
|
||||
}
|
||||
item, exists, err := ttlStore.Get(testObj)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to get from store, %v", err)
|
||||
}
|
||||
if exists || item != nil {
|
||||
t.Errorf("Got unexpected item %#v", item)
|
||||
}
|
||||
key, _ := testStoreKeyFunc(testObj)
|
||||
select {
|
||||
case delKey := <-deleteChan:
|
||||
if delKey != key {
|
||||
t.Errorf("Unexpected delete for key %s", key)
|
||||
}
|
||||
case <-time.After(wait.ForeverTestTimeout):
|
||||
t.Errorf("Unexpected timeout waiting on delete")
|
||||
}
|
||||
close(deleteChan)
|
||||
}
|
||||
|
||||
func TestReAddExpiredItem(t *testing.T) {
|
||||
deleteChan := make(chan string, 1)
|
||||
exp := &FakeExpirationPolicy{
|
||||
NeverExpire: sets.NewString(),
|
||||
RetrieveKeyFunc: func(obj interface{}) (string, error) {
|
||||
return obj.(*timestampedEntry).obj.(testStoreObject).id, nil
|
||||
},
|
||||
}
|
||||
ttlStore := NewFakeExpirationStore(
|
||||
testStoreKeyFunc, deleteChan, exp, clock.RealClock{})
|
||||
testKey := "foo"
|
||||
testObj := testStoreObject{id: testKey, val: "bar"}
|
||||
err := ttlStore.Add(testObj)
|
||||
if err != nil {
|
||||
t.Errorf("Unable to add obj %#v", testObj)
|
||||
}
|
||||
|
||||
// This get will expire the item.
|
||||
item, exists, err := ttlStore.Get(testObj)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to get from store, %v", err)
|
||||
}
|
||||
if exists || item != nil {
|
||||
t.Errorf("Got unexpected item %#v", item)
|
||||
}
|
||||
|
||||
key, _ := testStoreKeyFunc(testObj)
|
||||
differentValue := "different_bar"
|
||||
err = ttlStore.Add(
|
||||
testStoreObject{id: testKey, val: differentValue})
|
||||
if err != nil {
|
||||
t.Errorf("Failed to add second value")
|
||||
}
|
||||
|
||||
select {
|
||||
case delKey := <-deleteChan:
|
||||
if delKey != key {
|
||||
t.Errorf("Unexpected delete for key %s", key)
|
||||
}
|
||||
case <-time.After(wait.ForeverTestTimeout):
|
||||
t.Errorf("Unexpected timeout waiting on delete")
|
||||
}
|
||||
exp.NeverExpire = sets.NewString(testKey)
|
||||
item, exists, err = ttlStore.GetByKey(testKey)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to get from store, %v", err)
|
||||
}
|
||||
if !exists || item == nil || item.(testStoreObject).val != differentValue {
|
||||
t.Errorf("Got unexpected item %#v", item)
|
||||
}
|
||||
close(deleteChan)
|
||||
}
|
||||
|
||||
func TestTTLList(t *testing.T) {
|
||||
testObjs := []testStoreObject{
|
||||
{id: "foo", val: "bar"},
|
||||
{id: "foo1", val: "bar1"},
|
||||
{id: "foo2", val: "bar2"},
|
||||
}
|
||||
expireKeys := sets.NewString(testObjs[0].id, testObjs[2].id)
|
||||
deleteChan := make(chan string, len(testObjs))
|
||||
defer close(deleteChan)
|
||||
|
||||
ttlStore := NewFakeExpirationStore(
|
||||
testStoreKeyFunc, deleteChan,
|
||||
&FakeExpirationPolicy{
|
||||
NeverExpire: sets.NewString(testObjs[1].id),
|
||||
RetrieveKeyFunc: func(obj interface{}) (string, error) {
|
||||
return obj.(*timestampedEntry).obj.(testStoreObject).id, nil
|
||||
},
|
||||
},
|
||||
clock.RealClock{},
|
||||
)
|
||||
for _, obj := range testObjs {
|
||||
err := ttlStore.Add(obj)
|
||||
if err != nil {
|
||||
t.Errorf("Unable to add obj %#v", obj)
|
||||
}
|
||||
}
|
||||
listObjs := ttlStore.List()
|
||||
if len(listObjs) != 1 || !reflect.DeepEqual(listObjs[0], testObjs[1]) {
|
||||
t.Errorf("List returned unexpected results %#v", listObjs)
|
||||
}
|
||||
|
||||
// Make sure all our deletes come through in an acceptable rate (1/100ms)
|
||||
for expireKeys.Len() != 0 {
|
||||
select {
|
||||
case delKey := <-deleteChan:
|
||||
if !expireKeys.Has(delKey) {
|
||||
t.Errorf("Unexpected delete for key %s", delKey)
|
||||
}
|
||||
expireKeys.Delete(delKey)
|
||||
case <-time.After(wait.ForeverTestTimeout):
|
||||
t.Errorf("Unexpected timeout waiting on delete")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTTLPolicy(t *testing.T) {
|
||||
fakeTime := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
|
||||
ttl := 30 * time.Second
|
||||
exactlyOnTTL := fakeTime.Add(-ttl)
|
||||
expiredTime := fakeTime.Add(-(ttl + 1))
|
||||
|
||||
policy := TTLPolicy{ttl, clock.NewFakeClock(fakeTime)}
|
||||
fakeTimestampedEntry := ×tampedEntry{obj: struct{}{}, timestamp: exactlyOnTTL}
|
||||
if policy.IsExpired(fakeTimestampedEntry) {
|
||||
t.Errorf("TTL cache should not expire entries exactly on ttl")
|
||||
}
|
||||
fakeTimestampedEntry.timestamp = fakeTime
|
||||
if policy.IsExpired(fakeTimestampedEntry) {
|
||||
t.Errorf("TTL Cache should not expire entries before ttl")
|
||||
}
|
||||
fakeTimestampedEntry.timestamp = expiredTime
|
||||
if !policy.IsExpired(fakeTimestampedEntry) {
|
||||
t.Errorf("TTL Cache should expire entries older than ttl")
|
||||
}
|
||||
for _, ttl = range []time.Duration{0, -1} {
|
||||
policy.Ttl = ttl
|
||||
if policy.IsExpired(fakeTimestampedEntry) {
|
||||
t.Errorf("TTL policy should only expire entries when initialized with a ttl > 0")
|
||||
}
|
||||
}
|
||||
}
|
||||
280
vendor/k8s.io/client-go/tools/cache/fifo_test.go
generated
vendored
280
vendor/k8s.io/client-go/tools/cache/fifo_test.go
generated
vendored
|
|
@ -1,280 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cache
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func testFifoObjectKeyFunc(obj interface{}) (string, error) {
|
||||
return obj.(testFifoObject).name, nil
|
||||
}
|
||||
|
||||
type testFifoObject struct {
|
||||
name string
|
||||
val interface{}
|
||||
}
|
||||
|
||||
func mkFifoObj(name string, val interface{}) testFifoObject {
|
||||
return testFifoObject{name: name, val: val}
|
||||
}
|
||||
|
||||
func TestFIFO_basic(t *testing.T) {
|
||||
f := NewFIFO(testFifoObjectKeyFunc)
|
||||
const amount = 500
|
||||
go func() {
|
||||
for i := 0; i < amount; i++ {
|
||||
f.Add(mkFifoObj(string([]rune{'a', rune(i)}), i+1))
|
||||
}
|
||||
}()
|
||||
go func() {
|
||||
for u := uint64(0); u < amount; u++ {
|
||||
f.Add(mkFifoObj(string([]rune{'b', rune(u)}), u+1))
|
||||
}
|
||||
}()
|
||||
|
||||
lastInt := int(0)
|
||||
lastUint := uint64(0)
|
||||
for i := 0; i < amount*2; i++ {
|
||||
switch obj := Pop(f).(testFifoObject).val.(type) {
|
||||
case int:
|
||||
if obj <= lastInt {
|
||||
t.Errorf("got %v (int) out of order, last was %v", obj, lastInt)
|
||||
}
|
||||
lastInt = obj
|
||||
case uint64:
|
||||
if obj <= lastUint {
|
||||
t.Errorf("got %v (uint) out of order, last was %v", obj, lastUint)
|
||||
} else {
|
||||
lastUint = obj
|
||||
}
|
||||
default:
|
||||
t.Fatalf("unexpected type %#v", obj)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFIFO_requeueOnPop(t *testing.T) {
|
||||
f := NewFIFO(testFifoObjectKeyFunc)
|
||||
|
||||
f.Add(mkFifoObj("foo", 10))
|
||||
_, err := f.Pop(func(obj interface{}) error {
|
||||
if obj.(testFifoObject).name != "foo" {
|
||||
t.Fatalf("unexpected object: %#v", obj)
|
||||
}
|
||||
return ErrRequeue{Err: nil}
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if _, ok, err := f.GetByKey("foo"); !ok || err != nil {
|
||||
t.Fatalf("object should have been requeued: %t %v", ok, err)
|
||||
}
|
||||
|
||||
_, err = f.Pop(func(obj interface{}) error {
|
||||
if obj.(testFifoObject).name != "foo" {
|
||||
t.Fatalf("unexpected object: %#v", obj)
|
||||
}
|
||||
return ErrRequeue{Err: fmt.Errorf("test error")}
|
||||
})
|
||||
if err == nil || err.Error() != "test error" {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if _, ok, err := f.GetByKey("foo"); !ok || err != nil {
|
||||
t.Fatalf("object should have been requeued: %t %v", ok, err)
|
||||
}
|
||||
|
||||
_, err = f.Pop(func(obj interface{}) error {
|
||||
if obj.(testFifoObject).name != "foo" {
|
||||
t.Fatalf("unexpected object: %#v", obj)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if _, ok, err := f.GetByKey("foo"); ok || err != nil {
|
||||
t.Fatalf("object should have been removed: %t %v", ok, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFIFO_addUpdate(t *testing.T) {
|
||||
f := NewFIFO(testFifoObjectKeyFunc)
|
||||
f.Add(mkFifoObj("foo", 10))
|
||||
f.Update(mkFifoObj("foo", 15))
|
||||
|
||||
if e, a := []interface{}{mkFifoObj("foo", 15)}, f.List(); !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("Expected %+v, got %+v", e, a)
|
||||
}
|
||||
if e, a := []string{"foo"}, f.ListKeys(); !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("Expected %+v, got %+v", e, a)
|
||||
}
|
||||
|
||||
got := make(chan testFifoObject, 2)
|
||||
go func() {
|
||||
for {
|
||||
got <- Pop(f).(testFifoObject)
|
||||
}
|
||||
}()
|
||||
|
||||
first := <-got
|
||||
if e, a := 15, first.val; e != a {
|
||||
t.Errorf("Didn't get updated value (%v), got %v", e, a)
|
||||
}
|
||||
select {
|
||||
case unexpected := <-got:
|
||||
t.Errorf("Got second value %v", unexpected.val)
|
||||
case <-time.After(50 * time.Millisecond):
|
||||
}
|
||||
_, exists, _ := f.Get(mkFifoObj("foo", ""))
|
||||
if exists {
|
||||
t.Errorf("item did not get removed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFIFO_addReplace(t *testing.T) {
|
||||
f := NewFIFO(testFifoObjectKeyFunc)
|
||||
f.Add(mkFifoObj("foo", 10))
|
||||
f.Replace([]interface{}{mkFifoObj("foo", 15)}, "15")
|
||||
got := make(chan testFifoObject, 2)
|
||||
go func() {
|
||||
for {
|
||||
got <- Pop(f).(testFifoObject)
|
||||
}
|
||||
}()
|
||||
|
||||
first := <-got
|
||||
if e, a := 15, first.val; e != a {
|
||||
t.Errorf("Didn't get updated value (%v), got %v", e, a)
|
||||
}
|
||||
select {
|
||||
case unexpected := <-got:
|
||||
t.Errorf("Got second value %v", unexpected.val)
|
||||
case <-time.After(50 * time.Millisecond):
|
||||
}
|
||||
_, exists, _ := f.Get(mkFifoObj("foo", ""))
|
||||
if exists {
|
||||
t.Errorf("item did not get removed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFIFO_detectLineJumpers(t *testing.T) {
|
||||
f := NewFIFO(testFifoObjectKeyFunc)
|
||||
|
||||
f.Add(mkFifoObj("foo", 10))
|
||||
f.Add(mkFifoObj("bar", 1))
|
||||
f.Add(mkFifoObj("foo", 11))
|
||||
f.Add(mkFifoObj("foo", 13))
|
||||
f.Add(mkFifoObj("zab", 30))
|
||||
|
||||
if e, a := 13, Pop(f).(testFifoObject).val; a != e {
|
||||
t.Fatalf("expected %d, got %d", e, a)
|
||||
}
|
||||
|
||||
f.Add(mkFifoObj("foo", 14)) // ensure foo doesn't jump back in line
|
||||
|
||||
if e, a := 1, Pop(f).(testFifoObject).val; a != e {
|
||||
t.Fatalf("expected %d, got %d", e, a)
|
||||
}
|
||||
|
||||
if e, a := 30, Pop(f).(testFifoObject).val; a != e {
|
||||
t.Fatalf("expected %d, got %d", e, a)
|
||||
}
|
||||
|
||||
if e, a := 14, Pop(f).(testFifoObject).val; a != e {
|
||||
t.Fatalf("expected %d, got %d", e, a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFIFO_addIfNotPresent(t *testing.T) {
|
||||
f := NewFIFO(testFifoObjectKeyFunc)
|
||||
|
||||
f.Add(mkFifoObj("a", 1))
|
||||
f.Add(mkFifoObj("b", 2))
|
||||
f.AddIfNotPresent(mkFifoObj("b", 3))
|
||||
f.AddIfNotPresent(mkFifoObj("c", 4))
|
||||
|
||||
if e, a := 3, len(f.items); a != e {
|
||||
t.Fatalf("expected queue length %d, got %d", e, a)
|
||||
}
|
||||
|
||||
expectedValues := []int{1, 2, 4}
|
||||
for _, expected := range expectedValues {
|
||||
if actual := Pop(f).(testFifoObject).val; actual != expected {
|
||||
t.Fatalf("expected value %d, got %d", expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFIFO_HasSynced(t *testing.T) {
|
||||
tests := []struct {
|
||||
actions []func(f *FIFO)
|
||||
expectedSynced bool
|
||||
}{
|
||||
{
|
||||
actions: []func(f *FIFO){},
|
||||
expectedSynced: false,
|
||||
},
|
||||
{
|
||||
actions: []func(f *FIFO){
|
||||
func(f *FIFO) { f.Add(mkFifoObj("a", 1)) },
|
||||
},
|
||||
expectedSynced: true,
|
||||
},
|
||||
{
|
||||
actions: []func(f *FIFO){
|
||||
func(f *FIFO) { f.Replace([]interface{}{}, "0") },
|
||||
},
|
||||
expectedSynced: true,
|
||||
},
|
||||
{
|
||||
actions: []func(f *FIFO){
|
||||
func(f *FIFO) { f.Replace([]interface{}{mkFifoObj("a", 1), mkFifoObj("b", 2)}, "0") },
|
||||
},
|
||||
expectedSynced: false,
|
||||
},
|
||||
{
|
||||
actions: []func(f *FIFO){
|
||||
func(f *FIFO) { f.Replace([]interface{}{mkFifoObj("a", 1), mkFifoObj("b", 2)}, "0") },
|
||||
func(f *FIFO) { Pop(f) },
|
||||
},
|
||||
expectedSynced: false,
|
||||
},
|
||||
{
|
||||
actions: []func(f *FIFO){
|
||||
func(f *FIFO) { f.Replace([]interface{}{mkFifoObj("a", 1), mkFifoObj("b", 2)}, "0") },
|
||||
func(f *FIFO) { Pop(f) },
|
||||
func(f *FIFO) { Pop(f) },
|
||||
},
|
||||
expectedSynced: true,
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
f := NewFIFO(testFifoObjectKeyFunc)
|
||||
|
||||
for _, action := range test.actions {
|
||||
action(f)
|
||||
}
|
||||
if e, a := test.expectedSynced, f.HasSynced(); a != e {
|
||||
t.Errorf("test case %v failed, expected: %v , got %v", i, e, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
382
vendor/k8s.io/client-go/tools/cache/heap_test.go
generated
vendored
382
vendor/k8s.io/client-go/tools/cache/heap_test.go
generated
vendored
|
|
@ -1,382 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cache
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func testHeapObjectKeyFunc(obj interface{}) (string, error) {
|
||||
return obj.(testHeapObject).name, nil
|
||||
}
|
||||
|
||||
type testHeapObject struct {
|
||||
name string
|
||||
val interface{}
|
||||
}
|
||||
|
||||
func mkHeapObj(name string, val interface{}) testHeapObject {
|
||||
return testHeapObject{name: name, val: val}
|
||||
}
|
||||
|
||||
func compareInts(val1 interface{}, val2 interface{}) bool {
|
||||
first := val1.(testHeapObject).val.(int)
|
||||
second := val2.(testHeapObject).val.(int)
|
||||
return first < second
|
||||
}
|
||||
|
||||
// TestHeapBasic tests Heap invariant and synchronization.
|
||||
func TestHeapBasic(t *testing.T) {
|
||||
h := NewHeap(testHeapObjectKeyFunc, compareInts)
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(2)
|
||||
const amount = 500
|
||||
var i, u int
|
||||
// Insert items in the heap in opposite orders in two go routines.
|
||||
go func() {
|
||||
for i = amount; i > 0; i-- {
|
||||
h.Add(mkHeapObj(string([]rune{'a', rune(i)}), i))
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
go func() {
|
||||
for u = 0; u < amount; u++ {
|
||||
h.Add(mkHeapObj(string([]rune{'b', rune(u)}), u+1))
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
// Wait for the two go routines to finish.
|
||||
wg.Wait()
|
||||
// Make sure that the numbers are popped in ascending order.
|
||||
prevNum := 0
|
||||
for i := 0; i < amount*2; i++ {
|
||||
obj, err := h.Pop()
|
||||
num := obj.(testHeapObject).val.(int)
|
||||
// All the items must be sorted.
|
||||
if err != nil || prevNum > num {
|
||||
t.Errorf("got %v out of order, last was %v", obj, prevNum)
|
||||
}
|
||||
prevNum = num
|
||||
}
|
||||
}
|
||||
|
||||
// Tests Heap.Add and ensures that heap invariant is preserved after adding items.
|
||||
func TestHeap_Add(t *testing.T) {
|
||||
h := NewHeap(testHeapObjectKeyFunc, compareInts)
|
||||
h.Add(mkHeapObj("foo", 10))
|
||||
h.Add(mkHeapObj("bar", 1))
|
||||
h.Add(mkHeapObj("baz", 11))
|
||||
h.Add(mkHeapObj("zab", 30))
|
||||
h.Add(mkHeapObj("foo", 13)) // This updates "foo".
|
||||
|
||||
item, err := h.Pop()
|
||||
if e, a := 1, item.(testHeapObject).val; err != nil || a != e {
|
||||
t.Fatalf("expected %d, got %d", e, a)
|
||||
}
|
||||
item, err = h.Pop()
|
||||
if e, a := 11, item.(testHeapObject).val; err != nil || a != e {
|
||||
t.Fatalf("expected %d, got %d", e, a)
|
||||
}
|
||||
h.Delete(mkHeapObj("baz", 11)) // Nothing is deleted.
|
||||
h.Add(mkHeapObj("foo", 14)) // foo is updated.
|
||||
item, err = h.Pop()
|
||||
if e, a := 14, item.(testHeapObject).val; err != nil || a != e {
|
||||
t.Fatalf("expected %d, got %d", e, a)
|
||||
}
|
||||
item, err = h.Pop()
|
||||
if e, a := 30, item.(testHeapObject).val; err != nil || a != e {
|
||||
t.Fatalf("expected %d, got %d", e, a)
|
||||
}
|
||||
}
|
||||
|
||||
// TestHeap_BulkAdd tests Heap.BulkAdd functionality and ensures that all the
|
||||
// items given to BulkAdd are added to the queue before Pop reads them.
|
||||
func TestHeap_BulkAdd(t *testing.T) {
|
||||
h := NewHeap(testHeapObjectKeyFunc, compareInts)
|
||||
const amount = 500
|
||||
// Insert items in the heap in opposite orders in a go routine.
|
||||
go func() {
|
||||
l := []interface{}{}
|
||||
for i := amount; i > 0; i-- {
|
||||
l = append(l, mkHeapObj(string([]rune{'a', rune(i)}), i))
|
||||
}
|
||||
h.BulkAdd(l)
|
||||
}()
|
||||
prevNum := -1
|
||||
for i := 0; i < amount; i++ {
|
||||
obj, err := h.Pop()
|
||||
num := obj.(testHeapObject).val.(int)
|
||||
// All the items must be sorted.
|
||||
if err != nil || prevNum >= num {
|
||||
t.Errorf("got %v out of order, last was %v", obj, prevNum)
|
||||
}
|
||||
prevNum = num
|
||||
}
|
||||
}
|
||||
|
||||
// TestHeapEmptyPop tests that pop returns properly after heap is closed.
|
||||
func TestHeapEmptyPop(t *testing.T) {
|
||||
h := NewHeap(testHeapObjectKeyFunc, compareInts)
|
||||
go func() {
|
||||
time.Sleep(1 * time.Second)
|
||||
h.Close()
|
||||
}()
|
||||
_, err := h.Pop()
|
||||
if err == nil || err.Error() != closedMsg {
|
||||
t.Errorf("pop should have returned heap closed error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestHeap_AddIfNotPresent tests Heap.AddIfNotPresent and ensures that heap
|
||||
// invariant is preserved after adding items.
|
||||
func TestHeap_AddIfNotPresent(t *testing.T) {
|
||||
h := NewHeap(testHeapObjectKeyFunc, compareInts)
|
||||
h.AddIfNotPresent(mkHeapObj("foo", 10))
|
||||
h.AddIfNotPresent(mkHeapObj("bar", 1))
|
||||
h.AddIfNotPresent(mkHeapObj("baz", 11))
|
||||
h.AddIfNotPresent(mkHeapObj("zab", 30))
|
||||
h.AddIfNotPresent(mkHeapObj("foo", 13)) // This is not added.
|
||||
|
||||
if len := len(h.data.items); len != 4 {
|
||||
t.Errorf("unexpected number of items: %d", len)
|
||||
}
|
||||
if val := h.data.items["foo"].obj.(testHeapObject).val; val != 10 {
|
||||
t.Errorf("unexpected value: %d", val)
|
||||
}
|
||||
item, err := h.Pop()
|
||||
if e, a := 1, item.(testHeapObject).val; err != nil || a != e {
|
||||
t.Fatalf("expected %d, got %d", e, a)
|
||||
}
|
||||
item, err = h.Pop()
|
||||
if e, a := 10, item.(testHeapObject).val; err != nil || a != e {
|
||||
t.Fatalf("expected %d, got %d", e, a)
|
||||
}
|
||||
// bar is already popped. Let's add another one.
|
||||
h.AddIfNotPresent(mkHeapObj("bar", 14))
|
||||
item, err = h.Pop()
|
||||
if e, a := 11, item.(testHeapObject).val; err != nil || a != e {
|
||||
t.Fatalf("expected %d, got %d", e, a)
|
||||
}
|
||||
item, err = h.Pop()
|
||||
if e, a := 14, item.(testHeapObject).val; err != nil || a != e {
|
||||
t.Fatalf("expected %d, got %d", e, a)
|
||||
}
|
||||
}
|
||||
|
||||
// TestHeap_Delete tests Heap.Delete and ensures that heap invariant is
|
||||
// preserved after deleting items.
|
||||
func TestHeap_Delete(t *testing.T) {
|
||||
h := NewHeap(testHeapObjectKeyFunc, compareInts)
|
||||
h.Add(mkHeapObj("foo", 10))
|
||||
h.Add(mkHeapObj("bar", 1))
|
||||
h.Add(mkHeapObj("bal", 31))
|
||||
h.Add(mkHeapObj("baz", 11))
|
||||
|
||||
// Delete head. Delete should work with "key" and doesn't care about the value.
|
||||
if err := h.Delete(mkHeapObj("bar", 200)); err != nil {
|
||||
t.Fatalf("Failed to delete head.")
|
||||
}
|
||||
item, err := h.Pop()
|
||||
if e, a := 10, item.(testHeapObject).val; err != nil || a != e {
|
||||
t.Fatalf("expected %d, got %d", e, a)
|
||||
}
|
||||
h.Add(mkHeapObj("zab", 30))
|
||||
h.Add(mkHeapObj("faz", 30))
|
||||
len := h.data.Len()
|
||||
// Delete non-existing item.
|
||||
if err = h.Delete(mkHeapObj("non-existent", 10)); err == nil || len != h.data.Len() {
|
||||
t.Fatalf("Didn't expect any item removal")
|
||||
}
|
||||
// Delete tail.
|
||||
if err = h.Delete(mkHeapObj("bal", 31)); err != nil {
|
||||
t.Fatalf("Failed to delete tail.")
|
||||
}
|
||||
// Delete one of the items with value 30.
|
||||
if err = h.Delete(mkHeapObj("zab", 30)); err != nil {
|
||||
t.Fatalf("Failed to delete item.")
|
||||
}
|
||||
item, err = h.Pop()
|
||||
if e, a := 11, item.(testHeapObject).val; err != nil || a != e {
|
||||
t.Fatalf("expected %d, got %d", e, a)
|
||||
}
|
||||
item, err = h.Pop()
|
||||
if e, a := 30, item.(testHeapObject).val; err != nil || a != e {
|
||||
t.Fatalf("expected %d, got %d", e, a)
|
||||
}
|
||||
if h.data.Len() != 0 {
|
||||
t.Fatalf("expected an empty heap.")
|
||||
}
|
||||
}
|
||||
|
||||
// TestHeap_Update tests Heap.Update and ensures that heap invariant is
|
||||
// preserved after adding items.
|
||||
func TestHeap_Update(t *testing.T) {
|
||||
h := NewHeap(testHeapObjectKeyFunc, compareInts)
|
||||
h.Add(mkHeapObj("foo", 10))
|
||||
h.Add(mkHeapObj("bar", 1))
|
||||
h.Add(mkHeapObj("bal", 31))
|
||||
h.Add(mkHeapObj("baz", 11))
|
||||
|
||||
// Update an item to a value that should push it to the head.
|
||||
h.Update(mkHeapObj("baz", 0))
|
||||
if h.data.queue[0] != "baz" || h.data.items["baz"].index != 0 {
|
||||
t.Fatalf("expected baz to be at the head")
|
||||
}
|
||||
item, err := h.Pop()
|
||||
if e, a := 0, item.(testHeapObject).val; err != nil || a != e {
|
||||
t.Fatalf("expected %d, got %d", e, a)
|
||||
}
|
||||
// Update bar to push it farther back in the queue.
|
||||
h.Update(mkHeapObj("bar", 100))
|
||||
if h.data.queue[0] != "foo" || h.data.items["foo"].index != 0 {
|
||||
t.Fatalf("expected foo to be at the head")
|
||||
}
|
||||
}
|
||||
|
||||
// TestHeap_Get tests Heap.Get.
|
||||
func TestHeap_Get(t *testing.T) {
|
||||
h := NewHeap(testHeapObjectKeyFunc, compareInts)
|
||||
h.Add(mkHeapObj("foo", 10))
|
||||
h.Add(mkHeapObj("bar", 1))
|
||||
h.Add(mkHeapObj("bal", 31))
|
||||
h.Add(mkHeapObj("baz", 11))
|
||||
|
||||
// Get works with the key.
|
||||
obj, exists, err := h.Get(mkHeapObj("baz", 0))
|
||||
if err != nil || exists == false || obj.(testHeapObject).val != 11 {
|
||||
t.Fatalf("unexpected error in getting element")
|
||||
}
|
||||
// Get non-existing object.
|
||||
_, exists, err = h.Get(mkHeapObj("non-existing", 0))
|
||||
if err != nil || exists == true {
|
||||
t.Fatalf("didn't expect to get any object")
|
||||
}
|
||||
}
|
||||
|
||||
// TestHeap_GetByKey tests Heap.GetByKey and is very similar to TestHeap_Get.
|
||||
func TestHeap_GetByKey(t *testing.T) {
|
||||
h := NewHeap(testHeapObjectKeyFunc, compareInts)
|
||||
h.Add(mkHeapObj("foo", 10))
|
||||
h.Add(mkHeapObj("bar", 1))
|
||||
h.Add(mkHeapObj("bal", 31))
|
||||
h.Add(mkHeapObj("baz", 11))
|
||||
|
||||
obj, exists, err := h.GetByKey("baz")
|
||||
if err != nil || exists == false || obj.(testHeapObject).val != 11 {
|
||||
t.Fatalf("unexpected error in getting element")
|
||||
}
|
||||
// Get non-existing object.
|
||||
_, exists, err = h.GetByKey("non-existing")
|
||||
if err != nil || exists == true {
|
||||
t.Fatalf("didn't expect to get any object")
|
||||
}
|
||||
}
|
||||
|
||||
// TestHeap_Close tests Heap.Close and Heap.IsClosed functions.
|
||||
func TestHeap_Close(t *testing.T) {
|
||||
h := NewHeap(testHeapObjectKeyFunc, compareInts)
|
||||
h.Add(mkHeapObj("foo", 10))
|
||||
h.Add(mkHeapObj("bar", 1))
|
||||
|
||||
if h.IsClosed() {
|
||||
t.Fatalf("didn't expect heap to be closed")
|
||||
}
|
||||
h.Close()
|
||||
if !h.IsClosed() {
|
||||
t.Fatalf("expect heap to be closed")
|
||||
}
|
||||
}
|
||||
|
||||
// TestHeap_List tests Heap.List function.
|
||||
func TestHeap_List(t *testing.T) {
|
||||
h := NewHeap(testHeapObjectKeyFunc, compareInts)
|
||||
list := h.List()
|
||||
if len(list) != 0 {
|
||||
t.Errorf("expected an empty list")
|
||||
}
|
||||
|
||||
items := map[string]int{
|
||||
"foo": 10,
|
||||
"bar": 1,
|
||||
"bal": 30,
|
||||
"baz": 11,
|
||||
"faz": 30,
|
||||
}
|
||||
for k, v := range items {
|
||||
h.Add(mkHeapObj(k, v))
|
||||
}
|
||||
list = h.List()
|
||||
if len(list) != len(items) {
|
||||
t.Errorf("expected %d items, got %d", len(items), len(list))
|
||||
}
|
||||
for _, obj := range list {
|
||||
heapObj := obj.(testHeapObject)
|
||||
v, ok := items[heapObj.name]
|
||||
if !ok || v != heapObj.val {
|
||||
t.Errorf("unexpected item in the list: %v", heapObj)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestHeap_ListKeys tests Heap.ListKeys function. Scenario is the same as
|
||||
// TestHeap_list.
|
||||
func TestHeap_ListKeys(t *testing.T) {
|
||||
h := NewHeap(testHeapObjectKeyFunc, compareInts)
|
||||
list := h.ListKeys()
|
||||
if len(list) != 0 {
|
||||
t.Errorf("expected an empty list")
|
||||
}
|
||||
|
||||
items := map[string]int{
|
||||
"foo": 10,
|
||||
"bar": 1,
|
||||
"bal": 30,
|
||||
"baz": 11,
|
||||
"faz": 30,
|
||||
}
|
||||
for k, v := range items {
|
||||
h.Add(mkHeapObj(k, v))
|
||||
}
|
||||
list = h.ListKeys()
|
||||
if len(list) != len(items) {
|
||||
t.Errorf("expected %d items, got %d", len(items), len(list))
|
||||
}
|
||||
for _, key := range list {
|
||||
_, ok := items[key]
|
||||
if !ok {
|
||||
t.Errorf("unexpected item in the list: %v", key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestHeapAddAfterClose tests that heap returns an error if anything is added
|
||||
// after it is closed.
|
||||
func TestHeapAddAfterClose(t *testing.T) {
|
||||
h := NewHeap(testHeapObjectKeyFunc, compareInts)
|
||||
h.Close()
|
||||
if err := h.Add(mkHeapObj("test", 1)); err == nil || err.Error() != closedMsg {
|
||||
t.Errorf("expected heap closed error")
|
||||
}
|
||||
if err := h.AddIfNotPresent(mkHeapObj("test", 1)); err == nil || err.Error() != closedMsg {
|
||||
t.Errorf("expected heap closed error")
|
||||
}
|
||||
if err := h.BulkAdd([]interface{}{mkHeapObj("test", 1)}); err == nil || err.Error() != closedMsg {
|
||||
t.Errorf("expected heap closed error")
|
||||
}
|
||||
}
|
||||
163
vendor/k8s.io/client-go/tools/cache/index_test.go
generated
vendored
163
vendor/k8s.io/client-go/tools/cache/index_test.go
generated
vendored
|
|
@ -1,163 +0,0 @@
|
|||
/*
|
||||
Copyright 2015 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cache
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func testIndexFunc(obj interface{}) ([]string, error) {
|
||||
pod := obj.(*v1.Pod)
|
||||
return []string{pod.Labels["foo"]}, nil
|
||||
}
|
||||
|
||||
func TestGetIndexFuncValues(t *testing.T) {
|
||||
index := NewIndexer(MetaNamespaceKeyFunc, Indexers{"testmodes": testIndexFunc})
|
||||
|
||||
pod1 := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "one", Labels: map[string]string{"foo": "bar"}}}
|
||||
pod2 := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "two", Labels: map[string]string{"foo": "bar"}}}
|
||||
pod3 := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "tre", Labels: map[string]string{"foo": "biz"}}}
|
||||
|
||||
index.Add(pod1)
|
||||
index.Add(pod2)
|
||||
index.Add(pod3)
|
||||
|
||||
keys := index.ListIndexFuncValues("testmodes")
|
||||
if len(keys) != 2 {
|
||||
t.Errorf("Expected 2 keys but got %v", len(keys))
|
||||
}
|
||||
|
||||
for _, key := range keys {
|
||||
if key != "bar" && key != "biz" {
|
||||
t.Errorf("Expected only 'bar' or 'biz' but got %s", key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testUsersIndexFunc(obj interface{}) ([]string, error) {
|
||||
pod := obj.(*v1.Pod)
|
||||
usersString := pod.Annotations["users"]
|
||||
|
||||
return strings.Split(usersString, ","), nil
|
||||
}
|
||||
|
||||
func TestMultiIndexKeys(t *testing.T) {
|
||||
index := NewIndexer(MetaNamespaceKeyFunc, Indexers{"byUser": testUsersIndexFunc})
|
||||
|
||||
pod1 := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "one", Annotations: map[string]string{"users": "ernie,bert"}}}
|
||||
pod2 := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "two", Annotations: map[string]string{"users": "bert,oscar"}}}
|
||||
pod3 := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "tre", Annotations: map[string]string{"users": "ernie,elmo"}}}
|
||||
|
||||
index.Add(pod1)
|
||||
index.Add(pod2)
|
||||
index.Add(pod3)
|
||||
|
||||
erniePods, err := index.ByIndex("byUser", "ernie")
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
if len(erniePods) != 2 {
|
||||
t.Errorf("Expected 2 pods but got %v", len(erniePods))
|
||||
}
|
||||
for _, erniePod := range erniePods {
|
||||
if erniePod.(*v1.Pod).Name != "one" && erniePod.(*v1.Pod).Name != "tre" {
|
||||
t.Errorf("Expected only 'one' or 'tre' but got %s", erniePod.(*v1.Pod).Name)
|
||||
}
|
||||
}
|
||||
|
||||
bertPods, err := index.ByIndex("byUser", "bert")
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
if len(bertPods) != 2 {
|
||||
t.Errorf("Expected 2 pods but got %v", len(bertPods))
|
||||
}
|
||||
for _, bertPod := range bertPods {
|
||||
if bertPod.(*v1.Pod).Name != "one" && bertPod.(*v1.Pod).Name != "two" {
|
||||
t.Errorf("Expected only 'one' or 'two' but got %s", bertPod.(*v1.Pod).Name)
|
||||
}
|
||||
}
|
||||
|
||||
oscarPods, err := index.ByIndex("byUser", "oscar")
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
if len(oscarPods) != 1 {
|
||||
t.Errorf("Expected 1 pods but got %v", len(erniePods))
|
||||
}
|
||||
for _, oscarPod := range oscarPods {
|
||||
if oscarPod.(*v1.Pod).Name != "two" {
|
||||
t.Errorf("Expected only 'two' but got %s", oscarPod.(*v1.Pod).Name)
|
||||
}
|
||||
}
|
||||
|
||||
ernieAndBertKeys, err := index.Index("byUser", pod1)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
if len(ernieAndBertKeys) != 3 {
|
||||
t.Errorf("Expected 3 pods but got %v", len(ernieAndBertKeys))
|
||||
}
|
||||
for _, ernieAndBertKey := range ernieAndBertKeys {
|
||||
if ernieAndBertKey.(*v1.Pod).Name != "one" && ernieAndBertKey.(*v1.Pod).Name != "two" && ernieAndBertKey.(*v1.Pod).Name != "tre" {
|
||||
t.Errorf("Expected only 'one', 'two' or 'tre' but got %s", ernieAndBertKey.(*v1.Pod).Name)
|
||||
}
|
||||
}
|
||||
|
||||
index.Delete(pod3)
|
||||
erniePods, err = index.ByIndex("byUser", "ernie")
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
if len(erniePods) != 1 {
|
||||
t.Errorf("Expected 1 pods but got %v", len(erniePods))
|
||||
}
|
||||
for _, erniePod := range erniePods {
|
||||
if erniePod.(*v1.Pod).Name != "one" {
|
||||
t.Errorf("Expected only 'one' but got %s", erniePod.(*v1.Pod).Name)
|
||||
}
|
||||
}
|
||||
|
||||
elmoPods, err := index.ByIndex("byUser", "elmo")
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
if len(elmoPods) != 0 {
|
||||
t.Errorf("Expected 0 pods but got %v", len(elmoPods))
|
||||
}
|
||||
|
||||
copyOfPod2 := pod2.DeepCopy()
|
||||
copyOfPod2.Annotations["users"] = "oscar"
|
||||
index.Update(copyOfPod2)
|
||||
bertPods, err = index.ByIndex("byUser", "bert")
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
if len(bertPods) != 1 {
|
||||
t.Errorf("Expected 1 pods but got %v", len(bertPods))
|
||||
}
|
||||
for _, bertPod := range bertPods {
|
||||
if bertPod.(*v1.Pod).Name != "one" {
|
||||
t.Errorf("Expected only 'one' but got %s", bertPod.(*v1.Pod).Name)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
81
vendor/k8s.io/client-go/tools/cache/mutation_detector_test.go
generated
vendored
81
vendor/k8s.io/client-go/tools/cache/mutation_detector_test.go
generated
vendored
|
|
@ -1,81 +0,0 @@
|
|||
// +build !race
|
||||
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cache
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
)
|
||||
|
||||
func TestMutationDetector(t *testing.T) {
|
||||
fakeWatch := watch.NewFake()
|
||||
lw := &testLW{
|
||||
WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
|
||||
return fakeWatch, nil
|
||||
},
|
||||
ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
|
||||
return &v1.PodList{}, nil
|
||||
},
|
||||
}
|
||||
pod := &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "anything",
|
||||
Labels: map[string]string{"check": "foo"},
|
||||
},
|
||||
}
|
||||
stopCh := make(chan struct{})
|
||||
defer close(stopCh)
|
||||
addReceived := make(chan bool)
|
||||
mutationFound := make(chan bool)
|
||||
|
||||
informer := NewSharedInformer(lw, &v1.Pod{}, 1*time.Second).(*sharedIndexInformer)
|
||||
informer.cacheMutationDetector = &defaultCacheMutationDetector{
|
||||
name: "name",
|
||||
period: 1 * time.Second,
|
||||
failureFunc: func(message string) {
|
||||
mutationFound <- true
|
||||
},
|
||||
}
|
||||
informer.AddEventHandler(
|
||||
ResourceEventHandlerFuncs{
|
||||
AddFunc: func(obj interface{}) {
|
||||
addReceived <- true
|
||||
},
|
||||
},
|
||||
)
|
||||
go informer.Run(stopCh)
|
||||
|
||||
fakeWatch.Add(pod)
|
||||
|
||||
select {
|
||||
case <-addReceived:
|
||||
}
|
||||
|
||||
pod.Labels["change"] = "true"
|
||||
|
||||
select {
|
||||
case <-mutationFound:
|
||||
}
|
||||
|
||||
}
|
||||
58
vendor/k8s.io/client-go/tools/cache/processor_listener_test.go
generated
vendored
58
vendor/k8s.io/client-go/tools/cache/processor_listener_test.go
generated
vendored
|
|
@ -1,58 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cache
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
)
|
||||
|
||||
const (
|
||||
concurrencyLevel = 5
|
||||
)
|
||||
|
||||
func BenchmarkListener(b *testing.B) {
|
||||
var notification addNotification
|
||||
|
||||
var swg sync.WaitGroup
|
||||
swg.Add(b.N)
|
||||
b.SetParallelism(concurrencyLevel)
|
||||
// Preallocate enough space so that benchmark does not run out of it
|
||||
pl := newProcessListener(&ResourceEventHandlerFuncs{
|
||||
AddFunc: func(obj interface{}) {
|
||||
swg.Done()
|
||||
},
|
||||
}, 0, 0, time.Now(), 1024*1024)
|
||||
var wg wait.Group
|
||||
defer wg.Wait() // Wait for .run and .pop to stop
|
||||
defer close(pl.addCh) // Tell .run and .pop to stop
|
||||
wg.Start(pl.run)
|
||||
wg.Start(pl.pop)
|
||||
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
pl.add(notification)
|
||||
}
|
||||
})
|
||||
swg.Wait() // Block until all notifications have been received
|
||||
b.StopTimer()
|
||||
}
|
||||
389
vendor/k8s.io/client-go/tools/cache/reflector_test.go
generated
vendored
389
vendor/k8s.io/client-go/tools/cache/reflector_test.go
generated
vendored
|
|
@ -1,389 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cache
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
)
|
||||
|
||||
var nevererrc chan error
|
||||
|
||||
type testLW struct {
|
||||
ListFunc func(options metav1.ListOptions) (runtime.Object, error)
|
||||
WatchFunc func(options metav1.ListOptions) (watch.Interface, error)
|
||||
}
|
||||
|
||||
func (t *testLW) List(options metav1.ListOptions) (runtime.Object, error) {
|
||||
return t.ListFunc(options)
|
||||
}
|
||||
func (t *testLW) Watch(options metav1.ListOptions) (watch.Interface, error) {
|
||||
return t.WatchFunc(options)
|
||||
}
|
||||
|
||||
func TestCloseWatchChannelOnError(t *testing.T) {
|
||||
r := NewReflector(&testLW{}, &v1.Pod{}, NewStore(MetaNamespaceKeyFunc), 0)
|
||||
pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "bar"}}
|
||||
fw := watch.NewFake()
|
||||
r.listerWatcher = &testLW{
|
||||
WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
|
||||
return fw, nil
|
||||
},
|
||||
ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
|
||||
return &v1.PodList{ListMeta: metav1.ListMeta{ResourceVersion: "1"}}, nil
|
||||
},
|
||||
}
|
||||
go r.ListAndWatch(wait.NeverStop)
|
||||
fw.Error(pod)
|
||||
select {
|
||||
case _, ok := <-fw.ResultChan():
|
||||
if ok {
|
||||
t.Errorf("Watch channel left open after cancellation")
|
||||
}
|
||||
case <-time.After(wait.ForeverTestTimeout):
|
||||
t.Errorf("the cancellation is at least %s late", wait.ForeverTestTimeout.String())
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunUntil(t *testing.T) {
|
||||
stopCh := make(chan struct{})
|
||||
store := NewStore(MetaNamespaceKeyFunc)
|
||||
r := NewReflector(&testLW{}, &v1.Pod{}, store, 0)
|
||||
fw := watch.NewFake()
|
||||
r.listerWatcher = &testLW{
|
||||
WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
|
||||
return fw, nil
|
||||
},
|
||||
ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
|
||||
return &v1.PodList{ListMeta: metav1.ListMeta{ResourceVersion: "1"}}, nil
|
||||
},
|
||||
}
|
||||
go r.Run(stopCh)
|
||||
// Synchronously add a dummy pod into the watch channel so we
|
||||
// know the RunUntil go routine is in the watch handler.
|
||||
fw.Add(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "bar"}})
|
||||
close(stopCh)
|
||||
select {
|
||||
case _, ok := <-fw.ResultChan():
|
||||
if ok {
|
||||
t.Errorf("Watch channel left open after stopping the watch")
|
||||
}
|
||||
case <-time.After(wait.ForeverTestTimeout):
|
||||
t.Errorf("the cancellation is at least %s late", wait.ForeverTestTimeout.String())
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func TestReflectorResyncChan(t *testing.T) {
|
||||
s := NewStore(MetaNamespaceKeyFunc)
|
||||
g := NewReflector(&testLW{}, &v1.Pod{}, s, time.Millisecond)
|
||||
a, _ := g.resyncChan()
|
||||
b := time.After(wait.ForeverTestTimeout)
|
||||
select {
|
||||
case <-a:
|
||||
t.Logf("got timeout as expected")
|
||||
case <-b:
|
||||
t.Errorf("resyncChan() is at least 99 milliseconds late??")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkReflectorResyncChanMany(b *testing.B) {
|
||||
s := NewStore(MetaNamespaceKeyFunc)
|
||||
g := NewReflector(&testLW{}, &v1.Pod{}, s, 25*time.Millisecond)
|
||||
// The improvement to this (calling the timer's Stop() method) makes
|
||||
// this benchmark about 40% faster.
|
||||
for i := 0; i < b.N; i++ {
|
||||
g.resyncPeriod = time.Duration(rand.Float64() * float64(time.Millisecond) * 25)
|
||||
_, stop := g.resyncChan()
|
||||
stop()
|
||||
}
|
||||
}
|
||||
|
||||
func TestReflectorWatchHandlerError(t *testing.T) {
|
||||
s := NewStore(MetaNamespaceKeyFunc)
|
||||
g := NewReflector(&testLW{}, &v1.Pod{}, s, 0)
|
||||
fw := watch.NewFake()
|
||||
go func() {
|
||||
fw.Stop()
|
||||
}()
|
||||
var resumeRV string
|
||||
err := g.watchHandler(fw, &resumeRV, nevererrc, wait.NeverStop)
|
||||
if err == nil {
|
||||
t.Errorf("unexpected non-error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestReflectorWatchHandler(t *testing.T) {
|
||||
s := NewStore(MetaNamespaceKeyFunc)
|
||||
g := NewReflector(&testLW{}, &v1.Pod{}, s, 0)
|
||||
fw := watch.NewFake()
|
||||
s.Add(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}})
|
||||
s.Add(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "bar"}})
|
||||
go func() {
|
||||
fw.Add(&v1.Service{ObjectMeta: metav1.ObjectMeta{Name: "rejected"}})
|
||||
fw.Delete(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}})
|
||||
fw.Modify(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "bar", ResourceVersion: "55"}})
|
||||
fw.Add(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "baz", ResourceVersion: "32"}})
|
||||
fw.Stop()
|
||||
}()
|
||||
var resumeRV string
|
||||
err := g.watchHandler(fw, &resumeRV, nevererrc, wait.NeverStop)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error %v", err)
|
||||
}
|
||||
|
||||
mkPod := func(id string, rv string) *v1.Pod {
|
||||
return &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: id, ResourceVersion: rv}}
|
||||
}
|
||||
|
||||
table := []struct {
|
||||
Pod *v1.Pod
|
||||
exists bool
|
||||
}{
|
||||
{mkPod("foo", ""), false},
|
||||
{mkPod("rejected", ""), false},
|
||||
{mkPod("bar", "55"), true},
|
||||
{mkPod("baz", "32"), true},
|
||||
}
|
||||
for _, item := range table {
|
||||
obj, exists, _ := s.Get(item.Pod)
|
||||
if e, a := item.exists, exists; e != a {
|
||||
t.Errorf("%v: expected %v, got %v", item.Pod, e, a)
|
||||
}
|
||||
if !exists {
|
||||
continue
|
||||
}
|
||||
if e, a := item.Pod.ResourceVersion, obj.(*v1.Pod).ResourceVersion; e != a {
|
||||
t.Errorf("%v: expected %v, got %v", item.Pod, e, a)
|
||||
}
|
||||
}
|
||||
|
||||
// RV should send the last version we see.
|
||||
if e, a := "32", resumeRV; e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
|
||||
// last sync resource version should be the last version synced with store
|
||||
if e, a := "32", g.LastSyncResourceVersion(); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReflectorStopWatch(t *testing.T) {
|
||||
s := NewStore(MetaNamespaceKeyFunc)
|
||||
g := NewReflector(&testLW{}, &v1.Pod{}, s, 0)
|
||||
fw := watch.NewFake()
|
||||
var resumeRV string
|
||||
stopWatch := make(chan struct{}, 1)
|
||||
stopWatch <- struct{}{}
|
||||
err := g.watchHandler(fw, &resumeRV, nevererrc, stopWatch)
|
||||
if err != errorStopRequested {
|
||||
t.Errorf("expected stop error, got %q", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReflectorListAndWatch(t *testing.T) {
|
||||
createdFakes := make(chan *watch.FakeWatcher)
|
||||
|
||||
// The ListFunc says that it's at revision 1. Therefore, we expect our WatchFunc
|
||||
// to get called at the beginning of the watch with 1, and again with 3 when we
|
||||
// inject an error.
|
||||
expectedRVs := []string{"1", "3"}
|
||||
lw := &testLW{
|
||||
WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
|
||||
rv := options.ResourceVersion
|
||||
fw := watch.NewFake()
|
||||
if e, a := expectedRVs[0], rv; e != a {
|
||||
t.Errorf("Expected rv %v, but got %v", e, a)
|
||||
}
|
||||
expectedRVs = expectedRVs[1:]
|
||||
// channel is not buffered because the for loop below needs to block. But
|
||||
// we don't want to block here, so report the new fake via a go routine.
|
||||
go func() { createdFakes <- fw }()
|
||||
return fw, nil
|
||||
},
|
||||
ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
|
||||
return &v1.PodList{ListMeta: metav1.ListMeta{ResourceVersion: "1"}}, nil
|
||||
},
|
||||
}
|
||||
s := NewFIFO(MetaNamespaceKeyFunc)
|
||||
r := NewReflector(lw, &v1.Pod{}, s, 0)
|
||||
go r.ListAndWatch(wait.NeverStop)
|
||||
|
||||
ids := []string{"foo", "bar", "baz", "qux", "zoo"}
|
||||
var fw *watch.FakeWatcher
|
||||
for i, id := range ids {
|
||||
if fw == nil {
|
||||
fw = <-createdFakes
|
||||
}
|
||||
sendingRV := strconv.FormatUint(uint64(i+2), 10)
|
||||
fw.Add(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: id, ResourceVersion: sendingRV}})
|
||||
if sendingRV == "3" {
|
||||
// Inject a failure.
|
||||
fw.Stop()
|
||||
fw = nil
|
||||
}
|
||||
}
|
||||
|
||||
// Verify we received the right ids with the right resource versions.
|
||||
for i, id := range ids {
|
||||
pod := Pop(s).(*v1.Pod)
|
||||
if e, a := id, pod.Name; e != a {
|
||||
t.Errorf("%v: Expected %v, got %v", i, e, a)
|
||||
}
|
||||
if e, a := strconv.FormatUint(uint64(i+2), 10), pod.ResourceVersion; e != a {
|
||||
t.Errorf("%v: Expected %v, got %v", i, e, a)
|
||||
}
|
||||
}
|
||||
|
||||
if len(expectedRVs) != 0 {
|
||||
t.Error("called watchStarter an unexpected number of times")
|
||||
}
|
||||
}
|
||||
|
||||
func TestReflectorListAndWatchWithErrors(t *testing.T) {
|
||||
mkPod := func(id string, rv string) *v1.Pod {
|
||||
return &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: id, ResourceVersion: rv}}
|
||||
}
|
||||
mkList := func(rv string, pods ...*v1.Pod) *v1.PodList {
|
||||
list := &v1.PodList{ListMeta: metav1.ListMeta{ResourceVersion: rv}}
|
||||
for _, pod := range pods {
|
||||
list.Items = append(list.Items, *pod)
|
||||
}
|
||||
return list
|
||||
}
|
||||
table := []struct {
|
||||
list *v1.PodList
|
||||
listErr error
|
||||
events []watch.Event
|
||||
watchErr error
|
||||
}{
|
||||
{
|
||||
list: mkList("1"),
|
||||
events: []watch.Event{
|
||||
{Type: watch.Added, Object: mkPod("foo", "2")},
|
||||
{Type: watch.Added, Object: mkPod("bar", "3")},
|
||||
},
|
||||
}, {
|
||||
list: mkList("3", mkPod("foo", "2"), mkPod("bar", "3")),
|
||||
events: []watch.Event{
|
||||
{Type: watch.Deleted, Object: mkPod("foo", "4")},
|
||||
{Type: watch.Added, Object: mkPod("qux", "5")},
|
||||
},
|
||||
}, {
|
||||
listErr: fmt.Errorf("a list error"),
|
||||
}, {
|
||||
list: mkList("5", mkPod("bar", "3"), mkPod("qux", "5")),
|
||||
watchErr: fmt.Errorf("a watch error"),
|
||||
}, {
|
||||
list: mkList("5", mkPod("bar", "3"), mkPod("qux", "5")),
|
||||
events: []watch.Event{
|
||||
{Type: watch.Added, Object: mkPod("baz", "6")},
|
||||
},
|
||||
}, {
|
||||
list: mkList("6", mkPod("bar", "3"), mkPod("qux", "5"), mkPod("baz", "6")),
|
||||
},
|
||||
}
|
||||
|
||||
s := NewFIFO(MetaNamespaceKeyFunc)
|
||||
for line, item := range table {
|
||||
if item.list != nil {
|
||||
// Test that the list is what currently exists in the store.
|
||||
current := s.List()
|
||||
checkMap := map[string]string{}
|
||||
for _, item := range current {
|
||||
pod := item.(*v1.Pod)
|
||||
checkMap[pod.Name] = pod.ResourceVersion
|
||||
}
|
||||
for _, pod := range item.list.Items {
|
||||
if e, a := pod.ResourceVersion, checkMap[pod.Name]; e != a {
|
||||
t.Errorf("%v: expected %v, got %v for pod %v", line, e, a, pod.Name)
|
||||
}
|
||||
}
|
||||
if e, a := len(item.list.Items), len(checkMap); e != a {
|
||||
t.Errorf("%v: expected %v, got %v", line, e, a)
|
||||
}
|
||||
}
|
||||
watchRet, watchErr := item.events, item.watchErr
|
||||
lw := &testLW{
|
||||
WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
|
||||
if watchErr != nil {
|
||||
return nil, watchErr
|
||||
}
|
||||
watchErr = fmt.Errorf("second watch")
|
||||
fw := watch.NewFake()
|
||||
go func() {
|
||||
for _, e := range watchRet {
|
||||
fw.Action(e.Type, e.Object)
|
||||
}
|
||||
fw.Stop()
|
||||
}()
|
||||
return fw, nil
|
||||
},
|
||||
ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
|
||||
return item.list, item.listErr
|
||||
},
|
||||
}
|
||||
r := NewReflector(lw, &v1.Pod{}, s, 0)
|
||||
r.ListAndWatch(wait.NeverStop)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReflectorResync(t *testing.T) {
|
||||
iteration := 0
|
||||
stopCh := make(chan struct{})
|
||||
rerr := errors.New("expected resync reached")
|
||||
s := &FakeCustomStore{
|
||||
ResyncFunc: func() error {
|
||||
iteration++
|
||||
if iteration == 2 {
|
||||
return rerr
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
lw := &testLW{
|
||||
WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
|
||||
fw := watch.NewFake()
|
||||
return fw, nil
|
||||
},
|
||||
ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
|
||||
return &v1.PodList{ListMeta: metav1.ListMeta{ResourceVersion: "0"}}, nil
|
||||
},
|
||||
}
|
||||
resyncPeriod := 1 * time.Millisecond
|
||||
r := NewReflector(lw, &v1.Pod{}, s, resyncPeriod)
|
||||
if err := r.ListAndWatch(stopCh); err != nil {
|
||||
// error from Resync is not propaged up to here.
|
||||
t.Errorf("expected error %v", err)
|
||||
}
|
||||
if iteration != 2 {
|
||||
t.Errorf("exactly 2 iterations were expected, got: %v", iteration)
|
||||
}
|
||||
}
|
||||
265
vendor/k8s.io/client-go/tools/cache/shared_informer_test.go
generated
vendored
265
vendor/k8s.io/client-go/tools/cache/shared_informer_test.go
generated
vendored
|
|
@ -1,265 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cache
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/clock"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
fcache "k8s.io/client-go/tools/cache/testing"
|
||||
)
|
||||
|
||||
type testListener struct {
|
||||
lock sync.RWMutex
|
||||
resyncPeriod time.Duration
|
||||
expectedItemNames sets.String
|
||||
receivedItemNames []string
|
||||
name string
|
||||
}
|
||||
|
||||
func newTestListener(name string, resyncPeriod time.Duration, expected ...string) *testListener {
|
||||
l := &testListener{
|
||||
resyncPeriod: resyncPeriod,
|
||||
expectedItemNames: sets.NewString(expected...),
|
||||
name: name,
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *testListener) OnAdd(obj interface{}) {
|
||||
l.handle(obj)
|
||||
}
|
||||
|
||||
func (l *testListener) OnUpdate(old, new interface{}) {
|
||||
l.handle(new)
|
||||
}
|
||||
|
||||
func (l *testListener) OnDelete(obj interface{}) {
|
||||
}
|
||||
|
||||
func (l *testListener) handle(obj interface{}) {
|
||||
key, _ := MetaNamespaceKeyFunc(obj)
|
||||
fmt.Printf("%s: handle: %v\n", l.name, key)
|
||||
l.lock.Lock()
|
||||
defer l.lock.Unlock()
|
||||
|
||||
objectMeta, _ := meta.Accessor(obj)
|
||||
l.receivedItemNames = append(l.receivedItemNames, objectMeta.GetName())
|
||||
}
|
||||
|
||||
func (l *testListener) ok() bool {
|
||||
fmt.Println("polling")
|
||||
err := wait.PollImmediate(100*time.Millisecond, 2*time.Second, func() (bool, error) {
|
||||
if l.satisfiedExpectations() {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
})
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// wait just a bit to allow any unexpected stragglers to come in
|
||||
fmt.Println("sleeping")
|
||||
time.Sleep(1 * time.Second)
|
||||
fmt.Println("final check")
|
||||
return l.satisfiedExpectations()
|
||||
}
|
||||
|
||||
func (l *testListener) satisfiedExpectations() bool {
|
||||
l.lock.RLock()
|
||||
defer l.lock.RUnlock()
|
||||
|
||||
return len(l.receivedItemNames) == l.expectedItemNames.Len() && sets.NewString(l.receivedItemNames...).Equal(l.expectedItemNames)
|
||||
}
|
||||
|
||||
func TestListenerResyncPeriods(t *testing.T) {
|
||||
// source simulates an apiserver object endpoint.
|
||||
source := fcache.NewFakeControllerSource()
|
||||
source.Add(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod1"}})
|
||||
source.Add(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod2"}})
|
||||
|
||||
// create the shared informer and resync every 1s
|
||||
informer := NewSharedInformer(source, &v1.Pod{}, 1*time.Second).(*sharedIndexInformer)
|
||||
|
||||
clock := clock.NewFakeClock(time.Now())
|
||||
informer.clock = clock
|
||||
informer.processor.clock = clock
|
||||
|
||||
// listener 1, never resync
|
||||
listener1 := newTestListener("listener1", 0, "pod1", "pod2")
|
||||
informer.AddEventHandlerWithResyncPeriod(listener1, listener1.resyncPeriod)
|
||||
|
||||
// listener 2, resync every 2s
|
||||
listener2 := newTestListener("listener2", 2*time.Second, "pod1", "pod2")
|
||||
informer.AddEventHandlerWithResyncPeriod(listener2, listener2.resyncPeriod)
|
||||
|
||||
// listener 3, resync every 3s
|
||||
listener3 := newTestListener("listener3", 3*time.Second, "pod1", "pod2")
|
||||
informer.AddEventHandlerWithResyncPeriod(listener3, listener3.resyncPeriod)
|
||||
listeners := []*testListener{listener1, listener2, listener3}
|
||||
|
||||
stop := make(chan struct{})
|
||||
defer close(stop)
|
||||
|
||||
go informer.Run(stop)
|
||||
|
||||
// ensure all listeners got the initial List
|
||||
for _, listener := range listeners {
|
||||
if !listener.ok() {
|
||||
t.Errorf("%s: expected %v, got %v", listener.name, listener.expectedItemNames, listener.receivedItemNames)
|
||||
}
|
||||
}
|
||||
|
||||
// reset
|
||||
for _, listener := range listeners {
|
||||
listener.receivedItemNames = []string{}
|
||||
}
|
||||
|
||||
// advance so listener2 gets a resync
|
||||
clock.Step(2 * time.Second)
|
||||
|
||||
// make sure listener2 got the resync
|
||||
if !listener2.ok() {
|
||||
t.Errorf("%s: expected %v, got %v", listener2.name, listener2.expectedItemNames, listener2.receivedItemNames)
|
||||
}
|
||||
|
||||
// wait a bit to give errant items a chance to go to 1 and 3
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
// make sure listeners 1 and 3 got nothing
|
||||
if len(listener1.receivedItemNames) != 0 {
|
||||
t.Errorf("listener1: should not have resynced (got %d)", len(listener1.receivedItemNames))
|
||||
}
|
||||
if len(listener3.receivedItemNames) != 0 {
|
||||
t.Errorf("listener3: should not have resynced (got %d)", len(listener3.receivedItemNames))
|
||||
}
|
||||
|
||||
// reset
|
||||
for _, listener := range listeners {
|
||||
listener.receivedItemNames = []string{}
|
||||
}
|
||||
|
||||
// advance so listener3 gets a resync
|
||||
clock.Step(1 * time.Second)
|
||||
|
||||
// make sure listener3 got the resync
|
||||
if !listener3.ok() {
|
||||
t.Errorf("%s: expected %v, got %v", listener3.name, listener3.expectedItemNames, listener3.receivedItemNames)
|
||||
}
|
||||
|
||||
// wait a bit to give errant items a chance to go to 1 and 2
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
// make sure listeners 1 and 2 got nothing
|
||||
if len(listener1.receivedItemNames) != 0 {
|
||||
t.Errorf("listener1: should not have resynced (got %d)", len(listener1.receivedItemNames))
|
||||
}
|
||||
if len(listener2.receivedItemNames) != 0 {
|
||||
t.Errorf("listener2: should not have resynced (got %d)", len(listener2.receivedItemNames))
|
||||
}
|
||||
}
|
||||
|
||||
func TestResyncCheckPeriod(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{}, 12*time.Hour).(*sharedIndexInformer)
|
||||
|
||||
clock := clock.NewFakeClock(time.Now())
|
||||
informer.clock = clock
|
||||
informer.processor.clock = clock
|
||||
|
||||
// listener 1, never resync
|
||||
listener1 := newTestListener("listener1", 0)
|
||||
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), 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)
|
||||
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), informer.processor.listeners[0].resyncPeriod; e != a {
|
||||
t.Errorf("expected %d, got %d", 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)
|
||||
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), informer.processor.listeners[0].resyncPeriod; e != a {
|
||||
t.Errorf("expected %d, got %d", 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, 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)
|
||||
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), informer.processor.listeners[0].resyncPeriod; e != a {
|
||||
t.Errorf("expected %d, got %d", 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, informer.processor.listeners[2].resyncPeriod; e != a {
|
||||
t.Errorf("expected %d, got %d", e, a)
|
||||
}
|
||||
if e, a := 5*time.Second, informer.processor.listeners[3].resyncPeriod; e != a {
|
||||
t.Errorf("expected %d, got %d", e, a)
|
||||
}
|
||||
}
|
||||
|
||||
// verify that https://github.com/kubernetes/kubernetes/issues/59822 is fixed
|
||||
func TestSharedInformerInitializationRace(t *testing.T) {
|
||||
source := fcache.NewFakeControllerSource()
|
||||
informer := NewSharedInformer(source, &v1.Pod{}, 1*time.Second).(*sharedIndexInformer)
|
||||
listener := newTestListener("raceListener", 0)
|
||||
|
||||
stop := make(chan struct{})
|
||||
go informer.AddEventHandlerWithResyncPeriod(listener, listener.resyncPeriod)
|
||||
go informer.Run(stop)
|
||||
close(stop)
|
||||
}
|
||||
156
vendor/k8s.io/client-go/tools/cache/store_test.go
generated
vendored
156
vendor/k8s.io/client-go/tools/cache/store_test.go
generated
vendored
|
|
@ -1,156 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cache
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
)
|
||||
|
||||
// Test public interface
|
||||
func doTestStore(t *testing.T, store Store) {
|
||||
mkObj := func(id string, val string) testStoreObject {
|
||||
return testStoreObject{id: id, val: val}
|
||||
}
|
||||
|
||||
store.Add(mkObj("foo", "bar"))
|
||||
if item, ok, _ := store.Get(mkObj("foo", "")); !ok {
|
||||
t.Errorf("didn't find inserted item")
|
||||
} else {
|
||||
if e, a := "bar", item.(testStoreObject).val; e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
}
|
||||
store.Update(mkObj("foo", "baz"))
|
||||
if item, ok, _ := store.Get(mkObj("foo", "")); !ok {
|
||||
t.Errorf("didn't find inserted item")
|
||||
} else {
|
||||
if e, a := "baz", item.(testStoreObject).val; e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
}
|
||||
store.Delete(mkObj("foo", ""))
|
||||
if _, ok, _ := store.Get(mkObj("foo", "")); ok {
|
||||
t.Errorf("found deleted item??")
|
||||
}
|
||||
|
||||
// Test List.
|
||||
store.Add(mkObj("a", "b"))
|
||||
store.Add(mkObj("c", "d"))
|
||||
store.Add(mkObj("e", "e"))
|
||||
{
|
||||
found := sets.String{}
|
||||
for _, item := range store.List() {
|
||||
found.Insert(item.(testStoreObject).val)
|
||||
}
|
||||
if !found.HasAll("b", "d", "e") {
|
||||
t.Errorf("missing items, found: %v", found)
|
||||
}
|
||||
if len(found) != 3 {
|
||||
t.Errorf("extra items")
|
||||
}
|
||||
}
|
||||
|
||||
// Test Replace.
|
||||
store.Replace([]interface{}{
|
||||
mkObj("foo", "foo"),
|
||||
mkObj("bar", "bar"),
|
||||
}, "0")
|
||||
|
||||
{
|
||||
found := sets.String{}
|
||||
for _, item := range store.List() {
|
||||
found.Insert(item.(testStoreObject).val)
|
||||
}
|
||||
if !found.HasAll("foo", "bar") {
|
||||
t.Errorf("missing items")
|
||||
}
|
||||
if len(found) != 2 {
|
||||
t.Errorf("extra items")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test public interface
|
||||
func doTestIndex(t *testing.T, indexer Indexer) {
|
||||
mkObj := func(id string, val string) testStoreObject {
|
||||
return testStoreObject{id: id, val: val}
|
||||
}
|
||||
|
||||
// Test Index
|
||||
expected := map[string]sets.String{}
|
||||
expected["b"] = sets.NewString("a", "c")
|
||||
expected["f"] = sets.NewString("e")
|
||||
expected["h"] = sets.NewString("g")
|
||||
indexer.Add(mkObj("a", "b"))
|
||||
indexer.Add(mkObj("c", "b"))
|
||||
indexer.Add(mkObj("e", "f"))
|
||||
indexer.Add(mkObj("g", "h"))
|
||||
{
|
||||
for k, v := range expected {
|
||||
found := sets.String{}
|
||||
indexResults, err := indexer.Index("by_val", mkObj("", k))
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error %v", err)
|
||||
}
|
||||
for _, item := range indexResults {
|
||||
found.Insert(item.(testStoreObject).id)
|
||||
}
|
||||
items := v.List()
|
||||
if !found.HasAll(items...) {
|
||||
t.Errorf("missing items, index %s, expected %v but found %v", k, items, found.List())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testStoreKeyFunc(obj interface{}) (string, error) {
|
||||
return obj.(testStoreObject).id, nil
|
||||
}
|
||||
|
||||
func testStoreIndexFunc(obj interface{}) ([]string, error) {
|
||||
return []string{obj.(testStoreObject).val}, nil
|
||||
}
|
||||
|
||||
func testStoreIndexers() Indexers {
|
||||
indexers := Indexers{}
|
||||
indexers["by_val"] = testStoreIndexFunc
|
||||
return indexers
|
||||
}
|
||||
|
||||
type testStoreObject struct {
|
||||
id string
|
||||
val string
|
||||
}
|
||||
|
||||
func TestCache(t *testing.T) {
|
||||
doTestStore(t, NewStore(testStoreKeyFunc))
|
||||
}
|
||||
|
||||
func TestFIFOCache(t *testing.T) {
|
||||
doTestStore(t, NewFIFO(testStoreKeyFunc))
|
||||
}
|
||||
|
||||
func TestUndeltaStore(t *testing.T) {
|
||||
nop := func([]interface{}) {}
|
||||
doTestStore(t, NewUndeltaStore(nop, testStoreKeyFunc))
|
||||
}
|
||||
|
||||
func TestIndex(t *testing.T) {
|
||||
doTestIndex(t, NewIndexer(testStoreKeyFunc, testStoreIndexers()))
|
||||
}
|
||||
131
vendor/k8s.io/client-go/tools/cache/undelta_store_test.go
generated
vendored
131
vendor/k8s.io/client-go/tools/cache/undelta_store_test.go
generated
vendored
|
|
@ -1,131 +0,0 @@
|
|||
/*
|
||||
Copyright 2015 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cache
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// store_test.go checks that UndeltaStore conforms to the Store interface
|
||||
// behavior. This test just tests that it calls the push func in addition.
|
||||
|
||||
type testUndeltaObject struct {
|
||||
name string
|
||||
val interface{}
|
||||
}
|
||||
|
||||
func testUndeltaKeyFunc(obj interface{}) (string, error) {
|
||||
return obj.(testUndeltaObject).name, nil
|
||||
}
|
||||
|
||||
/*
|
||||
var (
|
||||
o1 interface{} = t{1}
|
||||
o2 interface{} = t{2}
|
||||
l1 []interface{} = []interface{}{t{1}}
|
||||
)
|
||||
*/
|
||||
|
||||
func TestUpdateCallsPush(t *testing.T) {
|
||||
mkObj := func(name string, val interface{}) testUndeltaObject {
|
||||
return testUndeltaObject{name: name, val: val}
|
||||
}
|
||||
|
||||
var got []interface{}
|
||||
var callcount int = 0
|
||||
push := func(m []interface{}) {
|
||||
callcount++
|
||||
got = m
|
||||
}
|
||||
|
||||
u := NewUndeltaStore(push, testUndeltaKeyFunc)
|
||||
|
||||
u.Add(mkObj("a", 2))
|
||||
u.Update(mkObj("a", 1))
|
||||
if callcount != 2 {
|
||||
t.Errorf("Expected 2 calls, got %d", callcount)
|
||||
}
|
||||
|
||||
l := []interface{}{mkObj("a", 1)}
|
||||
if !reflect.DeepEqual(l, got) {
|
||||
t.Errorf("Expected %#v, Got %#v", l, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteCallsPush(t *testing.T) {
|
||||
mkObj := func(name string, val interface{}) testUndeltaObject {
|
||||
return testUndeltaObject{name: name, val: val}
|
||||
}
|
||||
|
||||
var got []interface{}
|
||||
var callcount int = 0
|
||||
push := func(m []interface{}) {
|
||||
callcount++
|
||||
got = m
|
||||
}
|
||||
|
||||
u := NewUndeltaStore(push, testUndeltaKeyFunc)
|
||||
|
||||
u.Add(mkObj("a", 2))
|
||||
u.Delete(mkObj("a", ""))
|
||||
if callcount != 2 {
|
||||
t.Errorf("Expected 2 calls, got %d", callcount)
|
||||
}
|
||||
expected := []interface{}{}
|
||||
if !reflect.DeepEqual(expected, got) {
|
||||
t.Errorf("Expected %#v, Got %#v", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadsDoNotCallPush(t *testing.T) {
|
||||
push := func(m []interface{}) {
|
||||
t.Errorf("Unexpected call to push!")
|
||||
}
|
||||
|
||||
u := NewUndeltaStore(push, testUndeltaKeyFunc)
|
||||
|
||||
// These should not call push.
|
||||
_ = u.List()
|
||||
_, _, _ = u.Get(testUndeltaObject{"a", ""})
|
||||
}
|
||||
|
||||
func TestReplaceCallsPush(t *testing.T) {
|
||||
mkObj := func(name string, val interface{}) testUndeltaObject {
|
||||
return testUndeltaObject{name: name, val: val}
|
||||
}
|
||||
|
||||
var got []interface{}
|
||||
var callcount int = 0
|
||||
push := func(m []interface{}) {
|
||||
callcount++
|
||||
got = m
|
||||
}
|
||||
|
||||
u := NewUndeltaStore(push, testUndeltaKeyFunc)
|
||||
|
||||
m := []interface{}{mkObj("a", 1)}
|
||||
|
||||
u.Replace(m, "0")
|
||||
if callcount != 1 {
|
||||
t.Errorf("Expected 1 calls, got %d", callcount)
|
||||
}
|
||||
expected := []interface{}{mkObj("a", 1)}
|
||||
if !reflect.DeepEqual(expected, got) {
|
||||
t.Errorf("Expected %#v, Got %#v", expected, got)
|
||||
}
|
||||
}
|
||||
301
vendor/k8s.io/client-go/tools/clientcmd/api/helpers_test.go
generated
vendored
301
vendor/k8s.io/client-go/tools/clientcmd/api/helpers_test.go
generated
vendored
|
|
@ -1,301 +0,0 @@
|
|||
/*
|
||||
Copyright 2015 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
)
|
||||
|
||||
func newMergedConfig(certFile, certContent, keyFile, keyContent, caFile, caContent string, t *testing.T) Config {
|
||||
if err := ioutil.WriteFile(certFile, []byte(certContent), 0644); err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
if err := ioutil.WriteFile(keyFile, []byte(keyContent), 0600); err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
if err := ioutil.WriteFile(caFile, []byte(caContent), 0644); err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
return Config{
|
||||
AuthInfos: map[string]*AuthInfo{
|
||||
"red-user": {Token: "red-token", ClientCertificateData: []byte(certContent), ClientKeyData: []byte(keyContent)},
|
||||
"blue-user": {Token: "blue-token", ClientCertificate: certFile, ClientKey: keyFile}},
|
||||
Clusters: map[string]*Cluster{
|
||||
"cow-cluster": {Server: "http://cow.org:8080", CertificateAuthorityData: []byte(caContent)},
|
||||
"chicken-cluster": {Server: "http://chicken.org:8080", CertificateAuthority: caFile}},
|
||||
Contexts: map[string]*Context{
|
||||
"federal-context": {AuthInfo: "red-user", Cluster: "cow-cluster"},
|
||||
"shaker-context": {AuthInfo: "blue-user", Cluster: "chicken-cluster"}},
|
||||
CurrentContext: "federal-context",
|
||||
}
|
||||
}
|
||||
|
||||
func TestMinifySuccess(t *testing.T) {
|
||||
certFile, _ := ioutil.TempFile("", "")
|
||||
defer os.Remove(certFile.Name())
|
||||
keyFile, _ := ioutil.TempFile("", "")
|
||||
defer os.Remove(keyFile.Name())
|
||||
caFile, _ := ioutil.TempFile("", "")
|
||||
defer os.Remove(caFile.Name())
|
||||
|
||||
mutatingConfig := newMergedConfig(certFile.Name(), "cert", keyFile.Name(), "key", caFile.Name(), "ca", t)
|
||||
|
||||
if err := MinifyConfig(&mutatingConfig); err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if len(mutatingConfig.Contexts) > 1 {
|
||||
t.Errorf("unexpected contexts: %v", mutatingConfig.Contexts)
|
||||
}
|
||||
if _, exists := mutatingConfig.Contexts["federal-context"]; !exists {
|
||||
t.Errorf("missing context")
|
||||
}
|
||||
|
||||
if len(mutatingConfig.Clusters) > 1 {
|
||||
t.Errorf("unexpected clusters: %v", mutatingConfig.Clusters)
|
||||
}
|
||||
if _, exists := mutatingConfig.Clusters["cow-cluster"]; !exists {
|
||||
t.Errorf("missing cluster")
|
||||
}
|
||||
|
||||
if len(mutatingConfig.AuthInfos) > 1 {
|
||||
t.Errorf("unexpected users: %v", mutatingConfig.AuthInfos)
|
||||
}
|
||||
if _, exists := mutatingConfig.AuthInfos["red-user"]; !exists {
|
||||
t.Errorf("missing user")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMinifyMissingContext(t *testing.T) {
|
||||
certFile, _ := ioutil.TempFile("", "")
|
||||
defer os.Remove(certFile.Name())
|
||||
keyFile, _ := ioutil.TempFile("", "")
|
||||
defer os.Remove(keyFile.Name())
|
||||
caFile, _ := ioutil.TempFile("", "")
|
||||
defer os.Remove(caFile.Name())
|
||||
|
||||
mutatingConfig := newMergedConfig(certFile.Name(), "cert", keyFile.Name(), "key", caFile.Name(), "ca", t)
|
||||
mutatingConfig.CurrentContext = "missing"
|
||||
|
||||
errMsg := "cannot locate context missing"
|
||||
|
||||
if err := MinifyConfig(&mutatingConfig); err == nil || err.Error() != errMsg {
|
||||
t.Errorf("expected %v, got %v", errMsg, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMinifyMissingCluster(t *testing.T) {
|
||||
certFile, _ := ioutil.TempFile("", "")
|
||||
defer os.Remove(certFile.Name())
|
||||
keyFile, _ := ioutil.TempFile("", "")
|
||||
defer os.Remove(keyFile.Name())
|
||||
caFile, _ := ioutil.TempFile("", "")
|
||||
defer os.Remove(caFile.Name())
|
||||
|
||||
mutatingConfig := newMergedConfig(certFile.Name(), "cert", keyFile.Name(), "key", caFile.Name(), "ca", t)
|
||||
delete(mutatingConfig.Clusters, mutatingConfig.Contexts[mutatingConfig.CurrentContext].Cluster)
|
||||
|
||||
errMsg := "cannot locate cluster cow-cluster"
|
||||
|
||||
if err := MinifyConfig(&mutatingConfig); err == nil || err.Error() != errMsg {
|
||||
t.Errorf("expected %v, got %v", errMsg, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMinifyMissingAuthInfo(t *testing.T) {
|
||||
certFile, _ := ioutil.TempFile("", "")
|
||||
defer os.Remove(certFile.Name())
|
||||
keyFile, _ := ioutil.TempFile("", "")
|
||||
defer os.Remove(keyFile.Name())
|
||||
caFile, _ := ioutil.TempFile("", "")
|
||||
defer os.Remove(caFile.Name())
|
||||
|
||||
mutatingConfig := newMergedConfig(certFile.Name(), "cert", keyFile.Name(), "key", caFile.Name(), "ca", t)
|
||||
delete(mutatingConfig.AuthInfos, mutatingConfig.Contexts[mutatingConfig.CurrentContext].AuthInfo)
|
||||
|
||||
errMsg := "cannot locate user red-user"
|
||||
|
||||
if err := MinifyConfig(&mutatingConfig); err == nil || err.Error() != errMsg {
|
||||
t.Errorf("expected %v, got %v", errMsg, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFlattenSuccess(t *testing.T) {
|
||||
certFile, _ := ioutil.TempFile("", "")
|
||||
defer os.Remove(certFile.Name())
|
||||
keyFile, _ := ioutil.TempFile("", "")
|
||||
defer os.Remove(keyFile.Name())
|
||||
caFile, _ := ioutil.TempFile("", "")
|
||||
defer os.Remove(caFile.Name())
|
||||
|
||||
certData := "cert"
|
||||
keyData := "key"
|
||||
caData := "ca"
|
||||
|
||||
unchangingCluster := "cow-cluster"
|
||||
unchangingAuthInfo := "red-user"
|
||||
changingCluster := "chicken-cluster"
|
||||
changingAuthInfo := "blue-user"
|
||||
|
||||
startingConfig := newMergedConfig(certFile.Name(), certData, keyFile.Name(), keyData, caFile.Name(), caData, t)
|
||||
mutatingConfig := startingConfig
|
||||
|
||||
if err := FlattenConfig(&mutatingConfig); err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if len(mutatingConfig.Contexts) != 2 {
|
||||
t.Errorf("unexpected contexts: %v", mutatingConfig.Contexts)
|
||||
}
|
||||
if !reflect.DeepEqual(startingConfig.Contexts, mutatingConfig.Contexts) {
|
||||
t.Errorf("expected %v, got %v", startingConfig.Contexts, mutatingConfig.Contexts)
|
||||
}
|
||||
|
||||
if len(mutatingConfig.Clusters) != 2 {
|
||||
t.Errorf("unexpected clusters: %v", mutatingConfig.Clusters)
|
||||
}
|
||||
if !reflect.DeepEqual(startingConfig.Clusters[unchangingCluster], mutatingConfig.Clusters[unchangingCluster]) {
|
||||
t.Errorf("expected %v, got %v", startingConfig.Clusters[unchangingCluster], mutatingConfig.Clusters[unchangingCluster])
|
||||
}
|
||||
if len(mutatingConfig.Clusters[changingCluster].CertificateAuthority) != 0 {
|
||||
t.Errorf("unexpected caFile")
|
||||
}
|
||||
if string(mutatingConfig.Clusters[changingCluster].CertificateAuthorityData) != caData {
|
||||
t.Errorf("expected %v, got %v", caData, string(mutatingConfig.Clusters[changingCluster].CertificateAuthorityData))
|
||||
}
|
||||
|
||||
if len(mutatingConfig.AuthInfos) != 2 {
|
||||
t.Errorf("unexpected users: %v", mutatingConfig.AuthInfos)
|
||||
}
|
||||
if !reflect.DeepEqual(startingConfig.AuthInfos[unchangingAuthInfo], mutatingConfig.AuthInfos[unchangingAuthInfo]) {
|
||||
t.Errorf("expected %v, got %v", startingConfig.AuthInfos[unchangingAuthInfo], mutatingConfig.AuthInfos[unchangingAuthInfo])
|
||||
}
|
||||
if len(mutatingConfig.AuthInfos[changingAuthInfo].ClientCertificate) != 0 {
|
||||
t.Errorf("unexpected caFile")
|
||||
}
|
||||
if string(mutatingConfig.AuthInfos[changingAuthInfo].ClientCertificateData) != certData {
|
||||
t.Errorf("expected %v, got %v", certData, string(mutatingConfig.AuthInfos[changingAuthInfo].ClientCertificateData))
|
||||
}
|
||||
if len(mutatingConfig.AuthInfos[changingAuthInfo].ClientKey) != 0 {
|
||||
t.Errorf("unexpected caFile")
|
||||
}
|
||||
if string(mutatingConfig.AuthInfos[changingAuthInfo].ClientKeyData) != keyData {
|
||||
t.Errorf("expected %v, got %v", keyData, string(mutatingConfig.AuthInfos[changingAuthInfo].ClientKeyData))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func Example_minifyAndShorten() {
|
||||
certFile, _ := ioutil.TempFile("", "")
|
||||
defer os.Remove(certFile.Name())
|
||||
keyFile, _ := ioutil.TempFile("", "")
|
||||
defer os.Remove(keyFile.Name())
|
||||
caFile, _ := ioutil.TempFile("", "")
|
||||
defer os.Remove(caFile.Name())
|
||||
|
||||
certData := "cert"
|
||||
keyData := "key"
|
||||
caData := "ca"
|
||||
|
||||
config := newMergedConfig(certFile.Name(), certData, keyFile.Name(), keyData, caFile.Name(), caData, nil)
|
||||
|
||||
MinifyConfig(&config)
|
||||
ShortenConfig(&config)
|
||||
|
||||
output, _ := yaml.Marshal(config)
|
||||
fmt.Printf("%s", string(output))
|
||||
// Output:
|
||||
// clusters:
|
||||
// cow-cluster:
|
||||
// LocationOfOrigin: ""
|
||||
// certificate-authority-data: REDACTED
|
||||
// server: http://cow.org:8080
|
||||
// contexts:
|
||||
// federal-context:
|
||||
// LocationOfOrigin: ""
|
||||
// cluster: cow-cluster
|
||||
// user: red-user
|
||||
// current-context: federal-context
|
||||
// preferences: {}
|
||||
// users:
|
||||
// red-user:
|
||||
// LocationOfOrigin: ""
|
||||
// client-certificate-data: REDACTED
|
||||
// client-key-data: REDACTED
|
||||
// token: red-token
|
||||
}
|
||||
|
||||
func TestShortenSuccess(t *testing.T) {
|
||||
certFile, _ := ioutil.TempFile("", "")
|
||||
defer os.Remove(certFile.Name())
|
||||
keyFile, _ := ioutil.TempFile("", "")
|
||||
defer os.Remove(keyFile.Name())
|
||||
caFile, _ := ioutil.TempFile("", "")
|
||||
defer os.Remove(caFile.Name())
|
||||
|
||||
certData := "cert"
|
||||
keyData := "key"
|
||||
caData := "ca"
|
||||
|
||||
unchangingCluster := "chicken-cluster"
|
||||
unchangingAuthInfo := "blue-user"
|
||||
changingCluster := "cow-cluster"
|
||||
changingAuthInfo := "red-user"
|
||||
|
||||
startingConfig := newMergedConfig(certFile.Name(), certData, keyFile.Name(), keyData, caFile.Name(), caData, t)
|
||||
mutatingConfig := startingConfig
|
||||
|
||||
ShortenConfig(&mutatingConfig)
|
||||
|
||||
if len(mutatingConfig.Contexts) != 2 {
|
||||
t.Errorf("unexpected contexts: %v", mutatingConfig.Contexts)
|
||||
}
|
||||
if !reflect.DeepEqual(startingConfig.Contexts, mutatingConfig.Contexts) {
|
||||
t.Errorf("expected %v, got %v", startingConfig.Contexts, mutatingConfig.Contexts)
|
||||
}
|
||||
|
||||
redacted := string(redactedBytes)
|
||||
if len(mutatingConfig.Clusters) != 2 {
|
||||
t.Errorf("unexpected clusters: %v", mutatingConfig.Clusters)
|
||||
}
|
||||
if !reflect.DeepEqual(startingConfig.Clusters[unchangingCluster], mutatingConfig.Clusters[unchangingCluster]) {
|
||||
t.Errorf("expected %v, got %v", startingConfig.Clusters[unchangingCluster], mutatingConfig.Clusters[unchangingCluster])
|
||||
}
|
||||
if string(mutatingConfig.Clusters[changingCluster].CertificateAuthorityData) != redacted {
|
||||
t.Errorf("expected %v, got %v", redacted, string(mutatingConfig.Clusters[changingCluster].CertificateAuthorityData))
|
||||
}
|
||||
|
||||
if len(mutatingConfig.AuthInfos) != 2 {
|
||||
t.Errorf("unexpected users: %v", mutatingConfig.AuthInfos)
|
||||
}
|
||||
if !reflect.DeepEqual(startingConfig.AuthInfos[unchangingAuthInfo], mutatingConfig.AuthInfos[unchangingAuthInfo]) {
|
||||
t.Errorf("expected %v, got %v", startingConfig.AuthInfos[unchangingAuthInfo], mutatingConfig.AuthInfos[unchangingAuthInfo])
|
||||
}
|
||||
if string(mutatingConfig.AuthInfos[changingAuthInfo].ClientCertificateData) != redacted {
|
||||
t.Errorf("expected %v, got %v", redacted, string(mutatingConfig.AuthInfos[changingAuthInfo].ClientCertificateData))
|
||||
}
|
||||
if string(mutatingConfig.AuthInfos[changingAuthInfo].ClientKeyData) != redacted {
|
||||
t.Errorf("expected %v, got %v", redacted, string(mutatingConfig.AuthInfos[changingAuthInfo].ClientKeyData))
|
||||
}
|
||||
}
|
||||
135
vendor/k8s.io/client-go/tools/clientcmd/api/types_test.go
generated
vendored
135
vendor/k8s.io/client-go/tools/clientcmd/api/types_test.go
generated
vendored
|
|
@ -1,135 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 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 api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
)
|
||||
|
||||
func Example_emptyConfig() {
|
||||
defaultConfig := NewConfig()
|
||||
|
||||
output, err := yaml.Marshal(defaultConfig)
|
||||
if err != nil {
|
||||
fmt.Printf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
fmt.Printf("%v", string(output))
|
||||
// Output:
|
||||
// clusters: {}
|
||||
// contexts: {}
|
||||
// current-context: ""
|
||||
// preferences: {}
|
||||
// users: {}
|
||||
}
|
||||
|
||||
func Example_ofOptionsConfig() {
|
||||
defaultConfig := NewConfig()
|
||||
defaultConfig.Preferences.Colors = true
|
||||
defaultConfig.Clusters["alfa"] = &Cluster{
|
||||
Server: "https://alfa.org:8080",
|
||||
InsecureSkipTLSVerify: true,
|
||||
CertificateAuthority: "path/to/my/cert-ca-filename",
|
||||
}
|
||||
defaultConfig.Clusters["bravo"] = &Cluster{
|
||||
Server: "https://bravo.org:8080",
|
||||
InsecureSkipTLSVerify: false,
|
||||
}
|
||||
defaultConfig.AuthInfos["white-mage-via-cert"] = &AuthInfo{
|
||||
ClientCertificate: "path/to/my/client-cert-filename",
|
||||
ClientKey: "path/to/my/client-key-filename",
|
||||
}
|
||||
defaultConfig.AuthInfos["red-mage-via-token"] = &AuthInfo{
|
||||
Token: "my-secret-token",
|
||||
}
|
||||
defaultConfig.AuthInfos["black-mage-via-auth-provider"] = &AuthInfo{
|
||||
AuthProvider: &AuthProviderConfig{
|
||||
Name: "gcp",
|
||||
Config: map[string]string{
|
||||
"foo": "bar",
|
||||
"token": "s3cr3t-t0k3n",
|
||||
},
|
||||
},
|
||||
}
|
||||
defaultConfig.Contexts["bravo-as-black-mage"] = &Context{
|
||||
Cluster: "bravo",
|
||||
AuthInfo: "black-mage-via-auth-provider",
|
||||
Namespace: "yankee",
|
||||
}
|
||||
defaultConfig.Contexts["alfa-as-black-mage"] = &Context{
|
||||
Cluster: "alfa",
|
||||
AuthInfo: "black-mage-via-auth-provider",
|
||||
Namespace: "zulu",
|
||||
}
|
||||
defaultConfig.Contexts["alfa-as-white-mage"] = &Context{
|
||||
Cluster: "alfa",
|
||||
AuthInfo: "white-mage-via-cert",
|
||||
}
|
||||
defaultConfig.CurrentContext = "alfa-as-white-mage"
|
||||
|
||||
output, err := yaml.Marshal(defaultConfig)
|
||||
if err != nil {
|
||||
fmt.Printf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
fmt.Printf("%v", string(output))
|
||||
// Output:
|
||||
// clusters:
|
||||
// alfa:
|
||||
// LocationOfOrigin: ""
|
||||
// certificate-authority: path/to/my/cert-ca-filename
|
||||
// insecure-skip-tls-verify: true
|
||||
// server: https://alfa.org:8080
|
||||
// bravo:
|
||||
// LocationOfOrigin: ""
|
||||
// server: https://bravo.org:8080
|
||||
// contexts:
|
||||
// alfa-as-black-mage:
|
||||
// LocationOfOrigin: ""
|
||||
// cluster: alfa
|
||||
// namespace: zulu
|
||||
// user: black-mage-via-auth-provider
|
||||
// alfa-as-white-mage:
|
||||
// LocationOfOrigin: ""
|
||||
// cluster: alfa
|
||||
// user: white-mage-via-cert
|
||||
// bravo-as-black-mage:
|
||||
// LocationOfOrigin: ""
|
||||
// cluster: bravo
|
||||
// namespace: yankee
|
||||
// user: black-mage-via-auth-provider
|
||||
// current-context: alfa-as-white-mage
|
||||
// preferences:
|
||||
// colors: true
|
||||
// users:
|
||||
// black-mage-via-auth-provider:
|
||||
// LocationOfOrigin: ""
|
||||
// auth-provider:
|
||||
// config:
|
||||
// foo: bar
|
||||
// token: s3cr3t-t0k3n
|
||||
// name: gcp
|
||||
// red-mage-via-token:
|
||||
// LocationOfOrigin: ""
|
||||
// token: my-secret-token
|
||||
// white-mage-via-cert:
|
||||
// LocationOfOrigin: ""
|
||||
// client-certificate: path/to/my/client-cert-filename
|
||||
// client-key: path/to/my/client-key-filename
|
||||
}
|
||||
528
vendor/k8s.io/client-go/tools/clientcmd/client_config_test.go
generated
vendored
528
vendor/k8s.io/client-go/tools/clientcmd/client_config_test.go
generated
vendored
|
|
@ -1,528 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 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 clientcmd
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/imdario/mergo"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
)
|
||||
|
||||
func TestOldMergoLib(t *testing.T) {
|
||||
type T struct {
|
||||
X string
|
||||
}
|
||||
dst := T{X: "one"}
|
||||
src := T{X: "two"}
|
||||
mergo.Merge(&dst, &src)
|
||||
if dst.X != "two" {
|
||||
// mergo.Merge changed in an incompatible way with
|
||||
//
|
||||
// https://github.com/imdario/mergo/commit/d304790b2ed594794496464fadd89d2bb266600a
|
||||
//
|
||||
// We have to stay with the old version which still does eager
|
||||
// copying from src to dst in structs.
|
||||
t.Errorf("mergo.Merge library found with incompatible, new behavior")
|
||||
}
|
||||
}
|
||||
|
||||
func createValidTestConfig() *clientcmdapi.Config {
|
||||
const (
|
||||
server = "https://anything.com:8080"
|
||||
token = "the-token"
|
||||
)
|
||||
|
||||
config := clientcmdapi.NewConfig()
|
||||
config.Clusters["clean"] = &clientcmdapi.Cluster{
|
||||
Server: server,
|
||||
}
|
||||
config.AuthInfos["clean"] = &clientcmdapi.AuthInfo{
|
||||
Token: token,
|
||||
}
|
||||
config.Contexts["clean"] = &clientcmdapi.Context{
|
||||
Cluster: "clean",
|
||||
AuthInfo: "clean",
|
||||
}
|
||||
config.CurrentContext = "clean"
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
func createCAValidTestConfig() *clientcmdapi.Config {
|
||||
|
||||
config := createValidTestConfig()
|
||||
config.Clusters["clean"].CertificateAuthorityData = []byte{0, 0}
|
||||
return config
|
||||
}
|
||||
|
||||
func TestInsecureOverridesCA(t *testing.T) {
|
||||
config := createCAValidTestConfig()
|
||||
clientBuilder := NewNonInteractiveClientConfig(*config, "clean", &ConfigOverrides{
|
||||
ClusterInfo: clientcmdapi.Cluster{
|
||||
InsecureSkipTLSVerify: true,
|
||||
},
|
||||
}, nil)
|
||||
|
||||
actualCfg, err := clientBuilder.ClientConfig()
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
matchBoolArg(true, actualCfg.Insecure, t)
|
||||
matchStringArg("", actualCfg.TLSClientConfig.CAFile, t)
|
||||
matchByteArg(nil, actualCfg.TLSClientConfig.CAData, t)
|
||||
}
|
||||
|
||||
func TestMergeContext(t *testing.T) {
|
||||
const namespace = "overridden-namespace"
|
||||
|
||||
config := createValidTestConfig()
|
||||
clientBuilder := NewNonInteractiveClientConfig(*config, "clean", &ConfigOverrides{}, nil)
|
||||
|
||||
_, overridden, err := clientBuilder.Namespace()
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if overridden {
|
||||
t.Error("Expected namespace to not be overridden")
|
||||
}
|
||||
|
||||
clientBuilder = NewNonInteractiveClientConfig(*config, "clean", &ConfigOverrides{
|
||||
Context: clientcmdapi.Context{
|
||||
Namespace: namespace,
|
||||
},
|
||||
}, nil)
|
||||
|
||||
actual, overridden, err := clientBuilder.Namespace()
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if !overridden {
|
||||
t.Error("Expected namespace to be overridden")
|
||||
}
|
||||
|
||||
matchStringArg(namespace, actual, t)
|
||||
}
|
||||
|
||||
func TestCertificateData(t *testing.T) {
|
||||
caData := []byte("ca-data")
|
||||
certData := []byte("cert-data")
|
||||
keyData := []byte("key-data")
|
||||
|
||||
config := clientcmdapi.NewConfig()
|
||||
config.Clusters["clean"] = &clientcmdapi.Cluster{
|
||||
Server: "https://localhost:8443",
|
||||
CertificateAuthorityData: caData,
|
||||
}
|
||||
config.AuthInfos["clean"] = &clientcmdapi.AuthInfo{
|
||||
ClientCertificateData: certData,
|
||||
ClientKeyData: keyData,
|
||||
}
|
||||
config.Contexts["clean"] = &clientcmdapi.Context{
|
||||
Cluster: "clean",
|
||||
AuthInfo: "clean",
|
||||
}
|
||||
config.CurrentContext = "clean"
|
||||
|
||||
clientBuilder := NewNonInteractiveClientConfig(*config, "clean", &ConfigOverrides{}, nil)
|
||||
|
||||
clientConfig, err := clientBuilder.ClientConfig()
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// Make sure cert data gets into config (will override file paths)
|
||||
matchByteArg(caData, clientConfig.TLSClientConfig.CAData, t)
|
||||
matchByteArg(certData, clientConfig.TLSClientConfig.CertData, t)
|
||||
matchByteArg(keyData, clientConfig.TLSClientConfig.KeyData, t)
|
||||
}
|
||||
|
||||
func TestBasicAuthData(t *testing.T) {
|
||||
username := "myuser"
|
||||
password := "mypass"
|
||||
|
||||
config := clientcmdapi.NewConfig()
|
||||
config.Clusters["clean"] = &clientcmdapi.Cluster{
|
||||
Server: "https://localhost:8443",
|
||||
}
|
||||
config.AuthInfos["clean"] = &clientcmdapi.AuthInfo{
|
||||
Username: username,
|
||||
Password: password,
|
||||
}
|
||||
config.Contexts["clean"] = &clientcmdapi.Context{
|
||||
Cluster: "clean",
|
||||
AuthInfo: "clean",
|
||||
}
|
||||
config.CurrentContext = "clean"
|
||||
|
||||
clientBuilder := NewNonInteractiveClientConfig(*config, "clean", &ConfigOverrides{}, nil)
|
||||
|
||||
clientConfig, err := clientBuilder.ClientConfig()
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// Make sure basic auth data gets into config
|
||||
matchStringArg(username, clientConfig.Username, t)
|
||||
matchStringArg(password, clientConfig.Password, t)
|
||||
}
|
||||
|
||||
func TestBasicTokenFile(t *testing.T) {
|
||||
token := "exampletoken"
|
||||
f, err := ioutil.TempFile("", "tokenfile")
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
return
|
||||
}
|
||||
defer os.Remove(f.Name())
|
||||
if err := ioutil.WriteFile(f.Name(), []byte(token), 0644); err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
config := clientcmdapi.NewConfig()
|
||||
config.Clusters["clean"] = &clientcmdapi.Cluster{
|
||||
Server: "https://localhost:8443",
|
||||
}
|
||||
config.AuthInfos["clean"] = &clientcmdapi.AuthInfo{
|
||||
TokenFile: f.Name(),
|
||||
}
|
||||
config.Contexts["clean"] = &clientcmdapi.Context{
|
||||
Cluster: "clean",
|
||||
AuthInfo: "clean",
|
||||
}
|
||||
config.CurrentContext = "clean"
|
||||
|
||||
clientBuilder := NewNonInteractiveClientConfig(*config, "clean", &ConfigOverrides{}, nil)
|
||||
|
||||
clientConfig, err := clientBuilder.ClientConfig()
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
matchStringArg(token, clientConfig.BearerToken, t)
|
||||
}
|
||||
|
||||
func TestPrecedenceTokenFile(t *testing.T) {
|
||||
token := "exampletoken"
|
||||
f, err := ioutil.TempFile("", "tokenfile")
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
return
|
||||
}
|
||||
defer os.Remove(f.Name())
|
||||
if err := ioutil.WriteFile(f.Name(), []byte(token), 0644); err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
config := clientcmdapi.NewConfig()
|
||||
config.Clusters["clean"] = &clientcmdapi.Cluster{
|
||||
Server: "https://localhost:8443",
|
||||
}
|
||||
expectedToken := "expected"
|
||||
config.AuthInfos["clean"] = &clientcmdapi.AuthInfo{
|
||||
Token: expectedToken,
|
||||
TokenFile: f.Name(),
|
||||
}
|
||||
config.Contexts["clean"] = &clientcmdapi.Context{
|
||||
Cluster: "clean",
|
||||
AuthInfo: "clean",
|
||||
}
|
||||
config.CurrentContext = "clean"
|
||||
|
||||
clientBuilder := NewNonInteractiveClientConfig(*config, "clean", &ConfigOverrides{}, nil)
|
||||
|
||||
clientConfig, err := clientBuilder.ClientConfig()
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
matchStringArg(expectedToken, clientConfig.BearerToken, t)
|
||||
}
|
||||
|
||||
func TestCreateClean(t *testing.T) {
|
||||
config := createValidTestConfig()
|
||||
clientBuilder := NewNonInteractiveClientConfig(*config, "clean", &ConfigOverrides{}, nil)
|
||||
|
||||
clientConfig, err := clientBuilder.ClientConfig()
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
matchStringArg(config.Clusters["clean"].Server, clientConfig.Host, t)
|
||||
matchStringArg("", clientConfig.APIPath, t)
|
||||
matchBoolArg(config.Clusters["clean"].InsecureSkipTLSVerify, clientConfig.Insecure, t)
|
||||
matchStringArg(config.AuthInfos["clean"].Token, clientConfig.BearerToken, t)
|
||||
}
|
||||
|
||||
func TestCreateCleanWithPrefix(t *testing.T) {
|
||||
tt := []struct {
|
||||
server string
|
||||
host string
|
||||
}{
|
||||
{"https://anything.com:8080/foo/bar", "https://anything.com:8080/foo/bar"},
|
||||
{"http://anything.com:8080/foo/bar", "http://anything.com:8080/foo/bar"},
|
||||
{"http://anything.com:8080/foo/bar/", "http://anything.com:8080/foo/bar/"},
|
||||
{"http://anything.com:8080/", "http://anything.com:8080/"},
|
||||
{"http://anything.com:8080//", "http://anything.com:8080//"},
|
||||
{"anything.com:8080/foo/bar", "anything.com:8080/foo/bar"},
|
||||
{"anything.com:8080", "anything.com:8080"},
|
||||
{"anything.com", "anything.com"},
|
||||
{"anything", "anything"},
|
||||
}
|
||||
|
||||
tt = append(tt, struct{ server, host string }{"", "http://localhost:8080"})
|
||||
|
||||
for _, tc := range tt {
|
||||
config := createValidTestConfig()
|
||||
|
||||
cleanConfig := config.Clusters["clean"]
|
||||
cleanConfig.Server = tc.server
|
||||
config.Clusters["clean"] = cleanConfig
|
||||
|
||||
clientBuilder := NewNonInteractiveClientConfig(*config, "clean", &ConfigOverrides{
|
||||
ClusterDefaults: clientcmdapi.Cluster{Server: "http://localhost:8080"},
|
||||
}, nil)
|
||||
|
||||
clientConfig, err := clientBuilder.ClientConfig()
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
matchStringArg(tc.host, clientConfig.Host, t)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateCleanDefault(t *testing.T) {
|
||||
config := createValidTestConfig()
|
||||
clientBuilder := NewDefaultClientConfig(*config, &ConfigOverrides{})
|
||||
|
||||
clientConfig, err := clientBuilder.ClientConfig()
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
matchStringArg(config.Clusters["clean"].Server, clientConfig.Host, t)
|
||||
matchBoolArg(config.Clusters["clean"].InsecureSkipTLSVerify, clientConfig.Insecure, t)
|
||||
matchStringArg(config.AuthInfos["clean"].Token, clientConfig.BearerToken, t)
|
||||
}
|
||||
|
||||
func TestCreateCleanDefaultCluster(t *testing.T) {
|
||||
config := createValidTestConfig()
|
||||
clientBuilder := NewDefaultClientConfig(*config, &ConfigOverrides{
|
||||
ClusterDefaults: clientcmdapi.Cluster{Server: "http://localhost:8080"},
|
||||
})
|
||||
|
||||
clientConfig, err := clientBuilder.ClientConfig()
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
matchStringArg(config.Clusters["clean"].Server, clientConfig.Host, t)
|
||||
matchBoolArg(config.Clusters["clean"].InsecureSkipTLSVerify, clientConfig.Insecure, t)
|
||||
matchStringArg(config.AuthInfos["clean"].Token, clientConfig.BearerToken, t)
|
||||
}
|
||||
|
||||
func TestCreateMissingContextNoDefault(t *testing.T) {
|
||||
const expectedErrorContains = "Context was not found for specified context"
|
||||
config := createValidTestConfig()
|
||||
clientBuilder := NewNonInteractiveClientConfig(*config, "not-present", &ConfigOverrides{}, nil)
|
||||
|
||||
_, err := clientBuilder.ClientConfig()
|
||||
if err == nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateMissingContext(t *testing.T) {
|
||||
const expectedErrorContains = "context was not found for specified context: not-present"
|
||||
config := createValidTestConfig()
|
||||
clientBuilder := NewNonInteractiveClientConfig(*config, "not-present", &ConfigOverrides{
|
||||
ClusterDefaults: clientcmdapi.Cluster{Server: "http://localhost:8080"},
|
||||
}, nil)
|
||||
|
||||
_, err := clientBuilder.ClientConfig()
|
||||
if err == nil {
|
||||
t.Fatalf("Expected error: %v", expectedErrorContains)
|
||||
}
|
||||
if !strings.Contains(err.Error(), expectedErrorContains) {
|
||||
t.Fatalf("Expected error: %v, but got %v", expectedErrorContains, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInClusterClientConfigPrecedence(t *testing.T) {
|
||||
tt := []struct {
|
||||
overrides *ConfigOverrides
|
||||
}{
|
||||
{
|
||||
overrides: &ConfigOverrides{
|
||||
ClusterInfo: clientcmdapi.Cluster{
|
||||
Server: "https://host-from-overrides.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
overrides: &ConfigOverrides{
|
||||
AuthInfo: clientcmdapi.AuthInfo{
|
||||
Token: "https://host-from-overrides.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
overrides: &ConfigOverrides{
|
||||
ClusterInfo: clientcmdapi.Cluster{
|
||||
CertificateAuthority: "/path/to/ca-from-overrides.crt",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
overrides: &ConfigOverrides{
|
||||
ClusterInfo: clientcmdapi.Cluster{
|
||||
Server: "https://host-from-overrides.com",
|
||||
},
|
||||
AuthInfo: clientcmdapi.AuthInfo{
|
||||
Token: "https://host-from-overrides.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
overrides: &ConfigOverrides{
|
||||
ClusterInfo: clientcmdapi.Cluster{
|
||||
Server: "https://host-from-overrides.com",
|
||||
CertificateAuthority: "/path/to/ca-from-overrides.crt",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
overrides: &ConfigOverrides{
|
||||
ClusterInfo: clientcmdapi.Cluster{
|
||||
CertificateAuthority: "/path/to/ca-from-overrides.crt",
|
||||
},
|
||||
AuthInfo: clientcmdapi.AuthInfo{
|
||||
Token: "https://host-from-overrides.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
overrides: &ConfigOverrides{
|
||||
ClusterInfo: clientcmdapi.Cluster{
|
||||
Server: "https://host-from-overrides.com",
|
||||
CertificateAuthority: "/path/to/ca-from-overrides.crt",
|
||||
},
|
||||
AuthInfo: clientcmdapi.AuthInfo{
|
||||
Token: "https://host-from-overrides.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
overrides: &ConfigOverrides{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tt {
|
||||
expectedServer := "https://host-from-cluster.com"
|
||||
expectedToken := "token-from-cluster"
|
||||
expectedCAFile := "/path/to/ca-from-cluster.crt"
|
||||
|
||||
icc := &inClusterClientConfig{
|
||||
inClusterConfigProvider: func() (*restclient.Config, error) {
|
||||
return &restclient.Config{
|
||||
Host: expectedServer,
|
||||
BearerToken: expectedToken,
|
||||
TLSClientConfig: restclient.TLSClientConfig{
|
||||
CAFile: expectedCAFile,
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
overrides: tc.overrides,
|
||||
}
|
||||
|
||||
clientConfig, err := icc.ClientConfig()
|
||||
if err != nil {
|
||||
t.Fatalf("Unxpected error: %v", err)
|
||||
}
|
||||
|
||||
if overridenServer := tc.overrides.ClusterInfo.Server; len(overridenServer) > 0 {
|
||||
expectedServer = overridenServer
|
||||
}
|
||||
if overridenToken := tc.overrides.AuthInfo.Token; len(overridenToken) > 0 {
|
||||
expectedToken = overridenToken
|
||||
}
|
||||
if overridenCAFile := tc.overrides.ClusterInfo.CertificateAuthority; len(overridenCAFile) > 0 {
|
||||
expectedCAFile = overridenCAFile
|
||||
}
|
||||
|
||||
if clientConfig.Host != expectedServer {
|
||||
t.Errorf("Expected server %v, got %v", expectedServer, clientConfig.Host)
|
||||
}
|
||||
if clientConfig.BearerToken != expectedToken {
|
||||
t.Errorf("Expected token %v, got %v", expectedToken, clientConfig.BearerToken)
|
||||
}
|
||||
if clientConfig.TLSClientConfig.CAFile != expectedCAFile {
|
||||
t.Errorf("Expected Certificate Authority %v, got %v", expectedCAFile, clientConfig.TLSClientConfig.CAFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func matchBoolArg(expected, got bool, t *testing.T) {
|
||||
if expected != got {
|
||||
t.Errorf("Expected %v, got %v", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
func matchStringArg(expected, got string, t *testing.T) {
|
||||
if expected != got {
|
||||
t.Errorf("Expected %q, got %q", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
func matchByteArg(expected, got []byte, t *testing.T) {
|
||||
if !reflect.DeepEqual(expected, got) {
|
||||
t.Errorf("Expected %v, got %v", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNamespaceOverride(t *testing.T) {
|
||||
config := &DirectClientConfig{
|
||||
overrides: &ConfigOverrides{
|
||||
Context: clientcmdapi.Context{
|
||||
Namespace: "foo",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
ns, overridden, err := config.Namespace()
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if !overridden {
|
||||
t.Errorf("Expected overridden = true")
|
||||
}
|
||||
|
||||
matchStringArg("foo", ns, t)
|
||||
}
|
||||
594
vendor/k8s.io/client-go/tools/clientcmd/loader_test.go
generated
vendored
594
vendor/k8s.io/client-go/tools/clientcmd/loader_test.go
generated
vendored
|
|
@ -1,594 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 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 clientcmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
clientcmdlatest "k8s.io/client-go/tools/clientcmd/api/latest"
|
||||
)
|
||||
|
||||
var (
|
||||
testConfigAlfa = clientcmdapi.Config{
|
||||
AuthInfos: map[string]*clientcmdapi.AuthInfo{
|
||||
"red-user": {Token: "red-token"}},
|
||||
Clusters: map[string]*clientcmdapi.Cluster{
|
||||
"cow-cluster": {Server: "http://cow.org:8080"}},
|
||||
Contexts: map[string]*clientcmdapi.Context{
|
||||
"federal-context": {AuthInfo: "red-user", Cluster: "cow-cluster", Namespace: "hammer-ns"}},
|
||||
}
|
||||
testConfigBravo = clientcmdapi.Config{
|
||||
AuthInfos: map[string]*clientcmdapi.AuthInfo{
|
||||
"black-user": {Token: "black-token"}},
|
||||
Clusters: map[string]*clientcmdapi.Cluster{
|
||||
"pig-cluster": {Server: "http://pig.org:8080"}},
|
||||
Contexts: map[string]*clientcmdapi.Context{
|
||||
"queen-anne-context": {AuthInfo: "black-user", Cluster: "pig-cluster", Namespace: "saw-ns"}},
|
||||
}
|
||||
testConfigCharlie = clientcmdapi.Config{
|
||||
AuthInfos: map[string]*clientcmdapi.AuthInfo{
|
||||
"green-user": {Token: "green-token"}},
|
||||
Clusters: map[string]*clientcmdapi.Cluster{
|
||||
"horse-cluster": {Server: "http://horse.org:8080"}},
|
||||
Contexts: map[string]*clientcmdapi.Context{
|
||||
"shaker-context": {AuthInfo: "green-user", Cluster: "horse-cluster", Namespace: "chisel-ns"}},
|
||||
}
|
||||
testConfigDelta = clientcmdapi.Config{
|
||||
AuthInfos: map[string]*clientcmdapi.AuthInfo{
|
||||
"blue-user": {Token: "blue-token"}},
|
||||
Clusters: map[string]*clientcmdapi.Cluster{
|
||||
"chicken-cluster": {Server: "http://chicken.org:8080"}},
|
||||
Contexts: map[string]*clientcmdapi.Context{
|
||||
"gothic-context": {AuthInfo: "blue-user", Cluster: "chicken-cluster", Namespace: "plane-ns"}},
|
||||
}
|
||||
|
||||
testConfigConflictAlfa = clientcmdapi.Config{
|
||||
AuthInfos: map[string]*clientcmdapi.AuthInfo{
|
||||
"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},
|
||||
"donkey-cluster": {Server: "http://donkey.org:8080", InsecureSkipTLSVerify: true}},
|
||||
CurrentContext: "federal-context",
|
||||
}
|
||||
)
|
||||
|
||||
func TestNonExistentCommandLineFile(t *testing.T) {
|
||||
loadingRules := ClientConfigLoadingRules{
|
||||
ExplicitPath: "bogus_file",
|
||||
}
|
||||
|
||||
_, err := loadingRules.Load()
|
||||
if err == nil {
|
||||
t.Fatalf("Expected error for missing command-line file, got none")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "bogus_file") {
|
||||
t.Fatalf("Expected error about 'bogus_file', got %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestToleratingMissingFiles(t *testing.T) {
|
||||
loadingRules := ClientConfigLoadingRules{
|
||||
Precedence: []string{"bogus1", "bogus2", "bogus3"},
|
||||
}
|
||||
|
||||
_, err := loadingRules.Load()
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrorReadingFile(t *testing.T) {
|
||||
commandLineFile, _ := ioutil.TempFile("", "")
|
||||
defer os.Remove(commandLineFile.Name())
|
||||
|
||||
if err := ioutil.WriteFile(commandLineFile.Name(), []byte("bogus value"), 0644); err != nil {
|
||||
t.Fatalf("Error creating tempfile: %v", err)
|
||||
}
|
||||
|
||||
loadingRules := ClientConfigLoadingRules{
|
||||
ExplicitPath: commandLineFile.Name(),
|
||||
}
|
||||
|
||||
_, err := loadingRules.Load()
|
||||
if err == nil {
|
||||
t.Fatalf("Expected error for unloadable file, got none")
|
||||
}
|
||||
if !strings.Contains(err.Error(), commandLineFile.Name()) {
|
||||
t.Fatalf("Expected error about '%s', got %s", commandLineFile.Name(), err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrorReadingNonFile(t *testing.T) {
|
||||
tmpdir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't create tmpdir")
|
||||
}
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
loadingRules := ClientConfigLoadingRules{
|
||||
ExplicitPath: tmpdir,
|
||||
}
|
||||
|
||||
_, err = loadingRules.Load()
|
||||
if err == nil {
|
||||
t.Fatalf("Expected error for non-file, got none")
|
||||
}
|
||||
if !strings.Contains(err.Error(), tmpdir) {
|
||||
t.Fatalf("Expected error about '%s', got %s", tmpdir, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestConflictingCurrentContext(t *testing.T) {
|
||||
commandLineFile, _ := ioutil.TempFile("", "")
|
||||
defer os.Remove(commandLineFile.Name())
|
||||
envVarFile, _ := ioutil.TempFile("", "")
|
||||
defer os.Remove(envVarFile.Name())
|
||||
|
||||
mockCommandLineConfig := clientcmdapi.Config{
|
||||
CurrentContext: "any-context-value",
|
||||
}
|
||||
mockEnvVarConfig := clientcmdapi.Config{
|
||||
CurrentContext: "a-different-context",
|
||||
}
|
||||
|
||||
WriteToFile(mockCommandLineConfig, commandLineFile.Name())
|
||||
WriteToFile(mockEnvVarConfig, envVarFile.Name())
|
||||
|
||||
loadingRules := ClientConfigLoadingRules{
|
||||
ExplicitPath: commandLineFile.Name(),
|
||||
Precedence: []string{envVarFile.Name()},
|
||||
}
|
||||
|
||||
mergedConfig, err := loadingRules.Load()
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if mergedConfig.CurrentContext != mockCommandLineConfig.CurrentContext {
|
||||
t.Errorf("expected %v, got %v", mockCommandLineConfig.CurrentContext, mergedConfig.CurrentContext)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadingEmptyMaps(t *testing.T) {
|
||||
configFile, _ := ioutil.TempFile("", "")
|
||||
defer os.Remove(configFile.Name())
|
||||
|
||||
mockConfig := clientcmdapi.Config{
|
||||
CurrentContext: "any-context-value",
|
||||
}
|
||||
|
||||
WriteToFile(mockConfig, configFile.Name())
|
||||
|
||||
config, err := LoadFromFile(configFile.Name())
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if config.Clusters == nil {
|
||||
t.Error("expected config.Clusters to be non-nil")
|
||||
}
|
||||
if config.AuthInfos == nil {
|
||||
t.Error("expected config.AuthInfos to be non-nil")
|
||||
}
|
||||
if config.Contexts == nil {
|
||||
t.Error("expected config.Contexts to be non-nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveRelativePaths(t *testing.T) {
|
||||
pathResolutionConfig1 := clientcmdapi.Config{
|
||||
AuthInfos: map[string]*clientcmdapi.AuthInfo{
|
||||
"relative-user-1": {ClientCertificate: "relative/client/cert", ClientKey: "../relative/client/key"},
|
||||
"absolute-user-1": {ClientCertificate: "/absolute/client/cert", ClientKey: "/absolute/client/key"},
|
||||
"relative-cmd-1": {Exec: &clientcmdapi.ExecConfig{Command: "../relative/client/cmd"}},
|
||||
"absolute-cmd-1": {Exec: &clientcmdapi.ExecConfig{Command: "/absolute/client/cmd"}},
|
||||
"PATH-cmd-1": {Exec: &clientcmdapi.ExecConfig{Command: "cmd"}},
|
||||
},
|
||||
Clusters: map[string]*clientcmdapi.Cluster{
|
||||
"relative-server-1": {CertificateAuthority: "../relative/ca"},
|
||||
"absolute-server-1": {CertificateAuthority: "/absolute/ca"},
|
||||
},
|
||||
}
|
||||
pathResolutionConfig2 := clientcmdapi.Config{
|
||||
AuthInfos: map[string]*clientcmdapi.AuthInfo{
|
||||
"relative-user-2": {ClientCertificate: "relative/client/cert2", ClientKey: "../relative/client/key2"},
|
||||
"absolute-user-2": {ClientCertificate: "/absolute/client/cert2", ClientKey: "/absolute/client/key2"},
|
||||
},
|
||||
Clusters: map[string]*clientcmdapi.Cluster{
|
||||
"relative-server-2": {CertificateAuthority: "../relative/ca2"},
|
||||
"absolute-server-2": {CertificateAuthority: "/absolute/ca2"},
|
||||
},
|
||||
}
|
||||
|
||||
configDir1, _ := ioutil.TempDir("", "")
|
||||
defer os.RemoveAll(configDir1)
|
||||
configFile1 := path.Join(configDir1, ".kubeconfig")
|
||||
configDir1, _ = filepath.Abs(configDir1)
|
||||
|
||||
configDir2, _ := ioutil.TempDir("", "")
|
||||
defer os.RemoveAll(configDir2)
|
||||
configDir2, _ = ioutil.TempDir(configDir2, "")
|
||||
configFile2 := path.Join(configDir2, ".kubeconfig")
|
||||
configDir2, _ = filepath.Abs(configDir2)
|
||||
|
||||
WriteToFile(pathResolutionConfig1, configFile1)
|
||||
WriteToFile(pathResolutionConfig2, configFile2)
|
||||
|
||||
loadingRules := ClientConfigLoadingRules{
|
||||
Precedence: []string{configFile1, configFile2},
|
||||
}
|
||||
|
||||
mergedConfig, err := loadingRules.Load()
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
foundClusterCount := 0
|
||||
for key, cluster := range mergedConfig.Clusters {
|
||||
if key == "relative-server-1" {
|
||||
foundClusterCount++
|
||||
matchStringArg(path.Join(configDir1, pathResolutionConfig1.Clusters["relative-server-1"].CertificateAuthority), cluster.CertificateAuthority, t)
|
||||
}
|
||||
if key == "relative-server-2" {
|
||||
foundClusterCount++
|
||||
matchStringArg(path.Join(configDir2, pathResolutionConfig2.Clusters["relative-server-2"].CertificateAuthority), cluster.CertificateAuthority, t)
|
||||
}
|
||||
if key == "absolute-server-1" {
|
||||
foundClusterCount++
|
||||
matchStringArg(pathResolutionConfig1.Clusters["absolute-server-1"].CertificateAuthority, cluster.CertificateAuthority, t)
|
||||
}
|
||||
if key == "absolute-server-2" {
|
||||
foundClusterCount++
|
||||
matchStringArg(pathResolutionConfig2.Clusters["absolute-server-2"].CertificateAuthority, cluster.CertificateAuthority, t)
|
||||
}
|
||||
}
|
||||
if foundClusterCount != 4 {
|
||||
t.Errorf("Expected 4 clusters, found %v: %v", foundClusterCount, mergedConfig.Clusters)
|
||||
}
|
||||
|
||||
foundAuthInfoCount := 0
|
||||
for key, authInfo := range mergedConfig.AuthInfos {
|
||||
if key == "relative-user-1" {
|
||||
foundAuthInfoCount++
|
||||
matchStringArg(path.Join(configDir1, pathResolutionConfig1.AuthInfos["relative-user-1"].ClientCertificate), authInfo.ClientCertificate, t)
|
||||
matchStringArg(path.Join(configDir1, pathResolutionConfig1.AuthInfos["relative-user-1"].ClientKey), authInfo.ClientKey, t)
|
||||
}
|
||||
if key == "relative-user-2" {
|
||||
foundAuthInfoCount++
|
||||
matchStringArg(path.Join(configDir2, pathResolutionConfig2.AuthInfos["relative-user-2"].ClientCertificate), authInfo.ClientCertificate, t)
|
||||
matchStringArg(path.Join(configDir2, pathResolutionConfig2.AuthInfos["relative-user-2"].ClientKey), authInfo.ClientKey, t)
|
||||
}
|
||||
if key == "absolute-user-1" {
|
||||
foundAuthInfoCount++
|
||||
matchStringArg(pathResolutionConfig1.AuthInfos["absolute-user-1"].ClientCertificate, authInfo.ClientCertificate, t)
|
||||
matchStringArg(pathResolutionConfig1.AuthInfos["absolute-user-1"].ClientKey, authInfo.ClientKey, t)
|
||||
}
|
||||
if key == "absolute-user-2" {
|
||||
foundAuthInfoCount++
|
||||
matchStringArg(pathResolutionConfig2.AuthInfos["absolute-user-2"].ClientCertificate, authInfo.ClientCertificate, t)
|
||||
matchStringArg(pathResolutionConfig2.AuthInfos["absolute-user-2"].ClientKey, authInfo.ClientKey, t)
|
||||
}
|
||||
if key == "relative-cmd-1" {
|
||||
foundAuthInfoCount++
|
||||
matchStringArg(path.Join(configDir1, pathResolutionConfig1.AuthInfos[key].Exec.Command), authInfo.Exec.Command, t)
|
||||
}
|
||||
if key == "absolute-cmd-1" {
|
||||
foundAuthInfoCount++
|
||||
matchStringArg(pathResolutionConfig1.AuthInfos[key].Exec.Command, authInfo.Exec.Command, t)
|
||||
}
|
||||
if key == "PATH-cmd-1" {
|
||||
foundAuthInfoCount++
|
||||
matchStringArg(pathResolutionConfig1.AuthInfos[key].Exec.Command, authInfo.Exec.Command, t)
|
||||
}
|
||||
}
|
||||
if foundAuthInfoCount != 7 {
|
||||
t.Errorf("Expected 7 users, found %v: %v", foundAuthInfoCount, mergedConfig.AuthInfos)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestMigratingFile(t *testing.T) {
|
||||
sourceFile, _ := ioutil.TempFile("", "")
|
||||
defer os.Remove(sourceFile.Name())
|
||||
destinationFile, _ := ioutil.TempFile("", "")
|
||||
// delete the file so that we'll write to it
|
||||
os.Remove(destinationFile.Name())
|
||||
|
||||
WriteToFile(testConfigAlfa, sourceFile.Name())
|
||||
|
||||
loadingRules := ClientConfigLoadingRules{
|
||||
MigrationRules: map[string]string{destinationFile.Name(): sourceFile.Name()},
|
||||
}
|
||||
|
||||
if _, err := loadingRules.Load(); err != nil {
|
||||
t.Errorf("unexpected error %v", err)
|
||||
}
|
||||
|
||||
// the load should have recreated this file
|
||||
defer os.Remove(destinationFile.Name())
|
||||
|
||||
sourceContent, err := ioutil.ReadFile(sourceFile.Name())
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error %v", err)
|
||||
}
|
||||
destinationContent, err := ioutil.ReadFile(destinationFile.Name())
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error %v", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(sourceContent, destinationContent) {
|
||||
t.Errorf("source and destination do not match")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMigratingFileLeaveExistingFileAlone(t *testing.T) {
|
||||
sourceFile, _ := ioutil.TempFile("", "")
|
||||
defer os.Remove(sourceFile.Name())
|
||||
destinationFile, _ := ioutil.TempFile("", "")
|
||||
defer os.Remove(destinationFile.Name())
|
||||
|
||||
WriteToFile(testConfigAlfa, sourceFile.Name())
|
||||
|
||||
loadingRules := ClientConfigLoadingRules{
|
||||
MigrationRules: map[string]string{destinationFile.Name(): sourceFile.Name()},
|
||||
}
|
||||
|
||||
if _, err := loadingRules.Load(); err != nil {
|
||||
t.Errorf("unexpected error %v", err)
|
||||
}
|
||||
|
||||
destinationContent, err := ioutil.ReadFile(destinationFile.Name())
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error %v", err)
|
||||
}
|
||||
|
||||
if len(destinationContent) > 0 {
|
||||
t.Errorf("destination should not have been touched")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMigratingFileSourceMissingSkip(t *testing.T) {
|
||||
sourceFilename := "some-missing-file"
|
||||
destinationFile, _ := ioutil.TempFile("", "")
|
||||
// delete the file so that we'll write to it
|
||||
os.Remove(destinationFile.Name())
|
||||
|
||||
loadingRules := ClientConfigLoadingRules{
|
||||
MigrationRules: map[string]string{destinationFile.Name(): sourceFilename},
|
||||
}
|
||||
|
||||
if _, err := loadingRules.Load(); err != nil {
|
||||
t.Errorf("unexpected error %v", err)
|
||||
}
|
||||
|
||||
if _, err := os.Stat(destinationFile.Name()); !os.IsNotExist(err) {
|
||||
t.Errorf("destination should not exist")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileLocking(t *testing.T) {
|
||||
f, _ := ioutil.TempFile("", "")
|
||||
defer os.Remove(f.Name())
|
||||
|
||||
err := lockFile(f.Name())
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error while locking file: %v", err)
|
||||
}
|
||||
defer unlockFile(f.Name())
|
||||
|
||||
err = lockFile(f.Name())
|
||||
if err == nil {
|
||||
t.Error("expected error while locking file.")
|
||||
}
|
||||
}
|
||||
|
||||
func Example_noMergingOnExplicitPaths() {
|
||||
commandLineFile, _ := ioutil.TempFile("", "")
|
||||
defer os.Remove(commandLineFile.Name())
|
||||
envVarFile, _ := ioutil.TempFile("", "")
|
||||
defer os.Remove(envVarFile.Name())
|
||||
|
||||
WriteToFile(testConfigAlfa, commandLineFile.Name())
|
||||
WriteToFile(testConfigConflictAlfa, envVarFile.Name())
|
||||
|
||||
loadingRules := ClientConfigLoadingRules{
|
||||
ExplicitPath: commandLineFile.Name(),
|
||||
Precedence: []string{envVarFile.Name()},
|
||||
}
|
||||
|
||||
mergedConfig, err := loadingRules.Load()
|
||||
|
||||
json, err := runtime.Encode(clientcmdlatest.Codec, mergedConfig)
|
||||
if err != nil {
|
||||
fmt.Printf("Unexpected error: %v", err)
|
||||
}
|
||||
output, err := yaml.JSONToYAML(json)
|
||||
if err != nil {
|
||||
fmt.Printf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
fmt.Printf("%v", string(output))
|
||||
// Output:
|
||||
// apiVersion: v1
|
||||
// clusters:
|
||||
// - cluster:
|
||||
// server: http://cow.org:8080
|
||||
// name: cow-cluster
|
||||
// contexts:
|
||||
// - context:
|
||||
// cluster: cow-cluster
|
||||
// namespace: hammer-ns
|
||||
// user: red-user
|
||||
// name: federal-context
|
||||
// current-context: ""
|
||||
// kind: Config
|
||||
// preferences: {}
|
||||
// users:
|
||||
// - name: red-user
|
||||
// user:
|
||||
// token: red-token
|
||||
}
|
||||
|
||||
func Example_mergingSomeWithConflict() {
|
||||
commandLineFile, _ := ioutil.TempFile("", "")
|
||||
defer os.Remove(commandLineFile.Name())
|
||||
envVarFile, _ := ioutil.TempFile("", "")
|
||||
defer os.Remove(envVarFile.Name())
|
||||
|
||||
WriteToFile(testConfigAlfa, commandLineFile.Name())
|
||||
WriteToFile(testConfigConflictAlfa, envVarFile.Name())
|
||||
|
||||
loadingRules := ClientConfigLoadingRules{
|
||||
Precedence: []string{commandLineFile.Name(), envVarFile.Name()},
|
||||
}
|
||||
|
||||
mergedConfig, err := loadingRules.Load()
|
||||
|
||||
json, err := runtime.Encode(clientcmdlatest.Codec, mergedConfig)
|
||||
if err != nil {
|
||||
fmt.Printf("Unexpected error: %v", err)
|
||||
}
|
||||
output, err := yaml.JSONToYAML(json)
|
||||
if err != nil {
|
||||
fmt.Printf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
fmt.Printf("%v", string(output))
|
||||
// Output:
|
||||
// apiVersion: v1
|
||||
// clusters:
|
||||
// - cluster:
|
||||
// server: http://cow.org:8080
|
||||
// name: cow-cluster
|
||||
// - cluster:
|
||||
// insecure-skip-tls-verify: true
|
||||
// server: http://donkey.org:8080
|
||||
// name: donkey-cluster
|
||||
// contexts:
|
||||
// - context:
|
||||
// cluster: cow-cluster
|
||||
// namespace: hammer-ns
|
||||
// user: red-user
|
||||
// name: federal-context
|
||||
// current-context: federal-context
|
||||
// kind: Config
|
||||
// preferences: {}
|
||||
// users:
|
||||
// - name: red-user
|
||||
// user:
|
||||
// token: red-token
|
||||
// - name: yellow-user
|
||||
// user:
|
||||
// token: yellow-token
|
||||
}
|
||||
|
||||
func Example_mergingEverythingNoConflicts() {
|
||||
commandLineFile, _ := ioutil.TempFile("", "")
|
||||
defer os.Remove(commandLineFile.Name())
|
||||
envVarFile, _ := ioutil.TempFile("", "")
|
||||
defer os.Remove(envVarFile.Name())
|
||||
currentDirFile, _ := ioutil.TempFile("", "")
|
||||
defer os.Remove(currentDirFile.Name())
|
||||
homeDirFile, _ := ioutil.TempFile("", "")
|
||||
defer os.Remove(homeDirFile.Name())
|
||||
|
||||
WriteToFile(testConfigAlfa, commandLineFile.Name())
|
||||
WriteToFile(testConfigBravo, envVarFile.Name())
|
||||
WriteToFile(testConfigCharlie, currentDirFile.Name())
|
||||
WriteToFile(testConfigDelta, homeDirFile.Name())
|
||||
|
||||
loadingRules := ClientConfigLoadingRules{
|
||||
Precedence: []string{commandLineFile.Name(), envVarFile.Name(), currentDirFile.Name(), homeDirFile.Name()},
|
||||
}
|
||||
|
||||
mergedConfig, err := loadingRules.Load()
|
||||
|
||||
json, err := runtime.Encode(clientcmdlatest.Codec, mergedConfig)
|
||||
if err != nil {
|
||||
fmt.Printf("Unexpected error: %v", err)
|
||||
}
|
||||
output, err := yaml.JSONToYAML(json)
|
||||
if err != nil {
|
||||
fmt.Printf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
fmt.Printf("%v", string(output))
|
||||
// Output:
|
||||
// apiVersion: v1
|
||||
// clusters:
|
||||
// - cluster:
|
||||
// server: http://chicken.org:8080
|
||||
// name: chicken-cluster
|
||||
// - cluster:
|
||||
// server: http://cow.org:8080
|
||||
// name: cow-cluster
|
||||
// - cluster:
|
||||
// server: http://horse.org:8080
|
||||
// name: horse-cluster
|
||||
// - cluster:
|
||||
// server: http://pig.org:8080
|
||||
// name: pig-cluster
|
||||
// contexts:
|
||||
// - context:
|
||||
// cluster: cow-cluster
|
||||
// namespace: hammer-ns
|
||||
// user: red-user
|
||||
// name: federal-context
|
||||
// - context:
|
||||
// cluster: chicken-cluster
|
||||
// namespace: plane-ns
|
||||
// user: blue-user
|
||||
// name: gothic-context
|
||||
// - context:
|
||||
// cluster: pig-cluster
|
||||
// namespace: saw-ns
|
||||
// user: black-user
|
||||
// name: queen-anne-context
|
||||
// - context:
|
||||
// cluster: horse-cluster
|
||||
// namespace: chisel-ns
|
||||
// user: green-user
|
||||
// name: shaker-context
|
||||
// current-context: ""
|
||||
// kind: Config
|
||||
// preferences: {}
|
||||
// users:
|
||||
// - name: black-user
|
||||
// user:
|
||||
// token: black-token
|
||||
// - name: blue-user
|
||||
// user:
|
||||
// token: blue-token
|
||||
// - name: green-user
|
||||
// user:
|
||||
// token: green-token
|
||||
// - name: red-user
|
||||
// user:
|
||||
// token: red-token
|
||||
}
|
||||
328
vendor/k8s.io/client-go/tools/clientcmd/merged_client_builder_test.go
generated
vendored
328
vendor/k8s.io/client-go/tools/clientcmd/merged_client_builder_test.go
generated
vendored
|
|
@ -1,328 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 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 clientcmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
restclient "k8s.io/client-go/rest"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
)
|
||||
|
||||
type testLoader struct {
|
||||
ClientConfigLoader
|
||||
|
||||
called bool
|
||||
config *clientcmdapi.Config
|
||||
err error
|
||||
}
|
||||
|
||||
func (l *testLoader) Load() (*clientcmdapi.Config, error) {
|
||||
l.called = true
|
||||
return l.config, l.err
|
||||
}
|
||||
|
||||
type testClientConfig struct {
|
||||
config *restclient.Config
|
||||
namespace string
|
||||
namespaceSpecified bool
|
||||
err error
|
||||
}
|
||||
|
||||
func (c *testClientConfig) RawConfig() (clientcmdapi.Config, error) {
|
||||
return clientcmdapi.Config{}, fmt.Errorf("unexpected call")
|
||||
}
|
||||
func (c *testClientConfig) ClientConfig() (*restclient.Config, error) {
|
||||
return c.config, c.err
|
||||
}
|
||||
func (c *testClientConfig) Namespace() (string, bool, error) {
|
||||
return c.namespace, c.namespaceSpecified, c.err
|
||||
}
|
||||
func (c *testClientConfig) ConfigAccess() ConfigAccess {
|
||||
return nil
|
||||
}
|
||||
|
||||
type testICC struct {
|
||||
testClientConfig
|
||||
|
||||
possible bool
|
||||
called bool
|
||||
}
|
||||
|
||||
func (icc *testICC) Possible() bool {
|
||||
icc.called = true
|
||||
return icc.possible
|
||||
}
|
||||
|
||||
func TestInClusterConfig(t *testing.T) {
|
||||
default1 := &DirectClientConfig{
|
||||
config: *createValidTestConfig(),
|
||||
contextName: "clean",
|
||||
overrides: &ConfigOverrides{},
|
||||
}
|
||||
invalidDefaultConfig := clientcmdapi.NewConfig()
|
||||
invalidDefaultConfig.Clusters["clean"] = &clientcmdapi.Cluster{
|
||||
Server: "http://localhost:8080",
|
||||
}
|
||||
invalidDefaultConfig.Contexts["other"] = &clientcmdapi.Context{
|
||||
Cluster: "clean",
|
||||
}
|
||||
invalidDefaultConfig.CurrentContext = "clean"
|
||||
|
||||
defaultInvalid := &DirectClientConfig{
|
||||
config: *invalidDefaultConfig,
|
||||
overrides: &ConfigOverrides{},
|
||||
}
|
||||
if _, err := defaultInvalid.ClientConfig(); err == nil || !IsConfigurationInvalid(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
config1, err := default1.ClientConfig()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
config2 := &restclient.Config{Host: "config2"}
|
||||
err1 := fmt.Errorf("unique error")
|
||||
|
||||
testCases := map[string]struct {
|
||||
clientConfig *testClientConfig
|
||||
icc *testICC
|
||||
defaultConfig *DirectClientConfig
|
||||
|
||||
checkedICC bool
|
||||
result *restclient.Config
|
||||
err error
|
||||
}{
|
||||
"in-cluster checked on other error": {
|
||||
clientConfig: &testClientConfig{err: ErrEmptyConfig},
|
||||
icc: &testICC{},
|
||||
|
||||
checkedICC: true,
|
||||
result: nil,
|
||||
err: ErrEmptyConfig,
|
||||
},
|
||||
|
||||
"in-cluster not checked on non-empty error": {
|
||||
clientConfig: &testClientConfig{err: ErrEmptyCluster},
|
||||
icc: &testICC{},
|
||||
|
||||
checkedICC: false,
|
||||
result: nil,
|
||||
err: ErrEmptyCluster,
|
||||
},
|
||||
|
||||
"in-cluster checked when config is default": {
|
||||
defaultConfig: default1,
|
||||
clientConfig: &testClientConfig{config: config1},
|
||||
icc: &testICC{},
|
||||
|
||||
checkedICC: true,
|
||||
result: config1,
|
||||
err: nil,
|
||||
},
|
||||
|
||||
"in-cluster not checked when default config is invalid": {
|
||||
defaultConfig: defaultInvalid,
|
||||
clientConfig: &testClientConfig{config: config1},
|
||||
icc: &testICC{},
|
||||
|
||||
checkedICC: false,
|
||||
result: config1,
|
||||
err: nil,
|
||||
},
|
||||
|
||||
"in-cluster not checked when config is not equal to default": {
|
||||
defaultConfig: default1,
|
||||
clientConfig: &testClientConfig{config: config2},
|
||||
icc: &testICC{},
|
||||
|
||||
checkedICC: false,
|
||||
result: config2,
|
||||
err: nil,
|
||||
},
|
||||
|
||||
"in-cluster checked when config is not equal to default and error is empty": {
|
||||
clientConfig: &testClientConfig{config: config2, err: ErrEmptyConfig},
|
||||
icc: &testICC{},
|
||||
|
||||
checkedICC: true,
|
||||
result: config2,
|
||||
err: ErrEmptyConfig,
|
||||
},
|
||||
|
||||
"in-cluster error returned when config is empty": {
|
||||
clientConfig: &testClientConfig{err: ErrEmptyConfig},
|
||||
icc: &testICC{
|
||||
possible: true,
|
||||
testClientConfig: testClientConfig{
|
||||
err: err1,
|
||||
},
|
||||
},
|
||||
|
||||
checkedICC: true,
|
||||
result: nil,
|
||||
err: err1,
|
||||
},
|
||||
|
||||
"in-cluster config returned when config is empty": {
|
||||
clientConfig: &testClientConfig{err: ErrEmptyConfig},
|
||||
icc: &testICC{
|
||||
possible: true,
|
||||
testClientConfig: testClientConfig{
|
||||
config: config2,
|
||||
},
|
||||
},
|
||||
|
||||
checkedICC: true,
|
||||
result: config2,
|
||||
err: nil,
|
||||
},
|
||||
|
||||
"in-cluster not checked when standard default is invalid": {
|
||||
defaultConfig: &DefaultClientConfig,
|
||||
clientConfig: &testClientConfig{config: config2},
|
||||
icc: &testICC{},
|
||||
|
||||
checkedICC: false,
|
||||
result: config2,
|
||||
err: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range testCases {
|
||||
c := &DeferredLoadingClientConfig{icc: test.icc}
|
||||
c.loader = &ClientConfigLoadingRules{DefaultClientConfig: test.defaultConfig}
|
||||
c.clientConfig = test.clientConfig
|
||||
|
||||
cfg, err := c.ClientConfig()
|
||||
if test.icc.called != test.checkedICC {
|
||||
t.Errorf("%s: unexpected in-cluster-config call %t", name, test.icc.called)
|
||||
}
|
||||
if err != test.err || cfg != test.result {
|
||||
t.Errorf("%s: unexpected result: %v %#v", name, err, cfg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestInClusterConfigNamespace(t *testing.T) {
|
||||
err1 := fmt.Errorf("unique error")
|
||||
|
||||
testCases := map[string]struct {
|
||||
clientConfig *testClientConfig
|
||||
icc *testICC
|
||||
|
||||
checkedICC bool
|
||||
result string
|
||||
ok bool
|
||||
err error
|
||||
}{
|
||||
"in-cluster checked on empty error": {
|
||||
clientConfig: &testClientConfig{err: ErrEmptyConfig},
|
||||
icc: &testICC{},
|
||||
|
||||
checkedICC: true,
|
||||
err: ErrEmptyConfig,
|
||||
},
|
||||
|
||||
"in-cluster not checked on non-empty error": {
|
||||
clientConfig: &testClientConfig{err: ErrEmptyCluster},
|
||||
icc: &testICC{},
|
||||
|
||||
err: ErrEmptyCluster,
|
||||
},
|
||||
|
||||
"in-cluster checked when config is default": {
|
||||
clientConfig: &testClientConfig{},
|
||||
icc: &testICC{},
|
||||
|
||||
checkedICC: true,
|
||||
},
|
||||
|
||||
"in-cluster not checked when config is not equal to default": {
|
||||
clientConfig: &testClientConfig{namespace: "test", namespaceSpecified: true},
|
||||
icc: &testICC{},
|
||||
|
||||
result: "test",
|
||||
ok: true,
|
||||
},
|
||||
|
||||
"in-cluster checked when namespace is not specified, but is defaulted": {
|
||||
clientConfig: &testClientConfig{namespace: "test", namespaceSpecified: false},
|
||||
icc: &testICC{},
|
||||
|
||||
checkedICC: true,
|
||||
result: "test",
|
||||
ok: false,
|
||||
},
|
||||
|
||||
"in-cluster error returned when config is empty": {
|
||||
clientConfig: &testClientConfig{err: ErrEmptyConfig},
|
||||
icc: &testICC{
|
||||
possible: true,
|
||||
testClientConfig: testClientConfig{
|
||||
err: err1,
|
||||
},
|
||||
},
|
||||
|
||||
checkedICC: true,
|
||||
err: err1,
|
||||
},
|
||||
|
||||
"in-cluster config returned when config is empty": {
|
||||
clientConfig: &testClientConfig{err: ErrEmptyConfig},
|
||||
icc: &testICC{
|
||||
possible: true,
|
||||
testClientConfig: testClientConfig{
|
||||
namespace: "test",
|
||||
namespaceSpecified: true,
|
||||
},
|
||||
},
|
||||
|
||||
checkedICC: true,
|
||||
result: "test",
|
||||
ok: true,
|
||||
},
|
||||
|
||||
"in-cluster config returned when config is empty and namespace is defaulted but not explicitly set": {
|
||||
clientConfig: &testClientConfig{err: ErrEmptyConfig},
|
||||
icc: &testICC{
|
||||
possible: true,
|
||||
testClientConfig: testClientConfig{
|
||||
namespace: "test",
|
||||
namespaceSpecified: false,
|
||||
},
|
||||
},
|
||||
|
||||
checkedICC: true,
|
||||
result: "test",
|
||||
ok: false,
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range testCases {
|
||||
c := &DeferredLoadingClientConfig{icc: test.icc}
|
||||
c.clientConfig = test.clientConfig
|
||||
|
||||
ns, ok, err := c.Namespace()
|
||||
if test.icc.called != test.checkedICC {
|
||||
t.Errorf("%s: unexpected in-cluster-config call %t", name, test.icc.called)
|
||||
}
|
||||
if err != test.err || ns != test.result || ok != test.ok {
|
||||
t.Errorf("%s: unexpected result: %v %s %t", name, err, ns, ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
50
vendor/k8s.io/client-go/tools/clientcmd/overrides_test.go
generated
vendored
50
vendor/k8s.io/client-go/tools/clientcmd/overrides_test.go
generated
vendored
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 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 clientcmd
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
func TestNamespacePrefixStrip(t *testing.T) {
|
||||
testData := map[string]string{
|
||||
"namespaces/foo": "foo",
|
||||
"NAMESPACES/foo": "foo",
|
||||
"NameSpaces/foo": "foo",
|
||||
"namespace/foo": "foo",
|
||||
"NAMESPACE/foo": "foo",
|
||||
"nameSpace/foo": "foo",
|
||||
"ns/foo": "foo",
|
||||
"NS/foo": "foo",
|
||||
"namespaces/": "namespaces/",
|
||||
"namespace/": "namespace/",
|
||||
"ns/": "ns/",
|
||||
}
|
||||
|
||||
for before, after := range testData {
|
||||
overrides := &ConfigOverrides{}
|
||||
fs := &pflag.FlagSet{}
|
||||
BindOverrideFlags(overrides, fs, RecommendedConfigOverrideFlags(""))
|
||||
fs.Parse([]string{"--namespace", before})
|
||||
|
||||
if overrides.Context.Namespace != after {
|
||||
t.Fatalf("Expected %s, got %s", after, overrides.Context.Namespace)
|
||||
}
|
||||
}
|
||||
}
|
||||
574
vendor/k8s.io/client-go/tools/clientcmd/validation_test.go
generated
vendored
574
vendor/k8s.io/client-go/tools/clientcmd/validation_test.go
generated
vendored
|
|
@ -1,574 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 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 clientcmd
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
)
|
||||
|
||||
func TestConfirmUsableBadInfoButOkConfig(t *testing.T) {
|
||||
config := clientcmdapi.NewConfig()
|
||||
config.Clusters["missing ca"] = &clientcmdapi.Cluster{
|
||||
Server: "anything",
|
||||
CertificateAuthority: "missing",
|
||||
}
|
||||
config.AuthInfos["error"] = &clientcmdapi.AuthInfo{
|
||||
Username: "anything",
|
||||
Token: "here",
|
||||
}
|
||||
config.Contexts["dirty"] = &clientcmdapi.Context{
|
||||
Cluster: "missing ca",
|
||||
AuthInfo: "error",
|
||||
}
|
||||
config.Clusters["clean"] = &clientcmdapi.Cluster{
|
||||
Server: "anything",
|
||||
}
|
||||
config.AuthInfos["clean"] = &clientcmdapi.AuthInfo{
|
||||
Token: "here",
|
||||
}
|
||||
config.Contexts["clean"] = &clientcmdapi.Context{
|
||||
Cluster: "clean",
|
||||
AuthInfo: "clean",
|
||||
}
|
||||
|
||||
badValidation := configValidationTest{
|
||||
config: config,
|
||||
expectedErrorSubstring: []string{"unable to read certificate-authority"},
|
||||
}
|
||||
okTest := configValidationTest{
|
||||
config: config,
|
||||
}
|
||||
|
||||
okTest.testConfirmUsable("clean", t)
|
||||
badValidation.testConfig(t)
|
||||
}
|
||||
|
||||
func TestConfirmUsableBadInfoConfig(t *testing.T) {
|
||||
config := clientcmdapi.NewConfig()
|
||||
config.Clusters["missing ca"] = &clientcmdapi.Cluster{
|
||||
Server: "anything",
|
||||
CertificateAuthority: "missing",
|
||||
}
|
||||
config.AuthInfos["error"] = &clientcmdapi.AuthInfo{
|
||||
Username: "anything",
|
||||
Token: "here",
|
||||
}
|
||||
config.Contexts["first"] = &clientcmdapi.Context{
|
||||
Cluster: "missing ca",
|
||||
AuthInfo: "error",
|
||||
}
|
||||
test := configValidationTest{
|
||||
config: config,
|
||||
expectedErrorSubstring: []string{"unable to read certificate-authority"},
|
||||
}
|
||||
|
||||
test.testConfirmUsable("first", t)
|
||||
}
|
||||
|
||||
func TestConfirmUsableEmptyConfig(t *testing.T) {
|
||||
config := clientcmdapi.NewConfig()
|
||||
test := configValidationTest{
|
||||
config: config,
|
||||
expectedErrorSubstring: []string{"invalid configuration: no configuration has been provided"},
|
||||
}
|
||||
|
||||
test.testConfirmUsable("", t)
|
||||
}
|
||||
|
||||
func TestConfirmUsableMissingConfig(t *testing.T) {
|
||||
config := clientcmdapi.NewConfig()
|
||||
test := configValidationTest{
|
||||
config: config,
|
||||
expectedErrorSubstring: []string{"invalid configuration: no configuration has been provided"},
|
||||
}
|
||||
|
||||
test.testConfirmUsable("not-here", t)
|
||||
}
|
||||
|
||||
func TestValidateEmptyConfig(t *testing.T) {
|
||||
config := clientcmdapi.NewConfig()
|
||||
test := configValidationTest{
|
||||
config: config,
|
||||
expectedErrorSubstring: []string{"invalid configuration: no configuration has been provided"},
|
||||
}
|
||||
|
||||
test.testConfig(t)
|
||||
}
|
||||
|
||||
func TestValidateMissingCurrentContextConfig(t *testing.T) {
|
||||
config := clientcmdapi.NewConfig()
|
||||
config.CurrentContext = "anything"
|
||||
test := configValidationTest{
|
||||
config: config,
|
||||
expectedErrorSubstring: []string{"context was not found for specified "},
|
||||
}
|
||||
|
||||
test.testConfig(t)
|
||||
}
|
||||
|
||||
func TestIsContextNotFound(t *testing.T) {
|
||||
config := clientcmdapi.NewConfig()
|
||||
config.CurrentContext = "anything"
|
||||
|
||||
err := Validate(*config)
|
||||
if !IsContextNotFound(err) {
|
||||
t.Errorf("Expected context not found, but got %v", err)
|
||||
}
|
||||
if !IsConfigurationInvalid(err) {
|
||||
t.Errorf("Expected configuration invalid, but got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsEmptyConfig(t *testing.T) {
|
||||
config := clientcmdapi.NewConfig()
|
||||
|
||||
err := Validate(*config)
|
||||
if !IsEmptyConfig(err) {
|
||||
t.Errorf("Expected context not found, but got %v", err)
|
||||
}
|
||||
if !IsConfigurationInvalid(err) {
|
||||
t.Errorf("Expected configuration invalid, but got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsConfigurationInvalid(t *testing.T) {
|
||||
if newErrConfigurationInvalid([]error{}) != nil {
|
||||
t.Errorf("unexpected error")
|
||||
}
|
||||
if newErrConfigurationInvalid([]error{ErrNoContext}) == ErrNoContext {
|
||||
t.Errorf("unexpected error")
|
||||
}
|
||||
if newErrConfigurationInvalid([]error{ErrNoContext, ErrNoContext}) == nil {
|
||||
t.Errorf("unexpected error")
|
||||
}
|
||||
if !IsConfigurationInvalid(newErrConfigurationInvalid([]error{ErrNoContext, ErrNoContext})) {
|
||||
t.Errorf("unexpected error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateMissingReferencesConfig(t *testing.T) {
|
||||
config := clientcmdapi.NewConfig()
|
||||
config.CurrentContext = "anything"
|
||||
config.Contexts["anything"] = &clientcmdapi.Context{Cluster: "missing", AuthInfo: "missing"}
|
||||
test := configValidationTest{
|
||||
config: config,
|
||||
expectedErrorSubstring: []string{"user \"missing\" was not found for context \"anything\"", "cluster \"missing\" was not found for context \"anything\""},
|
||||
}
|
||||
|
||||
test.testContext("anything", t)
|
||||
test.testConfig(t)
|
||||
}
|
||||
|
||||
func TestValidateEmptyContext(t *testing.T) {
|
||||
config := clientcmdapi.NewConfig()
|
||||
config.CurrentContext = "anything"
|
||||
config.Contexts["anything"] = &clientcmdapi.Context{}
|
||||
test := configValidationTest{
|
||||
config: config,
|
||||
expectedErrorSubstring: []string{"user was not specified for context \"anything\"", "cluster was not specified for context \"anything\""},
|
||||
}
|
||||
|
||||
test.testContext("anything", t)
|
||||
test.testConfig(t)
|
||||
}
|
||||
|
||||
func TestValidateEmptyContextName(t *testing.T) {
|
||||
config := clientcmdapi.NewConfig()
|
||||
config.CurrentContext = "anything"
|
||||
config.Contexts[""] = &clientcmdapi.Context{Cluster: "missing", AuthInfo: "missing"}
|
||||
test := configValidationTest{
|
||||
config: config,
|
||||
expectedErrorSubstring: []string{"empty context name", "is not allowed"},
|
||||
}
|
||||
|
||||
test.testContext("", t)
|
||||
test.testConfig(t)
|
||||
}
|
||||
|
||||
func TestValidateEmptyClusterInfo(t *testing.T) {
|
||||
config := clientcmdapi.NewConfig()
|
||||
config.Clusters["empty"] = clientcmdapi.NewCluster()
|
||||
test := configValidationTest{
|
||||
config: config,
|
||||
expectedErrorSubstring: []string{"cluster has no server defined"},
|
||||
}
|
||||
|
||||
test.testCluster("empty", t)
|
||||
test.testConfig(t)
|
||||
}
|
||||
|
||||
func TestValidateClusterInfoErrEmptyCluster(t *testing.T) {
|
||||
cluster := clientcmdapi.NewCluster()
|
||||
errs := validateClusterInfo("", *cluster)
|
||||
|
||||
if len(errs) != 1 {
|
||||
t.Fatalf("unexpected errors: %v", errs)
|
||||
}
|
||||
if errs[0] != ErrEmptyCluster {
|
||||
t.Errorf("unexpected error: %v", errs[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateMissingCAFileClusterInfo(t *testing.T) {
|
||||
config := clientcmdapi.NewConfig()
|
||||
config.Clusters["missing ca"] = &clientcmdapi.Cluster{
|
||||
Server: "anything",
|
||||
CertificateAuthority: "missing",
|
||||
}
|
||||
test := configValidationTest{
|
||||
config: config,
|
||||
expectedErrorSubstring: []string{"unable to read certificate-authority"},
|
||||
}
|
||||
|
||||
test.testCluster("missing ca", t)
|
||||
test.testConfig(t)
|
||||
}
|
||||
|
||||
func TestValidateCleanClusterInfo(t *testing.T) {
|
||||
config := clientcmdapi.NewConfig()
|
||||
config.Clusters["clean"] = &clientcmdapi.Cluster{
|
||||
Server: "anything",
|
||||
}
|
||||
test := configValidationTest{
|
||||
config: config,
|
||||
}
|
||||
|
||||
test.testCluster("clean", t)
|
||||
test.testConfig(t)
|
||||
}
|
||||
|
||||
func TestValidateCleanWithCAClusterInfo(t *testing.T) {
|
||||
tempFile, _ := ioutil.TempFile("", "")
|
||||
defer os.Remove(tempFile.Name())
|
||||
|
||||
config := clientcmdapi.NewConfig()
|
||||
config.Clusters["clean"] = &clientcmdapi.Cluster{
|
||||
Server: "anything",
|
||||
CertificateAuthority: tempFile.Name(),
|
||||
}
|
||||
test := configValidationTest{
|
||||
config: config,
|
||||
}
|
||||
|
||||
test.testCluster("clean", t)
|
||||
test.testConfig(t)
|
||||
}
|
||||
|
||||
func TestValidateEmptyAuthInfo(t *testing.T) {
|
||||
config := clientcmdapi.NewConfig()
|
||||
config.AuthInfos["error"] = &clientcmdapi.AuthInfo{}
|
||||
test := configValidationTest{
|
||||
config: config,
|
||||
}
|
||||
|
||||
test.testAuthInfo("error", t)
|
||||
test.testConfig(t)
|
||||
}
|
||||
|
||||
func TestValidateCertFilesNotFoundAuthInfo(t *testing.T) {
|
||||
config := clientcmdapi.NewConfig()
|
||||
config.AuthInfos["error"] = &clientcmdapi.AuthInfo{
|
||||
ClientCertificate: "missing",
|
||||
ClientKey: "missing",
|
||||
}
|
||||
test := configValidationTest{
|
||||
config: config,
|
||||
expectedErrorSubstring: []string{"unable to read client-cert", "unable to read client-key"},
|
||||
}
|
||||
|
||||
test.testAuthInfo("error", t)
|
||||
test.testConfig(t)
|
||||
}
|
||||
|
||||
func TestValidateCertDataOverridesFiles(t *testing.T) {
|
||||
tempFile, _ := ioutil.TempFile("", "")
|
||||
defer os.Remove(tempFile.Name())
|
||||
|
||||
config := clientcmdapi.NewConfig()
|
||||
config.AuthInfos["clean"] = &clientcmdapi.AuthInfo{
|
||||
ClientCertificate: tempFile.Name(),
|
||||
ClientCertificateData: []byte("certdata"),
|
||||
ClientKey: tempFile.Name(),
|
||||
ClientKeyData: []byte("keydata"),
|
||||
}
|
||||
test := configValidationTest{
|
||||
config: config,
|
||||
expectedErrorSubstring: []string{"client-cert-data and client-cert are both specified", "client-key-data and client-key are both specified"},
|
||||
}
|
||||
|
||||
test.testAuthInfo("clean", t)
|
||||
test.testConfig(t)
|
||||
}
|
||||
|
||||
func TestValidateCleanCertFilesAuthInfo(t *testing.T) {
|
||||
tempFile, _ := ioutil.TempFile("", "")
|
||||
defer os.Remove(tempFile.Name())
|
||||
|
||||
config := clientcmdapi.NewConfig()
|
||||
config.AuthInfos["clean"] = &clientcmdapi.AuthInfo{
|
||||
ClientCertificate: tempFile.Name(),
|
||||
ClientKey: tempFile.Name(),
|
||||
}
|
||||
test := configValidationTest{
|
||||
config: config,
|
||||
}
|
||||
|
||||
test.testAuthInfo("clean", t)
|
||||
test.testConfig(t)
|
||||
}
|
||||
|
||||
func TestValidateCleanTokenAuthInfo(t *testing.T) {
|
||||
config := clientcmdapi.NewConfig()
|
||||
config.AuthInfos["clean"] = &clientcmdapi.AuthInfo{
|
||||
Token: "any-value",
|
||||
}
|
||||
test := configValidationTest{
|
||||
config: config,
|
||||
}
|
||||
|
||||
test.testAuthInfo("clean", t)
|
||||
test.testConfig(t)
|
||||
}
|
||||
|
||||
func TestValidateMultipleMethodsAuthInfo(t *testing.T) {
|
||||
config := clientcmdapi.NewConfig()
|
||||
config.AuthInfos["error"] = &clientcmdapi.AuthInfo{
|
||||
Token: "token",
|
||||
Username: "username",
|
||||
}
|
||||
test := configValidationTest{
|
||||
config: config,
|
||||
expectedErrorSubstring: []string{"more than one authentication method", "token", "basicAuth"},
|
||||
}
|
||||
|
||||
test.testAuthInfo("error", t)
|
||||
test.testConfig(t)
|
||||
}
|
||||
|
||||
func TestValidateAuthInfoExec(t *testing.T) {
|
||||
config := clientcmdapi.NewConfig()
|
||||
config.AuthInfos["user"] = &clientcmdapi.AuthInfo{
|
||||
Exec: &clientcmdapi.ExecConfig{
|
||||
Command: "/bin/example",
|
||||
APIVersion: "clientauthentication.k8s.io/v1alpha1",
|
||||
Args: []string{"hello", "world"},
|
||||
Env: []clientcmdapi.ExecEnvVar{
|
||||
{Name: "foo", Value: "bar"},
|
||||
},
|
||||
},
|
||||
}
|
||||
test := configValidationTest{
|
||||
config: config,
|
||||
}
|
||||
|
||||
test.testAuthInfo("user", t)
|
||||
test.testConfig(t)
|
||||
}
|
||||
|
||||
func TestValidateAuthInfoExecNoVersion(t *testing.T) {
|
||||
config := clientcmdapi.NewConfig()
|
||||
config.AuthInfos["user"] = &clientcmdapi.AuthInfo{
|
||||
Exec: &clientcmdapi.ExecConfig{
|
||||
Command: "/bin/example",
|
||||
},
|
||||
}
|
||||
test := configValidationTest{
|
||||
config: config,
|
||||
expectedErrorSubstring: []string{
|
||||
"apiVersion must be specified for user to use exec authentication plugin",
|
||||
},
|
||||
}
|
||||
|
||||
test.testAuthInfo("user", t)
|
||||
test.testConfig(t)
|
||||
}
|
||||
|
||||
func TestValidateAuthInfoExecNoCommand(t *testing.T) {
|
||||
config := clientcmdapi.NewConfig()
|
||||
config.AuthInfos["user"] = &clientcmdapi.AuthInfo{
|
||||
Exec: &clientcmdapi.ExecConfig{
|
||||
APIVersion: "clientauthentication.k8s.io/v1alpha1",
|
||||
},
|
||||
}
|
||||
test := configValidationTest{
|
||||
config: config,
|
||||
expectedErrorSubstring: []string{
|
||||
"command must be specified for user to use exec authentication plugin",
|
||||
},
|
||||
}
|
||||
|
||||
test.testAuthInfo("user", t)
|
||||
test.testConfig(t)
|
||||
}
|
||||
|
||||
func TestValidateAuthInfoExecWithAuthProvider(t *testing.T) {
|
||||
config := clientcmdapi.NewConfig()
|
||||
config.AuthInfos["user"] = &clientcmdapi.AuthInfo{
|
||||
AuthProvider: &clientcmdapi.AuthProviderConfig{
|
||||
Name: "oidc",
|
||||
},
|
||||
Exec: &clientcmdapi.ExecConfig{
|
||||
Command: "/bin/example",
|
||||
APIVersion: "clientauthentication.k8s.io/v1alpha1",
|
||||
},
|
||||
}
|
||||
test := configValidationTest{
|
||||
config: config,
|
||||
expectedErrorSubstring: []string{
|
||||
"authProvider cannot be provided in combination with an exec plugin for user",
|
||||
},
|
||||
}
|
||||
|
||||
test.testAuthInfo("user", t)
|
||||
test.testConfig(t)
|
||||
}
|
||||
|
||||
func TestValidateAuthInfoExecInvalidEnv(t *testing.T) {
|
||||
config := clientcmdapi.NewConfig()
|
||||
config.AuthInfos["user"] = &clientcmdapi.AuthInfo{
|
||||
Exec: &clientcmdapi.ExecConfig{
|
||||
Command: "/bin/example",
|
||||
APIVersion: "clientauthentication.k8s.io/v1alpha1",
|
||||
Env: []clientcmdapi.ExecEnvVar{
|
||||
{Name: "foo"}, // No value
|
||||
},
|
||||
},
|
||||
}
|
||||
test := configValidationTest{
|
||||
config: config,
|
||||
expectedErrorSubstring: []string{
|
||||
"env variable foo value must be specified for user to use exec authentication plugin",
|
||||
},
|
||||
}
|
||||
|
||||
test.testAuthInfo("user", t)
|
||||
test.testConfig(t)
|
||||
}
|
||||
|
||||
type configValidationTest struct {
|
||||
config *clientcmdapi.Config
|
||||
expectedErrorSubstring []string
|
||||
}
|
||||
|
||||
func (c configValidationTest) testContext(contextName string, t *testing.T) {
|
||||
errs := validateContext(contextName, *c.config.Contexts[contextName], *c.config)
|
||||
|
||||
if len(c.expectedErrorSubstring) != 0 {
|
||||
if len(errs) == 0 {
|
||||
t.Errorf("Expected error containing: %v", c.expectedErrorSubstring)
|
||||
}
|
||||
for _, curr := range c.expectedErrorSubstring {
|
||||
if len(errs) != 0 && !strings.Contains(utilerrors.NewAggregate(errs).Error(), curr) {
|
||||
t.Errorf("Expected error containing: %v, but got %v", c.expectedErrorSubstring, utilerrors.NewAggregate(errs))
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
if len(errs) != 0 {
|
||||
t.Errorf("Unexpected error: %v", utilerrors.NewAggregate(errs))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c configValidationTest) testConfirmUsable(contextName string, t *testing.T) {
|
||||
err := ConfirmUsable(*c.config, contextName)
|
||||
|
||||
if len(c.expectedErrorSubstring) != 0 {
|
||||
if err == nil {
|
||||
t.Errorf("Expected error containing: %v", c.expectedErrorSubstring)
|
||||
} else {
|
||||
for _, curr := range c.expectedErrorSubstring {
|
||||
if err != nil && !strings.Contains(err.Error(), curr) {
|
||||
t.Errorf("Expected error containing: %v, but got %v", c.expectedErrorSubstring, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c configValidationTest) testConfig(t *testing.T) {
|
||||
err := Validate(*c.config)
|
||||
|
||||
if len(c.expectedErrorSubstring) != 0 {
|
||||
if err == nil {
|
||||
t.Errorf("Expected error containing: %v", c.expectedErrorSubstring)
|
||||
} else {
|
||||
for _, curr := range c.expectedErrorSubstring {
|
||||
if err != nil && !strings.Contains(err.Error(), curr) {
|
||||
t.Errorf("Expected error containing: %v, but got %v", c.expectedErrorSubstring, err)
|
||||
}
|
||||
}
|
||||
if !IsConfigurationInvalid(err) {
|
||||
t.Errorf("all errors should be configuration invalid: %v", err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c configValidationTest) testCluster(clusterName string, t *testing.T) {
|
||||
errs := validateClusterInfo(clusterName, *c.config.Clusters[clusterName])
|
||||
|
||||
if len(c.expectedErrorSubstring) != 0 {
|
||||
if len(errs) == 0 {
|
||||
t.Errorf("Expected error containing: %v", c.expectedErrorSubstring)
|
||||
}
|
||||
for _, curr := range c.expectedErrorSubstring {
|
||||
if len(errs) != 0 && !strings.Contains(utilerrors.NewAggregate(errs).Error(), curr) {
|
||||
t.Errorf("Expected error containing: %v, but got %v", c.expectedErrorSubstring, utilerrors.NewAggregate(errs))
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
if len(errs) != 0 {
|
||||
t.Errorf("Unexpected error: %v", utilerrors.NewAggregate(errs))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c configValidationTest) testAuthInfo(authInfoName string, t *testing.T) {
|
||||
errs := validateAuthInfo(authInfoName, *c.config.AuthInfos[authInfoName])
|
||||
|
||||
if len(c.expectedErrorSubstring) != 0 {
|
||||
if len(errs) == 0 {
|
||||
t.Errorf("Expected error containing: %v", c.expectedErrorSubstring)
|
||||
}
|
||||
for _, curr := range c.expectedErrorSubstring {
|
||||
if len(errs) != 0 && !strings.Contains(utilerrors.NewAggregate(errs).Error(), curr) {
|
||||
t.Errorf("Expected error containing: %v, but got %v", c.expectedErrorSubstring, utilerrors.NewAggregate(errs))
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
if len(errs) != 0 {
|
||||
t.Errorf("Unexpected error: %v", utilerrors.NewAggregate(errs))
|
||||
}
|
||||
}
|
||||
}
|
||||
292
vendor/k8s.io/client-go/tools/leaderelection/leaderelection_test.go
generated
vendored
292
vendor/k8s.io/client-go/tools/leaderelection/leaderelection_test.go
generated
vendored
|
|
@ -1,292 +0,0 @@
|
|||
/*
|
||||
Copyright 2015 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package leaderelection
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
fakecorev1 "k8s.io/client-go/kubernetes/typed/core/v1/fake"
|
||||
core "k8s.io/client-go/testing"
|
||||
rl "k8s.io/client-go/tools/leaderelection/resourcelock"
|
||||
"k8s.io/client-go/tools/record"
|
||||
)
|
||||
|
||||
func createLockObject(objectType string, objectMeta metav1.ObjectMeta) (obj runtime.Object) {
|
||||
switch objectType {
|
||||
case "endpoints":
|
||||
obj = &v1.Endpoints{ObjectMeta: objectMeta}
|
||||
case "configmaps":
|
||||
obj = &v1.ConfigMap{ObjectMeta: objectMeta}
|
||||
default:
|
||||
panic("unexpected objType:" + objectType)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Will test leader election using endpoints as the resource
|
||||
func TestTryAcquireOrRenewEndpoints(t *testing.T) {
|
||||
testTryAcquireOrRenew(t, "endpoints")
|
||||
}
|
||||
|
||||
func testTryAcquireOrRenew(t *testing.T, objectType string) {
|
||||
future := time.Now().Add(1000 * time.Hour)
|
||||
past := time.Now().Add(-1000 * time.Hour)
|
||||
|
||||
tests := []struct {
|
||||
observedRecord rl.LeaderElectionRecord
|
||||
observedTime time.Time
|
||||
reactors []struct {
|
||||
verb string
|
||||
reaction core.ReactionFunc
|
||||
}
|
||||
|
||||
expectSuccess bool
|
||||
transitionLeader bool
|
||||
outHolder string
|
||||
}{
|
||||
// acquire from no object
|
||||
{
|
||||
reactors: []struct {
|
||||
verb string
|
||||
reaction core.ReactionFunc
|
||||
}{
|
||||
{
|
||||
verb: "get",
|
||||
reaction: func(action core.Action) (handled bool, ret runtime.Object, err error) {
|
||||
return true, nil, errors.NewNotFound(action.(core.GetAction).GetResource().GroupResource(), action.(core.GetAction).GetName())
|
||||
},
|
||||
},
|
||||
{
|
||||
verb: "create",
|
||||
reaction: func(action core.Action) (handled bool, ret runtime.Object, err error) {
|
||||
return true, action.(core.CreateAction).GetObject(), nil
|
||||
},
|
||||
},
|
||||
},
|
||||
expectSuccess: true,
|
||||
outHolder: "baz",
|
||||
},
|
||||
// acquire from unled object
|
||||
{
|
||||
reactors: []struct {
|
||||
verb string
|
||||
reaction core.ReactionFunc
|
||||
}{
|
||||
{
|
||||
verb: "get",
|
||||
reaction: func(action core.Action) (handled bool, ret runtime.Object, err error) {
|
||||
objectMeta := metav1.ObjectMeta{
|
||||
Namespace: action.GetNamespace(),
|
||||
Name: action.(core.GetAction).GetName(),
|
||||
}
|
||||
return true, createLockObject(objectType, objectMeta), nil
|
||||
},
|
||||
},
|
||||
{
|
||||
verb: "update",
|
||||
reaction: func(action core.Action) (handled bool, ret runtime.Object, err error) {
|
||||
return true, action.(core.CreateAction).GetObject(), nil
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
expectSuccess: true,
|
||||
transitionLeader: true,
|
||||
outHolder: "baz",
|
||||
},
|
||||
// acquire from led, unacked object
|
||||
{
|
||||
reactors: []struct {
|
||||
verb string
|
||||
reaction core.ReactionFunc
|
||||
}{
|
||||
{
|
||||
verb: "get",
|
||||
reaction: func(action core.Action) (handled bool, ret runtime.Object, err error) {
|
||||
objectMeta := metav1.ObjectMeta{
|
||||
Namespace: action.GetNamespace(),
|
||||
Name: action.(core.GetAction).GetName(),
|
||||
Annotations: map[string]string{
|
||||
rl.LeaderElectionRecordAnnotationKey: `{"holderIdentity":"bing"}`,
|
||||
},
|
||||
}
|
||||
return true, createLockObject(objectType, objectMeta), nil
|
||||
},
|
||||
},
|
||||
{
|
||||
verb: "update",
|
||||
reaction: func(action core.Action) (handled bool, ret runtime.Object, err error) {
|
||||
return true, action.(core.CreateAction).GetObject(), nil
|
||||
},
|
||||
},
|
||||
},
|
||||
observedRecord: rl.LeaderElectionRecord{HolderIdentity: "bing"},
|
||||
observedTime: past,
|
||||
|
||||
expectSuccess: true,
|
||||
transitionLeader: true,
|
||||
outHolder: "baz",
|
||||
},
|
||||
// don't acquire from led, acked object
|
||||
{
|
||||
reactors: []struct {
|
||||
verb string
|
||||
reaction core.ReactionFunc
|
||||
}{
|
||||
{
|
||||
verb: "get",
|
||||
reaction: func(action core.Action) (handled bool, ret runtime.Object, err error) {
|
||||
objectMeta := metav1.ObjectMeta{
|
||||
Namespace: action.GetNamespace(),
|
||||
Name: action.(core.GetAction).GetName(),
|
||||
Annotations: map[string]string{
|
||||
rl.LeaderElectionRecordAnnotationKey: `{"holderIdentity":"bing"}`,
|
||||
},
|
||||
}
|
||||
return true, createLockObject(objectType, objectMeta), nil
|
||||
},
|
||||
},
|
||||
},
|
||||
observedTime: future,
|
||||
|
||||
expectSuccess: false,
|
||||
outHolder: "bing",
|
||||
},
|
||||
// renew already acquired object
|
||||
{
|
||||
reactors: []struct {
|
||||
verb string
|
||||
reaction core.ReactionFunc
|
||||
}{
|
||||
{
|
||||
verb: "get",
|
||||
reaction: func(action core.Action) (handled bool, ret runtime.Object, err error) {
|
||||
objectMeta := metav1.ObjectMeta{
|
||||
Namespace: action.GetNamespace(),
|
||||
Name: action.(core.GetAction).GetName(),
|
||||
Annotations: map[string]string{
|
||||
rl.LeaderElectionRecordAnnotationKey: `{"holderIdentity":"baz"}`,
|
||||
},
|
||||
}
|
||||
return true, createLockObject(objectType, objectMeta), nil
|
||||
},
|
||||
},
|
||||
{
|
||||
verb: "update",
|
||||
reaction: func(action core.Action) (handled bool, ret runtime.Object, err error) {
|
||||
return true, action.(core.CreateAction).GetObject(), nil
|
||||
},
|
||||
},
|
||||
},
|
||||
observedTime: future,
|
||||
observedRecord: rl.LeaderElectionRecord{HolderIdentity: "baz"},
|
||||
|
||||
expectSuccess: true,
|
||||
outHolder: "baz",
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
// OnNewLeader is called async so we have to wait for it.
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
var reportedLeader string
|
||||
var lock rl.Interface
|
||||
|
||||
objectMeta := metav1.ObjectMeta{Namespace: "foo", Name: "bar"}
|
||||
resourceLockConfig := rl.ResourceLockConfig{
|
||||
Identity: "baz",
|
||||
EventRecorder: &record.FakeRecorder{},
|
||||
}
|
||||
c := &fakecorev1.FakeCoreV1{Fake: &core.Fake{}}
|
||||
for _, reactor := range test.reactors {
|
||||
c.AddReactor(reactor.verb, objectType, reactor.reaction)
|
||||
}
|
||||
c.AddReactor("*", "*", func(action core.Action) (bool, runtime.Object, error) {
|
||||
t.Errorf("[%v] unreachable action. testclient called too many times: %+v", i, action)
|
||||
return true, nil, fmt.Errorf("unreachable action")
|
||||
})
|
||||
|
||||
switch objectType {
|
||||
case "endpoints":
|
||||
lock = &rl.EndpointsLock{
|
||||
EndpointsMeta: objectMeta,
|
||||
LockConfig: resourceLockConfig,
|
||||
Client: c,
|
||||
}
|
||||
case "configmaps":
|
||||
lock = &rl.ConfigMapLock{
|
||||
ConfigMapMeta: objectMeta,
|
||||
LockConfig: resourceLockConfig,
|
||||
Client: c,
|
||||
}
|
||||
}
|
||||
|
||||
lec := LeaderElectionConfig{
|
||||
Lock: lock,
|
||||
LeaseDuration: 10 * time.Second,
|
||||
Callbacks: LeaderCallbacks{
|
||||
OnNewLeader: func(l string) {
|
||||
defer wg.Done()
|
||||
reportedLeader = l
|
||||
},
|
||||
},
|
||||
}
|
||||
le := &LeaderElector{
|
||||
config: lec,
|
||||
observedRecord: test.observedRecord,
|
||||
observedTime: test.observedTime,
|
||||
}
|
||||
|
||||
if test.expectSuccess != le.tryAcquireOrRenew() {
|
||||
t.Errorf("[%v]unexpected result of tryAcquireOrRenew: [succeeded=%v]", i, !test.expectSuccess)
|
||||
}
|
||||
|
||||
le.observedRecord.AcquireTime = metav1.Time{}
|
||||
le.observedRecord.RenewTime = metav1.Time{}
|
||||
if le.observedRecord.HolderIdentity != test.outHolder {
|
||||
t.Errorf("[%v]expected holder:\n\t%+v\ngot:\n\t%+v", i, test.outHolder, le.observedRecord.HolderIdentity)
|
||||
}
|
||||
if len(test.reactors) != len(c.Actions()) {
|
||||
t.Errorf("[%v]wrong number of api interactions", i)
|
||||
}
|
||||
if test.transitionLeader && le.observedRecord.LeaderTransitions != 1 {
|
||||
t.Errorf("[%v]leader should have transitioned but did not", i)
|
||||
}
|
||||
if !test.transitionLeader && le.observedRecord.LeaderTransitions != 0 {
|
||||
t.Errorf("[%v]leader should not have transitioned but did", i)
|
||||
}
|
||||
|
||||
le.maybeReportTransition()
|
||||
wg.Wait()
|
||||
if reportedLeader != test.outHolder {
|
||||
t.Errorf("[%v]reported leader was not the new leader. expected %q, got %q", i, test.outHolder, reportedLeader)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Will test leader election using configmap as the resource
|
||||
func TestTryAcquireOrRenewConfigMaps(t *testing.T) {
|
||||
testTryAcquireOrRenew(t, "configmaps")
|
||||
}
|
||||
206
vendor/k8s.io/client-go/tools/pager/pager_test.go
generated
vendored
206
vendor/k8s.io/client-go/tools/pager/pager_test.go
generated
vendored
|
|
@ -1,206 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 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 pager
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
func list(count int, rv string) *metainternalversion.List {
|
||||
var list metainternalversion.List
|
||||
for i := 0; i < count; i++ {
|
||||
list.Items = append(list.Items, &metav1beta1.PartialObjectMetadata{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: fmt.Sprintf("%d", i),
|
||||
},
|
||||
})
|
||||
}
|
||||
list.ResourceVersion = rv
|
||||
return &list
|
||||
}
|
||||
|
||||
type testPager struct {
|
||||
t *testing.T
|
||||
rv string
|
||||
index int
|
||||
remaining int
|
||||
last int
|
||||
continuing bool
|
||||
done bool
|
||||
expectPage int64
|
||||
}
|
||||
|
||||
func (p *testPager) reset() {
|
||||
p.continuing = false
|
||||
p.remaining += p.index
|
||||
p.index = 0
|
||||
p.last = 0
|
||||
p.done = false
|
||||
}
|
||||
|
||||
func (p *testPager) PagedList(ctx context.Context, options metav1.ListOptions) (runtime.Object, error) {
|
||||
if p.done {
|
||||
p.t.Errorf("did not expect additional call to paged list")
|
||||
return nil, fmt.Errorf("unexpected list call")
|
||||
}
|
||||
expectedContinue := fmt.Sprintf("%s:%d", p.rv, p.last)
|
||||
if options.Limit != p.expectPage || (p.continuing && options.Continue != expectedContinue) {
|
||||
p.t.Errorf("invariant violated, expected limit %d and continue %s, got %#v", p.expectPage, expectedContinue, options)
|
||||
return nil, fmt.Errorf("invariant violated")
|
||||
}
|
||||
var list metainternalversion.List
|
||||
total := options.Limit
|
||||
if total == 0 {
|
||||
total = int64(p.remaining)
|
||||
}
|
||||
for i := int64(0); i < total; i++ {
|
||||
if p.remaining <= 0 {
|
||||
break
|
||||
}
|
||||
list.Items = append(list.Items, &metav1beta1.PartialObjectMetadata{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: fmt.Sprintf("%d", p.index),
|
||||
},
|
||||
})
|
||||
p.remaining--
|
||||
p.index++
|
||||
}
|
||||
p.last = p.index
|
||||
if p.remaining > 0 {
|
||||
list.Continue = fmt.Sprintf("%s:%d", p.rv, p.last)
|
||||
p.continuing = true
|
||||
} else {
|
||||
p.done = true
|
||||
}
|
||||
list.ResourceVersion = p.rv
|
||||
return &list, nil
|
||||
}
|
||||
|
||||
func (p *testPager) ExpiresOnSecondPage(ctx context.Context, options metav1.ListOptions) (runtime.Object, error) {
|
||||
if p.continuing {
|
||||
p.done = true
|
||||
return nil, errors.NewResourceExpired("this list has expired")
|
||||
}
|
||||
return p.PagedList(ctx, options)
|
||||
}
|
||||
|
||||
func (p *testPager) ExpiresOnSecondPageThenFullList(ctx context.Context, options metav1.ListOptions) (runtime.Object, error) {
|
||||
if p.continuing {
|
||||
p.reset()
|
||||
p.expectPage = 0
|
||||
return nil, errors.NewResourceExpired("this list has expired")
|
||||
}
|
||||
return p.PagedList(ctx, options)
|
||||
}
|
||||
|
||||
func TestListPager_List(t *testing.T) {
|
||||
type fields struct {
|
||||
PageSize int64
|
||||
PageFn ListPageFunc
|
||||
FullListIfExpired bool
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
options metav1.ListOptions
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
want runtime.Object
|
||||
wantErr bool
|
||||
isExpired bool
|
||||
}{
|
||||
{
|
||||
name: "empty page",
|
||||
fields: fields{PageSize: 10, PageFn: (&testPager{t: t, expectPage: 10, remaining: 0, rv: "rv:20"}).PagedList},
|
||||
args: args{},
|
||||
want: list(0, "rv:20"),
|
||||
},
|
||||
{
|
||||
name: "one page",
|
||||
fields: fields{PageSize: 10, PageFn: (&testPager{t: t, expectPage: 10, remaining: 9, rv: "rv:20"}).PagedList},
|
||||
args: args{},
|
||||
want: list(9, "rv:20"),
|
||||
},
|
||||
{
|
||||
name: "one full page",
|
||||
fields: fields{PageSize: 10, PageFn: (&testPager{t: t, expectPage: 10, remaining: 10, rv: "rv:20"}).PagedList},
|
||||
args: args{},
|
||||
want: list(10, "rv:20"),
|
||||
},
|
||||
{
|
||||
name: "two pages",
|
||||
fields: fields{PageSize: 10, PageFn: (&testPager{t: t, expectPage: 10, remaining: 11, rv: "rv:20"}).PagedList},
|
||||
args: args{},
|
||||
want: list(11, "rv:20"),
|
||||
},
|
||||
{
|
||||
name: "three pages",
|
||||
fields: fields{PageSize: 10, PageFn: (&testPager{t: t, expectPage: 10, remaining: 21, rv: "rv:20"}).PagedList},
|
||||
args: args{},
|
||||
want: list(21, "rv:20"),
|
||||
},
|
||||
{
|
||||
name: "expires on second page",
|
||||
fields: fields{PageSize: 10, PageFn: (&testPager{t: t, expectPage: 10, remaining: 21, rv: "rv:20"}).ExpiresOnSecondPage},
|
||||
args: args{},
|
||||
wantErr: true,
|
||||
isExpired: true,
|
||||
},
|
||||
{
|
||||
name: "expires on second page and then lists",
|
||||
fields: fields{
|
||||
FullListIfExpired: true,
|
||||
PageSize: 10,
|
||||
PageFn: (&testPager{t: t, expectPage: 10, remaining: 21, rv: "rv:20"}).ExpiresOnSecondPageThenFullList,
|
||||
},
|
||||
args: args{},
|
||||
want: list(21, "rv:20"),
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
p := &ListPager{
|
||||
PageSize: tt.fields.PageSize,
|
||||
PageFn: tt.fields.PageFn,
|
||||
FullListIfExpired: tt.fields.FullListIfExpired,
|
||||
}
|
||||
got, err := p.List(tt.args.ctx, tt.args.options)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("ListPager.List() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if tt.isExpired != errors.IsResourceExpired(err) {
|
||||
t.Errorf("ListPager.List() error = %v, isExpired %v", err, tt.isExpired)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("ListPager.List() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
924
vendor/k8s.io/client-go/tools/record/event_test.go
generated
vendored
924
vendor/k8s.io/client-go/tools/record/event_test.go
generated
vendored
|
|
@ -1,924 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 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 record
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
k8sruntime "k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/clock"
|
||||
"k8s.io/apimachinery/pkg/util/strategicpatch"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
ref "k8s.io/client-go/tools/reference"
|
||||
)
|
||||
|
||||
type testEventSink struct {
|
||||
OnCreate func(e *v1.Event) (*v1.Event, error)
|
||||
OnUpdate func(e *v1.Event) (*v1.Event, error)
|
||||
OnPatch func(e *v1.Event, p []byte) (*v1.Event, error)
|
||||
}
|
||||
|
||||
// CreateEvent records the event for testing.
|
||||
func (t *testEventSink) Create(e *v1.Event) (*v1.Event, error) {
|
||||
if t.OnCreate != nil {
|
||||
return t.OnCreate(e)
|
||||
}
|
||||
return e, nil
|
||||
}
|
||||
|
||||
// UpdateEvent records the event for testing.
|
||||
func (t *testEventSink) Update(e *v1.Event) (*v1.Event, error) {
|
||||
if t.OnUpdate != nil {
|
||||
return t.OnUpdate(e)
|
||||
}
|
||||
return e, nil
|
||||
}
|
||||
|
||||
// PatchEvent records the event for testing.
|
||||
func (t *testEventSink) Patch(e *v1.Event, p []byte) (*v1.Event, error) {
|
||||
if t.OnPatch != nil {
|
||||
return t.OnPatch(e, p)
|
||||
}
|
||||
return e, nil
|
||||
}
|
||||
|
||||
type OnCreateFunc func(*v1.Event) (*v1.Event, error)
|
||||
|
||||
func OnCreateFactory(testCache map[string]*v1.Event, createEvent chan<- *v1.Event) OnCreateFunc {
|
||||
return func(event *v1.Event) (*v1.Event, error) {
|
||||
testCache[getEventKey(event)] = event
|
||||
createEvent <- event
|
||||
return event, nil
|
||||
}
|
||||
}
|
||||
|
||||
type OnPatchFunc func(*v1.Event, []byte) (*v1.Event, error)
|
||||
|
||||
func OnPatchFactory(testCache map[string]*v1.Event, patchEvent chan<- *v1.Event) OnPatchFunc {
|
||||
return func(event *v1.Event, patch []byte) (*v1.Event, error) {
|
||||
cachedEvent, found := testCache[getEventKey(event)]
|
||||
if !found {
|
||||
return nil, fmt.Errorf("unexpected error: couldn't find Event in testCache.")
|
||||
}
|
||||
originalData, err := json.Marshal(cachedEvent)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
patched, err := strategicpatch.StrategicMergePatch(originalData, patch, event)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
patchedObj := &v1.Event{}
|
||||
err = json.Unmarshal(patched, patchedObj)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
patchEvent <- patchedObj
|
||||
return patchedObj, nil
|
||||
}
|
||||
}
|
||||
|
||||
func TestEventf(t *testing.T) {
|
||||
testPod := &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
SelfLink: "/api/version/pods/foo",
|
||||
Name: "foo",
|
||||
Namespace: "baz",
|
||||
UID: "bar",
|
||||
},
|
||||
}
|
||||
testPod2 := &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
SelfLink: "/api/version/pods/foo",
|
||||
Name: "foo",
|
||||
Namespace: "baz",
|
||||
UID: "differentUid",
|
||||
},
|
||||
}
|
||||
testRef, err := ref.GetPartialReference(scheme.Scheme, testPod, "spec.containers[2]")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
testRef2, err := ref.GetPartialReference(scheme.Scheme, testPod2, "spec.containers[3]")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
table := []struct {
|
||||
obj k8sruntime.Object
|
||||
eventtype string
|
||||
reason string
|
||||
messageFmt string
|
||||
elements []interface{}
|
||||
expect *v1.Event
|
||||
expectLog string
|
||||
expectUpdate bool
|
||||
}{
|
||||
{
|
||||
obj: testRef,
|
||||
eventtype: v1.EventTypeNormal,
|
||||
reason: "Started",
|
||||
messageFmt: "some verbose message: %v",
|
||||
elements: []interface{}{1},
|
||||
expect: &v1.Event{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: "baz",
|
||||
},
|
||||
InvolvedObject: v1.ObjectReference{
|
||||
Kind: "Pod",
|
||||
Name: "foo",
|
||||
Namespace: "baz",
|
||||
UID: "bar",
|
||||
APIVersion: "version",
|
||||
FieldPath: "spec.containers[2]",
|
||||
},
|
||||
Reason: "Started",
|
||||
Message: "some verbose message: 1",
|
||||
Source: v1.EventSource{Component: "eventTest"},
|
||||
Count: 1,
|
||||
Type: v1.EventTypeNormal,
|
||||
},
|
||||
expectLog: `Event(v1.ObjectReference{Kind:"Pod", Namespace:"baz", Name:"foo", UID:"bar", APIVersion:"version", ResourceVersion:"", FieldPath:"spec.containers[2]"}): type: 'Normal' reason: 'Started' some verbose message: 1`,
|
||||
expectUpdate: false,
|
||||
},
|
||||
{
|
||||
obj: testPod,
|
||||
eventtype: v1.EventTypeNormal,
|
||||
reason: "Killed",
|
||||
messageFmt: "some other verbose message: %v",
|
||||
elements: []interface{}{1},
|
||||
expect: &v1.Event{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: "baz",
|
||||
},
|
||||
InvolvedObject: v1.ObjectReference{
|
||||
Kind: "Pod",
|
||||
Name: "foo",
|
||||
Namespace: "baz",
|
||||
UID: "bar",
|
||||
APIVersion: "version",
|
||||
},
|
||||
Reason: "Killed",
|
||||
Message: "some other verbose message: 1",
|
||||
Source: v1.EventSource{Component: "eventTest"},
|
||||
Count: 1,
|
||||
Type: v1.EventTypeNormal,
|
||||
},
|
||||
expectLog: `Event(v1.ObjectReference{Kind:"Pod", Namespace:"baz", Name:"foo", UID:"bar", APIVersion:"version", ResourceVersion:"", FieldPath:""}): type: 'Normal' reason: 'Killed' some other verbose message: 1`,
|
||||
expectUpdate: false,
|
||||
},
|
||||
{
|
||||
obj: testRef,
|
||||
eventtype: v1.EventTypeNormal,
|
||||
reason: "Started",
|
||||
messageFmt: "some verbose message: %v",
|
||||
elements: []interface{}{1},
|
||||
expect: &v1.Event{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: "baz",
|
||||
},
|
||||
InvolvedObject: v1.ObjectReference{
|
||||
Kind: "Pod",
|
||||
Name: "foo",
|
||||
Namespace: "baz",
|
||||
UID: "bar",
|
||||
APIVersion: "version",
|
||||
FieldPath: "spec.containers[2]",
|
||||
},
|
||||
Reason: "Started",
|
||||
Message: "some verbose message: 1",
|
||||
Source: v1.EventSource{Component: "eventTest"},
|
||||
Count: 2,
|
||||
Type: v1.EventTypeNormal,
|
||||
},
|
||||
expectLog: `Event(v1.ObjectReference{Kind:"Pod", Namespace:"baz", Name:"foo", UID:"bar", APIVersion:"version", ResourceVersion:"", FieldPath:"spec.containers[2]"}): type: 'Normal' reason: 'Started' some verbose message: 1`,
|
||||
expectUpdate: true,
|
||||
},
|
||||
{
|
||||
obj: testRef2,
|
||||
eventtype: v1.EventTypeNormal,
|
||||
reason: "Started",
|
||||
messageFmt: "some verbose message: %v",
|
||||
elements: []interface{}{1},
|
||||
expect: &v1.Event{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: "baz",
|
||||
},
|
||||
InvolvedObject: v1.ObjectReference{
|
||||
Kind: "Pod",
|
||||
Name: "foo",
|
||||
Namespace: "baz",
|
||||
UID: "differentUid",
|
||||
APIVersion: "version",
|
||||
FieldPath: "spec.containers[3]",
|
||||
},
|
||||
Reason: "Started",
|
||||
Message: "some verbose message: 1",
|
||||
Source: v1.EventSource{Component: "eventTest"},
|
||||
Count: 1,
|
||||
Type: v1.EventTypeNormal,
|
||||
},
|
||||
expectLog: `Event(v1.ObjectReference{Kind:"Pod", Namespace:"baz", Name:"foo", UID:"differentUid", APIVersion:"version", ResourceVersion:"", FieldPath:"spec.containers[3]"}): type: 'Normal' reason: 'Started' some verbose message: 1`,
|
||||
expectUpdate: false,
|
||||
},
|
||||
{
|
||||
obj: testRef,
|
||||
eventtype: v1.EventTypeNormal,
|
||||
reason: "Started",
|
||||
messageFmt: "some verbose message: %v",
|
||||
elements: []interface{}{1},
|
||||
expect: &v1.Event{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: "baz",
|
||||
},
|
||||
InvolvedObject: v1.ObjectReference{
|
||||
Kind: "Pod",
|
||||
Name: "foo",
|
||||
Namespace: "baz",
|
||||
UID: "bar",
|
||||
APIVersion: "version",
|
||||
FieldPath: "spec.containers[2]",
|
||||
},
|
||||
Reason: "Started",
|
||||
Message: "some verbose message: 1",
|
||||
Source: v1.EventSource{Component: "eventTest"},
|
||||
Count: 3,
|
||||
Type: v1.EventTypeNormal,
|
||||
},
|
||||
expectLog: `Event(v1.ObjectReference{Kind:"Pod", Namespace:"baz", Name:"foo", UID:"bar", APIVersion:"version", ResourceVersion:"", FieldPath:"spec.containers[2]"}): type: 'Normal' reason: 'Started' some verbose message: 1`,
|
||||
expectUpdate: true,
|
||||
},
|
||||
{
|
||||
obj: testRef2,
|
||||
eventtype: v1.EventTypeNormal,
|
||||
reason: "Stopped",
|
||||
messageFmt: "some verbose message: %v",
|
||||
elements: []interface{}{1},
|
||||
expect: &v1.Event{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: "baz",
|
||||
},
|
||||
InvolvedObject: v1.ObjectReference{
|
||||
Kind: "Pod",
|
||||
Name: "foo",
|
||||
Namespace: "baz",
|
||||
UID: "differentUid",
|
||||
APIVersion: "version",
|
||||
FieldPath: "spec.containers[3]",
|
||||
},
|
||||
Reason: "Stopped",
|
||||
Message: "some verbose message: 1",
|
||||
Source: v1.EventSource{Component: "eventTest"},
|
||||
Count: 1,
|
||||
Type: v1.EventTypeNormal,
|
||||
},
|
||||
expectLog: `Event(v1.ObjectReference{Kind:"Pod", Namespace:"baz", Name:"foo", UID:"differentUid", APIVersion:"version", ResourceVersion:"", FieldPath:"spec.containers[3]"}): type: 'Normal' reason: 'Stopped' some verbose message: 1`,
|
||||
expectUpdate: false,
|
||||
},
|
||||
{
|
||||
obj: testRef2,
|
||||
eventtype: v1.EventTypeNormal,
|
||||
reason: "Stopped",
|
||||
messageFmt: "some verbose message: %v",
|
||||
elements: []interface{}{1},
|
||||
expect: &v1.Event{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: "baz",
|
||||
},
|
||||
InvolvedObject: v1.ObjectReference{
|
||||
Kind: "Pod",
|
||||
Name: "foo",
|
||||
Namespace: "baz",
|
||||
UID: "differentUid",
|
||||
APIVersion: "version",
|
||||
FieldPath: "spec.containers[3]",
|
||||
},
|
||||
Reason: "Stopped",
|
||||
Message: "some verbose message: 1",
|
||||
Source: v1.EventSource{Component: "eventTest"},
|
||||
Count: 2,
|
||||
Type: v1.EventTypeNormal,
|
||||
},
|
||||
expectLog: `Event(v1.ObjectReference{Kind:"Pod", Namespace:"baz", Name:"foo", UID:"differentUid", APIVersion:"version", ResourceVersion:"", FieldPath:"spec.containers[3]"}): type: 'Normal' reason: 'Stopped' some verbose message: 1`,
|
||||
expectUpdate: true,
|
||||
},
|
||||
}
|
||||
|
||||
testCache := map[string]*v1.Event{}
|
||||
logCalled := make(chan struct{})
|
||||
createEvent := make(chan *v1.Event)
|
||||
updateEvent := make(chan *v1.Event)
|
||||
patchEvent := make(chan *v1.Event)
|
||||
testEvents := testEventSink{
|
||||
OnCreate: OnCreateFactory(testCache, createEvent),
|
||||
OnUpdate: func(event *v1.Event) (*v1.Event, error) {
|
||||
updateEvent <- event
|
||||
return event, nil
|
||||
},
|
||||
OnPatch: OnPatchFactory(testCache, patchEvent),
|
||||
}
|
||||
eventBroadcaster := NewBroadcasterForTests(0)
|
||||
sinkWatcher := eventBroadcaster.StartRecordingToSink(&testEvents)
|
||||
|
||||
clock := clock.NewFakeClock(time.Now())
|
||||
recorder := recorderWithFakeClock(v1.EventSource{Component: "eventTest"}, eventBroadcaster, clock)
|
||||
for index, item := range table {
|
||||
clock.Step(1 * time.Second)
|
||||
logWatcher := eventBroadcaster.StartLogging(func(formatter string, args ...interface{}) {
|
||||
if e, a := item.expectLog, fmt.Sprintf(formatter, args...); e != a {
|
||||
t.Errorf("Expected '%v', got '%v'", e, a)
|
||||
}
|
||||
logCalled <- struct{}{}
|
||||
})
|
||||
recorder.Eventf(item.obj, item.eventtype, item.reason, item.messageFmt, item.elements...)
|
||||
|
||||
<-logCalled
|
||||
|
||||
// validate event
|
||||
if item.expectUpdate {
|
||||
actualEvent := <-patchEvent
|
||||
validateEvent(strconv.Itoa(index), actualEvent, item.expect, t)
|
||||
} else {
|
||||
actualEvent := <-createEvent
|
||||
validateEvent(strconv.Itoa(index), actualEvent, item.expect, t)
|
||||
}
|
||||
logWatcher.Stop()
|
||||
}
|
||||
sinkWatcher.Stop()
|
||||
}
|
||||
|
||||
func recorderWithFakeClock(eventSource v1.EventSource, eventBroadcaster EventBroadcaster, clock clock.Clock) EventRecorder {
|
||||
return &recorderImpl{scheme.Scheme, eventSource, eventBroadcaster.(*eventBroadcasterImpl).Broadcaster, clock}
|
||||
}
|
||||
|
||||
func TestWriteEventError(t *testing.T) {
|
||||
type entry struct {
|
||||
timesToSendError int
|
||||
attemptsWanted int
|
||||
err error
|
||||
}
|
||||
table := map[string]*entry{
|
||||
"giveUp1": {
|
||||
timesToSendError: 1000,
|
||||
attemptsWanted: 1,
|
||||
err: &restclient.RequestConstructionError{},
|
||||
},
|
||||
"giveUp2": {
|
||||
timesToSendError: 1000,
|
||||
attemptsWanted: 1,
|
||||
err: &errors.StatusError{},
|
||||
},
|
||||
"retry1": {
|
||||
timesToSendError: 1000,
|
||||
attemptsWanted: 12,
|
||||
err: &errors.UnexpectedObjectError{},
|
||||
},
|
||||
"retry2": {
|
||||
timesToSendError: 1000,
|
||||
attemptsWanted: 12,
|
||||
err: fmt.Errorf("A weird error"),
|
||||
},
|
||||
"succeedEventually": {
|
||||
timesToSendError: 2,
|
||||
attemptsWanted: 2,
|
||||
err: fmt.Errorf("A weird error"),
|
||||
},
|
||||
}
|
||||
|
||||
clock := clock.IntervalClock{Time: time.Now(), Duration: time.Second}
|
||||
eventCorrelator := NewEventCorrelator(&clock)
|
||||
randGen := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
|
||||
for caseName, ent := range table {
|
||||
attempts := 0
|
||||
sink := &testEventSink{
|
||||
OnCreate: func(event *v1.Event) (*v1.Event, error) {
|
||||
attempts++
|
||||
if attempts < ent.timesToSendError {
|
||||
return nil, ent.err
|
||||
}
|
||||
return event, nil
|
||||
},
|
||||
}
|
||||
ev := &v1.Event{}
|
||||
recordToSink(sink, ev, eventCorrelator, randGen, 0)
|
||||
if attempts != ent.attemptsWanted {
|
||||
t.Errorf("case %v: wanted %d, got %d attempts", caseName, ent.attemptsWanted, attempts)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateExpiredEvent(t *testing.T) {
|
||||
clock := clock.IntervalClock{Time: time.Now(), Duration: time.Second}
|
||||
eventCorrelator := NewEventCorrelator(&clock)
|
||||
randGen := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
|
||||
var createdEvent *v1.Event
|
||||
|
||||
sink := &testEventSink{
|
||||
OnPatch: func(*v1.Event, []byte) (*v1.Event, error) {
|
||||
return nil, &errors.StatusError{
|
||||
ErrStatus: metav1.Status{
|
||||
Code: http.StatusNotFound,
|
||||
Reason: metav1.StatusReasonNotFound,
|
||||
}}
|
||||
},
|
||||
OnCreate: func(event *v1.Event) (*v1.Event, error) {
|
||||
createdEvent = event
|
||||
return event, nil
|
||||
},
|
||||
}
|
||||
|
||||
ev := &v1.Event{}
|
||||
ev.ResourceVersion = "updated-resource-version"
|
||||
ev.Count = 2
|
||||
recordToSink(sink, ev, eventCorrelator, randGen, 0)
|
||||
|
||||
if createdEvent == nil {
|
||||
t.Error("Event did not get created after patch failed")
|
||||
return
|
||||
}
|
||||
|
||||
if createdEvent.ResourceVersion != "" {
|
||||
t.Errorf("Event did not have its resource version cleared, was %s", createdEvent.ResourceVersion)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLotsOfEvents(t *testing.T) {
|
||||
recorderCalled := make(chan struct{})
|
||||
loggerCalled := make(chan struct{})
|
||||
|
||||
// Fail each event a few times to ensure there's some load on the tested code.
|
||||
var counts [1000]int
|
||||
testEvents := testEventSink{
|
||||
OnCreate: func(event *v1.Event) (*v1.Event, error) {
|
||||
num, err := strconv.Atoi(event.Message)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return event, nil
|
||||
}
|
||||
counts[num]++
|
||||
if counts[num] < 5 {
|
||||
return nil, fmt.Errorf("fake error")
|
||||
}
|
||||
recorderCalled <- struct{}{}
|
||||
return event, nil
|
||||
},
|
||||
}
|
||||
|
||||
eventBroadcaster := NewBroadcasterForTests(0)
|
||||
sinkWatcher := eventBroadcaster.StartRecordingToSink(&testEvents)
|
||||
logWatcher := eventBroadcaster.StartLogging(func(formatter string, args ...interface{}) {
|
||||
loggerCalled <- struct{}{}
|
||||
})
|
||||
recorder := eventBroadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "eventTest"})
|
||||
for i := 0; i < maxQueuedEvents; i++ {
|
||||
// we want a unique object to stop spam filtering
|
||||
ref := &v1.ObjectReference{
|
||||
Kind: "Pod",
|
||||
Name: fmt.Sprintf("foo-%v", i),
|
||||
Namespace: "baz",
|
||||
UID: "bar",
|
||||
APIVersion: "version",
|
||||
}
|
||||
// we need to vary the reason to prevent aggregation
|
||||
go recorder.Eventf(ref, v1.EventTypeNormal, "Reason-"+string(i), strconv.Itoa(i))
|
||||
}
|
||||
// Make sure no events were dropped by either of the listeners.
|
||||
for i := 0; i < maxQueuedEvents; i++ {
|
||||
<-recorderCalled
|
||||
<-loggerCalled
|
||||
}
|
||||
// Make sure that every event was attempted 5 times
|
||||
for i := 0; i < maxQueuedEvents; i++ {
|
||||
if counts[i] < 5 {
|
||||
t.Errorf("Only attempted to record event '%d' %d times.", i, counts[i])
|
||||
}
|
||||
}
|
||||
sinkWatcher.Stop()
|
||||
logWatcher.Stop()
|
||||
}
|
||||
|
||||
func TestEventfNoNamespace(t *testing.T) {
|
||||
testPod := &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
SelfLink: "/api/version/pods/foo",
|
||||
Name: "foo",
|
||||
UID: "bar",
|
||||
},
|
||||
}
|
||||
testRef, err := ref.GetPartialReference(scheme.Scheme, testPod, "spec.containers[2]")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
table := []struct {
|
||||
obj k8sruntime.Object
|
||||
eventtype string
|
||||
reason string
|
||||
messageFmt string
|
||||
elements []interface{}
|
||||
expect *v1.Event
|
||||
expectLog string
|
||||
expectUpdate bool
|
||||
}{
|
||||
{
|
||||
obj: testRef,
|
||||
eventtype: v1.EventTypeNormal,
|
||||
reason: "Started",
|
||||
messageFmt: "some verbose message: %v",
|
||||
elements: []interface{}{1},
|
||||
expect: &v1.Event{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: "default",
|
||||
},
|
||||
InvolvedObject: v1.ObjectReference{
|
||||
Kind: "Pod",
|
||||
Name: "foo",
|
||||
Namespace: "",
|
||||
UID: "bar",
|
||||
APIVersion: "version",
|
||||
FieldPath: "spec.containers[2]",
|
||||
},
|
||||
Reason: "Started",
|
||||
Message: "some verbose message: 1",
|
||||
Source: v1.EventSource{Component: "eventTest"},
|
||||
Count: 1,
|
||||
Type: v1.EventTypeNormal,
|
||||
},
|
||||
expectLog: `Event(v1.ObjectReference{Kind:"Pod", Namespace:"", Name:"foo", UID:"bar", APIVersion:"version", ResourceVersion:"", FieldPath:"spec.containers[2]"}): type: 'Normal' reason: 'Started' some verbose message: 1`,
|
||||
expectUpdate: false,
|
||||
},
|
||||
}
|
||||
|
||||
testCache := map[string]*v1.Event{}
|
||||
logCalled := make(chan struct{})
|
||||
createEvent := make(chan *v1.Event)
|
||||
updateEvent := make(chan *v1.Event)
|
||||
patchEvent := make(chan *v1.Event)
|
||||
testEvents := testEventSink{
|
||||
OnCreate: OnCreateFactory(testCache, createEvent),
|
||||
OnUpdate: func(event *v1.Event) (*v1.Event, error) {
|
||||
updateEvent <- event
|
||||
return event, nil
|
||||
},
|
||||
OnPatch: OnPatchFactory(testCache, patchEvent),
|
||||
}
|
||||
eventBroadcaster := NewBroadcasterForTests(0)
|
||||
sinkWatcher := eventBroadcaster.StartRecordingToSink(&testEvents)
|
||||
|
||||
clock := clock.NewFakeClock(time.Now())
|
||||
recorder := recorderWithFakeClock(v1.EventSource{Component: "eventTest"}, eventBroadcaster, clock)
|
||||
|
||||
for index, item := range table {
|
||||
clock.Step(1 * time.Second)
|
||||
logWatcher := eventBroadcaster.StartLogging(func(formatter string, args ...interface{}) {
|
||||
if e, a := item.expectLog, fmt.Sprintf(formatter, args...); e != a {
|
||||
t.Errorf("Expected '%v', got '%v'", e, a)
|
||||
}
|
||||
logCalled <- struct{}{}
|
||||
})
|
||||
recorder.Eventf(item.obj, item.eventtype, item.reason, item.messageFmt, item.elements...)
|
||||
|
||||
<-logCalled
|
||||
|
||||
// validate event
|
||||
if item.expectUpdate {
|
||||
actualEvent := <-patchEvent
|
||||
validateEvent(strconv.Itoa(index), actualEvent, item.expect, t)
|
||||
} else {
|
||||
actualEvent := <-createEvent
|
||||
validateEvent(strconv.Itoa(index), actualEvent, item.expect, t)
|
||||
}
|
||||
|
||||
logWatcher.Stop()
|
||||
}
|
||||
sinkWatcher.Stop()
|
||||
}
|
||||
|
||||
func TestMultiSinkCache(t *testing.T) {
|
||||
testPod := &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
SelfLink: "/api/version/pods/foo",
|
||||
Name: "foo",
|
||||
Namespace: "baz",
|
||||
UID: "bar",
|
||||
},
|
||||
}
|
||||
testPod2 := &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
SelfLink: "/api/version/pods/foo",
|
||||
Name: "foo",
|
||||
Namespace: "baz",
|
||||
UID: "differentUid",
|
||||
},
|
||||
}
|
||||
testRef, err := ref.GetPartialReference(scheme.Scheme, testPod, "spec.containers[2]")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
testRef2, err := ref.GetPartialReference(scheme.Scheme, testPod2, "spec.containers[3]")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
table := []struct {
|
||||
obj k8sruntime.Object
|
||||
eventtype string
|
||||
reason string
|
||||
messageFmt string
|
||||
elements []interface{}
|
||||
expect *v1.Event
|
||||
expectLog string
|
||||
expectUpdate bool
|
||||
}{
|
||||
{
|
||||
obj: testRef,
|
||||
eventtype: v1.EventTypeNormal,
|
||||
reason: "Started",
|
||||
messageFmt: "some verbose message: %v",
|
||||
elements: []interface{}{1},
|
||||
expect: &v1.Event{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: "baz",
|
||||
},
|
||||
InvolvedObject: v1.ObjectReference{
|
||||
Kind: "Pod",
|
||||
Name: "foo",
|
||||
Namespace: "baz",
|
||||
UID: "bar",
|
||||
APIVersion: "version",
|
||||
FieldPath: "spec.containers[2]",
|
||||
},
|
||||
Reason: "Started",
|
||||
Message: "some verbose message: 1",
|
||||
Source: v1.EventSource{Component: "eventTest"},
|
||||
Count: 1,
|
||||
Type: v1.EventTypeNormal,
|
||||
},
|
||||
expectLog: `Event(v1.ObjectReference{Kind:"Pod", Namespace:"baz", Name:"foo", UID:"bar", APIVersion:"version", ResourceVersion:"", FieldPath:"spec.containers[2]"}): type: 'Normal' reason: 'Started' some verbose message: 1`,
|
||||
expectUpdate: false,
|
||||
},
|
||||
{
|
||||
obj: testPod,
|
||||
eventtype: v1.EventTypeNormal,
|
||||
reason: "Killed",
|
||||
messageFmt: "some other verbose message: %v",
|
||||
elements: []interface{}{1},
|
||||
expect: &v1.Event{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: "baz",
|
||||
},
|
||||
InvolvedObject: v1.ObjectReference{
|
||||
Kind: "Pod",
|
||||
Name: "foo",
|
||||
Namespace: "baz",
|
||||
UID: "bar",
|
||||
APIVersion: "version",
|
||||
},
|
||||
Reason: "Killed",
|
||||
Message: "some other verbose message: 1",
|
||||
Source: v1.EventSource{Component: "eventTest"},
|
||||
Count: 1,
|
||||
Type: v1.EventTypeNormal,
|
||||
},
|
||||
expectLog: `Event(v1.ObjectReference{Kind:"Pod", Namespace:"baz", Name:"foo", UID:"bar", APIVersion:"version", ResourceVersion:"", FieldPath:""}): type: 'Normal' reason: 'Killed' some other verbose message: 1`,
|
||||
expectUpdate: false,
|
||||
},
|
||||
{
|
||||
obj: testRef,
|
||||
eventtype: v1.EventTypeNormal,
|
||||
reason: "Started",
|
||||
messageFmt: "some verbose message: %v",
|
||||
elements: []interface{}{1},
|
||||
expect: &v1.Event{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: "baz",
|
||||
},
|
||||
InvolvedObject: v1.ObjectReference{
|
||||
Kind: "Pod",
|
||||
Name: "foo",
|
||||
Namespace: "baz",
|
||||
UID: "bar",
|
||||
APIVersion: "version",
|
||||
FieldPath: "spec.containers[2]",
|
||||
},
|
||||
Reason: "Started",
|
||||
Message: "some verbose message: 1",
|
||||
Source: v1.EventSource{Component: "eventTest"},
|
||||
Count: 2,
|
||||
Type: v1.EventTypeNormal,
|
||||
},
|
||||
expectLog: `Event(v1.ObjectReference{Kind:"Pod", Namespace:"baz", Name:"foo", UID:"bar", APIVersion:"version", ResourceVersion:"", FieldPath:"spec.containers[2]"}): type: 'Normal' reason: 'Started' some verbose message: 1`,
|
||||
expectUpdate: true,
|
||||
},
|
||||
{
|
||||
obj: testRef2,
|
||||
eventtype: v1.EventTypeNormal,
|
||||
reason: "Started",
|
||||
messageFmt: "some verbose message: %v",
|
||||
elements: []interface{}{1},
|
||||
expect: &v1.Event{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: "baz",
|
||||
},
|
||||
InvolvedObject: v1.ObjectReference{
|
||||
Kind: "Pod",
|
||||
Name: "foo",
|
||||
Namespace: "baz",
|
||||
UID: "differentUid",
|
||||
APIVersion: "version",
|
||||
FieldPath: "spec.containers[3]",
|
||||
},
|
||||
Reason: "Started",
|
||||
Message: "some verbose message: 1",
|
||||
Source: v1.EventSource{Component: "eventTest"},
|
||||
Count: 1,
|
||||
Type: v1.EventTypeNormal,
|
||||
},
|
||||
expectLog: `Event(v1.ObjectReference{Kind:"Pod", Namespace:"baz", Name:"foo", UID:"differentUid", APIVersion:"version", ResourceVersion:"", FieldPath:"spec.containers[3]"}): type: 'Normal' reason: 'Started' some verbose message: 1`,
|
||||
expectUpdate: false,
|
||||
},
|
||||
{
|
||||
obj: testRef,
|
||||
eventtype: v1.EventTypeNormal,
|
||||
reason: "Started",
|
||||
messageFmt: "some verbose message: %v",
|
||||
elements: []interface{}{1},
|
||||
expect: &v1.Event{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: "baz",
|
||||
},
|
||||
InvolvedObject: v1.ObjectReference{
|
||||
Kind: "Pod",
|
||||
Name: "foo",
|
||||
Namespace: "baz",
|
||||
UID: "bar",
|
||||
APIVersion: "version",
|
||||
FieldPath: "spec.containers[2]",
|
||||
},
|
||||
Reason: "Started",
|
||||
Message: "some verbose message: 1",
|
||||
Source: v1.EventSource{Component: "eventTest"},
|
||||
Count: 3,
|
||||
Type: v1.EventTypeNormal,
|
||||
},
|
||||
expectLog: `Event(v1.ObjectReference{Kind:"Pod", Namespace:"baz", Name:"foo", UID:"bar", APIVersion:"version", ResourceVersion:"", FieldPath:"spec.containers[2]"}): type: 'Normal' reason: 'Started' some verbose message: 1`,
|
||||
expectUpdate: true,
|
||||
},
|
||||
{
|
||||
obj: testRef2,
|
||||
eventtype: v1.EventTypeNormal,
|
||||
reason: "Stopped",
|
||||
messageFmt: "some verbose message: %v",
|
||||
elements: []interface{}{1},
|
||||
expect: &v1.Event{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: "baz",
|
||||
},
|
||||
InvolvedObject: v1.ObjectReference{
|
||||
Kind: "Pod",
|
||||
Name: "foo",
|
||||
Namespace: "baz",
|
||||
UID: "differentUid",
|
||||
APIVersion: "version",
|
||||
FieldPath: "spec.containers[3]",
|
||||
},
|
||||
Reason: "Stopped",
|
||||
Message: "some verbose message: 1",
|
||||
Source: v1.EventSource{Component: "eventTest"},
|
||||
Count: 1,
|
||||
Type: v1.EventTypeNormal,
|
||||
},
|
||||
expectLog: `Event(v1.ObjectReference{Kind:"Pod", Namespace:"baz", Name:"foo", UID:"differentUid", APIVersion:"version", ResourceVersion:"", FieldPath:"spec.containers[3]"}): type: 'Normal' reason: 'Stopped' some verbose message: 1`,
|
||||
expectUpdate: false,
|
||||
},
|
||||
{
|
||||
obj: testRef2,
|
||||
eventtype: v1.EventTypeNormal,
|
||||
reason: "Stopped",
|
||||
messageFmt: "some verbose message: %v",
|
||||
elements: []interface{}{1},
|
||||
expect: &v1.Event{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: "baz",
|
||||
},
|
||||
InvolvedObject: v1.ObjectReference{
|
||||
Kind: "Pod",
|
||||
Name: "foo",
|
||||
Namespace: "baz",
|
||||
UID: "differentUid",
|
||||
APIVersion: "version",
|
||||
FieldPath: "spec.containers[3]",
|
||||
},
|
||||
Reason: "Stopped",
|
||||
Message: "some verbose message: 1",
|
||||
Source: v1.EventSource{Component: "eventTest"},
|
||||
Count: 2,
|
||||
Type: v1.EventTypeNormal,
|
||||
},
|
||||
expectLog: `Event(v1.ObjectReference{Kind:"Pod", Namespace:"baz", Name:"foo", UID:"differentUid", APIVersion:"version", ResourceVersion:"", FieldPath:"spec.containers[3]"}): type: 'Normal' reason: 'Stopped' some verbose message: 1`,
|
||||
expectUpdate: true,
|
||||
},
|
||||
}
|
||||
|
||||
testCache := map[string]*v1.Event{}
|
||||
createEvent := make(chan *v1.Event)
|
||||
updateEvent := make(chan *v1.Event)
|
||||
patchEvent := make(chan *v1.Event)
|
||||
testEvents := testEventSink{
|
||||
OnCreate: OnCreateFactory(testCache, createEvent),
|
||||
OnUpdate: func(event *v1.Event) (*v1.Event, error) {
|
||||
updateEvent <- event
|
||||
return event, nil
|
||||
},
|
||||
OnPatch: OnPatchFactory(testCache, patchEvent),
|
||||
}
|
||||
|
||||
testCache2 := map[string]*v1.Event{}
|
||||
createEvent2 := make(chan *v1.Event)
|
||||
updateEvent2 := make(chan *v1.Event)
|
||||
patchEvent2 := make(chan *v1.Event)
|
||||
testEvents2 := testEventSink{
|
||||
OnCreate: OnCreateFactory(testCache2, createEvent2),
|
||||
OnUpdate: func(event *v1.Event) (*v1.Event, error) {
|
||||
updateEvent2 <- event
|
||||
return event, nil
|
||||
},
|
||||
OnPatch: OnPatchFactory(testCache2, patchEvent2),
|
||||
}
|
||||
|
||||
eventBroadcaster := NewBroadcasterForTests(0)
|
||||
clock := clock.NewFakeClock(time.Now())
|
||||
recorder := recorderWithFakeClock(v1.EventSource{Component: "eventTest"}, eventBroadcaster, clock)
|
||||
|
||||
sinkWatcher := eventBroadcaster.StartRecordingToSink(&testEvents)
|
||||
for index, item := range table {
|
||||
clock.Step(1 * time.Second)
|
||||
recorder.Eventf(item.obj, item.eventtype, item.reason, item.messageFmt, item.elements...)
|
||||
|
||||
// validate event
|
||||
if item.expectUpdate {
|
||||
actualEvent := <-patchEvent
|
||||
validateEvent(strconv.Itoa(index), actualEvent, item.expect, t)
|
||||
} else {
|
||||
actualEvent := <-createEvent
|
||||
validateEvent(strconv.Itoa(index), actualEvent, item.expect, t)
|
||||
}
|
||||
}
|
||||
|
||||
// Another StartRecordingToSink call should start to record events with new clean cache.
|
||||
sinkWatcher2 := eventBroadcaster.StartRecordingToSink(&testEvents2)
|
||||
for index, item := range table {
|
||||
clock.Step(1 * time.Second)
|
||||
recorder.Eventf(item.obj, item.eventtype, item.reason, item.messageFmt, item.elements...)
|
||||
|
||||
// validate event
|
||||
if item.expectUpdate {
|
||||
actualEvent := <-patchEvent2
|
||||
validateEvent(strconv.Itoa(index), actualEvent, item.expect, t)
|
||||
} else {
|
||||
actualEvent := <-createEvent2
|
||||
validateEvent(strconv.Itoa(index), actualEvent, item.expect, t)
|
||||
}
|
||||
}
|
||||
|
||||
sinkWatcher.Stop()
|
||||
sinkWatcher2.Stop()
|
||||
}
|
||||
287
vendor/k8s.io/client-go/tools/record/events_cache_test.go
generated
vendored
287
vendor/k8s.io/client-go/tools/record/events_cache_test.go
generated
vendored
|
|
@ -1,287 +0,0 @@
|
|||
/*
|
||||
Copyright 2015 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package record
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/clock"
|
||||
"k8s.io/apimachinery/pkg/util/diff"
|
||||
)
|
||||
|
||||
func makeObjectReference(kind, name, namespace string) v1.ObjectReference {
|
||||
return v1.ObjectReference{
|
||||
Kind: kind,
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
UID: "C934D34AFB20242",
|
||||
APIVersion: "version",
|
||||
FieldPath: "spec.containers{mycontainer}",
|
||||
}
|
||||
}
|
||||
|
||||
func makeEvent(reason, message string, involvedObject v1.ObjectReference) v1.Event {
|
||||
eventTime := metav1.Now()
|
||||
event := v1.Event{
|
||||
Reason: reason,
|
||||
Message: message,
|
||||
InvolvedObject: involvedObject,
|
||||
Source: v1.EventSource{
|
||||
Component: "kubelet",
|
||||
Host: "kublet.node1",
|
||||
},
|
||||
Count: 1,
|
||||
FirstTimestamp: eventTime,
|
||||
LastTimestamp: eventTime,
|
||||
Type: v1.EventTypeNormal,
|
||||
}
|
||||
return event
|
||||
}
|
||||
|
||||
func makeEvents(num int, template v1.Event) []v1.Event {
|
||||
events := []v1.Event{}
|
||||
for i := 0; i < num; i++ {
|
||||
events = append(events, template)
|
||||
}
|
||||
return events
|
||||
}
|
||||
|
||||
func makeUniqueEvents(num int) []v1.Event {
|
||||
events := []v1.Event{}
|
||||
kind := "Pod"
|
||||
for i := 0; i < num; i++ {
|
||||
reason := strings.Join([]string{"reason", string(i)}, "-")
|
||||
message := strings.Join([]string{"message", string(i)}, "-")
|
||||
name := strings.Join([]string{"pod", string(i)}, "-")
|
||||
namespace := strings.Join([]string{"ns", string(i)}, "-")
|
||||
involvedObject := makeObjectReference(kind, name, namespace)
|
||||
events = append(events, makeEvent(reason, message, involvedObject))
|
||||
}
|
||||
return events
|
||||
}
|
||||
|
||||
func makeSimilarEvents(num int, template v1.Event, messagePrefix string) []v1.Event {
|
||||
events := makeEvents(num, template)
|
||||
for i := range events {
|
||||
events[i].Message = strings.Join([]string{messagePrefix, string(i), events[i].Message}, "-")
|
||||
}
|
||||
return events
|
||||
}
|
||||
|
||||
func setCount(event v1.Event, count int) v1.Event {
|
||||
event.Count = int32(count)
|
||||
return event
|
||||
}
|
||||
|
||||
func validateEvent(messagePrefix string, actualEvent *v1.Event, expectedEvent *v1.Event, t *testing.T) (*v1.Event, error) {
|
||||
recvEvent := *actualEvent
|
||||
expectCompression := expectedEvent.Count > 1
|
||||
t.Logf("%v - expectedEvent.Count is %d\n", messagePrefix, expectedEvent.Count)
|
||||
// Just check that the timestamp was set.
|
||||
if recvEvent.FirstTimestamp.IsZero() || recvEvent.LastTimestamp.IsZero() {
|
||||
t.Errorf("%v - timestamp wasn't set: %#v", messagePrefix, recvEvent)
|
||||
}
|
||||
actualFirstTimestamp := recvEvent.FirstTimestamp
|
||||
actualLastTimestamp := recvEvent.LastTimestamp
|
||||
if actualFirstTimestamp.Equal(&actualLastTimestamp) {
|
||||
if expectCompression {
|
||||
t.Errorf("%v - FirstTimestamp (%q) and LastTimestamp (%q) must be different to indicate event compression happened, but were the same. Actual Event: %#v", messagePrefix, actualFirstTimestamp, actualLastTimestamp, recvEvent)
|
||||
}
|
||||
} else {
|
||||
if expectedEvent.Count == 1 {
|
||||
t.Errorf("%v - FirstTimestamp (%q) and LastTimestamp (%q) must be equal to indicate only one occurrence of the event, but were different. Actual Event: %#v", messagePrefix, actualFirstTimestamp, actualLastTimestamp, recvEvent)
|
||||
}
|
||||
}
|
||||
// Temp clear time stamps for comparison because actual values don't matter for comparison
|
||||
recvEvent.FirstTimestamp = expectedEvent.FirstTimestamp
|
||||
recvEvent.LastTimestamp = expectedEvent.LastTimestamp
|
||||
// Check that name has the right prefix.
|
||||
if n, en := recvEvent.Name, expectedEvent.Name; !strings.HasPrefix(n, en) {
|
||||
t.Errorf("%v - Name '%v' does not contain prefix '%v'", messagePrefix, n, en)
|
||||
}
|
||||
recvEvent.Name = expectedEvent.Name
|
||||
if e, a := expectedEvent, &recvEvent; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("%v - diff: %s", messagePrefix, diff.ObjectGoPrintDiff(e, a))
|
||||
}
|
||||
recvEvent.FirstTimestamp = actualFirstTimestamp
|
||||
recvEvent.LastTimestamp = actualLastTimestamp
|
||||
return actualEvent, nil
|
||||
}
|
||||
|
||||
// TestDefaultEventFilterFunc ensures that no events are filtered
|
||||
func TestDefaultEventFilterFunc(t *testing.T) {
|
||||
event := makeEvent("end-of-world", "it was fun", makeObjectReference("Pod", "pod1", "other"))
|
||||
if DefaultEventFilterFunc(&event) {
|
||||
t.Fatalf("DefaultEventFilterFunc should always return false")
|
||||
}
|
||||
}
|
||||
|
||||
// TestEventAggregatorByReasonFunc ensures that two events are aggregated if they vary only by event.message
|
||||
func TestEventAggregatorByReasonFunc(t *testing.T) {
|
||||
event1 := makeEvent("end-of-world", "it was fun", makeObjectReference("Pod", "pod1", "other"))
|
||||
event2 := makeEvent("end-of-world", "it was awful", makeObjectReference("Pod", "pod1", "other"))
|
||||
event3 := makeEvent("nevermind", "it was a bug", makeObjectReference("Pod", "pod1", "other"))
|
||||
|
||||
aggKey1, localKey1 := EventAggregatorByReasonFunc(&event1)
|
||||
aggKey2, localKey2 := EventAggregatorByReasonFunc(&event2)
|
||||
aggKey3, _ := EventAggregatorByReasonFunc(&event3)
|
||||
|
||||
if aggKey1 != aggKey2 {
|
||||
t.Errorf("Expected %v equal %v", aggKey1, aggKey2)
|
||||
}
|
||||
if localKey1 == localKey2 {
|
||||
t.Errorf("Expected %v to not equal %v", aggKey1, aggKey3)
|
||||
}
|
||||
if aggKey1 == aggKey3 {
|
||||
t.Errorf("Expected %v to not equal %v", aggKey1, aggKey3)
|
||||
}
|
||||
}
|
||||
|
||||
// TestEventAggregatorByReasonMessageFunc validates the proper output for an aggregate message
|
||||
func TestEventAggregatorByReasonMessageFunc(t *testing.T) {
|
||||
expectedPrefix := "(combined from similar events): "
|
||||
event1 := makeEvent("end-of-world", "it was fun", makeObjectReference("Pod", "pod1", "other"))
|
||||
actual := EventAggregatorByReasonMessageFunc(&event1)
|
||||
if !strings.HasPrefix(actual, expectedPrefix) {
|
||||
t.Errorf("Expected %v to begin with prefix %v", actual, expectedPrefix)
|
||||
}
|
||||
}
|
||||
|
||||
// TestEventCorrelator validates proper counting, aggregation of events
|
||||
func TestEventCorrelator(t *testing.T) {
|
||||
firstEvent := makeEvent("first", "i am first", makeObjectReference("Pod", "my-pod", "my-ns"))
|
||||
duplicateEvent := makeEvent("duplicate", "me again", makeObjectReference("Pod", "my-pod", "my-ns"))
|
||||
uniqueEvent := makeEvent("unique", "snowflake", makeObjectReference("Pod", "my-pod", "my-ns"))
|
||||
similarEvent := makeEvent("similar", "similar message", makeObjectReference("Pod", "my-pod", "my-ns"))
|
||||
similarEvent.InvolvedObject.FieldPath = "spec.containers{container1}"
|
||||
aggregateEvent := makeEvent(similarEvent.Reason, EventAggregatorByReasonMessageFunc(&similarEvent), similarEvent.InvolvedObject)
|
||||
similarButDifferentContainerEvent := similarEvent
|
||||
similarButDifferentContainerEvent.InvolvedObject.FieldPath = "spec.containers{container2}"
|
||||
scenario := map[string]struct {
|
||||
previousEvents []v1.Event
|
||||
newEvent v1.Event
|
||||
expectedEvent v1.Event
|
||||
intervalSeconds int
|
||||
expectedSkip bool
|
||||
}{
|
||||
"create-a-single-event": {
|
||||
previousEvents: []v1.Event{},
|
||||
newEvent: firstEvent,
|
||||
expectedEvent: setCount(firstEvent, 1),
|
||||
intervalSeconds: 5,
|
||||
},
|
||||
"the-same-event-should-just-count": {
|
||||
previousEvents: makeEvents(1, duplicateEvent),
|
||||
newEvent: duplicateEvent,
|
||||
expectedEvent: setCount(duplicateEvent, 2),
|
||||
intervalSeconds: 5,
|
||||
},
|
||||
"the-same-event-should-just-count-even-if-more-than-aggregate": {
|
||||
previousEvents: makeEvents(defaultAggregateMaxEvents, duplicateEvent),
|
||||
newEvent: duplicateEvent,
|
||||
expectedEvent: setCount(duplicateEvent, defaultAggregateMaxEvents+1),
|
||||
intervalSeconds: 30, // larger interval induces aggregation but not spam.
|
||||
},
|
||||
"the-same-event-is-spam-if-happens-too-frequently": {
|
||||
previousEvents: makeEvents(defaultSpamBurst+1, duplicateEvent),
|
||||
newEvent: duplicateEvent,
|
||||
expectedSkip: true,
|
||||
intervalSeconds: 1,
|
||||
},
|
||||
"create-many-unique-events": {
|
||||
previousEvents: makeUniqueEvents(30),
|
||||
newEvent: uniqueEvent,
|
||||
expectedEvent: setCount(uniqueEvent, 1),
|
||||
intervalSeconds: 5,
|
||||
},
|
||||
"similar-events-should-aggregate-event": {
|
||||
previousEvents: makeSimilarEvents(defaultAggregateMaxEvents-1, similarEvent, similarEvent.Message),
|
||||
newEvent: similarEvent,
|
||||
expectedEvent: setCount(aggregateEvent, 1),
|
||||
intervalSeconds: 5,
|
||||
},
|
||||
"similar-events-many-times-should-count-the-aggregate": {
|
||||
previousEvents: makeSimilarEvents(defaultAggregateMaxEvents, similarEvent, similarEvent.Message),
|
||||
newEvent: similarEvent,
|
||||
expectedEvent: setCount(aggregateEvent, 2),
|
||||
intervalSeconds: 5,
|
||||
},
|
||||
"events-from-different-containers-do-not-aggregate": {
|
||||
previousEvents: makeEvents(1, similarButDifferentContainerEvent),
|
||||
newEvent: similarEvent,
|
||||
expectedEvent: setCount(similarEvent, 1),
|
||||
intervalSeconds: 5,
|
||||
},
|
||||
"similar-events-whose-interval-is-greater-than-aggregate-interval-do-not-aggregate": {
|
||||
previousEvents: makeSimilarEvents(defaultAggregateMaxEvents-1, similarEvent, similarEvent.Message),
|
||||
newEvent: similarEvent,
|
||||
expectedEvent: setCount(similarEvent, 1),
|
||||
intervalSeconds: defaultAggregateIntervalInSeconds,
|
||||
},
|
||||
}
|
||||
|
||||
for testScenario, testInput := range scenario {
|
||||
eventInterval := time.Duration(testInput.intervalSeconds) * time.Second
|
||||
clock := clock.IntervalClock{Time: time.Now(), Duration: eventInterval}
|
||||
correlator := NewEventCorrelator(&clock)
|
||||
for i := range testInput.previousEvents {
|
||||
event := testInput.previousEvents[i]
|
||||
now := metav1.NewTime(clock.Now())
|
||||
event.FirstTimestamp = now
|
||||
event.LastTimestamp = now
|
||||
result, err := correlator.EventCorrelate(&event)
|
||||
if err != nil {
|
||||
t.Errorf("scenario %v: unexpected error playing back prevEvents %v", testScenario, err)
|
||||
}
|
||||
// if we are skipping the event, we can avoid updating state
|
||||
if !result.Skip {
|
||||
correlator.UpdateState(result.Event)
|
||||
}
|
||||
}
|
||||
|
||||
// update the input to current clock value
|
||||
now := metav1.NewTime(clock.Now())
|
||||
testInput.newEvent.FirstTimestamp = now
|
||||
testInput.newEvent.LastTimestamp = now
|
||||
result, err := correlator.EventCorrelate(&testInput.newEvent)
|
||||
if err != nil {
|
||||
t.Errorf("scenario %v: unexpected error correlating input event %v", testScenario, err)
|
||||
}
|
||||
|
||||
// verify we did not get skip from filter function unexpectedly...
|
||||
if result.Skip != testInput.expectedSkip {
|
||||
t.Errorf("scenario %v: expected skip %v, but got %v", testScenario, testInput.expectedSkip, result.Skip)
|
||||
continue
|
||||
}
|
||||
|
||||
// we wanted to actually skip, so no event is needed to validate
|
||||
if testInput.expectedSkip {
|
||||
continue
|
||||
}
|
||||
|
||||
// validate event
|
||||
_, err = validateEvent(testScenario, result.Event, &testInput.expectedEvent, t)
|
||||
if err != nil {
|
||||
t.Errorf("scenario %v: unexpected error validating result %v", testScenario, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
228
vendor/k8s.io/client-go/tools/remotecommand/v2_test.go
generated
vendored
228
vendor/k8s.io/client-go/tools/remotecommand/v2_test.go
generated
vendored
|
|
@ -1,228 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package remotecommand
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/util/httpstream"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
)
|
||||
|
||||
type fakeReader struct {
|
||||
err error
|
||||
}
|
||||
|
||||
func (r *fakeReader) Read([]byte) (int, error) { return 0, r.err }
|
||||
|
||||
type fakeWriter struct{}
|
||||
|
||||
func (*fakeWriter) Write([]byte) (int, error) { return 0, nil }
|
||||
|
||||
type fakeStreamCreator struct {
|
||||
created map[string]bool
|
||||
errors map[string]error
|
||||
}
|
||||
|
||||
var _ streamCreator = &fakeStreamCreator{}
|
||||
|
||||
func (f *fakeStreamCreator) CreateStream(headers http.Header) (httpstream.Stream, error) {
|
||||
streamType := headers.Get(v1.StreamType)
|
||||
f.created[streamType] = true
|
||||
return nil, f.errors[streamType]
|
||||
}
|
||||
|
||||
func TestV2CreateStreams(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
stdin bool
|
||||
stdinError error
|
||||
stdout bool
|
||||
stdoutError error
|
||||
stderr bool
|
||||
stderrError error
|
||||
errorError error
|
||||
tty bool
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "stdin error",
|
||||
stdin: true,
|
||||
stdinError: errors.New("stdin error"),
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "stdout error",
|
||||
stdout: true,
|
||||
stdoutError: errors.New("stdout error"),
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "stderr error",
|
||||
stderr: true,
|
||||
stderrError: errors.New("stderr error"),
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "error stream error",
|
||||
stdin: true,
|
||||
stdout: true,
|
||||
stderr: true,
|
||||
errorError: errors.New("error stream error"),
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "no errors",
|
||||
stdin: true,
|
||||
stdout: true,
|
||||
stderr: true,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "no errors, stderr & tty set, don't expect stderr",
|
||||
stdin: true,
|
||||
stdout: true,
|
||||
stderr: true,
|
||||
tty: true,
|
||||
expectError: false,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
conn := &fakeStreamCreator{
|
||||
created: make(map[string]bool),
|
||||
errors: map[string]error{
|
||||
v1.StreamTypeStdin: test.stdinError,
|
||||
v1.StreamTypeStdout: test.stdoutError,
|
||||
v1.StreamTypeStderr: test.stderrError,
|
||||
v1.StreamTypeError: test.errorError,
|
||||
},
|
||||
}
|
||||
|
||||
opts := StreamOptions{Tty: test.tty}
|
||||
if test.stdin {
|
||||
opts.Stdin = &fakeReader{}
|
||||
}
|
||||
if test.stdout {
|
||||
opts.Stdout = &fakeWriter{}
|
||||
}
|
||||
if test.stderr {
|
||||
opts.Stderr = &fakeWriter{}
|
||||
}
|
||||
|
||||
h := newStreamProtocolV2(opts).(*streamProtocolV2)
|
||||
err := h.createStreams(conn)
|
||||
|
||||
if test.expectError {
|
||||
if err == nil {
|
||||
t.Errorf("%s: expected error", test.name)
|
||||
continue
|
||||
}
|
||||
if e, a := test.stdinError, err; test.stdinError != nil && e != a {
|
||||
t.Errorf("%s: expected %v, got %v", test.name, e, a)
|
||||
}
|
||||
if e, a := test.stdoutError, err; test.stdoutError != nil && e != a {
|
||||
t.Errorf("%s: expected %v, got %v", test.name, e, a)
|
||||
}
|
||||
if e, a := test.stderrError, err; test.stderrError != nil && e != a {
|
||||
t.Errorf("%s: expected %v, got %v", test.name, e, a)
|
||||
}
|
||||
if e, a := test.errorError, err; test.errorError != nil && e != a {
|
||||
t.Errorf("%s: expected %v, got %v", test.name, e, a)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if !test.expectError && err != nil {
|
||||
t.Errorf("%s: unexpected error: %v", test.name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if test.stdin && !conn.created[v1.StreamTypeStdin] {
|
||||
t.Errorf("%s: expected stdin stream", test.name)
|
||||
}
|
||||
if test.stdout && !conn.created[v1.StreamTypeStdout] {
|
||||
t.Errorf("%s: expected stdout stream", test.name)
|
||||
}
|
||||
if test.stderr {
|
||||
if test.tty && conn.created[v1.StreamTypeStderr] {
|
||||
t.Errorf("%s: unexpected stderr stream because tty is set", test.name)
|
||||
} else if !test.tty && !conn.created[v1.StreamTypeStderr] {
|
||||
t.Errorf("%s: expected stderr stream", test.name)
|
||||
}
|
||||
}
|
||||
if !conn.created[v1.StreamTypeError] {
|
||||
t.Errorf("%s: expected error stream", test.name)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestV2ErrorStreamReading(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
stream io.Reader
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
name: "error reading from stream",
|
||||
stream: &fakeReader{errors.New("foo")},
|
||||
expectedError: errors.New("error reading from error stream: foo"),
|
||||
},
|
||||
{
|
||||
name: "stream returns an error",
|
||||
stream: strings.NewReader("some error"),
|
||||
expectedError: errors.New("error executing remote command: some error"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
h := newStreamProtocolV2(StreamOptions{}).(*streamProtocolV2)
|
||||
h.errorStream = test.stream
|
||||
|
||||
ch := watchErrorStream(h.errorStream, &errorDecoderV2{})
|
||||
if ch == nil {
|
||||
t.Fatalf("%s: unexpected nil channel", test.name)
|
||||
}
|
||||
|
||||
var err error
|
||||
select {
|
||||
case err = <-ch:
|
||||
case <-time.After(wait.ForeverTestTimeout):
|
||||
t.Fatalf("%s: timed out", test.name)
|
||||
}
|
||||
|
||||
if test.expectedError != nil {
|
||||
if err == nil {
|
||||
t.Errorf("%s: expected an error", test.name)
|
||||
} else if e, a := test.expectedError, err; e.Error() != a.Error() {
|
||||
t.Errorf("%s: expected %q, got %q", test.name, e, a)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if test.expectedError == nil && err != nil {
|
||||
t.Errorf("%s: unexpected error: %v", test.name, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
71
vendor/k8s.io/client-go/tools/remotecommand/v4_test.go
generated
vendored
71
vendor/k8s.io/client-go/tools/remotecommand/v4_test.go
generated
vendored
|
|
@ -1,71 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package remotecommand
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestV4ErrorDecoder(t *testing.T) {
|
||||
dec := errorDecoderV4{}
|
||||
|
||||
type Test struct {
|
||||
message string
|
||||
err string
|
||||
}
|
||||
|
||||
for _, test := range []Test{
|
||||
{
|
||||
message: "{}",
|
||||
err: "error stream protocol error: unknown error",
|
||||
},
|
||||
{
|
||||
message: "{",
|
||||
err: "error stream protocol error: unexpected end of JSON input in \"{\"",
|
||||
},
|
||||
{
|
||||
message: `{"status": "Success" }`,
|
||||
err: "",
|
||||
},
|
||||
{
|
||||
message: `{"status": "Failure", "message": "foobar" }`,
|
||||
err: "foobar",
|
||||
},
|
||||
{
|
||||
message: `{"status": "Failure", "message": "foobar", "reason": "NonZeroExitCode", "details": {"causes": [{"reason": "foo"}] } }`,
|
||||
err: "error stream protocol error: no ExitCode cause given",
|
||||
},
|
||||
{
|
||||
message: `{"status": "Failure", "message": "foobar", "reason": "NonZeroExitCode", "details": {"causes": [{"reason": "ExitCode"}] } }`,
|
||||
err: "error stream protocol error: invalid exit code value \"\"",
|
||||
},
|
||||
{
|
||||
message: `{"status": "Failure", "message": "foobar", "reason": "NonZeroExitCode", "details": {"causes": [{"reason": "ExitCode", "message": "42"}] } }`,
|
||||
err: "command terminated with exit code 42",
|
||||
},
|
||||
} {
|
||||
err := dec.decode([]byte(test.message))
|
||||
want := test.err
|
||||
if want == "" {
|
||||
want = "<nil>"
|
||||
}
|
||||
if got := fmt.Sprintf("%v", err); got != want {
|
||||
t.Errorf("wrong error for message %q: want=%q, got=%q", test.message, want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
6
vendor/k8s.io/client-go/transport/cache.go
generated
vendored
6
vendor/k8s.io/client-go/transport/cache.go
generated
vendored
|
|
@ -44,6 +44,7 @@ type tlsCacheKey struct {
|
|||
certData string
|
||||
keyData string
|
||||
serverName string
|
||||
dial string
|
||||
}
|
||||
|
||||
func (t tlsCacheKey) String() string {
|
||||
|
|
@ -51,7 +52,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", t.insecure, t.caData, t.certData, keyText, t.serverName)
|
||||
return fmt.Sprintf("insecure:%v, caData:%#v, certData:%#v, keyData:%s, serverName:%s, dial:%s", t.insecure, t.caData, t.certData, keyText, t.serverName, t.dial)
|
||||
}
|
||||
|
||||
func (c *tlsTransportCache) get(config *Config) (http.RoundTripper, error) {
|
||||
|
|
@ -75,7 +76,7 @@ func (c *tlsTransportCache) get(config *Config) (http.RoundTripper, error) {
|
|||
return nil, err
|
||||
}
|
||||
// The options didn't require a custom TLS config
|
||||
if tlsConfig == nil {
|
||||
if tlsConfig == nil && config.Dial == nil {
|
||||
return http.DefaultTransport, nil
|
||||
}
|
||||
|
||||
|
|
@ -109,5 +110,6 @@ func tlsConfigKey(c *Config) (tlsCacheKey, error) {
|
|||
certData: string(c.TLS.CertData),
|
||||
keyData: string(c.TLS.KeyData),
|
||||
serverName: c.TLS.ServerName,
|
||||
dial: fmt.Sprintf("%p", c.Dial),
|
||||
}, nil
|
||||
}
|
||||
|
|
|
|||
128
vendor/k8s.io/client-go/transport/cache_test.go
generated
vendored
128
vendor/k8s.io/client-go/transport/cache_test.go
generated
vendored
|
|
@ -1,128 +0,0 @@
|
|||
/*
|
||||
Copyright 2015 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package transport
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTLSConfigKey(t *testing.T) {
|
||||
// Make sure config fields that don't affect the tls config don't affect the cache key
|
||||
identicalConfigurations := map[string]*Config{
|
||||
"empty": {},
|
||||
"basic": {Username: "bob", Password: "password"},
|
||||
"bearer": {BearerToken: "token"},
|
||||
"user agent": {UserAgent: "useragent"},
|
||||
"transport": {Transport: http.DefaultTransport},
|
||||
"wrap transport": {WrapTransport: func(http.RoundTripper) http.RoundTripper { return nil }},
|
||||
}
|
||||
for nameA, valueA := range identicalConfigurations {
|
||||
for nameB, valueB := range identicalConfigurations {
|
||||
keyA, err := tlsConfigKey(valueA)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error for %q: %v", nameA, err)
|
||||
continue
|
||||
}
|
||||
keyB, err := tlsConfigKey(valueB)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error for %q: %v", nameB, err)
|
||||
continue
|
||||
}
|
||||
if keyA != keyB {
|
||||
t.Errorf("Expected identical 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
|
||||
uniqueConfigurations := map[string]*Config{
|
||||
"no tls": {},
|
||||
"insecure": {TLS: TLSConfig{Insecure: true}},
|
||||
"cadata 1": {TLS: TLSConfig{CAData: []byte{1}}},
|
||||
"cadata 2": {TLS: TLSConfig{CAData: []byte{2}}},
|
||||
"cert 1, key 1": {
|
||||
TLS: TLSConfig{
|
||||
CertData: []byte{1},
|
||||
KeyData: []byte{1},
|
||||
},
|
||||
},
|
||||
"cert 1, key 1, servername 1": {
|
||||
TLS: TLSConfig{
|
||||
CertData: []byte{1},
|
||||
KeyData: []byte{1},
|
||||
ServerName: "1",
|
||||
},
|
||||
},
|
||||
"cert 1, key 1, servername 2": {
|
||||
TLS: TLSConfig{
|
||||
CertData: []byte{1},
|
||||
KeyData: []byte{1},
|
||||
ServerName: "2",
|
||||
},
|
||||
},
|
||||
"cert 1, key 2": {
|
||||
TLS: TLSConfig{
|
||||
CertData: []byte{1},
|
||||
KeyData: []byte{2},
|
||||
},
|
||||
},
|
||||
"cert 2, key 1": {
|
||||
TLS: TLSConfig{
|
||||
CertData: []byte{2},
|
||||
KeyData: []byte{1},
|
||||
},
|
||||
},
|
||||
"cert 2, key 2": {
|
||||
TLS: TLSConfig{
|
||||
CertData: []byte{2},
|
||||
KeyData: []byte{2},
|
||||
},
|
||||
},
|
||||
"cadata 1, cert 1, key 1": {
|
||||
TLS: TLSConfig{
|
||||
CAData: []byte{1},
|
||||
CertData: []byte{1},
|
||||
KeyData: []byte{1},
|
||||
},
|
||||
},
|
||||
}
|
||||
for nameA, valueA := range uniqueConfigurations {
|
||||
for nameB, valueB := range uniqueConfigurations {
|
||||
// Don't compare to ourselves
|
||||
if nameA == nameB {
|
||||
continue
|
||||
}
|
||||
|
||||
keyA, err := tlsConfigKey(valueA)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error for %q: %v", nameA, err)
|
||||
continue
|
||||
}
|
||||
keyB, err := tlsConfigKey(valueB)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error for %q: %v", nameB, err)
|
||||
continue
|
||||
}
|
||||
if keyA == keyB {
|
||||
t.Errorf("Expected unique cache keys for %q and %q, got:\n\t%s\n\t%s", nameA, nameB, keyA, keyB)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
218
vendor/k8s.io/client-go/transport/round_trippers_test.go
generated
vendored
218
vendor/k8s.io/client-go/transport/round_trippers_test.go
generated
vendored
|
|
@ -1,218 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 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 transport
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type testRoundTripper struct {
|
||||
Request *http.Request
|
||||
Response *http.Response
|
||||
Err error
|
||||
}
|
||||
|
||||
func (rt *testRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
rt.Request = req
|
||||
return rt.Response, rt.Err
|
||||
}
|
||||
|
||||
func TestBearerAuthRoundTripper(t *testing.T) {
|
||||
rt := &testRoundTripper{}
|
||||
req := &http.Request{}
|
||||
NewBearerAuthRoundTripper("test", rt).RoundTrip(req)
|
||||
if rt.Request == nil {
|
||||
t.Fatalf("unexpected nil request: %v", rt)
|
||||
}
|
||||
if rt.Request == req {
|
||||
t.Fatalf("round tripper should have copied request object: %#v", rt.Request)
|
||||
}
|
||||
if rt.Request.Header.Get("Authorization") != "Bearer test" {
|
||||
t.Errorf("unexpected authorization header: %#v", rt.Request)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBasicAuthRoundTripper(t *testing.T) {
|
||||
for n, tc := range map[string]struct {
|
||||
user string
|
||||
pass string
|
||||
}{
|
||||
"basic": {user: "user", pass: "pass"},
|
||||
"no pass": {user: "user"},
|
||||
} {
|
||||
rt := &testRoundTripper{}
|
||||
req := &http.Request{}
|
||||
NewBasicAuthRoundTripper(tc.user, tc.pass, rt).RoundTrip(req)
|
||||
if rt.Request == nil {
|
||||
t.Fatalf("%s: unexpected nil request: %v", n, rt)
|
||||
}
|
||||
if rt.Request == req {
|
||||
t.Fatalf("%s: round tripper should have copied request object: %#v", n, rt.Request)
|
||||
}
|
||||
if user, pass, found := rt.Request.BasicAuth(); !found || user != tc.user || pass != tc.pass {
|
||||
t.Errorf("%s: unexpected authorization header: %#v", n, rt.Request)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserAgentRoundTripper(t *testing.T) {
|
||||
rt := &testRoundTripper{}
|
||||
req := &http.Request{
|
||||
Header: make(http.Header),
|
||||
}
|
||||
req.Header.Set("User-Agent", "other")
|
||||
NewUserAgentRoundTripper("test", rt).RoundTrip(req)
|
||||
if rt.Request == nil {
|
||||
t.Fatalf("unexpected nil request: %v", rt)
|
||||
}
|
||||
if rt.Request != req {
|
||||
t.Fatalf("round tripper should not have copied request object: %#v", rt.Request)
|
||||
}
|
||||
if rt.Request.Header.Get("User-Agent") != "other" {
|
||||
t.Errorf("unexpected user agent header: %#v", rt.Request)
|
||||
}
|
||||
|
||||
req = &http.Request{}
|
||||
NewUserAgentRoundTripper("test", rt).RoundTrip(req)
|
||||
if rt.Request == nil {
|
||||
t.Fatalf("unexpected nil request: %v", rt)
|
||||
}
|
||||
if rt.Request == req {
|
||||
t.Fatalf("round tripper should have copied request object: %#v", rt.Request)
|
||||
}
|
||||
if rt.Request.Header.Get("User-Agent") != "test" {
|
||||
t.Errorf("unexpected user agent header: %#v", rt.Request)
|
||||
}
|
||||
}
|
||||
|
||||
func TestImpersonationRoundTripper(t *testing.T) {
|
||||
tcs := []struct {
|
||||
name string
|
||||
impersonationConfig ImpersonationConfig
|
||||
expected map[string][]string
|
||||
}{
|
||||
{
|
||||
name: "all",
|
||||
impersonationConfig: ImpersonationConfig{
|
||||
UserName: "user",
|
||||
Groups: []string{"one", "two"},
|
||||
Extra: map[string][]string{
|
||||
"first": {"A", "a"},
|
||||
"second": {"B", "b"},
|
||||
},
|
||||
},
|
||||
expected: map[string][]string{
|
||||
ImpersonateUserHeader: {"user"},
|
||||
ImpersonateGroupHeader: {"one", "two"},
|
||||
ImpersonateUserExtraHeaderPrefix + "First": {"A", "a"},
|
||||
ImpersonateUserExtraHeaderPrefix + "Second": {"B", "b"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tcs {
|
||||
rt := &testRoundTripper{}
|
||||
req := &http.Request{
|
||||
Header: make(http.Header),
|
||||
}
|
||||
NewImpersonatingRoundTripper(tc.impersonationConfig, rt).RoundTrip(req)
|
||||
|
||||
for k, v := range rt.Request.Header {
|
||||
expected, ok := tc.expected[k]
|
||||
if !ok {
|
||||
t.Errorf("%v missing %v=%v", tc.name, k, v)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(expected, v) {
|
||||
t.Errorf("%v expected %v: %v, got %v", tc.name, k, expected, v)
|
||||
}
|
||||
}
|
||||
for k, v := range tc.expected {
|
||||
expected, ok := rt.Request.Header[k]
|
||||
if !ok {
|
||||
t.Errorf("%v missing %v=%v", tc.name, k, v)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(expected, v) {
|
||||
t.Errorf("%v expected %v: %v, got %v", tc.name, k, expected, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuthProxyRoundTripper(t *testing.T) {
|
||||
for n, tc := range map[string]struct {
|
||||
username string
|
||||
groups []string
|
||||
extra map[string][]string
|
||||
}{
|
||||
"allfields": {
|
||||
username: "user",
|
||||
groups: []string{"groupA", "groupB"},
|
||||
extra: map[string][]string{
|
||||
"one": {"alpha", "bravo"},
|
||||
"two": {"charlie", "delta"},
|
||||
},
|
||||
},
|
||||
} {
|
||||
rt := &testRoundTripper{}
|
||||
req := &http.Request{}
|
||||
NewAuthProxyRoundTripper(tc.username, tc.groups, tc.extra, rt).RoundTrip(req)
|
||||
if rt.Request == nil {
|
||||
t.Errorf("%s: unexpected nil request: %v", n, rt)
|
||||
continue
|
||||
}
|
||||
if rt.Request == req {
|
||||
t.Errorf("%s: round tripper should have copied request object: %#v", n, rt.Request)
|
||||
continue
|
||||
}
|
||||
|
||||
actualUsernames, ok := rt.Request.Header["X-Remote-User"]
|
||||
if !ok {
|
||||
t.Errorf("%s missing value", n)
|
||||
continue
|
||||
}
|
||||
if e, a := []string{tc.username}, actualUsernames; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("%s expected %v, got %v", n, e, a)
|
||||
continue
|
||||
}
|
||||
actualGroups, ok := rt.Request.Header["X-Remote-Group"]
|
||||
if !ok {
|
||||
t.Errorf("%s missing value", n)
|
||||
continue
|
||||
}
|
||||
if e, a := tc.groups, actualGroups; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("%s expected %v, got %v", n, e, a)
|
||||
continue
|
||||
}
|
||||
|
||||
actualExtra := map[string][]string{}
|
||||
for key, values := range rt.Request.Header {
|
||||
if strings.HasPrefix(strings.ToLower(key), strings.ToLower("X-Remote-Extra-")) {
|
||||
extraKey := strings.ToLower(key[len("X-Remote-Extra-"):])
|
||||
actualExtra[extraKey] = append(actualExtra[key], values...)
|
||||
}
|
||||
}
|
||||
if e, a := tc.extra, actualExtra; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("%s expected %v, got %v", n, e, a)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
2
vendor/k8s.io/client-go/transport/transport.go
generated
vendored
2
vendor/k8s.io/client-go/transport/transport.go
generated
vendored
|
|
@ -52,7 +52,7 @@ func New(config *Config) (http.RoundTripper, error) {
|
|||
// 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) {
|
||||
if !(c.HasCA() || c.HasCertAuth() || c.TLS.Insecure) {
|
||||
if !(c.HasCA() || c.HasCertAuth() || c.TLS.Insecure || len(c.TLS.ServerName) > 0) {
|
||||
return nil, nil
|
||||
}
|
||||
if c.HasCA() && c.TLS.Insecure {
|
||||
|
|
|
|||
204
vendor/k8s.io/client-go/transport/transport_test.go
generated
vendored
204
vendor/k8s.io/client-go/transport/transport_test.go
generated
vendored
|
|
@ -1,204 +0,0 @@
|
|||
/*
|
||||
Copyright 2015 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package transport
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
)
|
||||
|
||||
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-----`
|
||||
|
||||
certData = `-----BEGIN CERTIFICATE-----
|
||||
MIIC6jCCAdSgAwIBAgIBCzALBgkqhkiG9w0BAQswIzEhMB8GA1UEAwwYMTAuMTMu
|
||||
MTI5LjEwNkAxNDIxMzU5MDU4MB4XDTE1MDExNTIyMDEzMVoXDTE2MDExNTIyMDEz
|
||||
MlowGzEZMBcGA1UEAxMQb3BlbnNoaWZ0LWNsaWVudDCCASIwDQYJKoZIhvcNAQEB
|
||||
BQADggEPADCCAQoCggEBAKtdhz0+uCLXw5cSYns9rU/XifFSpb/x24WDdrm72S/v
|
||||
b9BPYsAStiP148buylr1SOuNi8sTAZmlVDDIpIVwMLff+o2rKYDicn9fjbrTxTOj
|
||||
lI4pHJBH+JU3AJ0tbajupioh70jwFS0oYpwtneg2zcnE2Z4l6mhrj2okrc5Q1/X2
|
||||
I2HChtIU4JYTisObtin10QKJX01CLfYXJLa8upWzKZ4/GOcHG+eAV3jXWoXidtjb
|
||||
1Usw70amoTZ6mIVCkiu1QwCoa8+ycojGfZhvqMsAp1536ZcCul+Na+AbCv4zKS7F
|
||||
kQQaImVrXdUiFansIoofGlw/JNuoKK6ssVpS5Ic3pgcCAwEAAaM1MDMwDgYDVR0P
|
||||
AQH/BAQDAgCgMBMGA1UdJQQMMAoGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwCwYJ
|
||||
KoZIhvcNAQELA4IBAQCKLREH7bXtXtZ+8vI6cjD7W3QikiArGqbl36bAhhWsJLp/
|
||||
p/ndKz39iFNaiZ3GlwIURWOOKx3y3GA0x9m8FR+Llthf0EQ8sUjnwaknWs0Y6DQ3
|
||||
jjPFZOpV3KPCFrdMJ3++E3MgwFC/Ih/N2ebFX9EcV9Vcc6oVWMdwT0fsrhu683rq
|
||||
6GSR/3iVX1G/pmOiuaR0fNUaCyCfYrnI4zHBDgSfnlm3vIvN2lrsR/DQBakNL8DJ
|
||||
HBgKxMGeUPoneBv+c8DMXIL0EhaFXRlBv9QW45/GiAIOuyFJ0i6hCtGZpJjq4OpQ
|
||||
BRjCI+izPzFTjsxD4aORE+WOkyWFCGPWKfNejfw0
|
||||
-----END CERTIFICATE-----`
|
||||
|
||||
keyData = `-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEAq12HPT64ItfDlxJiez2tT9eJ8VKlv/HbhYN2ubvZL+9v0E9i
|
||||
wBK2I/Xjxu7KWvVI642LyxMBmaVUMMikhXAwt9/6jaspgOJyf1+NutPFM6OUjikc
|
||||
kEf4lTcAnS1tqO6mKiHvSPAVLShinC2d6DbNycTZniXqaGuPaiStzlDX9fYjYcKG
|
||||
0hTglhOKw5u2KfXRAolfTUIt9hcktry6lbMpnj8Y5wcb54BXeNdaheJ22NvVSzDv
|
||||
RqahNnqYhUKSK7VDAKhrz7JyiMZ9mG+oywCnXnfplwK6X41r4BsK/jMpLsWRBBoi
|
||||
ZWtd1SIVqewiih8aXD8k26gorqyxWlLkhzemBwIDAQABAoIBAD2XYRs3JrGHQUpU
|
||||
FkdbVKZkvrSY0vAZOqBTLuH0zUv4UATb8487anGkWBjRDLQCgxH+jucPTrztekQK
|
||||
aW94clo0S3aNtV4YhbSYIHWs1a0It0UdK6ID7CmdWkAj6s0T8W8lQT7C46mWYVLm
|
||||
5mFnCTHi6aB42jZrqmEpC7sivWwuU0xqj3Ml8kkxQCGmyc9JjmCB4OrFFC8NNt6M
|
||||
ObvQkUI6Z3nO4phTbpxkE1/9dT0MmPIF7GhHVzJMS+EyyRYUDllZ0wvVSOM3qZT0
|
||||
JMUaBerkNwm9foKJ1+dv2nMKZZbJajv7suUDCfU44mVeaEO+4kmTKSGCGjjTBGkr
|
||||
7L1ySDECgYEA5ElIMhpdBzIivCuBIH8LlUeuzd93pqssO1G2Xg0jHtfM4tz7fyeI
|
||||
cr90dc8gpli24dkSxzLeg3Tn3wIj/Bu64m2TpZPZEIlukYvgdgArmRIPQVxerYey
|
||||
OkrfTNkxU1HXsYjLCdGcGXs5lmb+K/kuTcFxaMOs7jZi7La+jEONwf8CgYEAwCs/
|
||||
rUOOA0klDsWWisbivOiNPII79c9McZCNBqncCBfMUoiGe8uWDEO4TFHN60vFuVk9
|
||||
8PkwpCfvaBUX+ajvbafIfHxsnfk1M04WLGCeqQ/ym5Q4sQoQOcC1b1y9qc/xEWfg
|
||||
nIUuia0ukYRpl7qQa3tNg+BNFyjypW8zukUAC/kCgYB1/Kojuxx5q5/oQVPrx73k
|
||||
2bevD+B3c+DYh9MJqSCNwFtUpYIWpggPxoQan4LwdsmO0PKzocb/ilyNFj4i/vII
|
||||
NToqSc/WjDFpaDIKyuu9oWfhECye45NqLWhb/6VOuu4QA/Nsj7luMhIBehnEAHW+
|
||||
GkzTKM8oD1PxpEG3nPKXYQKBgQC6AuMPRt3XBl1NkCrpSBy/uObFlFaP2Enpf39S
|
||||
3OZ0Gv0XQrnSaL1kP8TMcz68rMrGX8DaWYsgytstR4W+jyy7WvZwsUu+GjTJ5aMG
|
||||
77uEcEBpIi9CBzivfn7hPccE8ZgqPf+n4i6q66yxBJflW5xhvafJqDtW2LcPNbW/
|
||||
bvzdmQKBgExALRUXpq+5dbmkdXBHtvXdRDZ6rVmrnjy4nI5bPw+1GqQqk6uAR6B/
|
||||
F6NmLCQOO4PDG/cuatNHIr2FrwTmGdEL6ObLUGWn9Oer9gJhHVqqsY5I4sEPo4XX
|
||||
stR0Yiw0buV6DL/moUO0HIM9Bjh96HJp+LxiIS6UCdIhMPp5HoQa
|
||||
-----END RSA PRIVATE KEY-----`
|
||||
)
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
Config *Config
|
||||
Err bool
|
||||
TLS bool
|
||||
Default bool
|
||||
}{
|
||||
"default transport": {
|
||||
Default: true,
|
||||
Config: &Config{},
|
||||
},
|
||||
|
||||
"ca transport": {
|
||||
TLS: true,
|
||||
Config: &Config{
|
||||
TLS: TLSConfig{
|
||||
CAData: []byte(rootCACert),
|
||||
},
|
||||
},
|
||||
},
|
||||
"bad ca file transport": {
|
||||
Err: true,
|
||||
Config: &Config{
|
||||
TLS: TLSConfig{
|
||||
CAFile: "invalid file",
|
||||
},
|
||||
},
|
||||
},
|
||||
"ca data overriding bad ca file transport": {
|
||||
TLS: true,
|
||||
Config: &Config{
|
||||
TLS: TLSConfig{
|
||||
CAData: []byte(rootCACert),
|
||||
CAFile: "invalid file",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
"cert transport": {
|
||||
TLS: true,
|
||||
Config: &Config{
|
||||
TLS: TLSConfig{
|
||||
CAData: []byte(rootCACert),
|
||||
CertData: []byte(certData),
|
||||
KeyData: []byte(keyData),
|
||||
},
|
||||
},
|
||||
},
|
||||
"bad cert data transport": {
|
||||
Err: true,
|
||||
Config: &Config{
|
||||
TLS: TLSConfig{
|
||||
CAData: []byte(rootCACert),
|
||||
CertData: []byte(certData),
|
||||
KeyData: []byte("bad key data"),
|
||||
},
|
||||
},
|
||||
},
|
||||
"bad file cert transport": {
|
||||
Err: true,
|
||||
Config: &Config{
|
||||
TLS: TLSConfig{
|
||||
CAData: []byte(rootCACert),
|
||||
CertData: []byte(certData),
|
||||
KeyFile: "invalid file",
|
||||
},
|
||||
},
|
||||
},
|
||||
"key data overriding bad file cert transport": {
|
||||
TLS: true,
|
||||
Config: &Config{
|
||||
TLS: TLSConfig{
|
||||
CAData: []byte(rootCACert),
|
||||
CertData: []byte(certData),
|
||||
KeyData: []byte(keyData),
|
||||
KeyFile: "invalid file",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for k, testCase := range testCases {
|
||||
transport, err := New(testCase.Config)
|
||||
switch {
|
||||
case testCase.Err && err == nil:
|
||||
t.Errorf("%s: unexpected non-error", k)
|
||||
continue
|
||||
case !testCase.Err && err != nil:
|
||||
t.Errorf("%s: unexpected error: %v", k, err)
|
||||
continue
|
||||
}
|
||||
|
||||
switch {
|
||||
case testCase.Default && transport != http.DefaultTransport:
|
||||
t.Errorf("%s: expected the default transport, got %#v", k, transport)
|
||||
continue
|
||||
case !testCase.Default && transport == http.DefaultTransport:
|
||||
t.Errorf("%s: expected non-default transport, got %#v", k, transport)
|
||||
continue
|
||||
}
|
||||
|
||||
// We only know how to check TLSConfig on http.Transports
|
||||
if transport, ok := transport.(*http.Transport); ok {
|
||||
switch {
|
||||
case testCase.TLS && transport.TLSClientConfig == nil:
|
||||
t.Errorf("%s: expected TLSClientConfig, got %#v", k, transport)
|
||||
continue
|
||||
case !testCase.TLS && transport.TLSClientConfig != nil:
|
||||
t.Errorf("%s: expected no TLSClientConfig, got %#v", k, transport)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
50
vendor/k8s.io/client-go/util/buffer/ring_growing_test.go
generated
vendored
50
vendor/k8s.io/client-go/util/buffer/ring_growing_test.go
generated
vendored
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 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 buffer
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGrowth(t *testing.T) {
|
||||
t.Parallel()
|
||||
x := 10
|
||||
g := NewRingGrowing(1)
|
||||
for i := 0; i < x; i++ {
|
||||
assert.Equal(t, i, g.readable)
|
||||
g.WriteOne(i)
|
||||
}
|
||||
read := 0
|
||||
for g.readable > 0 {
|
||||
v, ok := g.ReadOne()
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, read, v)
|
||||
read++
|
||||
}
|
||||
assert.Equalf(t, x, read, "expected to have read %d items: %d", x, read)
|
||||
assert.Zerof(t, g.readable, "expected readable to be zero: %d", g.readable)
|
||||
assert.Equalf(t, g.n, 16, "expected N to be 16: %d", g.n)
|
||||
}
|
||||
|
||||
func TestEmpty(t *testing.T) {
|
||||
t.Parallel()
|
||||
g := NewRingGrowing(1)
|
||||
_, ok := g.ReadOne()
|
||||
assert.False(t, ok)
|
||||
}
|
||||
75
vendor/k8s.io/client-go/util/cert/csr_test.go
generated
vendored
75
vendor/k8s.io/client-go/util/cert/csr_test.go
generated
vendored
|
|
@ -1,75 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cert
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMakeCSR(t *testing.T) {
|
||||
keyFile := "testdata/dontUseThisKey.pem"
|
||||
subject := &pkix.Name{
|
||||
CommonName: "kube-worker",
|
||||
}
|
||||
dnsSANs := []string{"localhost"}
|
||||
ipSANs := []net.IP{net.ParseIP("127.0.0.1")}
|
||||
|
||||
keyData, err := ioutil.ReadFile(keyFile)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
key, err := ParsePrivateKeyPEM(keyData)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
csrPEM, err := MakeCSR(key, subject, dnsSANs, ipSANs)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
csrBlock, rest := pem.Decode(csrPEM)
|
||||
if csrBlock == nil {
|
||||
t.Error("Unable to decode MakeCSR result.")
|
||||
}
|
||||
if len(rest) != 0 {
|
||||
t.Error("Found more than one PEM encoded block in the result.")
|
||||
}
|
||||
if csrBlock.Type != CertificateRequestBlockType {
|
||||
t.Errorf("Found block type %q, wanted 'CERTIFICATE REQUEST'", csrBlock.Type)
|
||||
}
|
||||
csr, err := x509.ParseCertificateRequest(csrBlock.Bytes)
|
||||
if err != nil {
|
||||
t.Errorf("Found %v parsing MakeCSR result as a CertificateRequest.", err)
|
||||
}
|
||||
if csr.Subject.CommonName != subject.CommonName {
|
||||
t.Errorf("Wanted %v, got %v", subject, csr.Subject)
|
||||
}
|
||||
if len(csr.DNSNames) != 1 {
|
||||
t.Errorf("Wanted 1 DNS name in the result, got %d", len(csr.DNSNames))
|
||||
} else if csr.DNSNames[0] != dnsSANs[0] {
|
||||
t.Errorf("Wanted %v, got %v", dnsSANs[0], csr.DNSNames[0])
|
||||
}
|
||||
if len(csr.IPAddresses) != 1 {
|
||||
t.Errorf("Wanted 1 IP address in the result, got %d", len(csr.IPAddresses))
|
||||
} else if csr.IPAddresses[0].String() != ipSANs[0].String() {
|
||||
t.Errorf("Wanted %v, got %v", ipSANs[0], csr.IPAddresses[0])
|
||||
}
|
||||
}
|
||||
197
vendor/k8s.io/client-go/util/cert/pem_test.go
generated
vendored
197
vendor/k8s.io/client-go/util/cert/pem_test.go
generated
vendored
|
|
@ -1,197 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 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 cert
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const (
|
||||
// rsaPrivateKey is a RSA Private Key in PKCS#1 format
|
||||
// openssl genrsa -out rsa2048.pem 2048
|
||||
rsaPrivateKey = `-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEA92mVjhBKOFsdxFzb/Pjq+7b5TJlODAdY5hK+WxLZTIrfhDPq
|
||||
FWrGKdjSNiHbXrdEtwJh9V+RqPZVSN3aWy1224RgkyNdMJsXhJKuCC24ZKY8SXtW
|
||||
xuTYmMRaMnCsv6QBGRTIbZ2EFbAObVM7lDyv1VqY3amZIWFQMlZ9CNpxDSPa5yi4
|
||||
3gopbXkne0oGNmey9X0qtpk7NMZIgAL6Zz4rZ30bcfC2ag6RLOFI2E/c4n8c38R8
|
||||
9MfXfLkj8/Cxo4JfI9NvRCpPOpFO8d/ZtWVUuIrBQN+Y7tkN2T60Qq/TkKXUrhDe
|
||||
fwlTlktZVJ/GztLYU41b2GcWsh/XO+PH831rmwIDAQABAoIBAQCC9c6GDjVbM0/E
|
||||
WurPMusfJjE7zII1d8YkspM0HfwLug6qKdikUYpnKC/NG4rEzfl/bbFwco/lgc6O
|
||||
7W/hh2U8uQttlvCDA/Uk5YddKOZL0Hpk4vaB/SxxYK3luSKXpjY2knutGg2KdVCN
|
||||
qdsFkkH4iyYTXuyBcMNEgedZQldI/kEujIH/L7FE+DF5TMzT4lHhozDoG+fy564q
|
||||
qVGUZXJn0ubc3GaPn2QOLNNM44sfYA4UJCpKBXPu85bvNObjxVQO4WqwwxU1vRnL
|
||||
UUsaGaelhSVJCo0dVPRvrfPPKZ09HTwpy40EkgQo6VriFc1EBoQDjENLbAJv9OfQ
|
||||
aCc9wiZhAoGBAP/8oEy48Zbb0P8Vdy4djf5tfBW8yXFLWzXewJ4l3itKS1r42nbX
|
||||
9q3cJsgRTQm8uRcMIpWxsc3n6zG+lREvTkoTB3ViI7+uQPiqA+BtWyNy7jzufFke
|
||||
ONKZfg7QxxmYRWZBRnoNGNbMpNeERuLmhvQuom9D1WbhzAYJbfs/O4WTAoGBAPds
|
||||
2FNDU0gaesFDdkIUGq1nIJqRQDW485LXZm4pFqBFxdOpbdWRuYT2XZjd3fD0XY98
|
||||
Nhkpb7NTMCuK3BdKcqIptt+cK+quQgYid0hhhgZbpCQ5AL6c6KgyjgpYlh2enzU9
|
||||
Zo3yg8ej1zbbA11sBlhX+5iO2P1u5DG+JHLwUUbZAoGAUwaU102EzfEtsA4+QW7E
|
||||
hyjrfgFlNKHES4yb3K9bh57pIfBkqvcQwwMMcQdrfSUAw0DkVrjzel0mI1Q09QXq
|
||||
1ould6UFAz55RC2gZEITtUOpkYmoOx9aPrQZ9qQwb1S77ZZuTVfCHqjxLhVxCFbM
|
||||
npYhiQTvShciHTMhwMOZgpECgYAVV5EtVXBYltgh1YTc3EkUzgF087R7LdHsx6Gx
|
||||
POATwRD4WfP8aQ58lpeqOPEM+LcdSlSMRRO6fyF3kAm+BJDwxfJdRWZQXumZB94M
|
||||
I0VhRQRaj4Qt7PDwmTPBVrTUJzuKZxpyggm17b8Bn1Ch/VBqzGQKW8AB1E/grosM
|
||||
UwhfuQKBgQC2JO/iqTQScHClf0qlItCJsBuVukFmSAVCkpOD8YdbdlPdOOwSk1wQ
|
||||
C0eAlsC3BCMvkpidKQmra6IqIrvTGI6EFgkrb3aknWdup2w8j2udYCNqyE3W+fVe
|
||||
p8FdYQ1FkACQ+daO5VlClL/9l0sGjKXlNKbpmJ2H4ngZmXj5uGmxuQ==
|
||||
-----END RSA PRIVATE KEY-----`
|
||||
|
||||
// rsaPublicKey is a RSA Public Key in PEM encoded format
|
||||
// openssl rsa -in rsa2048.pem -pubout -out rsa2048pub.pem
|
||||
rsaPublicKey = `-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA92mVjhBKOFsdxFzb/Pjq
|
||||
+7b5TJlODAdY5hK+WxLZTIrfhDPqFWrGKdjSNiHbXrdEtwJh9V+RqPZVSN3aWy12
|
||||
24RgkyNdMJsXhJKuCC24ZKY8SXtWxuTYmMRaMnCsv6QBGRTIbZ2EFbAObVM7lDyv
|
||||
1VqY3amZIWFQMlZ9CNpxDSPa5yi43gopbXkne0oGNmey9X0qtpk7NMZIgAL6Zz4r
|
||||
Z30bcfC2ag6RLOFI2E/c4n8c38R89MfXfLkj8/Cxo4JfI9NvRCpPOpFO8d/ZtWVU
|
||||
uIrBQN+Y7tkN2T60Qq/TkKXUrhDefwlTlktZVJ/GztLYU41b2GcWsh/XO+PH831r
|
||||
mwIDAQAB
|
||||
-----END PUBLIC KEY-----`
|
||||
|
||||
// certificate is an x509 certificate in PEM encoded format
|
||||
// openssl req -new -key rsa2048.pem -sha256 -nodes -x509 -days 1826 -out x509certificate.pem -subj "/C=US/CN=not-valid"
|
||||
certificate = `-----BEGIN CERTIFICATE-----
|
||||
MIIDFTCCAf2gAwIBAgIJAN8B8NOwtiUCMA0GCSqGSIb3DQEBCwUAMCExCzAJBgNV
|
||||
BAYTAlVTMRIwEAYDVQQDDAlub3QtdmFsaWQwHhcNMTcwMzIyMDI1NjM2WhcNMjIw
|
||||
MzIyMDI1NjM2WjAhMQswCQYDVQQGEwJVUzESMBAGA1UEAwwJbm90LXZhbGlkMIIB
|
||||
IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA92mVjhBKOFsdxFzb/Pjq+7b5
|
||||
TJlODAdY5hK+WxLZTIrfhDPqFWrGKdjSNiHbXrdEtwJh9V+RqPZVSN3aWy1224Rg
|
||||
kyNdMJsXhJKuCC24ZKY8SXtWxuTYmMRaMnCsv6QBGRTIbZ2EFbAObVM7lDyv1VqY
|
||||
3amZIWFQMlZ9CNpxDSPa5yi43gopbXkne0oGNmey9X0qtpk7NMZIgAL6Zz4rZ30b
|
||||
cfC2ag6RLOFI2E/c4n8c38R89MfXfLkj8/Cxo4JfI9NvRCpPOpFO8d/ZtWVUuIrB
|
||||
QN+Y7tkN2T60Qq/TkKXUrhDefwlTlktZVJ/GztLYU41b2GcWsh/XO+PH831rmwID
|
||||
AQABo1AwTjAdBgNVHQ4EFgQU1I5GfinLF7ta+dBJ6UWcrYaexLswHwYDVR0jBBgw
|
||||
FoAU1I5GfinLF7ta+dBJ6UWcrYaexLswDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0B
|
||||
AQsFAAOCAQEAUl0wUD4y41juHFOVMYiziPYr1ShSpQXdwp8FfaHrzI5hsr8UMe8D
|
||||
dzb9QzZ4bx3yZhiG3ahrSBh956thMTHrKTEwAfJIEXI4cuSVWQAaOJ4Em5SDFxQe
|
||||
d0E6Ui2nGh1SFGF7oyuEXyzqgRMWFNDFw9HLUNgXaO18Zfouw8+K0BgbfEWEcSi1
|
||||
JLQbyhCjz088gltrliQGPWDFAg9cHBKtJhuTzZkvuqK1CLEmBhtzP1zFiGBfOJc8
|
||||
v+aKjAwrPUNX11cXOCPxBv2qXMetxaovBem6AI2hvypCInXaVQfP+yOLubzlTDjS
|
||||
Y708SlY38hmS1uTwDpyLOn8AKkZ8jtx75g==
|
||||
-----END CERTIFICATE-----`
|
||||
|
||||
// ecdsaPrivateKeyWithParams is a ECDSA Private Key with included EC Parameters block
|
||||
// openssl ecparam -name prime256v1 -genkey -out ecdsa256params.pem
|
||||
ecdsaPrivateKeyWithParams = `-----BEGIN EC PARAMETERS-----
|
||||
BggqhkjOPQMBBw==
|
||||
-----END EC PARAMETERS-----
|
||||
-----BEGIN EC PRIVATE KEY-----
|
||||
MHcCAQEEIAwSOWQqlMTZNqNF7tgua812Jxib1DVOgb2pHHyIEyNNoAoGCCqGSM49
|
||||
AwEHoUQDQgAEyxYNrs6a6tsNCFNYn+l+JDUZ0PnUZbcsDgJn2O62D1se8M5iQ5rY
|
||||
iIv6RpxE3VHvlHEIvYgCZkG0jHszTUopBg==
|
||||
-----END EC PRIVATE KEY-----`
|
||||
|
||||
// ecdsaPrivateKey is a ECDSA Private Key in ASN.1 format
|
||||
// openssl ecparam -name prime256v1 -genkey -noout -out ecdsa256.pem
|
||||
ecdsaPrivateKey = `-----BEGIN EC PRIVATE KEY-----
|
||||
MHcCAQEEIP6Qw6dHDiLsSnLXUhQVTPE0fTQQrj3XSbiQAZPXnk5+oAoGCCqGSM49
|
||||
AwEHoUQDQgAEZZzi1u5f2/AEGFI/HYUhU+u6cTK1q2bbtE7r1JMK+/sQA5sNAp+7
|
||||
Vdc3psr1OaNzyTyuhTECyRdFKXm63cMnGg==
|
||||
-----END EC PRIVATE KEY-----`
|
||||
|
||||
// ecdsaPublicKey is a ECDSA Public Key in PEM encoded format
|
||||
// openssl ec -in ecdsa256.pem -pubout -out ecdsa256pub.pem
|
||||
ecdsaPublicKey = `-----BEGIN PUBLIC KEY-----
|
||||
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZZzi1u5f2/AEGFI/HYUhU+u6cTK1
|
||||
q2bbtE7r1JMK+/sQA5sNAp+7Vdc3psr1OaNzyTyuhTECyRdFKXm63cMnGg==
|
||||
-----END PUBLIC KEY-----`
|
||||
)
|
||||
|
||||
func TestReadPrivateKey(t *testing.T) {
|
||||
f, err := ioutil.TempFile("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("error creating tmpfile: %v", err)
|
||||
}
|
||||
defer os.Remove(f.Name())
|
||||
|
||||
if _, err := PrivateKeyFromFile(f.Name()); err == nil {
|
||||
t.Fatalf("Expected error reading key from empty file, got none")
|
||||
}
|
||||
|
||||
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 := 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 := 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 {
|
||||
t.Fatalf("error reading private ECDSA key with params: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadPublicKeys(t *testing.T) {
|
||||
f, err := ioutil.TempFile("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("error creating tmpfile: %v", err)
|
||||
}
|
||||
defer os.Remove(f.Name())
|
||||
|
||||
if _, err := PublicKeysFromFile(f.Name()); err == nil {
|
||||
t.Fatalf("Expected error reading keys from empty file, got none")
|
||||
}
|
||||
|
||||
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 {
|
||||
t.Fatalf("error reading RSA public key: %v", err)
|
||||
} else if len(keys) != 1 {
|
||||
t.Fatalf("expected 1 key, got %d", len(keys))
|
||||
}
|
||||
|
||||
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 {
|
||||
t.Fatalf("error reading ECDSA public key: %v", err)
|
||||
} else if len(keys) != 1 {
|
||||
t.Fatalf("expected 1 key, got %d", len(keys))
|
||||
}
|
||||
|
||||
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 {
|
||||
t.Fatalf("error reading combined RSA/ECDSA public key file: %v", err)
|
||||
} else if len(keys) != 2 {
|
||||
t.Fatalf("expected 2 keys, got %d", len(keys))
|
||||
}
|
||||
|
||||
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 {
|
||||
t.Fatalf("error reading public key from certificate file: %v", err)
|
||||
} else if len(keys) != 1 {
|
||||
t.Fatalf("expected 1 keys, got %d", len(keys))
|
||||
}
|
||||
|
||||
}
|
||||
195
vendor/k8s.io/client-go/util/flowcontrol/backoff_test.go
generated
vendored
195
vendor/k8s.io/client-go/util/flowcontrol/backoff_test.go
generated
vendored
|
|
@ -1,195 +0,0 @@
|
|||
/*
|
||||
Copyright 2015 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package flowcontrol
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/clock"
|
||||
)
|
||||
|
||||
func TestSlowBackoff(t *testing.T) {
|
||||
id := "_idSlow"
|
||||
tc := clock.NewFakeClock(time.Now())
|
||||
step := time.Second
|
||||
maxDuration := 50 * step
|
||||
|
||||
b := NewFakeBackOff(step, maxDuration, tc)
|
||||
cases := []time.Duration{0, 1, 2, 4, 8, 16, 32, 50, 50, 50}
|
||||
for ix, c := range cases {
|
||||
tc.Step(step)
|
||||
w := b.Get(id)
|
||||
if w != c*step {
|
||||
t.Errorf("input: '%d': expected %s, got %s", ix, c*step, w)
|
||||
}
|
||||
b.Next(id, tc.Now())
|
||||
}
|
||||
|
||||
//Now confirm that the Reset cancels backoff.
|
||||
b.Next(id, tc.Now())
|
||||
b.Reset(id)
|
||||
if b.Get(id) != 0 {
|
||||
t.Errorf("Reset didn't clear the backoff.")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestBackoffReset(t *testing.T) {
|
||||
id := "_idReset"
|
||||
tc := clock.NewFakeClock(time.Now())
|
||||
step := time.Second
|
||||
maxDuration := step * 5
|
||||
b := NewFakeBackOff(step, maxDuration, tc)
|
||||
startTime := tc.Now()
|
||||
|
||||
// get to backoff = maxDuration
|
||||
for i := 0; i <= int(maxDuration/step); i++ {
|
||||
tc.Step(step)
|
||||
b.Next(id, tc.Now())
|
||||
}
|
||||
|
||||
// backoff should be capped at maxDuration
|
||||
if !b.IsInBackOffSince(id, tc.Now()) {
|
||||
t.Errorf("expected to be in Backoff got %s", b.Get(id))
|
||||
}
|
||||
|
||||
lastUpdate := tc.Now()
|
||||
tc.Step(2*maxDuration + step) // time += 11s, 11 > 2*maxDuration
|
||||
if b.IsInBackOffSince(id, lastUpdate) {
|
||||
t.Errorf("expected to not be in Backoff after reset (start=%s, now=%s, lastUpdate=%s), got %s", startTime, tc.Now(), lastUpdate, b.Get(id))
|
||||
}
|
||||
}
|
||||
|
||||
func TestBackoffHightWaterMark(t *testing.T) {
|
||||
id := "_idHiWaterMark"
|
||||
tc := clock.NewFakeClock(time.Now())
|
||||
step := time.Second
|
||||
maxDuration := 5 * step
|
||||
b := NewFakeBackOff(step, maxDuration, tc)
|
||||
|
||||
// get to backoff = maxDuration
|
||||
for i := 0; i <= int(maxDuration/step); i++ {
|
||||
tc.Step(step)
|
||||
b.Next(id, tc.Now())
|
||||
}
|
||||
|
||||
// backoff high watermark expires after 2*maxDuration
|
||||
tc.Step(maxDuration + step)
|
||||
b.Next(id, tc.Now())
|
||||
|
||||
if b.Get(id) != maxDuration {
|
||||
t.Errorf("expected Backoff to stay at high watermark %s got %s", maxDuration, b.Get(id))
|
||||
}
|
||||
}
|
||||
|
||||
func TestBackoffGC(t *testing.T) {
|
||||
id := "_idGC"
|
||||
tc := clock.NewFakeClock(time.Now())
|
||||
step := time.Second
|
||||
maxDuration := 5 * step
|
||||
|
||||
b := NewFakeBackOff(step, maxDuration, tc)
|
||||
|
||||
for i := 0; i <= int(maxDuration/step); i++ {
|
||||
tc.Step(step)
|
||||
b.Next(id, tc.Now())
|
||||
}
|
||||
lastUpdate := tc.Now()
|
||||
tc.Step(maxDuration + step)
|
||||
b.GC()
|
||||
_, found := b.perItemBackoff[id]
|
||||
if !found {
|
||||
t.Errorf("expected GC to skip entry, elapsed time=%s maxDuration=%s", tc.Now().Sub(lastUpdate), maxDuration)
|
||||
}
|
||||
|
||||
tc.Step(maxDuration + step)
|
||||
b.GC()
|
||||
r, found := b.perItemBackoff[id]
|
||||
if found {
|
||||
t.Errorf("expected GC of entry after %s got entry %v", tc.Now().Sub(lastUpdate), r)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsInBackOffSinceUpdate(t *testing.T) {
|
||||
id := "_idIsInBackOffSinceUpdate"
|
||||
tc := clock.NewFakeClock(time.Now())
|
||||
step := time.Second
|
||||
maxDuration := 10 * step
|
||||
b := NewFakeBackOff(step, maxDuration, tc)
|
||||
startTime := tc.Now()
|
||||
|
||||
cases := []struct {
|
||||
tick time.Duration
|
||||
inBackOff bool
|
||||
value int
|
||||
}{
|
||||
{tick: 0, inBackOff: false, value: 0},
|
||||
{tick: 1, inBackOff: false, value: 1},
|
||||
{tick: 2, inBackOff: true, value: 2},
|
||||
{tick: 3, inBackOff: false, value: 2},
|
||||
{tick: 4, inBackOff: true, value: 4},
|
||||
{tick: 5, inBackOff: true, value: 4},
|
||||
{tick: 6, inBackOff: true, value: 4},
|
||||
{tick: 7, inBackOff: false, value: 4},
|
||||
{tick: 8, inBackOff: true, value: 8},
|
||||
{tick: 9, inBackOff: true, value: 8},
|
||||
{tick: 10, inBackOff: true, value: 8},
|
||||
{tick: 11, inBackOff: true, value: 8},
|
||||
{tick: 12, inBackOff: true, value: 8},
|
||||
{tick: 13, inBackOff: true, value: 8},
|
||||
{tick: 14, inBackOff: true, value: 8},
|
||||
{tick: 15, inBackOff: false, value: 8},
|
||||
{tick: 16, inBackOff: true, value: 10},
|
||||
{tick: 17, inBackOff: true, value: 10},
|
||||
{tick: 18, inBackOff: true, value: 10},
|
||||
{tick: 19, inBackOff: true, value: 10},
|
||||
{tick: 20, inBackOff: true, value: 10},
|
||||
{tick: 21, inBackOff: true, value: 10},
|
||||
{tick: 22, inBackOff: true, value: 10},
|
||||
{tick: 23, inBackOff: true, value: 10},
|
||||
{tick: 24, inBackOff: true, value: 10},
|
||||
{tick: 25, inBackOff: false, value: 10},
|
||||
{tick: 26, inBackOff: true, value: 10},
|
||||
{tick: 27, inBackOff: true, value: 10},
|
||||
{tick: 28, inBackOff: true, value: 10},
|
||||
{tick: 29, inBackOff: true, value: 10},
|
||||
{tick: 30, inBackOff: true, value: 10},
|
||||
{tick: 31, inBackOff: true, value: 10},
|
||||
{tick: 32, inBackOff: true, value: 10},
|
||||
{tick: 33, inBackOff: true, value: 10},
|
||||
{tick: 34, inBackOff: true, value: 10},
|
||||
{tick: 35, inBackOff: false, value: 10},
|
||||
{tick: 56, inBackOff: false, value: 0},
|
||||
{tick: 57, inBackOff: false, value: 1},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
tc.SetTime(startTime.Add(c.tick * step))
|
||||
if c.inBackOff != b.IsInBackOffSinceUpdate(id, tc.Now()) {
|
||||
t.Errorf("expected IsInBackOffSinceUpdate %v got %v at tick %s", c.inBackOff, b.IsInBackOffSinceUpdate(id, tc.Now()), c.tick*step)
|
||||
}
|
||||
|
||||
if c.inBackOff && (time.Duration(c.value)*step != b.Get(id)) {
|
||||
t.Errorf("expected backoff value=%s got %s at tick %s", time.Duration(c.value)*step, b.Get(id), c.tick*step)
|
||||
}
|
||||
|
||||
if !c.inBackOff {
|
||||
b.Next(id, tc.Now())
|
||||
}
|
||||
}
|
||||
}
|
||||
153
vendor/k8s.io/client-go/util/flowcontrol/throttle_test.go
generated
vendored
153
vendor/k8s.io/client-go/util/flowcontrol/throttle_test.go
generated
vendored
|
|
@ -1,153 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 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 flowcontrol
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestMultithreadedThrottling(t *testing.T) {
|
||||
// Bucket with 100QPS and no burst
|
||||
r := NewTokenBucketRateLimiter(100, 1)
|
||||
|
||||
// channel to collect 100 tokens
|
||||
taken := make(chan bool, 100)
|
||||
|
||||
// Set up goroutines to hammer the throttler
|
||||
startCh := make(chan bool)
|
||||
endCh := make(chan bool)
|
||||
for i := 0; i < 10; i++ {
|
||||
go func() {
|
||||
// wait for the starting signal
|
||||
<-startCh
|
||||
for {
|
||||
// get a token
|
||||
r.Accept()
|
||||
select {
|
||||
// try to add it to the taken channel
|
||||
case taken <- true:
|
||||
continue
|
||||
// if taken is full, notify and return
|
||||
default:
|
||||
endCh <- true
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// record wall time
|
||||
startTime := time.Now()
|
||||
// take the initial capacity so all tokens are the result of refill
|
||||
r.Accept()
|
||||
// start the thundering herd
|
||||
close(startCh)
|
||||
// wait for the first signal that we collected 100 tokens
|
||||
<-endCh
|
||||
// record wall time
|
||||
endTime := time.Now()
|
||||
|
||||
// tolerate a 1% clock change because these things happen
|
||||
if duration := endTime.Sub(startTime); duration < (time.Second * 99 / 100) {
|
||||
// We shouldn't be able to get 100 tokens out of the bucket in less than 1 second of wall clock time, no matter what
|
||||
t.Errorf("Expected it to take at least 1 second to get 100 tokens, took %v", duration)
|
||||
} else {
|
||||
t.Logf("Took %v to get 100 tokens", duration)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBasicThrottle(t *testing.T) {
|
||||
r := NewTokenBucketRateLimiter(1, 3)
|
||||
for i := 0; i < 3; i++ {
|
||||
if !r.TryAccept() {
|
||||
t.Error("unexpected false accept")
|
||||
}
|
||||
}
|
||||
if r.TryAccept() {
|
||||
t.Error("unexpected true accept")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIncrementThrottle(t *testing.T) {
|
||||
r := NewTokenBucketRateLimiter(1, 1)
|
||||
if !r.TryAccept() {
|
||||
t.Error("unexpected false accept")
|
||||
}
|
||||
if r.TryAccept() {
|
||||
t.Error("unexpected true accept")
|
||||
}
|
||||
|
||||
// Allow to refill
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
if !r.TryAccept() {
|
||||
t.Error("unexpected false accept")
|
||||
}
|
||||
}
|
||||
|
||||
func TestThrottle(t *testing.T) {
|
||||
r := NewTokenBucketRateLimiter(10, 5)
|
||||
|
||||
// Should consume 5 tokens immediately, then
|
||||
// the remaining 11 should take at least 1 second (0.1s each)
|
||||
expectedFinish := time.Now().Add(time.Second * 1)
|
||||
for i := 0; i < 16; i++ {
|
||||
r.Accept()
|
||||
}
|
||||
if time.Now().Before(expectedFinish) {
|
||||
t.Error("rate limit was not respected, finished too early")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAlwaysFake(t *testing.T) {
|
||||
rl := NewFakeAlwaysRateLimiter()
|
||||
if !rl.TryAccept() {
|
||||
t.Error("TryAccept in AlwaysFake should return true.")
|
||||
}
|
||||
// If this will block the test will timeout
|
||||
rl.Accept()
|
||||
}
|
||||
|
||||
func TestNeverFake(t *testing.T) {
|
||||
rl := NewFakeNeverRateLimiter()
|
||||
if rl.TryAccept() {
|
||||
t.Error("TryAccept in NeverFake should return false.")
|
||||
}
|
||||
|
||||
finished := false
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
rl.Accept()
|
||||
finished = true
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
// Wait some time to make sure it never finished.
|
||||
time.Sleep(time.Second)
|
||||
if finished {
|
||||
t.Error("Accept should block forever in NeverFake.")
|
||||
}
|
||||
|
||||
rl.Stop()
|
||||
wg.Wait()
|
||||
if !finished {
|
||||
t.Error("Stop should make Accept unblock in NeverFake.")
|
||||
}
|
||||
}
|
||||
244
vendor/k8s.io/client-go/util/integer/integer_test.go
generated
vendored
244
vendor/k8s.io/client-go/util/integer/integer_test.go
generated
vendored
|
|
@ -1,244 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package integer
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestIntMax(t *testing.T) {
|
||||
tests := []struct {
|
||||
nums []int
|
||||
expectedMax int
|
||||
}{
|
||||
{
|
||||
nums: []int{-1, 0},
|
||||
expectedMax: 0,
|
||||
},
|
||||
{
|
||||
nums: []int{-1, -2},
|
||||
expectedMax: -1,
|
||||
},
|
||||
{
|
||||
nums: []int{0, 1},
|
||||
expectedMax: 1,
|
||||
},
|
||||
{
|
||||
nums: []int{1, 2},
|
||||
expectedMax: 2,
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
t.Logf("executing scenario %d", i)
|
||||
if max := IntMax(test.nums[0], test.nums[1]); max != test.expectedMax {
|
||||
t.Errorf("expected %v, got %v", test.expectedMax, max)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntMin(t *testing.T) {
|
||||
tests := []struct {
|
||||
nums []int
|
||||
expectedMin int
|
||||
}{
|
||||
{
|
||||
nums: []int{-1, 0},
|
||||
expectedMin: -1,
|
||||
},
|
||||
{
|
||||
nums: []int{-1, -2},
|
||||
expectedMin: -2,
|
||||
},
|
||||
{
|
||||
nums: []int{0, 1},
|
||||
expectedMin: 0,
|
||||
},
|
||||
{
|
||||
nums: []int{1, 2},
|
||||
expectedMin: 1,
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
t.Logf("executing scenario %d", i)
|
||||
if min := IntMin(test.nums[0], test.nums[1]); min != test.expectedMin {
|
||||
t.Errorf("expected %v, got %v", test.expectedMin, min)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestInt32Max(t *testing.T) {
|
||||
tests := []struct {
|
||||
nums []int32
|
||||
expectedMax int32
|
||||
}{
|
||||
{
|
||||
nums: []int32{-1, 0},
|
||||
expectedMax: 0,
|
||||
},
|
||||
{
|
||||
nums: []int32{-1, -2},
|
||||
expectedMax: -1,
|
||||
},
|
||||
{
|
||||
nums: []int32{0, 1},
|
||||
expectedMax: 1,
|
||||
},
|
||||
{
|
||||
nums: []int32{1, 2},
|
||||
expectedMax: 2,
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
t.Logf("executing scenario %d", i)
|
||||
if max := Int32Max(test.nums[0], test.nums[1]); max != test.expectedMax {
|
||||
t.Errorf("expected %v, got %v", test.expectedMax, max)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestInt32Min(t *testing.T) {
|
||||
tests := []struct {
|
||||
nums []int32
|
||||
expectedMin int32
|
||||
}{
|
||||
{
|
||||
nums: []int32{-1, 0},
|
||||
expectedMin: -1,
|
||||
},
|
||||
{
|
||||
nums: []int32{-1, -2},
|
||||
expectedMin: -2,
|
||||
},
|
||||
{
|
||||
nums: []int32{0, 1},
|
||||
expectedMin: 0,
|
||||
},
|
||||
{
|
||||
nums: []int32{1, 2},
|
||||
expectedMin: 1,
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
t.Logf("executing scenario %d", i)
|
||||
if min := Int32Min(test.nums[0], test.nums[1]); min != test.expectedMin {
|
||||
t.Errorf("expected %v, got %v", test.expectedMin, min)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestInt64Max(t *testing.T) {
|
||||
tests := []struct {
|
||||
nums []int64
|
||||
expectedMax int64
|
||||
}{
|
||||
{
|
||||
nums: []int64{-1, 0},
|
||||
expectedMax: 0,
|
||||
},
|
||||
{
|
||||
nums: []int64{-1, -2},
|
||||
expectedMax: -1,
|
||||
},
|
||||
{
|
||||
nums: []int64{0, 1},
|
||||
expectedMax: 1,
|
||||
},
|
||||
{
|
||||
nums: []int64{1, 2},
|
||||
expectedMax: 2,
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
t.Logf("executing scenario %d", i)
|
||||
if max := Int64Max(test.nums[0], test.nums[1]); max != test.expectedMax {
|
||||
t.Errorf("expected %v, got %v", test.expectedMax, max)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestInt64Min(t *testing.T) {
|
||||
tests := []struct {
|
||||
nums []int64
|
||||
expectedMin int64
|
||||
}{
|
||||
{
|
||||
nums: []int64{-1, 0},
|
||||
expectedMin: -1,
|
||||
},
|
||||
{
|
||||
nums: []int64{-1, -2},
|
||||
expectedMin: -2,
|
||||
},
|
||||
{
|
||||
nums: []int64{0, 1},
|
||||
expectedMin: 0,
|
||||
},
|
||||
{
|
||||
nums: []int64{1, 2},
|
||||
expectedMin: 1,
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
t.Logf("executing scenario %d", i)
|
||||
if min := Int64Min(test.nums[0], test.nums[1]); min != test.expectedMin {
|
||||
t.Errorf("expected %v, got %v", test.expectedMin, min)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoundToInt32(t *testing.T) {
|
||||
tests := []struct {
|
||||
num float64
|
||||
exp int32
|
||||
}{
|
||||
{
|
||||
num: 5.5,
|
||||
exp: 6,
|
||||
},
|
||||
{
|
||||
num: -3.7,
|
||||
exp: -4,
|
||||
},
|
||||
{
|
||||
num: 3.49,
|
||||
exp: 3,
|
||||
},
|
||||
{
|
||||
num: -7.9,
|
||||
exp: -8,
|
||||
},
|
||||
{
|
||||
num: -4.499999,
|
||||
exp: -4,
|
||||
},
|
||||
{
|
||||
num: 0,
|
||||
exp: 0,
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
t.Logf("executing scenario %d", i)
|
||||
if got := RoundToInt32(test.num); got != test.exp {
|
||||
t.Errorf("expected %d, got %d", test.exp, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
371
vendor/k8s.io/client-go/util/jsonpath/jsonpath_test.go
generated
vendored
371
vendor/k8s.io/client-go/util/jsonpath/jsonpath_test.go
generated
vendored
|
|
@ -1,371 +0,0 @@
|
|||
/*
|
||||
Copyright 2015 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package jsonpath
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type jsonpathTest struct {
|
||||
name string
|
||||
template string
|
||||
input interface{}
|
||||
expect string
|
||||
expectError bool
|
||||
}
|
||||
|
||||
func testJSONPath(tests []jsonpathTest, allowMissingKeys bool, t *testing.T) {
|
||||
for _, test := range tests {
|
||||
j := New(test.name)
|
||||
j.AllowMissingKeys(allowMissingKeys)
|
||||
err := j.Parse(test.template)
|
||||
if err != nil {
|
||||
t.Errorf("in %s, parse %s error %v", test.name, test.template, err)
|
||||
}
|
||||
buf := new(bytes.Buffer)
|
||||
err = j.Execute(buf, test.input)
|
||||
if test.expectError {
|
||||
if test.expectError && err == nil {
|
||||
t.Errorf("in %s, expected execute error", test.name)
|
||||
}
|
||||
continue
|
||||
} else if err != nil {
|
||||
t.Errorf("in %s, execute error %v", test.name, err)
|
||||
}
|
||||
out := buf.String()
|
||||
if out != test.expect {
|
||||
t.Errorf(`in %s, expect to get "%s", got "%s"`, test.name, test.expect, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// testJSONPathSortOutput test cases related to map, the results may print in random order
|
||||
func testJSONPathSortOutput(tests []jsonpathTest, t *testing.T) {
|
||||
for _, test := range tests {
|
||||
j := New(test.name)
|
||||
err := j.Parse(test.template)
|
||||
if err != nil {
|
||||
t.Errorf("in %s, parse %s error %v", test.name, test.template, err)
|
||||
}
|
||||
buf := new(bytes.Buffer)
|
||||
err = j.Execute(buf, test.input)
|
||||
if err != nil {
|
||||
t.Errorf("in %s, execute error %v", test.name, err)
|
||||
}
|
||||
out := buf.String()
|
||||
//since map is visited in random order, we need to sort the results.
|
||||
sortedOut := strings.Fields(out)
|
||||
sort.Strings(sortedOut)
|
||||
sortedExpect := strings.Fields(test.expect)
|
||||
sort.Strings(sortedExpect)
|
||||
if !reflect.DeepEqual(sortedOut, sortedExpect) {
|
||||
t.Errorf(`in %s, expect to get "%s", got "%s"`, test.name, test.expect, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testFailJSONPath(tests []jsonpathTest, t *testing.T) {
|
||||
for _, test := range tests {
|
||||
j := New(test.name)
|
||||
err := j.Parse(test.template)
|
||||
if err != nil {
|
||||
t.Errorf("in %s, parse %s error %v", test.name, test.template, err)
|
||||
}
|
||||
buf := new(bytes.Buffer)
|
||||
err = j.Execute(buf, test.input)
|
||||
var out string
|
||||
if err == nil {
|
||||
out = "nil"
|
||||
} else {
|
||||
out = err.Error()
|
||||
}
|
||||
if out != test.expect {
|
||||
t.Errorf("in %s, expect to get error %q, got %q", test.name, test.expect, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type book struct {
|
||||
Category string
|
||||
Author string
|
||||
Title string
|
||||
Price float32
|
||||
}
|
||||
|
||||
func (b book) String() string {
|
||||
return fmt.Sprintf("{Category: %s, Author: %s, Title: %s, Price: %v}", b.Category, b.Author, b.Title, b.Price)
|
||||
}
|
||||
|
||||
type bicycle struct {
|
||||
Color string
|
||||
Price float32
|
||||
IsNew bool
|
||||
}
|
||||
|
||||
type empName string
|
||||
type job string
|
||||
type store struct {
|
||||
Book []book
|
||||
Bicycle []bicycle
|
||||
Name string
|
||||
Labels map[string]int
|
||||
Employees map[empName]job
|
||||
}
|
||||
|
||||
func TestStructInput(t *testing.T) {
|
||||
|
||||
storeData := store{
|
||||
Name: "jsonpath",
|
||||
Book: []book{
|
||||
{"reference", "Nigel Rees", "Sayings of the Centurey", 8.95},
|
||||
{"fiction", "Evelyn Waugh", "Sword of Honour", 12.99},
|
||||
{"fiction", "Herman Melville", "Moby Dick", 8.99},
|
||||
},
|
||||
Bicycle: []bicycle{
|
||||
{"red", 19.95, true},
|
||||
{"green", 20.01, false},
|
||||
},
|
||||
Labels: map[string]int{
|
||||
"engieer": 10,
|
||||
"web/html": 15,
|
||||
"k8s-app": 20,
|
||||
},
|
||||
Employees: map[empName]job{
|
||||
"jason": "manager",
|
||||
"dan": "clerk",
|
||||
},
|
||||
}
|
||||
|
||||
storeTests := []jsonpathTest{
|
||||
{"plain", "hello jsonpath", nil, "hello jsonpath", false},
|
||||
{"recursive", "{..}", []int{1, 2, 3}, "[1 2 3]", false},
|
||||
{"filter", "{[?(@<5)]}", []int{2, 6, 3, 7}, "2 3", false},
|
||||
{"quote", `{"{"}`, nil, "{", false},
|
||||
{"union", "{[1,3,4]}", []int{0, 1, 2, 3, 4}, "1 3 4", false},
|
||||
{"array", "{[0:2]}", []string{"Monday", "Tudesday"}, "Monday Tudesday", false},
|
||||
{"variable", "hello {.Name}", storeData, "hello jsonpath", false},
|
||||
{"dict/", "{$.Labels.web/html}", storeData, "15", false},
|
||||
{"dict/", "{$.Employees.jason}", storeData, "manager", false},
|
||||
{"dict/", "{$.Employees.dan}", storeData, "clerk", false},
|
||||
{"dict-", "{.Labels.k8s-app}", storeData, "20", false},
|
||||
{"nest", "{.Bicycle[*].Color}", storeData, "red green", false},
|
||||
{"allarray", "{.Book[*].Author}", storeData, "Nigel Rees Evelyn Waugh Herman Melville", false},
|
||||
{"allfileds", "{.Bicycle.*}", storeData, "{red 19.95 true} {green 20.01 false}", false},
|
||||
{"recurfileds", "{..Price}", storeData, "8.95 12.99 8.99 19.95 20.01", false},
|
||||
{"lastarray", "{.Book[-1:]}", storeData,
|
||||
"{Category: fiction, Author: Herman Melville, Title: Moby Dick, Price: 8.99}", false},
|
||||
{"recurarray", "{..Book[2]}", storeData,
|
||||
"{Category: fiction, Author: Herman Melville, Title: Moby Dick, Price: 8.99}", false},
|
||||
{"bool", "{.Bicycle[?(@.IsNew==true)]}", storeData, "{red 19.95 true}", false},
|
||||
}
|
||||
testJSONPath(storeTests, false, t)
|
||||
|
||||
missingKeyTests := []jsonpathTest{
|
||||
{"nonexistent field", "{.hello}", storeData, "", false},
|
||||
}
|
||||
testJSONPath(missingKeyTests, true, t)
|
||||
|
||||
failStoreTests := []jsonpathTest{
|
||||
{"invalid identifier", "{hello}", storeData, "unrecognized identifier hello", false},
|
||||
{"nonexistent field", "{.hello}", storeData, "hello is not found", false},
|
||||
{"invalid array", "{.Labels[0]}", storeData, "map[string]int is not array or slice", false},
|
||||
{"invalid filter operator", "{.Book[?(@.Price<>10)]}", storeData, "unrecognized filter operator <>", false},
|
||||
{"redundant end", "{range .Labels.*}{@}{end}{end}", storeData, "not in range, nothing to end", false},
|
||||
}
|
||||
testFailJSONPath(failStoreTests, t)
|
||||
}
|
||||
|
||||
func TestJSONInput(t *testing.T) {
|
||||
var pointsJSON = []byte(`[
|
||||
{"id": "i1", "x":4, "y":-5},
|
||||
{"id": "i2", "x":-2, "y":-5, "z":1},
|
||||
{"id": "i3", "x": 8, "y": 3 },
|
||||
{"id": "i4", "x": -6, "y": -1 },
|
||||
{"id": "i5", "x": 0, "y": 2, "z": 1 },
|
||||
{"id": "i6", "x": 1, "y": 4 }
|
||||
]`)
|
||||
var pointsData interface{}
|
||||
err := json.Unmarshal(pointsJSON, &pointsData)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
pointsTests := []jsonpathTest{
|
||||
{"exists filter", "{[?(@.z)].id}", pointsData, "i2 i5", false},
|
||||
{"bracket key", "{[0]['id']}", pointsData, "i1", false},
|
||||
}
|
||||
testJSONPath(pointsTests, false, t)
|
||||
}
|
||||
|
||||
// TestKubernetes tests some use cases from kubernetes
|
||||
func TestKubernetes(t *testing.T) {
|
||||
var input = []byte(`{
|
||||
"kind": "List",
|
||||
"items":[
|
||||
{
|
||||
"kind":"None",
|
||||
"metadata":{
|
||||
"name":"127.0.0.1",
|
||||
"labels":{
|
||||
"kubernetes.io/hostname":"127.0.0.1"
|
||||
}
|
||||
},
|
||||
"status":{
|
||||
"capacity":{"cpu":"4"},
|
||||
"ready": true,
|
||||
"addresses":[{"type": "LegacyHostIP", "address":"127.0.0.1"}]
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind":"None",
|
||||
"metadata":{
|
||||
"name":"127.0.0.2",
|
||||
"labels":{
|
||||
"kubernetes.io/hostname":"127.0.0.2"
|
||||
}
|
||||
},
|
||||
"status":{
|
||||
"capacity":{"cpu":"8"},
|
||||
"ready": false,
|
||||
"addresses":[
|
||||
{"type": "LegacyHostIP", "address":"127.0.0.2"},
|
||||
{"type": "another", "address":"127.0.0.3"}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"users":[
|
||||
{
|
||||
"name": "myself",
|
||||
"user": {}
|
||||
},
|
||||
{
|
||||
"name": "e2e",
|
||||
"user": {"username": "admin", "password": "secret"}
|
||||
}
|
||||
]
|
||||
}`)
|
||||
var nodesData interface{}
|
||||
err := json.Unmarshal(input, &nodesData)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
nodesTests := []jsonpathTest{
|
||||
{"range item", `{range .items[*]}{.metadata.name}, {end}{.kind}`, nodesData, "127.0.0.1, 127.0.0.2, List", false},
|
||||
{"range item with quote", `{range .items[*]}{.metadata.name}{"\t"}{end}`, nodesData, "127.0.0.1\t127.0.0.2\t", false},
|
||||
{"range addresss", `{.items[*].status.addresses[*].address}`, nodesData,
|
||||
"127.0.0.1 127.0.0.2 127.0.0.3", false},
|
||||
{"double range", `{range .items[*]}{range .status.addresses[*]}{.address}, {end}{end}`, nodesData,
|
||||
"127.0.0.1, 127.0.0.2, 127.0.0.3, ", false},
|
||||
{"item name", `{.items[*].metadata.name}`, nodesData, "127.0.0.1 127.0.0.2", false},
|
||||
{"union nodes capacity", `{.items[*]['metadata.name', 'status.capacity']}`, nodesData,
|
||||
"127.0.0.1 127.0.0.2 map[cpu:4] map[cpu:8]", false},
|
||||
{"range nodes capacity", `{range .items[*]}[{.metadata.name}, {.status.capacity}] {end}`, nodesData,
|
||||
"[127.0.0.1, map[cpu:4]] [127.0.0.2, map[cpu:8]] ", false},
|
||||
{"user password", `{.users[?(@.name=="e2e")].user.password}`, &nodesData, "secret", false},
|
||||
{"hostname", `{.items[0].metadata.labels.kubernetes\.io/hostname}`, &nodesData, "127.0.0.1", false},
|
||||
{"hostname filter", `{.items[?(@.metadata.labels.kubernetes\.io/hostname=="127.0.0.1")].kind}`, &nodesData, "None", false},
|
||||
{"bool item", `{.items[?(@..ready==true)].metadata.name}`, &nodesData, "127.0.0.1", false},
|
||||
}
|
||||
testJSONPath(nodesTests, false, t)
|
||||
|
||||
randomPrintOrderTests := []jsonpathTest{
|
||||
{"recursive name", "{..name}", nodesData, `127.0.0.1 127.0.0.2 myself e2e`, false},
|
||||
}
|
||||
testJSONPathSortOutput(randomPrintOrderTests, t)
|
||||
}
|
||||
|
||||
func TestFilterPartialMatchesSometimesMissingAnnotations(t *testing.T) {
|
||||
// for https://issues.k8s.io/45546
|
||||
var input = []byte(`{
|
||||
"kind": "List",
|
||||
"items": [
|
||||
{
|
||||
"kind": "Pod",
|
||||
"metadata": {
|
||||
"name": "pod1",
|
||||
"annotations": {
|
||||
"color": "blue"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "Pod",
|
||||
"metadata": {
|
||||
"name": "pod2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "Pod",
|
||||
"metadata": {
|
||||
"name": "pod3",
|
||||
"annotations": {
|
||||
"color": "green"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "Pod",
|
||||
"metadata": {
|
||||
"name": "pod4",
|
||||
"annotations": {
|
||||
"color": "blue"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}`)
|
||||
var data interface{}
|
||||
err := json.Unmarshal(input, &data)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
testJSONPath(
|
||||
[]jsonpathTest{
|
||||
{
|
||||
"filter, should only match a subset, some items don't have annotations, tolerate missing items",
|
||||
`{.items[?(@.metadata.annotations.color=="blue")].metadata.name}`,
|
||||
data,
|
||||
"pod1 pod4",
|
||||
false, // expect no error
|
||||
},
|
||||
},
|
||||
true, // allow missing keys
|
||||
t,
|
||||
)
|
||||
|
||||
testJSONPath(
|
||||
[]jsonpathTest{
|
||||
{
|
||||
"filter, should only match a subset, some items don't have annotations, error on missing items",
|
||||
`{.items[?(@.metadata.annotations.color=="blue")].metadata.name}`,
|
||||
data,
|
||||
"",
|
||||
true, // expect an error
|
||||
},
|
||||
},
|
||||
false, // don't allow missing keys
|
||||
t,
|
||||
)
|
||||
}
|
||||
152
vendor/k8s.io/client-go/util/jsonpath/parser_test.go
generated
vendored
152
vendor/k8s.io/client-go/util/jsonpath/parser_test.go
generated
vendored
|
|
@ -1,152 +0,0 @@
|
|||
/*
|
||||
Copyright 2015 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package jsonpath
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
type parserTest struct {
|
||||
name string
|
||||
text string
|
||||
nodes []Node
|
||||
shouldError bool
|
||||
}
|
||||
|
||||
var parserTests = []parserTest{
|
||||
{"plain", `hello jsonpath`, []Node{newText("hello jsonpath")}, false},
|
||||
{"variable", `hello {.jsonpath}`,
|
||||
[]Node{newText("hello "), newList(), newField("jsonpath")}, false},
|
||||
{"arrayfiled", `hello {['jsonpath']}`,
|
||||
[]Node{newText("hello "), newList(), newField("jsonpath")}, false},
|
||||
{"quote", `{"{"}`, []Node{newList(), newText("{")}, false},
|
||||
{"array", `{[1:3]}`, []Node{newList(),
|
||||
newArray([3]ParamsEntry{{1, true}, {3, true}, {0, false}})}, false},
|
||||
{"allarray", `{.book[*].author}`,
|
||||
[]Node{newList(), newField("book"),
|
||||
newArray([3]ParamsEntry{{0, false}, {0, false}, {0, false}}), newField("author")}, false},
|
||||
{"wildcard", `{.bicycle.*}`,
|
||||
[]Node{newList(), newField("bicycle"), newWildcard()}, false},
|
||||
{"filter", `{[?(@.price<3)]}`,
|
||||
[]Node{newList(), newFilter(newList(), newList(), "<"),
|
||||
newList(), newField("price"), newList(), newInt(3)}, false},
|
||||
{"recursive", `{..}`, []Node{newList(), newRecursive()}, false},
|
||||
{"recurField", `{..price}`,
|
||||
[]Node{newList(), newRecursive(), newField("price")}, false},
|
||||
{"arraydict", `{['book.price']}`, []Node{newList(),
|
||||
newField("book"), newField("price"),
|
||||
}, false},
|
||||
{"union", `{['bicycle.price', 3, 'book.price']}`, []Node{newList(), newUnion([]*ListNode{}),
|
||||
newList(), newField("bicycle"), newField("price"),
|
||||
newList(), newArray([3]ParamsEntry{{3, true}, {4, true}, {0, false}}),
|
||||
newList(), newField("book"), newField("price"),
|
||||
}, false},
|
||||
{"range", `{range .items}{.name},{end}`, []Node{
|
||||
newList(), newIdentifier("range"), newField("items"),
|
||||
newList(), newField("name"), newText(","),
|
||||
newList(), newIdentifier("end"),
|
||||
}, false},
|
||||
{"malformat input", `{\\\}`, []Node{}, true},
|
||||
{"paired parentheses in quotes", `{[?(@.status.nodeInfo.osImage == "()")]}`,
|
||||
[]Node{newList(), newFilter(newList(), newList(), "=="), newList(), newField("status"), newField("nodeInfo"), newField("osImage"), newList(), newText("()")}, false},
|
||||
{"paired parentheses in double quotes and with double quotes escape", `{[?(@.status.nodeInfo.osImage == "(\"\")")]}`,
|
||||
[]Node{newList(), newFilter(newList(), newList(), "=="), newList(), newField("status"), newField("nodeInfo"), newField("osImage"), newList(), newText("(\"\")")}, false},
|
||||
{"unregular parentheses in double quotes", `{[?(@.test == "())(")]}`,
|
||||
[]Node{newList(), newFilter(newList(), newList(), "=="), newList(), newField("test"), newList(), newText("())(")}, false},
|
||||
{"plain text in single quotes", `{[?(@.status.nodeInfo.osImage == 'Linux')]}`,
|
||||
[]Node{newList(), newFilter(newList(), newList(), "=="), newList(), newField("status"), newField("nodeInfo"), newField("osImage"), newList(), newText("Linux")}, false},
|
||||
{"test filter suffix", `{[?(@.status.nodeInfo.osImage == "{[()]}")]}`,
|
||||
[]Node{newList(), newFilter(newList(), newList(), "=="), newList(), newField("status"), newField("nodeInfo"), newField("osImage"), newList(), newText("{[()]}")}, false},
|
||||
{"double inside single", `{[?(@.status.nodeInfo.osImage == "''")]}`,
|
||||
[]Node{newList(), newFilter(newList(), newList(), "=="), newList(), newField("status"), newField("nodeInfo"), newField("osImage"), newList(), newText("''")}, false},
|
||||
{"single inside double", `{[?(@.status.nodeInfo.osImage == '""')]}`,
|
||||
[]Node{newList(), newFilter(newList(), newList(), "=="), newList(), newField("status"), newField("nodeInfo"), newField("osImage"), newList(), newText("\"\"")}, false},
|
||||
{"single containing escaped single", `{[?(@.status.nodeInfo.osImage == '\\\'')]}`,
|
||||
[]Node{newList(), newFilter(newList(), newList(), "=="), newList(), newField("status"), newField("nodeInfo"), newField("osImage"), newList(), newText("\\'")}, false},
|
||||
}
|
||||
|
||||
func collectNode(nodes []Node, cur Node) []Node {
|
||||
nodes = append(nodes, cur)
|
||||
switch cur.Type() {
|
||||
case NodeList:
|
||||
for _, node := range cur.(*ListNode).Nodes {
|
||||
nodes = collectNode(nodes, node)
|
||||
}
|
||||
case NodeFilter:
|
||||
nodes = collectNode(nodes, cur.(*FilterNode).Left)
|
||||
nodes = collectNode(nodes, cur.(*FilterNode).Right)
|
||||
case NodeUnion:
|
||||
for _, node := range cur.(*UnionNode).Nodes {
|
||||
nodes = collectNode(nodes, node)
|
||||
}
|
||||
}
|
||||
return nodes
|
||||
}
|
||||
|
||||
func TestParser(t *testing.T) {
|
||||
for _, test := range parserTests {
|
||||
parser, err := Parse(test.name, test.text)
|
||||
if test.shouldError {
|
||||
if err == nil {
|
||||
t.Errorf("unexpected non-error when parsing %s", test.name)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("parse %s error %v", test.name, err)
|
||||
}
|
||||
result := collectNode([]Node{}, parser.Root)[1:]
|
||||
if len(result) != len(test.nodes) {
|
||||
t.Errorf("in %s, expect to get %d nodes, got %d nodes", test.name, len(test.nodes), len(result))
|
||||
t.Error(result)
|
||||
}
|
||||
for i, expect := range test.nodes {
|
||||
if result[i].String() != expect.String() {
|
||||
t.Errorf("in %s, %dth node, expect %v, got %v", test.name, i, expect, result[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type failParserTest struct {
|
||||
name string
|
||||
text string
|
||||
err string
|
||||
}
|
||||
|
||||
func TestFailParser(t *testing.T) {
|
||||
failParserTests := []failParserTest{
|
||||
{"unclosed action", "{.hello", "unclosed action"},
|
||||
{"unrecognized character", "{*}", "unrecognized character in action: U+002A '*'"},
|
||||
{"invalid number", "{+12.3.0}", "cannot parse number +12.3.0"},
|
||||
{"unterminated array", "{[1}", "unterminated array"},
|
||||
{"invalid index", "{[::-1]}", "invalid array index ::-1"},
|
||||
{"unterminated filter", "{[?(.price]}", "unterminated filter"},
|
||||
}
|
||||
for _, test := range failParserTests {
|
||||
_, err := Parse(test.name, test.text)
|
||||
var out string
|
||||
if err == nil {
|
||||
out = "nil"
|
||||
} else {
|
||||
out = err.Error()
|
||||
}
|
||||
if out != test.err {
|
||||
t.Errorf("in %s, expect to get error %v, got %v", test.name, test.err, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
71
vendor/k8s.io/client-go/util/retry/util_test.go
generated
vendored
71
vendor/k8s.io/client-go/util/retry/util_test.go
generated
vendored
|
|
@ -1,71 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package retry
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
)
|
||||
|
||||
func TestRetryOnConflict(t *testing.T) {
|
||||
opts := wait.Backoff{Factor: 1.0, Steps: 3}
|
||||
conflictErr := errors.NewConflict(schema.GroupResource{Resource: "test"}, "other", nil)
|
||||
|
||||
// never returns
|
||||
err := RetryOnConflict(opts, func() error {
|
||||
return conflictErr
|
||||
})
|
||||
if err != conflictErr {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// returns immediately
|
||||
i := 0
|
||||
err = RetryOnConflict(opts, func() error {
|
||||
i++
|
||||
return nil
|
||||
})
|
||||
if err != nil || i != 1 {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// returns immediately on error
|
||||
testErr := fmt.Errorf("some other error")
|
||||
err = RetryOnConflict(opts, func() error {
|
||||
return testErr
|
||||
})
|
||||
if err != testErr {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// keeps retrying
|
||||
i = 0
|
||||
err = RetryOnConflict(opts, func() error {
|
||||
if i < 2 {
|
||||
i++
|
||||
return errors.NewConflict(schema.GroupResource{Resource: "test"}, "other", nil)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil || i != 2 {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
184
vendor/k8s.io/client-go/util/workqueue/default_rate_limiters_test.go
generated
vendored
184
vendor/k8s.io/client-go/util/workqueue/default_rate_limiters_test.go
generated
vendored
|
|
@ -1,184 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package workqueue
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestItemExponentialFailureRateLimiter(t *testing.T) {
|
||||
limiter := NewItemExponentialFailureRateLimiter(1*time.Millisecond, 1*time.Second)
|
||||
|
||||
if e, a := 1*time.Millisecond, limiter.When("one"); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := 2*time.Millisecond, limiter.When("one"); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := 4*time.Millisecond, limiter.When("one"); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := 8*time.Millisecond, limiter.When("one"); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := 16*time.Millisecond, limiter.When("one"); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := 5, limiter.NumRequeues("one"); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
|
||||
if e, a := 1*time.Millisecond, limiter.When("two"); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := 2*time.Millisecond, limiter.When("two"); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := 2, limiter.NumRequeues("two"); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
|
||||
limiter.Forget("one")
|
||||
if e, a := 0, limiter.NumRequeues("one"); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := 1*time.Millisecond, limiter.When("one"); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestItemExponentialFailureRateLimiterOverFlow(t *testing.T) {
|
||||
limiter := NewItemExponentialFailureRateLimiter(1*time.Millisecond, 1000*time.Second)
|
||||
for i := 0; i < 5; i++ {
|
||||
limiter.When("one")
|
||||
}
|
||||
if e, a := 32*time.Millisecond, limiter.When("one"); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
|
||||
for i := 0; i < 1000; i++ {
|
||||
limiter.When("overflow1")
|
||||
}
|
||||
if e, a := 1000*time.Second, limiter.When("overflow1"); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
|
||||
limiter = NewItemExponentialFailureRateLimiter(1*time.Minute, 1000*time.Hour)
|
||||
for i := 0; i < 2; i++ {
|
||||
limiter.When("two")
|
||||
}
|
||||
if e, a := 4*time.Minute, limiter.When("two"); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
|
||||
for i := 0; i < 1000; i++ {
|
||||
limiter.When("overflow2")
|
||||
}
|
||||
if e, a := 1000*time.Hour, limiter.When("overflow2"); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestItemFastSlowRateLimiter(t *testing.T) {
|
||||
limiter := NewItemFastSlowRateLimiter(5*time.Millisecond, 10*time.Second, 3)
|
||||
|
||||
if e, a := 5*time.Millisecond, limiter.When("one"); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := 5*time.Millisecond, limiter.When("one"); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := 5*time.Millisecond, limiter.When("one"); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := 10*time.Second, limiter.When("one"); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := 10*time.Second, limiter.When("one"); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := 5, limiter.NumRequeues("one"); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
|
||||
if e, a := 5*time.Millisecond, limiter.When("two"); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := 5*time.Millisecond, limiter.When("two"); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := 2, limiter.NumRequeues("two"); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
|
||||
limiter.Forget("one")
|
||||
if e, a := 0, limiter.NumRequeues("one"); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := 5*time.Millisecond, limiter.When("one"); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestMaxOfRateLimiter(t *testing.T) {
|
||||
limiter := NewMaxOfRateLimiter(
|
||||
NewItemFastSlowRateLimiter(5*time.Millisecond, 3*time.Second, 3),
|
||||
NewItemExponentialFailureRateLimiter(1*time.Millisecond, 1*time.Second),
|
||||
)
|
||||
|
||||
if e, a := 5*time.Millisecond, limiter.When("one"); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := 5*time.Millisecond, limiter.When("one"); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := 5*time.Millisecond, limiter.When("one"); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := 3*time.Second, limiter.When("one"); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := 3*time.Second, limiter.When("one"); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := 5, limiter.NumRequeues("one"); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
|
||||
if e, a := 5*time.Millisecond, limiter.When("two"); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := 5*time.Millisecond, limiter.When("two"); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := 2, limiter.NumRequeues("two"); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
|
||||
limiter.Forget("one")
|
||||
if e, a := 0, limiter.NumRequeues("one"); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := 5*time.Millisecond, limiter.When("one"); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
|
||||
}
|
||||
255
vendor/k8s.io/client-go/util/workqueue/delaying_queue_test.go
generated
vendored
255
vendor/k8s.io/client-go/util/workqueue/delaying_queue_test.go
generated
vendored
|
|
@ -1,255 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package workqueue
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/clock"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
)
|
||||
|
||||
func TestSimpleQueue(t *testing.T) {
|
||||
fakeClock := clock.NewFakeClock(time.Now())
|
||||
q := newDelayingQueue(fakeClock, "")
|
||||
|
||||
first := "foo"
|
||||
|
||||
q.AddAfter(first, 50*time.Millisecond)
|
||||
if err := waitForWaitingQueueToFill(q); err != nil {
|
||||
t.Fatalf("unexpected err: %v", err)
|
||||
}
|
||||
|
||||
if q.Len() != 0 {
|
||||
t.Errorf("should not have added")
|
||||
}
|
||||
|
||||
fakeClock.Step(60 * time.Millisecond)
|
||||
|
||||
if err := waitForAdded(q, 1); err != nil {
|
||||
t.Errorf("should have added")
|
||||
}
|
||||
item, _ := q.Get()
|
||||
q.Done(item)
|
||||
|
||||
// step past the next heartbeat
|
||||
fakeClock.Step(10 * time.Second)
|
||||
|
||||
err := wait.Poll(1*time.Millisecond, 30*time.Millisecond, func() (done bool, err error) {
|
||||
if q.Len() > 0 {
|
||||
return false, fmt.Errorf("added to queue")
|
||||
}
|
||||
|
||||
return false, nil
|
||||
})
|
||||
if err != wait.ErrWaitTimeout {
|
||||
t.Errorf("expected timeout, got: %v", err)
|
||||
}
|
||||
|
||||
if q.Len() != 0 {
|
||||
t.Errorf("should not have added")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeduping(t *testing.T) {
|
||||
fakeClock := clock.NewFakeClock(time.Now())
|
||||
q := newDelayingQueue(fakeClock, "")
|
||||
|
||||
first := "foo"
|
||||
|
||||
q.AddAfter(first, 50*time.Millisecond)
|
||||
if err := waitForWaitingQueueToFill(q); err != nil {
|
||||
t.Fatalf("unexpected err: %v", err)
|
||||
}
|
||||
q.AddAfter(first, 70*time.Millisecond)
|
||||
if err := waitForWaitingQueueToFill(q); err != nil {
|
||||
t.Fatalf("unexpected err: %v", err)
|
||||
}
|
||||
if q.Len() != 0 {
|
||||
t.Errorf("should not have added")
|
||||
}
|
||||
|
||||
// step past the first block, we should receive now
|
||||
fakeClock.Step(60 * time.Millisecond)
|
||||
if err := waitForAdded(q, 1); err != nil {
|
||||
t.Errorf("should have added")
|
||||
}
|
||||
item, _ := q.Get()
|
||||
q.Done(item)
|
||||
|
||||
// step past the second add
|
||||
fakeClock.Step(20 * time.Millisecond)
|
||||
if q.Len() != 0 {
|
||||
t.Errorf("should not have added")
|
||||
}
|
||||
|
||||
// test again, but this time the earlier should override
|
||||
q.AddAfter(first, 50*time.Millisecond)
|
||||
q.AddAfter(first, 30*time.Millisecond)
|
||||
if err := waitForWaitingQueueToFill(q); err != nil {
|
||||
t.Fatalf("unexpected err: %v", err)
|
||||
}
|
||||
if q.Len() != 0 {
|
||||
t.Errorf("should not have added")
|
||||
}
|
||||
|
||||
fakeClock.Step(40 * time.Millisecond)
|
||||
if err := waitForAdded(q, 1); err != nil {
|
||||
t.Errorf("should have added")
|
||||
}
|
||||
item, _ = q.Get()
|
||||
q.Done(item)
|
||||
|
||||
// step past the second add
|
||||
fakeClock.Step(20 * time.Millisecond)
|
||||
if q.Len() != 0 {
|
||||
t.Errorf("should not have added")
|
||||
}
|
||||
if q.Len() != 0 {
|
||||
t.Errorf("should not have added")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddTwoFireEarly(t *testing.T) {
|
||||
fakeClock := clock.NewFakeClock(time.Now())
|
||||
q := newDelayingQueue(fakeClock, "")
|
||||
|
||||
first := "foo"
|
||||
second := "bar"
|
||||
third := "baz"
|
||||
|
||||
q.AddAfter(first, 1*time.Second)
|
||||
q.AddAfter(second, 50*time.Millisecond)
|
||||
if err := waitForWaitingQueueToFill(q); err != nil {
|
||||
t.Fatalf("unexpected err: %v", err)
|
||||
}
|
||||
|
||||
if q.Len() != 0 {
|
||||
t.Errorf("should not have added")
|
||||
}
|
||||
|
||||
fakeClock.Step(60 * time.Millisecond)
|
||||
|
||||
if err := waitForAdded(q, 1); err != nil {
|
||||
t.Fatalf("unexpected err: %v", err)
|
||||
}
|
||||
item, _ := q.Get()
|
||||
if !reflect.DeepEqual(item, second) {
|
||||
t.Errorf("expected %v, got %v", second, item)
|
||||
}
|
||||
|
||||
q.AddAfter(third, 2*time.Second)
|
||||
|
||||
fakeClock.Step(1 * time.Second)
|
||||
if err := waitForAdded(q, 1); err != nil {
|
||||
t.Fatalf("unexpected err: %v", err)
|
||||
}
|
||||
item, _ = q.Get()
|
||||
if !reflect.DeepEqual(item, first) {
|
||||
t.Errorf("expected %v, got %v", first, item)
|
||||
}
|
||||
|
||||
fakeClock.Step(2 * time.Second)
|
||||
if err := waitForAdded(q, 1); err != nil {
|
||||
t.Fatalf("unexpected err: %v", err)
|
||||
}
|
||||
item, _ = q.Get()
|
||||
if !reflect.DeepEqual(item, third) {
|
||||
t.Errorf("expected %v, got %v", third, item)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCopyShifting(t *testing.T) {
|
||||
fakeClock := clock.NewFakeClock(time.Now())
|
||||
q := newDelayingQueue(fakeClock, "")
|
||||
|
||||
first := "foo"
|
||||
second := "bar"
|
||||
third := "baz"
|
||||
|
||||
q.AddAfter(first, 1*time.Second)
|
||||
q.AddAfter(second, 500*time.Millisecond)
|
||||
q.AddAfter(third, 250*time.Millisecond)
|
||||
if err := waitForWaitingQueueToFill(q); err != nil {
|
||||
t.Fatalf("unexpected err: %v", err)
|
||||
}
|
||||
|
||||
if q.Len() != 0 {
|
||||
t.Errorf("should not have added")
|
||||
}
|
||||
|
||||
fakeClock.Step(2 * time.Second)
|
||||
|
||||
if err := waitForAdded(q, 3); err != nil {
|
||||
t.Fatalf("unexpected err: %v", err)
|
||||
}
|
||||
actualFirst, _ := q.Get()
|
||||
if !reflect.DeepEqual(actualFirst, third) {
|
||||
t.Errorf("expected %v, got %v", third, actualFirst)
|
||||
}
|
||||
actualSecond, _ := q.Get()
|
||||
if !reflect.DeepEqual(actualSecond, second) {
|
||||
t.Errorf("expected %v, got %v", second, actualSecond)
|
||||
}
|
||||
actualThird, _ := q.Get()
|
||||
if !reflect.DeepEqual(actualThird, first) {
|
||||
t.Errorf("expected %v, got %v", first, actualThird)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDelayingQueue_AddAfter(b *testing.B) {
|
||||
r := rand.New(rand.NewSource(time.Now().Unix()))
|
||||
|
||||
fakeClock := clock.NewFakeClock(time.Now())
|
||||
q := newDelayingQueue(fakeClock, "")
|
||||
|
||||
// Add items
|
||||
for n := 0; n < b.N; n++ {
|
||||
data := fmt.Sprintf("%d", n)
|
||||
q.AddAfter(data, time.Duration(r.Int63n(int64(10*time.Minute))))
|
||||
}
|
||||
|
||||
// Exercise item removal as well
|
||||
fakeClock.Step(11 * time.Minute)
|
||||
for n := 0; n < b.N; n++ {
|
||||
_, _ = q.Get()
|
||||
}
|
||||
}
|
||||
|
||||
func waitForAdded(q DelayingInterface, depth int) error {
|
||||
return wait.Poll(1*time.Millisecond, 10*time.Second, func() (done bool, err error) {
|
||||
if q.Len() == depth {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
})
|
||||
}
|
||||
|
||||
func waitForWaitingQueueToFill(q DelayingInterface) error {
|
||||
return wait.Poll(1*time.Millisecond, 10*time.Second, func() (done bool, err error) {
|
||||
if len(q.(*delayingType).waitingForAddCh) == 0 {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
})
|
||||
}
|
||||
161
vendor/k8s.io/client-go/util/workqueue/queue_test.go
generated
vendored
161
vendor/k8s.io/client-go/util/workqueue/queue_test.go
generated
vendored
|
|
@ -1,161 +0,0 @@
|
|||
/*
|
||||
Copyright 2015 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package workqueue_test
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
)
|
||||
|
||||
func TestBasic(t *testing.T) {
|
||||
// If something is seriously wrong this test will never complete.
|
||||
q := workqueue.New()
|
||||
|
||||
// Start producers
|
||||
const producers = 50
|
||||
producerWG := sync.WaitGroup{}
|
||||
producerWG.Add(producers)
|
||||
for i := 0; i < producers; i++ {
|
||||
go func(i int) {
|
||||
defer producerWG.Done()
|
||||
for j := 0; j < 50; j++ {
|
||||
q.Add(i)
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
}(i)
|
||||
}
|
||||
|
||||
// Start consumers
|
||||
const consumers = 10
|
||||
consumerWG := sync.WaitGroup{}
|
||||
consumerWG.Add(consumers)
|
||||
for i := 0; i < consumers; i++ {
|
||||
go func(i int) {
|
||||
defer consumerWG.Done()
|
||||
for {
|
||||
item, quit := q.Get()
|
||||
if item == "added after shutdown!" {
|
||||
t.Errorf("Got an item added after shutdown.")
|
||||
}
|
||||
if quit {
|
||||
return
|
||||
}
|
||||
t.Logf("Worker %v: begin processing %v", i, item)
|
||||
time.Sleep(3 * time.Millisecond)
|
||||
t.Logf("Worker %v: done processing %v", i, item)
|
||||
q.Done(item)
|
||||
}
|
||||
}(i)
|
||||
}
|
||||
|
||||
producerWG.Wait()
|
||||
q.ShutDown()
|
||||
q.Add("added after shutdown!")
|
||||
consumerWG.Wait()
|
||||
}
|
||||
|
||||
func TestAddWhileProcessing(t *testing.T) {
|
||||
q := workqueue.New()
|
||||
|
||||
// Start producers
|
||||
const producers = 50
|
||||
producerWG := sync.WaitGroup{}
|
||||
producerWG.Add(producers)
|
||||
for i := 0; i < producers; i++ {
|
||||
go func(i int) {
|
||||
defer producerWG.Done()
|
||||
q.Add(i)
|
||||
}(i)
|
||||
}
|
||||
|
||||
// Start consumers
|
||||
const consumers = 10
|
||||
consumerWG := sync.WaitGroup{}
|
||||
consumerWG.Add(consumers)
|
||||
for i := 0; i < consumers; i++ {
|
||||
go func(i int) {
|
||||
defer consumerWG.Done()
|
||||
// Every worker will re-add every item up to two times.
|
||||
// This tests the dirty-while-processing case.
|
||||
counters := map[interface{}]int{}
|
||||
for {
|
||||
item, quit := q.Get()
|
||||
if quit {
|
||||
return
|
||||
}
|
||||
counters[item]++
|
||||
if counters[item] < 2 {
|
||||
q.Add(item)
|
||||
}
|
||||
q.Done(item)
|
||||
}
|
||||
}(i)
|
||||
}
|
||||
|
||||
producerWG.Wait()
|
||||
q.ShutDown()
|
||||
consumerWG.Wait()
|
||||
}
|
||||
|
||||
func TestLen(t *testing.T) {
|
||||
q := workqueue.New()
|
||||
q.Add("foo")
|
||||
if e, a := 1, q.Len(); e != a {
|
||||
t.Errorf("Expected %v, got %v", e, a)
|
||||
}
|
||||
q.Add("bar")
|
||||
if e, a := 2, q.Len(); e != a {
|
||||
t.Errorf("Expected %v, got %v", e, a)
|
||||
}
|
||||
q.Add("foo") // should not increase the queue length.
|
||||
if e, a := 2, q.Len(); e != a {
|
||||
t.Errorf("Expected %v, got %v", e, a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReinsert(t *testing.T) {
|
||||
q := workqueue.New()
|
||||
q.Add("foo")
|
||||
|
||||
// Start processing
|
||||
i, _ := q.Get()
|
||||
if i != "foo" {
|
||||
t.Errorf("Expected %v, got %v", "foo", i)
|
||||
}
|
||||
|
||||
// Add it back while processing
|
||||
q.Add(i)
|
||||
|
||||
// Finish it up
|
||||
q.Done(i)
|
||||
|
||||
// It should be back on the queue
|
||||
i, _ = q.Get()
|
||||
if i != "foo" {
|
||||
t.Errorf("Expected %v, got %v", "foo", i)
|
||||
}
|
||||
|
||||
// Finish that one up
|
||||
q.Done(i)
|
||||
|
||||
if a := q.Len(); a != 0 {
|
||||
t.Errorf("Expected queue to be empty. Has %v items", a)
|
||||
}
|
||||
}
|
||||
75
vendor/k8s.io/client-go/util/workqueue/rate_limitting_queue_test.go
generated
vendored
75
vendor/k8s.io/client-go/util/workqueue/rate_limitting_queue_test.go
generated
vendored
|
|
@ -1,75 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package workqueue
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/clock"
|
||||
)
|
||||
|
||||
func TestRateLimitingQueue(t *testing.T) {
|
||||
limiter := NewItemExponentialFailureRateLimiter(1*time.Millisecond, 1*time.Second)
|
||||
queue := NewRateLimitingQueue(limiter).(*rateLimitingType)
|
||||
fakeClock := clock.NewFakeClock(time.Now())
|
||||
delayingQueue := &delayingType{
|
||||
Interface: New(),
|
||||
clock: fakeClock,
|
||||
heartbeat: fakeClock.Tick(maxWait),
|
||||
stopCh: make(chan struct{}),
|
||||
waitingForAddCh: make(chan *waitFor, 1000),
|
||||
metrics: newRetryMetrics(""),
|
||||
}
|
||||
queue.DelayingInterface = delayingQueue
|
||||
|
||||
queue.AddRateLimited("one")
|
||||
waitEntry := <-delayingQueue.waitingForAddCh
|
||||
if e, a := 1*time.Millisecond, waitEntry.readyAt.Sub(fakeClock.Now()); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
queue.AddRateLimited("one")
|
||||
waitEntry = <-delayingQueue.waitingForAddCh
|
||||
if e, a := 2*time.Millisecond, waitEntry.readyAt.Sub(fakeClock.Now()); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := 2, queue.NumRequeues("one"); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
|
||||
queue.AddRateLimited("two")
|
||||
waitEntry = <-delayingQueue.waitingForAddCh
|
||||
if e, a := 1*time.Millisecond, waitEntry.readyAt.Sub(fakeClock.Now()); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
queue.AddRateLimited("two")
|
||||
waitEntry = <-delayingQueue.waitingForAddCh
|
||||
if e, a := 2*time.Millisecond, waitEntry.readyAt.Sub(fakeClock.Now()); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
|
||||
queue.Forget("one")
|
||||
if e, a := 0, queue.NumRequeues("one"); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
queue.AddRateLimited("one")
|
||||
waitEntry = <-delayingQueue.waitingForAddCh
|
||||
if e, a := 1*time.Millisecond, waitEntry.readyAt.Sub(fakeClock.Now()); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue