Add generated file
This PR adds generated files under pkg/client and vendor folder.
This commit is contained in:
73
vendor/k8s.io/kubernetes/test/integration/BUILD
generated
vendored
Normal file
73
vendor/k8s.io/kubernetes/test/integration/BUILD
generated
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"doc.go",
|
||||
"utils.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/test/integration",
|
||||
deps = [
|
||||
"//vendor/github.com/coreos/etcd/clientv3:go_default_library",
|
||||
"//vendor/github.com/coreos/etcd/pkg/transport:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/storage/storagebackend:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes/typed/core/v1:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//test/integration/apiserver:all-srcs",
|
||||
"//test/integration/auth:all-srcs",
|
||||
"//test/integration/benchmark/jsonify:all-srcs",
|
||||
"//test/integration/client:all-srcs",
|
||||
"//test/integration/configmap:all-srcs",
|
||||
"//test/integration/daemonset:all-srcs",
|
||||
"//test/integration/defaulttolerationseconds:all-srcs",
|
||||
"//test/integration/deployment:all-srcs",
|
||||
"//test/integration/etcd:all-srcs",
|
||||
"//test/integration/evictions:all-srcs",
|
||||
"//test/integration/examples:all-srcs",
|
||||
"//test/integration/framework:all-srcs",
|
||||
"//test/integration/garbagecollector:all-srcs",
|
||||
"//test/integration/ipamperf:all-srcs",
|
||||
"//test/integration/master:all-srcs",
|
||||
"//test/integration/metrics:all-srcs",
|
||||
"//test/integration/objectmeta:all-srcs",
|
||||
"//test/integration/openshift:all-srcs",
|
||||
"//test/integration/pods:all-srcs",
|
||||
"//test/integration/quota:all-srcs",
|
||||
"//test/integration/replicaset:all-srcs",
|
||||
"//test/integration/replicationcontroller:all-srcs",
|
||||
"//test/integration/scale:all-srcs",
|
||||
"//test/integration/scheduler:all-srcs",
|
||||
"//test/integration/scheduler_perf:all-srcs",
|
||||
"//test/integration/secrets:all-srcs",
|
||||
"//test/integration/serviceaccount:all-srcs",
|
||||
"//test/integration/statefulset:all-srcs",
|
||||
"//test/integration/storageclasses:all-srcs",
|
||||
"//test/integration/tls:all-srcs",
|
||||
"//test/integration/ttlcontroller:all-srcs",
|
||||
"//test/integration/util:all-srcs",
|
||||
"//test/integration/volume:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
70
vendor/k8s.io/kubernetes/test/integration/apiserver/BUILD
generated
vendored
Normal file
70
vendor/k8s.io/kubernetes/test/integration/apiserver/BUILD
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
size = "large",
|
||||
srcs = [
|
||||
"apiserver_test.go",
|
||||
"main_test.go",
|
||||
"patch_test.go",
|
||||
"print_test.go",
|
||||
],
|
||||
tags = [
|
||||
"etcd",
|
||||
"integration",
|
||||
],
|
||||
deps = [
|
||||
"//pkg/api/legacyscheme:go_default_library",
|
||||
"//pkg/api/testapi:go_default_library",
|
||||
"//pkg/apis/core:go_default_library",
|
||||
"//pkg/kubectl/cmd/util:go_default_library",
|
||||
"//pkg/kubectl/genericclioptions:go_default_library",
|
||||
"//pkg/master:go_default_library",
|
||||
"//pkg/printers:go_default_library",
|
||||
"//pkg/printers/internalversion:go_default_library",
|
||||
"//test/integration/framework:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/github.com/pborman/uuid:go_default_library",
|
||||
"//vendor/k8s.io/api/batch/v2alpha1:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/api/extensions/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/api/rbac/v1alpha1:go_default_library",
|
||||
"//vendor/k8s.io/api/scheduling/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/api/settings/v1alpha1:go_default_library",
|
||||
"//vendor/k8s.io/api/storage/v1alpha1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/features:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||
"//vendor/k8s.io/client-go/discovery:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/clientcmd:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/clientcmd/api:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/pager:go_default_library",
|
||||
"//vendor/k8s.io/gengo/examples/set-gen/sets:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
323
vendor/k8s.io/kubernetes/test/integration/apiserver/apiserver_test.go
generated
vendored
Normal file
323
vendor/k8s.io/kubernetes/test/integration/apiserver/apiserver_test.go
generated
vendored
Normal file
@@ -0,0 +1,323 @@
|
||||
/*
|
||||
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 apiserver
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/api/extensions/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
genericfeatures "k8s.io/apiserver/pkg/features"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/pager"
|
||||
"k8s.io/kubernetes/pkg/api/testapi"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/master"
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
)
|
||||
|
||||
func setup(t *testing.T, groupVersions ...schema.GroupVersion) (*httptest.Server, clientset.Interface, framework.CloseFunc) {
|
||||
masterConfig := framework.NewIntegrationTestMasterConfig()
|
||||
if len(groupVersions) > 0 {
|
||||
resourceConfig := master.DefaultAPIResourceConfigSource()
|
||||
resourceConfig.EnableVersions(groupVersions...)
|
||||
masterConfig.ExtraConfig.APIResourceConfigSource = resourceConfig
|
||||
}
|
||||
_, s, closeFn := framework.RunAMaster(masterConfig)
|
||||
|
||||
clientSet, err := clientset.NewForConfig(&restclient.Config{Host: s.URL})
|
||||
if err != nil {
|
||||
t.Fatalf("Error in create clientset: %v", err)
|
||||
}
|
||||
return s, clientSet, closeFn
|
||||
}
|
||||
|
||||
func verifyStatusCode(t *testing.T, verb, URL, body string, expectedStatusCode int) {
|
||||
// We dont use the typed Go client to send this request to be able to verify the response status code.
|
||||
bodyBytes := bytes.NewReader([]byte(body))
|
||||
req, err := http.NewRequest(verb, URL, bodyBytes)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v in sending req with verb: %s, URL: %s and body: %s", err, verb, URL, body)
|
||||
}
|
||||
transport := http.DefaultTransport
|
||||
glog.Infof("Sending request: %v", req)
|
||||
resp, err := transport.RoundTrip(req)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v in req: %v", err, req)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
b, _ := ioutil.ReadAll(resp.Body)
|
||||
if resp.StatusCode != expectedStatusCode {
|
||||
t.Errorf("Expected status %v, but got %v", expectedStatusCode, resp.StatusCode)
|
||||
t.Errorf("Body: %v", string(b))
|
||||
}
|
||||
}
|
||||
|
||||
func path(resource, namespace, name string) string {
|
||||
return testapi.Extensions.ResourcePath(resource, namespace, name)
|
||||
}
|
||||
|
||||
func newRS(namespace string) *v1beta1.ReplicaSet {
|
||||
return &v1beta1.ReplicaSet{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "ReplicaSet",
|
||||
APIVersion: "extensions/v1beta1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: namespace,
|
||||
GenerateName: "apiserver-test",
|
||||
},
|
||||
Spec: v1beta1.ReplicaSetSpec{
|
||||
Template: v1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: map[string]string{"name": "test"},
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Name: "fake-name",
|
||||
Image: "fakeimage",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
var cascDel = `
|
||||
{
|
||||
"kind": "DeleteOptions",
|
||||
"apiVersion": "` + testapi.Groups[api.GroupName].GroupVersion().String() + `",
|
||||
"orphanDependents": false
|
||||
}
|
||||
`
|
||||
|
||||
// Tests that the apiserver returns 202 status code as expected.
|
||||
func Test202StatusCode(t *testing.T) {
|
||||
s, clientSet, closeFn := setup(t)
|
||||
defer closeFn()
|
||||
|
||||
ns := framework.CreateTestingNamespace("status-code", s, t)
|
||||
defer framework.DeleteTestingNamespace(ns, s, t)
|
||||
|
||||
rsClient := clientSet.ExtensionsV1beta1().ReplicaSets(ns.Name)
|
||||
|
||||
// 1. Create the resource without any finalizer and then delete it without setting DeleteOptions.
|
||||
// Verify that server returns 200 in this case.
|
||||
rs, err := rsClient.Create(newRS(ns.Name))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create rs: %v", err)
|
||||
}
|
||||
verifyStatusCode(t, "DELETE", s.URL+path("replicasets", ns.Name, rs.Name), "", 200)
|
||||
|
||||
// 2. Create the resource with a finalizer so that the resource is not immediately deleted and then delete it without setting DeleteOptions.
|
||||
// Verify that the apiserver still returns 200 since DeleteOptions.OrphanDependents is not set.
|
||||
rs = newRS(ns.Name)
|
||||
rs.ObjectMeta.Finalizers = []string{"kube.io/dummy-finalizer"}
|
||||
rs, err = rsClient.Create(rs)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create rs: %v", err)
|
||||
}
|
||||
verifyStatusCode(t, "DELETE", s.URL+path("replicasets", ns.Name, rs.Name), "", 200)
|
||||
|
||||
// 3. Create the resource and then delete it with DeleteOptions.OrphanDependents=false.
|
||||
// Verify that the server still returns 200 since the resource is immediately deleted.
|
||||
rs = newRS(ns.Name)
|
||||
rs, err = rsClient.Create(rs)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create rs: %v", err)
|
||||
}
|
||||
verifyStatusCode(t, "DELETE", s.URL+path("replicasets", ns.Name, rs.Name), cascDel, 200)
|
||||
|
||||
// 4. Create the resource with a finalizer so that the resource is not immediately deleted and then delete it with DeleteOptions.OrphanDependents=false.
|
||||
// Verify that the server returns 202 in this case.
|
||||
rs = newRS(ns.Name)
|
||||
rs.ObjectMeta.Finalizers = []string{"kube.io/dummy-finalizer"}
|
||||
rs, err = rsClient.Create(rs)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create rs: %v", err)
|
||||
}
|
||||
verifyStatusCode(t, "DELETE", s.URL+path("replicasets", ns.Name, rs.Name), cascDel, 202)
|
||||
}
|
||||
|
||||
func TestAPIListChunking(t *testing.T) {
|
||||
if err := utilfeature.DefaultFeatureGate.Set(string(genericfeatures.APIListChunking) + "=true"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
s, clientSet, closeFn := setup(t)
|
||||
defer closeFn()
|
||||
|
||||
ns := framework.CreateTestingNamespace("list-paging", s, t)
|
||||
defer framework.DeleteTestingNamespace(ns, s, t)
|
||||
|
||||
rsClient := clientSet.ExtensionsV1beta1().ReplicaSets(ns.Name)
|
||||
|
||||
for i := 0; i < 4; i++ {
|
||||
rs := newRS(ns.Name)
|
||||
rs.Name = fmt.Sprintf("test-%d", i)
|
||||
if _, err := rsClient.Create(rs); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
calls := 0
|
||||
firstRV := ""
|
||||
p := &pager.ListPager{
|
||||
PageSize: 1,
|
||||
PageFn: pager.SimplePageFunc(func(opts metav1.ListOptions) (runtime.Object, error) {
|
||||
calls++
|
||||
list, err := rsClient.List(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if calls == 1 {
|
||||
firstRV = list.ResourceVersion
|
||||
}
|
||||
if calls == 2 {
|
||||
rs := newRS(ns.Name)
|
||||
rs.Name = "test-5"
|
||||
if _, err := rsClient.Create(rs); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
return list, err
|
||||
}),
|
||||
}
|
||||
listObj, err := p.List(context.Background(), metav1.ListOptions{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if calls != 4 {
|
||||
t.Errorf("unexpected list invocations: %d", calls)
|
||||
}
|
||||
list := listObj.(metav1.ListInterface)
|
||||
if len(list.GetContinue()) != 0 {
|
||||
t.Errorf("unexpected continue: %s", list.GetContinue())
|
||||
}
|
||||
if list.GetResourceVersion() != firstRV {
|
||||
t.Errorf("unexpected resource version: %s instead of %s", list.GetResourceVersion(), firstRV)
|
||||
}
|
||||
var names []string
|
||||
if err := meta.EachListItem(listObj, func(obj runtime.Object) error {
|
||||
rs := obj.(*v1beta1.ReplicaSet)
|
||||
names = append(names, rs.Name)
|
||||
return nil
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(names, []string{"test-0", "test-1", "test-2", "test-3"}) {
|
||||
t.Errorf("unexpected items: %#v", list)
|
||||
}
|
||||
}
|
||||
|
||||
func makeSecret(name string) *v1.Secret {
|
||||
return &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"key": []byte("value"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestNameInFieldSelector(t *testing.T) {
|
||||
s, clientSet, closeFn := setup(t)
|
||||
defer closeFn()
|
||||
|
||||
numNamespaces := 3
|
||||
namespaces := make([]*v1.Namespace, 0, numNamespaces)
|
||||
for i := 0; i < 3; i++ {
|
||||
ns := framework.CreateTestingNamespace(fmt.Sprintf("ns%d", i), s, t)
|
||||
defer framework.DeleteTestingNamespace(ns, s, t)
|
||||
namespaces = append(namespaces, ns)
|
||||
|
||||
_, err := clientSet.CoreV1().Secrets(ns.Name).Create(makeSecret("foo"))
|
||||
if err != nil {
|
||||
t.Errorf("Couldn't create secret: %v", err)
|
||||
}
|
||||
_, err = clientSet.CoreV1().Secrets(ns.Name).Create(makeSecret("bar"))
|
||||
if err != nil {
|
||||
t.Errorf("Couldn't create secret: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
testcases := []struct {
|
||||
namespace string
|
||||
selector string
|
||||
expectedSecrets int
|
||||
}{
|
||||
{
|
||||
namespace: "",
|
||||
selector: "metadata.name=foo",
|
||||
expectedSecrets: numNamespaces,
|
||||
},
|
||||
{
|
||||
namespace: "",
|
||||
selector: "metadata.name=foo,metadata.name=bar",
|
||||
expectedSecrets: 0,
|
||||
},
|
||||
{
|
||||
namespace: "",
|
||||
selector: "metadata.name=foo,metadata.namespace=ns1",
|
||||
expectedSecrets: 1,
|
||||
},
|
||||
{
|
||||
namespace: "ns1",
|
||||
selector: "metadata.name=foo,metadata.namespace=ns1",
|
||||
expectedSecrets: 1,
|
||||
},
|
||||
{
|
||||
namespace: "ns1",
|
||||
selector: "metadata.name=foo,metadata.namespace=ns2",
|
||||
expectedSecrets: 0,
|
||||
},
|
||||
{
|
||||
namespace: "ns1",
|
||||
selector: "metadata.name=foo,metadata.namespace=",
|
||||
expectedSecrets: 0,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
opts := metav1.ListOptions{
|
||||
FieldSelector: tc.selector,
|
||||
}
|
||||
secrets, err := clientSet.CoreV1().Secrets(tc.namespace).List(opts)
|
||||
if err != nil {
|
||||
t.Errorf("%s: Unexpected error: %v", tc.selector, err)
|
||||
}
|
||||
if len(secrets.Items) != tc.expectedSecrets {
|
||||
t.Errorf("%s: Unexpected number of secrets: %d, expected: %d", tc.selector, len(secrets.Items), tc.expectedSecrets)
|
||||
}
|
||||
}
|
||||
}
|
||||
27
vendor/k8s.io/kubernetes/test/integration/apiserver/main_test.go
generated
vendored
Normal file
27
vendor/k8s.io/kubernetes/test/integration/apiserver/main_test.go
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
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 apiserver
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
framework.EtcdMain(m.Run)
|
||||
}
|
||||
138
vendor/k8s.io/kubernetes/test/integration/apiserver/patch_test.go
generated
vendored
Normal file
138
vendor/k8s.io/kubernetes/test/integration/apiserver/patch_test.go
generated
vendored
Normal file
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
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 apiserver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
|
||||
"github.com/pborman/uuid"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
)
|
||||
|
||||
// Tests that the apiserver retries patches
|
||||
func TestPatchConflicts(t *testing.T) {
|
||||
s, clientSet, closeFn := setup(t)
|
||||
defer closeFn()
|
||||
|
||||
ns := framework.CreateTestingNamespace("status-code", s, t)
|
||||
defer framework.DeleteTestingNamespace(ns, s, t)
|
||||
|
||||
numOfConcurrentPatches := 100
|
||||
|
||||
UIDs := make([]types.UID, numOfConcurrentPatches)
|
||||
ownerRefs := []metav1.OwnerReference{}
|
||||
for i := 0; i < numOfConcurrentPatches; i++ {
|
||||
uid := types.UID(uuid.NewRandom().String())
|
||||
ownerName := fmt.Sprintf("owner-%d", i)
|
||||
UIDs[i] = uid
|
||||
ownerRefs = append(ownerRefs, metav1.OwnerReference{
|
||||
APIVersion: "example.com/v1",
|
||||
Kind: "Foo",
|
||||
Name: ownerName,
|
||||
UID: uid,
|
||||
})
|
||||
}
|
||||
secret := &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test",
|
||||
OwnerReferences: ownerRefs,
|
||||
},
|
||||
}
|
||||
|
||||
// Create the object we're going to conflict on
|
||||
clientSet.CoreV1().Secrets(ns.Name).Create(secret)
|
||||
client := clientSet.CoreV1().RESTClient()
|
||||
|
||||
successes := int32(0)
|
||||
|
||||
// Run a lot of simultaneous patch operations to exercise internal API server retry of application of patches that do not specify resourceVersion.
|
||||
// They should all succeed.
|
||||
wg := sync.WaitGroup{}
|
||||
for i := 0; i < numOfConcurrentPatches; i++ {
|
||||
wg.Add(1)
|
||||
go func(i int) {
|
||||
defer wg.Done()
|
||||
labelName := fmt.Sprintf("label-%d", i)
|
||||
value := uuid.NewRandom().String()
|
||||
|
||||
obj, err := client.Patch(types.StrategicMergePatchType).
|
||||
Namespace(ns.Name).
|
||||
Resource("secrets").
|
||||
Name("test").
|
||||
Body([]byte(fmt.Sprintf(`{"metadata":{"labels":{"%s":"%s"}, "ownerReferences":[{"$patch":"delete","uid":"%s"}]}}`, labelName, value, UIDs[i]))).
|
||||
Do().
|
||||
Get()
|
||||
|
||||
if errors.IsConflict(err) {
|
||||
t.Logf("tolerated conflict error patching %s: %v", "secrets", err)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("error patching %s: %v", "secrets", err)
|
||||
return
|
||||
}
|
||||
|
||||
accessor, err := meta.Accessor(obj)
|
||||
if err != nil {
|
||||
t.Errorf("error getting object from %s: %v", "secrets", err)
|
||||
return
|
||||
}
|
||||
// make sure the label we wanted was effective
|
||||
if accessor.GetLabels()[labelName] != value {
|
||||
t.Errorf("patch of %s was ineffective, expected %s=%s, got labels %#v", "secrets", labelName, value, accessor.GetLabels())
|
||||
return
|
||||
}
|
||||
// make sure the patch directive didn't get lost, and that an entry in the ownerReference list was deleted.
|
||||
found := findOwnerRefByUID(accessor.GetOwnerReferences(), UIDs[i])
|
||||
if err != nil {
|
||||
t.Errorf("%v", err)
|
||||
return
|
||||
}
|
||||
if found {
|
||||
t.Errorf("patch of %s with $patch directive was ineffective, didn't delete the entry in the ownerReference slice: %#v", "secrets", UIDs[i])
|
||||
}
|
||||
|
||||
atomic.AddInt32(&successes, 1)
|
||||
}(i)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
if successes < int32(numOfConcurrentPatches) {
|
||||
t.Errorf("Expected at least %d successful patches for %s, got %d", numOfConcurrentPatches, "secrets", successes)
|
||||
} else {
|
||||
t.Logf("Got %d successful patches for %s", successes, "secrets")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func findOwnerRefByUID(ownerRefs []metav1.OwnerReference, uid types.UID) bool {
|
||||
for _, of := range ownerRefs {
|
||||
if of.UID == uid {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
304
vendor/k8s.io/kubernetes/test/integration/apiserver/print_test.go
generated
vendored
Normal file
304
vendor/k8s.io/kubernetes/test/integration/apiserver/print_test.go
generated
vendored
Normal file
@@ -0,0 +1,304 @@
|
||||
/*
|
||||
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 apiserver
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
batchv2alpha1 "k8s.io/api/batch/v2alpha1"
|
||||
rbacv1alpha1 "k8s.io/api/rbac/v1alpha1"
|
||||
schedulerapi "k8s.io/api/scheduling/v1beta1"
|
||||
settingsv1alpha1 "k8s.io/api/settings/v1alpha1"
|
||||
storagev1alpha1 "k8s.io/api/storage/v1alpha1"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/discovery"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
"k8s.io/gengo/examples/set-gen/sets"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
|
||||
"k8s.io/kubernetes/pkg/printers"
|
||||
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
)
|
||||
|
||||
var kindWhiteList = sets.NewString(
|
||||
// k8s.io/api/core
|
||||
"APIGroup",
|
||||
"APIVersions",
|
||||
"Binding",
|
||||
"DeleteOptions",
|
||||
"ExportOptions",
|
||||
"GetOptions",
|
||||
"ListOptions",
|
||||
"NodeProxyOptions",
|
||||
"PodAttachOptions",
|
||||
"PodExecOptions",
|
||||
"PodPortForwardOptions",
|
||||
"PodLogOptions",
|
||||
"PodProxyOptions",
|
||||
"PodStatusResult",
|
||||
"RangeAllocation",
|
||||
"ServiceProxyOptions",
|
||||
"SerializedReference",
|
||||
// --
|
||||
|
||||
// k8s.io/api/admission
|
||||
"AdmissionReview",
|
||||
// --
|
||||
|
||||
// k8s.io/api/admissionregistration
|
||||
"InitializerConfiguration",
|
||||
// --
|
||||
|
||||
// k8s.io/api/authentication
|
||||
"TokenRequest",
|
||||
"TokenReview",
|
||||
// --
|
||||
|
||||
// k8s.io/api/authorization
|
||||
"LocalSubjectAccessReview",
|
||||
"SelfSubjectAccessReview",
|
||||
"SelfSubjectRulesReview",
|
||||
"SubjectAccessReview",
|
||||
// --
|
||||
|
||||
// k8s.io/api/autoscaling
|
||||
"Scale",
|
||||
// --
|
||||
|
||||
// k8s.io/api/apps
|
||||
"DeploymentRollback",
|
||||
// --
|
||||
|
||||
// k8s.io/api/batch
|
||||
"JobTemplate",
|
||||
// --
|
||||
|
||||
// k8s.io/api/extensions
|
||||
"ReplicationControllerDummy",
|
||||
// --
|
||||
|
||||
// k8s.io/api/imagepolicy
|
||||
"ImageReview",
|
||||
// --
|
||||
|
||||
// k8s.io/api/policy
|
||||
"Eviction",
|
||||
// --
|
||||
|
||||
// k8s.io/kubernetes/pkg/apis/componentconfig
|
||||
"KubeSchedulerConfiguration",
|
||||
// --
|
||||
|
||||
// k8s.io/apimachinery/pkg/apis/meta
|
||||
"WatchEvent",
|
||||
"Status",
|
||||
// --
|
||||
)
|
||||
|
||||
// TODO (soltysh): this list has to go down to 0!
|
||||
var missingHanlders = sets.NewString(
|
||||
"ClusterRole",
|
||||
"LimitRange",
|
||||
"MutatingWebhookConfiguration",
|
||||
"ResourceQuota",
|
||||
"Role",
|
||||
"ValidatingWebhookConfiguration",
|
||||
"VolumeAttachment",
|
||||
"PriorityClass",
|
||||
"PodPreset",
|
||||
)
|
||||
|
||||
func TestServerSidePrint(t *testing.T) {
|
||||
s, _, closeFn := setup(t,
|
||||
// additional groupversions needed for the test to run
|
||||
batchv2alpha1.SchemeGroupVersion,
|
||||
rbacv1alpha1.SchemeGroupVersion,
|
||||
settingsv1alpha1.SchemeGroupVersion,
|
||||
schedulerapi.SchemeGroupVersion,
|
||||
storagev1alpha1.SchemeGroupVersion)
|
||||
defer closeFn()
|
||||
|
||||
ns := framework.CreateTestingNamespace("server-print", s, t)
|
||||
defer framework.DeleteTestingNamespace(ns, s, t)
|
||||
|
||||
tableParam := fmt.Sprintf("application/json;as=Table;g=%s;v=%s, application/json", metav1beta1.GroupName, metav1beta1.SchemeGroupVersion.Version)
|
||||
printer := newFakePrinter(printersinternal.AddHandlers)
|
||||
|
||||
configFlags := genericclioptions.NewTestConfigFlags().
|
||||
WithClientConfig(clientcmd.NewDefaultClientConfig(*createKubeConfig(s.URL), &clientcmd.ConfigOverrides{}))
|
||||
|
||||
restConfig, err := configFlags.ToRESTConfig()
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
cacheDir, err := ioutil.TempDir(os.TempDir(), "test-integration-apiserver-print")
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
defer func() {
|
||||
os.Remove(cacheDir)
|
||||
}()
|
||||
|
||||
cachedClient, err := discovery.NewCachedDiscoveryClientForConfig(restConfig, cacheDir, "", time.Duration(10*time.Minute))
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
configFlags.WithDiscoveryClient(cachedClient)
|
||||
|
||||
factory := util.NewFactory(configFlags)
|
||||
mapper, err := factory.ToRESTMapper()
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error getting mapper: %v", err)
|
||||
return
|
||||
}
|
||||
for gvk, apiType := range legacyscheme.Scheme.AllKnownTypes() {
|
||||
// we do not care about internal objects or lists // TODO make sure this is always true
|
||||
if gvk.Version == runtime.APIVersionInternal || strings.HasSuffix(apiType.Name(), "List") {
|
||||
continue
|
||||
}
|
||||
if kindWhiteList.Has(gvk.Kind) || missingHanlders.Has(gvk.Kind) {
|
||||
continue
|
||||
}
|
||||
|
||||
t.Logf("Checking %s", gvk)
|
||||
// read table definition as returned by the server
|
||||
mapping, err := mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error getting mapping for GVK %s: %v", gvk, err)
|
||||
continue
|
||||
}
|
||||
client, err := factory.ClientForMapping(mapping)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error getting client for GVK %s: %v", gvk, err)
|
||||
continue
|
||||
}
|
||||
req := client.Get()
|
||||
if mapping.Scope.Name() == meta.RESTScopeNameNamespace {
|
||||
req = req.Namespace(ns.Name)
|
||||
}
|
||||
body, err := req.Resource(mapping.Resource.Resource).SetHeader("Accept", tableParam).Do().Raw()
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error getting %s: %v", gvk, err)
|
||||
continue
|
||||
}
|
||||
actual, err := decodeIntoTable(body)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error decoding %s: %v", gvk, err)
|
||||
continue
|
||||
}
|
||||
|
||||
// get table definition used in printers
|
||||
obj, err := legacyscheme.Scheme.New(gvk)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error creating %s: %v", gvk, err)
|
||||
continue
|
||||
}
|
||||
intGV := gvk.GroupKind().WithVersion(runtime.APIVersionInternal).GroupVersion()
|
||||
intObj, err := legacyscheme.Scheme.ConvertToVersion(obj, intGV)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error converting %s to internal: %v", gvk, err)
|
||||
continue
|
||||
}
|
||||
expectedColumnDefinitions, ok := printer.handlers[reflect.TypeOf(intObj)]
|
||||
if !ok {
|
||||
t.Errorf("missing handler for type %v", gvk)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, e := range expectedColumnDefinitions {
|
||||
for _, a := range actual.ColumnDefinitions {
|
||||
if a.Name == e.Name && !reflect.DeepEqual(a, e) {
|
||||
t.Errorf("unexpected difference in column definition %s for %s:\nexpected:\n%#v\nactual:\n%#v\n", e.Name, gvk, e, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type fakePrinter struct {
|
||||
handlers map[reflect.Type][]metav1beta1.TableColumnDefinition
|
||||
}
|
||||
|
||||
var _ printers.PrintHandler = &fakePrinter{}
|
||||
|
||||
func (f *fakePrinter) Handler(columns, columnsWithWide []string, printFunc interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *fakePrinter) TableHandler(columns []metav1beta1.TableColumnDefinition, printFunc interface{}) error {
|
||||
printFuncValue := reflect.ValueOf(printFunc)
|
||||
objType := printFuncValue.Type().In(0)
|
||||
f.handlers[objType] = columns
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *fakePrinter) DefaultTableHandler(columns []metav1beta1.TableColumnDefinition, printFunc interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func newFakePrinter(fns ...func(printers.PrintHandler)) *fakePrinter {
|
||||
handlers := make(map[reflect.Type][]metav1beta1.TableColumnDefinition, len(fns))
|
||||
p := &fakePrinter{handlers: handlers}
|
||||
for _, fn := range fns {
|
||||
fn(p)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func decodeIntoTable(body []byte) (*metav1beta1.Table, error) {
|
||||
table := &metav1beta1.Table{}
|
||||
err := json.Unmarshal(body, table)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return table, nil
|
||||
}
|
||||
|
||||
func createKubeConfig(url string) *clientcmdapi.Config {
|
||||
clusterNick := "cluster"
|
||||
userNick := "user"
|
||||
contextNick := "context"
|
||||
|
||||
config := clientcmdapi.NewConfig()
|
||||
|
||||
cluster := clientcmdapi.NewCluster()
|
||||
cluster.Server = url
|
||||
cluster.InsecureSkipTLSVerify = true
|
||||
config.Clusters[clusterNick] = cluster
|
||||
|
||||
context := clientcmdapi.NewContext()
|
||||
context.Cluster = clusterNick
|
||||
context.AuthInfo = userNick
|
||||
config.Contexts[contextNick] = context
|
||||
config.CurrentContext = contextNick
|
||||
|
||||
return config
|
||||
}
|
||||
101
vendor/k8s.io/kubernetes/test/integration/auth/BUILD
generated
vendored
Normal file
101
vendor/k8s.io/kubernetes/test/integration/auth/BUILD
generated
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
size = "large",
|
||||
srcs = [
|
||||
"accessreview_test.go",
|
||||
"auth_test.go",
|
||||
"bootstraptoken_test.go",
|
||||
"main_test.go",
|
||||
"node_test.go",
|
||||
"rbac_test.go",
|
||||
"svcaccttoken_test.go",
|
||||
],
|
||||
tags = ["integration"],
|
||||
deps = [
|
||||
"//pkg/api/legacyscheme:go_default_library",
|
||||
"//pkg/api/testapi:go_default_library",
|
||||
"//pkg/apis/authorization:go_default_library",
|
||||
"//pkg/apis/autoscaling:go_default_library",
|
||||
"//pkg/apis/core:go_default_library",
|
||||
"//pkg/apis/extensions:go_default_library",
|
||||
"//pkg/apis/policy:go_default_library",
|
||||
"//pkg/apis/rbac:go_default_library",
|
||||
"//pkg/auth/authorizer/abac:go_default_library",
|
||||
"//pkg/auth/nodeidentifier:go_default_library",
|
||||
"//pkg/client/clientset_generated/internalclientset:go_default_library",
|
||||
"//pkg/client/informers/informers_generated/internalversion:go_default_library",
|
||||
"//pkg/controller/serviceaccount:go_default_library",
|
||||
"//pkg/features:go_default_library",
|
||||
"//pkg/kubeapiserver/authorizer:go_default_library",
|
||||
"//pkg/master:go_default_library",
|
||||
"//pkg/registry/rbac/clusterrole:go_default_library",
|
||||
"//pkg/registry/rbac/clusterrole/storage:go_default_library",
|
||||
"//pkg/registry/rbac/clusterrolebinding:go_default_library",
|
||||
"//pkg/registry/rbac/clusterrolebinding/storage:go_default_library",
|
||||
"//pkg/registry/rbac/role:go_default_library",
|
||||
"//pkg/registry/rbac/role/storage:go_default_library",
|
||||
"//pkg/registry/rbac/rolebinding:go_default_library",
|
||||
"//pkg/registry/rbac/rolebinding/storage:go_default_library",
|
||||
"//pkg/serviceaccount:go_default_library",
|
||||
"//plugin/pkg/admission/noderestriction:go_default_library",
|
||||
"//plugin/pkg/auth/authenticator/token/bootstrap:go_default_library",
|
||||
"//plugin/pkg/auth/authorizer/rbac:go_default_library",
|
||||
"//test/e2e/lifecycle/bootstrap:go_default_library",
|
||||
"//test/integration:go_default_library",
|
||||
"//test/integration/framework:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/gopkg.in/square/go-jose.v2/jwt:go_default_library",
|
||||
"//vendor/k8s.io/api/authentication/v1:go_default_library",
|
||||
"//vendor/k8s.io/api/authentication/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/api/storage/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/watch:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authentication/authenticator:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authentication/group:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authentication/request/bearertoken:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authentication/serviceaccount:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authentication/token/tokenfile:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authentication/user:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authorization/authorizerfactory:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/util/feature/testing:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/plugin/pkg/authenticator/token/tokentest:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/plugin/pkg/authenticator/token/webhook:go_default_library",
|
||||
"//vendor/k8s.io/client-go/informers:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/bootstrap/token/api:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/clientcmd/api/v1:go_default_library",
|
||||
"//vendor/k8s.io/client-go/transport:go_default_library",
|
||||
"//vendor/k8s.io/client-go/util/cert:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
343
vendor/k8s.io/kubernetes/test/integration/auth/accessreview_test.go
generated
vendored
Normal file
343
vendor/k8s.io/kubernetes/test/integration/auth/accessreview_test.go
generated
vendored
Normal file
@@ -0,0 +1,343 @@
|
||||
/*
|
||||
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 auth
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apiserver/pkg/authentication/authenticator"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/kubernetes/pkg/api/testapi"
|
||||
authorizationapi "k8s.io/kubernetes/pkg/apis/authorization"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
)
|
||||
|
||||
// Inject into master an authorizer that uses user info.
|
||||
// TODO(etune): remove this test once a more comprehensive built-in authorizer is implemented.
|
||||
type sarAuthorizer struct{}
|
||||
|
||||
func (sarAuthorizer) Authorize(a authorizer.Attributes) (authorizer.Decision, string, error) {
|
||||
if a.GetUser().GetName() == "dave" {
|
||||
return authorizer.DecisionNoOpinion, "no", errors.New("I'm sorry, Dave")
|
||||
}
|
||||
|
||||
return authorizer.DecisionAllow, "you're not dave", nil
|
||||
}
|
||||
|
||||
func alwaysAlice(req *http.Request) (user.Info, bool, error) {
|
||||
return &user.DefaultInfo{
|
||||
Name: "alice",
|
||||
}, true, nil
|
||||
}
|
||||
|
||||
func TestSubjectAccessReview(t *testing.T) {
|
||||
masterConfig := framework.NewIntegrationTestMasterConfig()
|
||||
masterConfig.GenericConfig.Authentication.Authenticator = authenticator.RequestFunc(alwaysAlice)
|
||||
masterConfig.GenericConfig.Authorization.Authorizer = sarAuthorizer{}
|
||||
_, s, closeFn := framework.RunAMaster(masterConfig)
|
||||
defer closeFn()
|
||||
|
||||
clientset := clientset.NewForConfigOrDie(&restclient.Config{Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: testapi.Groups[api.GroupName].GroupVersion()}})
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
sar *authorizationapi.SubjectAccessReview
|
||||
expectedError string
|
||||
expectedStatus authorizationapi.SubjectAccessReviewStatus
|
||||
}{
|
||||
{
|
||||
name: "simple allow",
|
||||
sar: &authorizationapi.SubjectAccessReview{
|
||||
Spec: authorizationapi.SubjectAccessReviewSpec{
|
||||
ResourceAttributes: &authorizationapi.ResourceAttributes{
|
||||
Verb: "list",
|
||||
Group: api.GroupName,
|
||||
Version: "v1",
|
||||
Resource: "pods",
|
||||
},
|
||||
User: "alice",
|
||||
},
|
||||
},
|
||||
expectedStatus: authorizationapi.SubjectAccessReviewStatus{
|
||||
Allowed: true,
|
||||
Reason: "you're not dave",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "simple deny",
|
||||
sar: &authorizationapi.SubjectAccessReview{
|
||||
Spec: authorizationapi.SubjectAccessReviewSpec{
|
||||
ResourceAttributes: &authorizationapi.ResourceAttributes{
|
||||
Verb: "list",
|
||||
Group: api.GroupName,
|
||||
Version: "v1",
|
||||
Resource: "pods",
|
||||
},
|
||||
User: "dave",
|
||||
},
|
||||
},
|
||||
expectedStatus: authorizationapi.SubjectAccessReviewStatus{
|
||||
Allowed: false,
|
||||
Reason: "no",
|
||||
EvaluationError: "I'm sorry, Dave",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "simple error",
|
||||
sar: &authorizationapi.SubjectAccessReview{
|
||||
Spec: authorizationapi.SubjectAccessReviewSpec{
|
||||
ResourceAttributes: &authorizationapi.ResourceAttributes{
|
||||
Verb: "list",
|
||||
Group: api.GroupName,
|
||||
Version: "v1",
|
||||
Resource: "pods",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedError: "at least one of user or group must be specified",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
response, err := clientset.Authorization().SubjectAccessReviews().Create(test.sar)
|
||||
switch {
|
||||
case err == nil && len(test.expectedError) == 0:
|
||||
|
||||
case err != nil && strings.Contains(err.Error(), test.expectedError):
|
||||
continue
|
||||
|
||||
case err != nil && len(test.expectedError) != 0:
|
||||
t.Errorf("%s: unexpected error: %v", test.name, err)
|
||||
continue
|
||||
default:
|
||||
t.Errorf("%s: expected %v, got %v", test.name, test.expectedError, err)
|
||||
continue
|
||||
}
|
||||
if response.Status != test.expectedStatus {
|
||||
t.Errorf("%s: expected %v, got %v", test.name, test.expectedStatus, response.Status)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSelfSubjectAccessReview(t *testing.T) {
|
||||
username := "alice"
|
||||
masterConfig := framework.NewIntegrationTestMasterConfig()
|
||||
masterConfig.GenericConfig.Authentication.Authenticator = authenticator.RequestFunc(func(req *http.Request) (user.Info, bool, error) {
|
||||
return &user.DefaultInfo{Name: username}, true, nil
|
||||
})
|
||||
masterConfig.GenericConfig.Authorization.Authorizer = sarAuthorizer{}
|
||||
_, s, closeFn := framework.RunAMaster(masterConfig)
|
||||
defer closeFn()
|
||||
|
||||
clientset := clientset.NewForConfigOrDie(&restclient.Config{Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: testapi.Groups[api.GroupName].GroupVersion()}})
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
username string
|
||||
sar *authorizationapi.SelfSubjectAccessReview
|
||||
expectedError string
|
||||
expectedStatus authorizationapi.SubjectAccessReviewStatus
|
||||
}{
|
||||
{
|
||||
name: "simple allow",
|
||||
username: "alice",
|
||||
sar: &authorizationapi.SelfSubjectAccessReview{
|
||||
Spec: authorizationapi.SelfSubjectAccessReviewSpec{
|
||||
ResourceAttributes: &authorizationapi.ResourceAttributes{
|
||||
Verb: "list",
|
||||
Group: api.GroupName,
|
||||
Version: "v1",
|
||||
Resource: "pods",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedStatus: authorizationapi.SubjectAccessReviewStatus{
|
||||
Allowed: true,
|
||||
Reason: "you're not dave",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "simple deny",
|
||||
username: "dave",
|
||||
sar: &authorizationapi.SelfSubjectAccessReview{
|
||||
Spec: authorizationapi.SelfSubjectAccessReviewSpec{
|
||||
ResourceAttributes: &authorizationapi.ResourceAttributes{
|
||||
Verb: "list",
|
||||
Group: api.GroupName,
|
||||
Version: "v1",
|
||||
Resource: "pods",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedStatus: authorizationapi.SubjectAccessReviewStatus{
|
||||
Allowed: false,
|
||||
Reason: "no",
|
||||
EvaluationError: "I'm sorry, Dave",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
username = test.username
|
||||
|
||||
response, err := clientset.Authorization().SelfSubjectAccessReviews().Create(test.sar)
|
||||
switch {
|
||||
case err == nil && len(test.expectedError) == 0:
|
||||
|
||||
case err != nil && strings.Contains(err.Error(), test.expectedError):
|
||||
continue
|
||||
|
||||
case err != nil && len(test.expectedError) != 0:
|
||||
t.Errorf("%s: unexpected error: %v", test.name, err)
|
||||
continue
|
||||
default:
|
||||
t.Errorf("%s: expected %v, got %v", test.name, test.expectedError, err)
|
||||
continue
|
||||
}
|
||||
if response.Status != test.expectedStatus {
|
||||
t.Errorf("%s: expected %v, got %v", test.name, test.expectedStatus, response.Status)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLocalSubjectAccessReview(t *testing.T) {
|
||||
masterConfig := framework.NewIntegrationTestMasterConfig()
|
||||
masterConfig.GenericConfig.Authentication.Authenticator = authenticator.RequestFunc(alwaysAlice)
|
||||
masterConfig.GenericConfig.Authorization.Authorizer = sarAuthorizer{}
|
||||
_, s, closeFn := framework.RunAMaster(masterConfig)
|
||||
defer closeFn()
|
||||
|
||||
clientset := clientset.NewForConfigOrDie(&restclient.Config{Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: testapi.Groups[api.GroupName].GroupVersion()}})
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
namespace string
|
||||
sar *authorizationapi.LocalSubjectAccessReview
|
||||
expectedError string
|
||||
expectedStatus authorizationapi.SubjectAccessReviewStatus
|
||||
}{
|
||||
{
|
||||
name: "simple allow",
|
||||
namespace: "foo",
|
||||
sar: &authorizationapi.LocalSubjectAccessReview{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: "foo"},
|
||||
Spec: authorizationapi.SubjectAccessReviewSpec{
|
||||
ResourceAttributes: &authorizationapi.ResourceAttributes{
|
||||
Verb: "list",
|
||||
Group: api.GroupName,
|
||||
Version: "v1",
|
||||
Resource: "pods",
|
||||
Namespace: "foo",
|
||||
},
|
||||
User: "alice",
|
||||
},
|
||||
},
|
||||
expectedStatus: authorizationapi.SubjectAccessReviewStatus{
|
||||
Allowed: true,
|
||||
Reason: "you're not dave",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "simple deny",
|
||||
namespace: "foo",
|
||||
sar: &authorizationapi.LocalSubjectAccessReview{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: "foo"},
|
||||
Spec: authorizationapi.SubjectAccessReviewSpec{
|
||||
ResourceAttributes: &authorizationapi.ResourceAttributes{
|
||||
Verb: "list",
|
||||
Group: api.GroupName,
|
||||
Version: "v1",
|
||||
Resource: "pods",
|
||||
Namespace: "foo",
|
||||
},
|
||||
User: "dave",
|
||||
},
|
||||
},
|
||||
expectedStatus: authorizationapi.SubjectAccessReviewStatus{
|
||||
Allowed: false,
|
||||
Reason: "no",
|
||||
EvaluationError: "I'm sorry, Dave",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "conflicting namespace",
|
||||
namespace: "foo",
|
||||
sar: &authorizationapi.LocalSubjectAccessReview{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: "foo"},
|
||||
Spec: authorizationapi.SubjectAccessReviewSpec{
|
||||
ResourceAttributes: &authorizationapi.ResourceAttributes{
|
||||
Verb: "list",
|
||||
Group: api.GroupName,
|
||||
Version: "v1",
|
||||
Resource: "pods",
|
||||
Namespace: "bar",
|
||||
},
|
||||
User: "dave",
|
||||
},
|
||||
},
|
||||
expectedError: "must match metadata.namespace",
|
||||
},
|
||||
{
|
||||
name: "missing namespace",
|
||||
namespace: "foo",
|
||||
sar: &authorizationapi.LocalSubjectAccessReview{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: "foo"},
|
||||
Spec: authorizationapi.SubjectAccessReviewSpec{
|
||||
ResourceAttributes: &authorizationapi.ResourceAttributes{
|
||||
Verb: "list",
|
||||
Group: api.GroupName,
|
||||
Version: "v1",
|
||||
Resource: "pods",
|
||||
},
|
||||
User: "dave",
|
||||
},
|
||||
},
|
||||
expectedError: "must match metadata.namespace",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
response, err := clientset.Authorization().LocalSubjectAccessReviews(test.namespace).Create(test.sar)
|
||||
switch {
|
||||
case err == nil && len(test.expectedError) == 0:
|
||||
|
||||
case err != nil && strings.Contains(err.Error(), test.expectedError):
|
||||
continue
|
||||
|
||||
case err != nil && len(test.expectedError) != 0:
|
||||
t.Errorf("%s: unexpected error: %v", test.name, err)
|
||||
continue
|
||||
default:
|
||||
t.Errorf("%s: expected %v, got %v", test.name, test.expectedError, err)
|
||||
continue
|
||||
}
|
||||
if response.Status != test.expectedStatus {
|
||||
t.Errorf("%s: expected %#v, got %#v", test.name, test.expectedStatus, response.Status)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
1288
vendor/k8s.io/kubernetes/test/integration/auth/auth_test.go
generated
vendored
Normal file
1288
vendor/k8s.io/kubernetes/test/integration/auth/auth_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
186
vendor/k8s.io/kubernetes/test/integration/auth/bootstraptoken_test.go
generated
vendored
Normal file
186
vendor/k8s.io/kubernetes/test/integration/auth/bootstraptoken_test.go
generated
vendored
Normal file
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
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 auth
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apiserver/pkg/authentication/request/bearertoken"
|
||||
bootstrapapi "k8s.io/client-go/tools/bootstrap/token/api"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/plugin/pkg/auth/authenticator/token/bootstrap"
|
||||
bootstraputil "k8s.io/kubernetes/test/e2e/lifecycle/bootstrap"
|
||||
"k8s.io/kubernetes/test/integration"
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
)
|
||||
|
||||
type bootstrapSecrets []*api.Secret
|
||||
|
||||
func (b bootstrapSecrets) List(selector labels.Selector) (ret []*api.Secret, err error) {
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (b bootstrapSecrets) Get(name string) (*api.Secret, error) {
|
||||
return b[0], nil
|
||||
}
|
||||
|
||||
// TestBootstrapTokenAuth tests the bootstrap token auth provider
|
||||
func TestBootstrapTokenAuth(t *testing.T) {
|
||||
tokenId, err := bootstraputil.GenerateTokenId()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
secret, err := bootstraputil.GenerateTokenSecret()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
var bootstrapSecretValid = &api.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: metav1.NamespaceSystem,
|
||||
Name: bootstrapapi.BootstrapTokenSecretPrefix,
|
||||
},
|
||||
Type: api.SecretTypeBootstrapToken,
|
||||
Data: map[string][]byte{
|
||||
bootstrapapi.BootstrapTokenIDKey: []byte(tokenId),
|
||||
bootstrapapi.BootstrapTokenSecretKey: []byte(secret),
|
||||
bootstrapapi.BootstrapTokenUsageAuthentication: []byte("true"),
|
||||
},
|
||||
}
|
||||
var bootstrapSecretInvalid = &api.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: metav1.NamespaceSystem,
|
||||
Name: bootstrapapi.BootstrapTokenSecretPrefix,
|
||||
},
|
||||
Type: api.SecretTypeBootstrapToken,
|
||||
Data: map[string][]byte{
|
||||
bootstrapapi.BootstrapTokenIDKey: []byte(tokenId),
|
||||
bootstrapapi.BootstrapTokenSecretKey: []byte("invalid"),
|
||||
bootstrapapi.BootstrapTokenUsageAuthentication: []byte("true"),
|
||||
},
|
||||
}
|
||||
var expiredBootstrapToken = &api.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: metav1.NamespaceSystem,
|
||||
Name: bootstrapapi.BootstrapTokenSecretPrefix,
|
||||
},
|
||||
Type: api.SecretTypeBootstrapToken,
|
||||
Data: map[string][]byte{
|
||||
bootstrapapi.BootstrapTokenIDKey: []byte(tokenId),
|
||||
bootstrapapi.BootstrapTokenSecretKey: []byte("invalid"),
|
||||
bootstrapapi.BootstrapTokenUsageAuthentication: []byte("true"),
|
||||
bootstrapapi.BootstrapTokenExpirationKey: []byte(bootstraputil.TimeStringFromNow(-time.Hour)),
|
||||
},
|
||||
}
|
||||
type request struct {
|
||||
verb string
|
||||
URL string
|
||||
body string
|
||||
statusCodes map[int]bool // Set of expected resp.StatusCode if all goes well.
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
request request
|
||||
secret *api.Secret
|
||||
}{
|
||||
{
|
||||
name: "valid token",
|
||||
request: request{verb: "GET", URL: path("pods", "", ""), body: "", statusCodes: integration.Code200},
|
||||
secret: bootstrapSecretValid,
|
||||
},
|
||||
{
|
||||
name: "invalid token format",
|
||||
request: request{verb: "GET", URL: path("pods", "", ""), body: "", statusCodes: integration.Code401},
|
||||
secret: bootstrapSecretInvalid,
|
||||
},
|
||||
{
|
||||
name: "invalid token expired",
|
||||
request: request{verb: "GET", URL: path("pods", "", ""), body: "", statusCodes: integration.Code401},
|
||||
secret: expiredBootstrapToken,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
|
||||
authenticator := bearertoken.New(bootstrap.NewTokenAuthenticator(bootstrapSecrets{test.secret}))
|
||||
// Set up a master
|
||||
masterConfig := framework.NewIntegrationTestMasterConfig()
|
||||
masterConfig.GenericConfig.Authentication.Authenticator = authenticator
|
||||
_, s, closeFn := framework.RunAMaster(masterConfig)
|
||||
defer closeFn()
|
||||
|
||||
ns := framework.CreateTestingNamespace("auth-bootstrap-token", s, t)
|
||||
defer framework.DeleteTestingNamespace(ns, s, t)
|
||||
|
||||
previousResourceVersion := make(map[string]float64)
|
||||
transport := http.DefaultTransport
|
||||
|
||||
token := tokenId + "." + secret
|
||||
var bodyStr string
|
||||
if test.request.body != "" {
|
||||
sub := ""
|
||||
if test.request.verb == "PUT" {
|
||||
// For update operations, insert previous resource version
|
||||
if resVersion := previousResourceVersion[getPreviousResourceVersionKey(test.request.URL, "")]; resVersion != 0 {
|
||||
sub += fmt.Sprintf(",\r\n\"resourceVersion\": \"%v\"", resVersion)
|
||||
}
|
||||
sub += fmt.Sprintf(",\r\n\"namespace\": %q", ns.Name)
|
||||
}
|
||||
bodyStr = fmt.Sprintf(test.request.body, sub)
|
||||
}
|
||||
test.request.body = bodyStr
|
||||
bodyBytes := bytes.NewReader([]byte(bodyStr))
|
||||
req, err := http.NewRequest(test.request.verb, s.URL+test.request.URL, bodyBytes)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
|
||||
if test.request.verb == "PATCH" {
|
||||
req.Header.Set("Content-Type", "application/merge-patch+json")
|
||||
}
|
||||
|
||||
func() {
|
||||
resp, err := transport.RoundTrip(req)
|
||||
defer resp.Body.Close()
|
||||
if err != nil {
|
||||
t.Logf("case %v", test.name)
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
b, _ := ioutil.ReadAll(resp.Body)
|
||||
if _, ok := test.request.statusCodes[resp.StatusCode]; !ok {
|
||||
t.Logf("case %v", test.name)
|
||||
t.Errorf("Expected status one of %v, but got %v", test.request.statusCodes, resp.StatusCode)
|
||||
t.Errorf("Body: %v", string(b))
|
||||
} else {
|
||||
if test.request.verb == "POST" {
|
||||
// For successful create operations, extract resourceVersion
|
||||
id, currentResourceVersion, err := parseResourceVersion(b)
|
||||
if err == nil {
|
||||
key := getPreviousResourceVersionKey(test.request.URL, id)
|
||||
previousResourceVersion[key] = currentResourceVersion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}()
|
||||
}
|
||||
}
|
||||
27
vendor/k8s.io/kubernetes/test/integration/auth/main_test.go
generated
vendored
Normal file
27
vendor/k8s.io/kubernetes/test/integration/auth/main_test.go
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
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 auth
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
framework.EtcdMain(m.Run)
|
||||
}
|
||||
551
vendor/k8s.io/kubernetes/test/integration/auth/node_test.go
generated
vendored
Normal file
551
vendor/k8s.io/kubernetes/test/integration/auth/node_test.go
generated
vendored
Normal file
@@ -0,0 +1,551 @@
|
||||
/*
|
||||
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 auth
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
storagev1beta1 "k8s.io/api/storage/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/apiserver/pkg/authentication/request/bearertoken"
|
||||
"k8s.io/apiserver/pkg/authentication/token/tokenfile"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing"
|
||||
versionedinformers "k8s.io/client-go/informers"
|
||||
externalclientset "k8s.io/client-go/kubernetes"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/apis/policy"
|
||||
"k8s.io/kubernetes/pkg/auth/nodeidentifier"
|
||||
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||
informers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
"k8s.io/kubernetes/pkg/kubeapiserver/authorizer"
|
||||
"k8s.io/kubernetes/plugin/pkg/admission/noderestriction"
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
)
|
||||
|
||||
func TestNodeAuthorizer(t *testing.T) {
|
||||
// Start the server so we know the address
|
||||
h := &framework.MasterHolder{Initialized: make(chan struct{})}
|
||||
apiServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
<-h.Initialized
|
||||
h.M.GenericAPIServer.Handler.ServeHTTP(w, req)
|
||||
}))
|
||||
|
||||
const (
|
||||
// Define credentials
|
||||
tokenMaster = "master-token"
|
||||
tokenNodeUnknown = "unknown-token"
|
||||
tokenNode1 = "node1-token"
|
||||
tokenNode2 = "node2-token"
|
||||
)
|
||||
|
||||
authenticator := bearertoken.New(tokenfile.New(map[string]*user.DefaultInfo{
|
||||
tokenMaster: {Name: "admin", Groups: []string{"system:masters"}},
|
||||
tokenNodeUnknown: {Name: "unknown", Groups: []string{"system:nodes"}},
|
||||
tokenNode1: {Name: "system:node:node1", Groups: []string{"system:nodes"}},
|
||||
tokenNode2: {Name: "system:node:node2", Groups: []string{"system:nodes"}},
|
||||
}))
|
||||
|
||||
// Build client config, clientset, and informers
|
||||
clientConfig := &restclient.Config{Host: apiServer.URL, ContentConfig: restclient.ContentConfig{NegotiatedSerializer: legacyscheme.Codecs}}
|
||||
superuserClient, superuserClientExternal := clientsetForToken(tokenMaster, clientConfig)
|
||||
informerFactory := informers.NewSharedInformerFactory(superuserClient, time.Minute)
|
||||
versionedInformerFactory := versionedinformers.NewSharedInformerFactory(superuserClientExternal, time.Minute)
|
||||
|
||||
// Enabled CSIPersistentVolume feature at startup so volumeattachments get watched
|
||||
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIPersistentVolume, true)()
|
||||
|
||||
// Enable DynamicKubeletConfig feature so that Node.Spec.ConfigSource can be set
|
||||
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DynamicKubeletConfig, true)()
|
||||
|
||||
// Set up Node+RBAC authorizer
|
||||
authorizerConfig := &authorizer.AuthorizationConfig{
|
||||
AuthorizationModes: []string{"Node", "RBAC"},
|
||||
InformerFactory: informerFactory,
|
||||
VersionedInformerFactory: versionedInformerFactory,
|
||||
}
|
||||
nodeRBACAuthorizer, _, err := authorizerConfig.New()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Set up NodeRestriction admission
|
||||
nodeRestrictionAdmission := noderestriction.NewPlugin(nodeidentifier.NewDefaultNodeIdentifier())
|
||||
nodeRestrictionAdmission.SetInternalKubeInformerFactory(informerFactory)
|
||||
if err := nodeRestrictionAdmission.ValidateInitialization(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Start the server
|
||||
masterConfig := framework.NewIntegrationTestMasterConfig()
|
||||
masterConfig.GenericConfig.Authentication.Authenticator = authenticator
|
||||
masterConfig.GenericConfig.Authorization.Authorizer = nodeRBACAuthorizer
|
||||
masterConfig.GenericConfig.AdmissionControl = nodeRestrictionAdmission
|
||||
|
||||
_, _, closeFn := framework.RunAMasterUsingServer(masterConfig, apiServer, h)
|
||||
defer closeFn()
|
||||
|
||||
// Start the informers
|
||||
stopCh := make(chan struct{})
|
||||
defer close(stopCh)
|
||||
informerFactory.Start(stopCh)
|
||||
versionedInformerFactory.Start(stopCh)
|
||||
|
||||
// Wait for a healthy server
|
||||
for {
|
||||
result := superuserClient.Core().RESTClient().Get().AbsPath("/healthz").Do()
|
||||
_, err := result.Raw()
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
t.Log(err)
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
|
||||
// Create objects
|
||||
if _, err := superuserClient.Core().Secrets("ns").Create(&api.Secret{ObjectMeta: metav1.ObjectMeta{Name: "mysecret"}}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := superuserClient.Core().Secrets("ns").Create(&api.Secret{ObjectMeta: metav1.ObjectMeta{Name: "mypvsecret"}}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := superuserClient.Core().ConfigMaps("ns").Create(&api.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: "myconfigmap"}}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := superuserClient.Core().ConfigMaps("ns").Create(&api.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: "myconfigmapconfigsource"}}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
pvName := "mypv"
|
||||
if _, err := superuserClientExternal.StorageV1beta1().VolumeAttachments().Create(&storagev1beta1.VolumeAttachment{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "myattachment"},
|
||||
Spec: storagev1beta1.VolumeAttachmentSpec{
|
||||
Attacher: "foo",
|
||||
Source: storagev1beta1.VolumeAttachmentSource{PersistentVolumeName: &pvName},
|
||||
NodeName: "node2",
|
||||
},
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := superuserClient.Core().PersistentVolumeClaims("ns").Create(&api.PersistentVolumeClaim{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "mypvc"},
|
||||
Spec: api.PersistentVolumeClaimSpec{
|
||||
AccessModes: []api.PersistentVolumeAccessMode{api.ReadOnlyMany},
|
||||
Resources: api.ResourceRequirements{Requests: api.ResourceList{api.ResourceStorage: resource.MustParse("1")}},
|
||||
},
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if _, err := superuserClient.Core().PersistentVolumes().Create(&api.PersistentVolume{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "mypv"},
|
||||
Spec: api.PersistentVolumeSpec{
|
||||
AccessModes: []api.PersistentVolumeAccessMode{api.ReadOnlyMany},
|
||||
Capacity: api.ResourceList{api.ResourceStorage: resource.MustParse("1")},
|
||||
ClaimRef: &api.ObjectReference{Namespace: "ns", Name: "mypvc"},
|
||||
PersistentVolumeSource: api.PersistentVolumeSource{AzureFile: &api.AzureFilePersistentVolumeSource{ShareName: "default", SecretName: "mypvsecret"}},
|
||||
},
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
getSecret := func(client clientset.Interface) func() error {
|
||||
return func() error {
|
||||
_, err := client.Core().Secrets("ns").Get("mysecret", metav1.GetOptions{})
|
||||
return err
|
||||
}
|
||||
}
|
||||
getPVSecret := func(client clientset.Interface) func() error {
|
||||
return func() error {
|
||||
_, err := client.Core().Secrets("ns").Get("mypvsecret", metav1.GetOptions{})
|
||||
return err
|
||||
}
|
||||
}
|
||||
getConfigMap := func(client clientset.Interface) func() error {
|
||||
return func() error {
|
||||
_, err := client.Core().ConfigMaps("ns").Get("myconfigmap", metav1.GetOptions{})
|
||||
return err
|
||||
}
|
||||
}
|
||||
getConfigMapConfigSource := func(client clientset.Interface) func() error {
|
||||
return func() error {
|
||||
_, err := client.Core().ConfigMaps("ns").Get("myconfigmapconfigsource", metav1.GetOptions{})
|
||||
return err
|
||||
}
|
||||
}
|
||||
getPVC := func(client clientset.Interface) func() error {
|
||||
return func() error {
|
||||
_, err := client.Core().PersistentVolumeClaims("ns").Get("mypvc", metav1.GetOptions{})
|
||||
return err
|
||||
}
|
||||
}
|
||||
getPV := func(client clientset.Interface) func() error {
|
||||
return func() error {
|
||||
_, err := client.Core().PersistentVolumes().Get("mypv", metav1.GetOptions{})
|
||||
return err
|
||||
}
|
||||
}
|
||||
getVolumeAttachment := func(client externalclientset.Interface) func() error {
|
||||
return func() error {
|
||||
_, err := client.StorageV1beta1().VolumeAttachments().Get("myattachment", metav1.GetOptions{})
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
createNode2NormalPod := func(client clientset.Interface) func() error {
|
||||
return func() error {
|
||||
_, err := client.Core().Pods("ns").Create(&api.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "node2normalpod"},
|
||||
Spec: api.PodSpec{
|
||||
NodeName: "node2",
|
||||
Containers: []api.Container{{Name: "image", Image: "busybox"}},
|
||||
Volumes: []api.Volume{
|
||||
{Name: "secret", VolumeSource: api.VolumeSource{Secret: &api.SecretVolumeSource{SecretName: "mysecret"}}},
|
||||
{Name: "cm", VolumeSource: api.VolumeSource{ConfigMap: &api.ConfigMapVolumeSource{LocalObjectReference: api.LocalObjectReference{Name: "myconfigmap"}}}},
|
||||
{Name: "pvc", VolumeSource: api.VolumeSource{PersistentVolumeClaim: &api.PersistentVolumeClaimVolumeSource{ClaimName: "mypvc"}}},
|
||||
},
|
||||
},
|
||||
})
|
||||
return err
|
||||
}
|
||||
}
|
||||
updateNode2NormalPodStatus := func(client clientset.Interface) func() error {
|
||||
return func() error {
|
||||
startTime := metav1.NewTime(time.Now())
|
||||
_, err := client.Core().Pods("ns").UpdateStatus(&api.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "node2normalpod"},
|
||||
Status: api.PodStatus{StartTime: &startTime},
|
||||
})
|
||||
return err
|
||||
}
|
||||
}
|
||||
deleteNode2NormalPod := func(client clientset.Interface) func() error {
|
||||
return func() error {
|
||||
zero := int64(0)
|
||||
return client.Core().Pods("ns").Delete("node2normalpod", &metav1.DeleteOptions{GracePeriodSeconds: &zero})
|
||||
}
|
||||
}
|
||||
|
||||
createNode2MirrorPod := func(client clientset.Interface) func() error {
|
||||
return func() error {
|
||||
_, err := client.Core().Pods("ns").Create(&api.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "node2mirrorpod",
|
||||
Annotations: map[string]string{api.MirrorPodAnnotationKey: "true"},
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
NodeName: "node2",
|
||||
Containers: []api.Container{{Name: "image", Image: "busybox"}},
|
||||
},
|
||||
})
|
||||
return err
|
||||
}
|
||||
}
|
||||
deleteNode2MirrorPod := func(client clientset.Interface) func() error {
|
||||
return func() error {
|
||||
zero := int64(0)
|
||||
return client.Core().Pods("ns").Delete("node2mirrorpod", &metav1.DeleteOptions{GracePeriodSeconds: &zero})
|
||||
}
|
||||
}
|
||||
|
||||
createNode2 := func(client clientset.Interface) func() error {
|
||||
return func() error {
|
||||
_, err := client.Core().Nodes().Create(&api.Node{ObjectMeta: metav1.ObjectMeta{Name: "node2"}})
|
||||
return err
|
||||
}
|
||||
}
|
||||
setNode2ConfigSource := func(client clientset.Interface) func() error {
|
||||
return func() error {
|
||||
node2, err := client.Core().Nodes().Get("node2", metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
node2.Spec.ConfigSource = &api.NodeConfigSource{
|
||||
ConfigMap: &api.ConfigMapNodeConfigSource{
|
||||
Namespace: "ns",
|
||||
Name: "myconfigmapconfigsource",
|
||||
KubeletConfigKey: "kubelet",
|
||||
},
|
||||
}
|
||||
_, err = client.Core().Nodes().Update(node2)
|
||||
return err
|
||||
}
|
||||
}
|
||||
unsetNode2ConfigSource := func(client clientset.Interface) func() error {
|
||||
return func() error {
|
||||
node2, err := client.Core().Nodes().Get("node2", metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
node2.Spec.ConfigSource = nil
|
||||
_, err = client.Core().Nodes().Update(node2)
|
||||
return err
|
||||
}
|
||||
}
|
||||
updateNode2Status := func(client clientset.Interface) func() error {
|
||||
return func() error {
|
||||
_, err := client.Core().Nodes().UpdateStatus(&api.Node{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "node2"},
|
||||
Status: api.NodeStatus{},
|
||||
})
|
||||
return err
|
||||
}
|
||||
}
|
||||
deleteNode2 := func(client clientset.Interface) func() error {
|
||||
return func() error {
|
||||
return client.Core().Nodes().Delete("node2", nil)
|
||||
}
|
||||
}
|
||||
createNode2NormalPodEviction := func(client clientset.Interface) func() error {
|
||||
return func() error {
|
||||
return client.Policy().Evictions("ns").Evict(&policy.Eviction{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "policy/v1beta1",
|
||||
Kind: "Eviction",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "node2normalpod",
|
||||
Namespace: "ns",
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
createNode2MirrorPodEviction := func(client clientset.Interface) func() error {
|
||||
return func() error {
|
||||
return client.Policy().Evictions("ns").Evict(&policy.Eviction{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "policy/v1beta1",
|
||||
Kind: "Eviction",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "node2mirrorpod",
|
||||
Namespace: "ns",
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
capacity := 50
|
||||
updatePVCCapacity := func(client clientset.Interface) func() error {
|
||||
return func() error {
|
||||
capacity++
|
||||
statusString := fmt.Sprintf("{\"status\": {\"capacity\": {\"storage\": \"%dG\"}}}", capacity)
|
||||
patchBytes := []byte(statusString)
|
||||
_, err := client.Core().PersistentVolumeClaims("ns").Patch("mypvc", types.StrategicMergePatchType, patchBytes, "status")
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
updatePVCPhase := func(client clientset.Interface) func() error {
|
||||
return func() error {
|
||||
patchBytes := []byte(`{"status":{"phase": "Bound"}}`)
|
||||
_, err := client.Core().PersistentVolumeClaims("ns").Patch("mypvc", types.StrategicMergePatchType, patchBytes, "status")
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
nodeanonClient, _ := clientsetForToken(tokenNodeUnknown, clientConfig)
|
||||
node1Client, node1ClientExternal := clientsetForToken(tokenNode1, clientConfig)
|
||||
node2Client, node2ClientExternal := clientsetForToken(tokenNode2, clientConfig)
|
||||
|
||||
// all node requests from node1 and unknown node fail
|
||||
expectForbidden(t, getSecret(nodeanonClient))
|
||||
expectForbidden(t, getPVSecret(nodeanonClient))
|
||||
expectForbidden(t, getConfigMap(nodeanonClient))
|
||||
expectForbidden(t, getPVC(nodeanonClient))
|
||||
expectForbidden(t, getPV(nodeanonClient))
|
||||
expectForbidden(t, createNode2NormalPod(nodeanonClient))
|
||||
expectForbidden(t, createNode2MirrorPod(nodeanonClient))
|
||||
expectForbidden(t, deleteNode2NormalPod(nodeanonClient))
|
||||
expectForbidden(t, deleteNode2MirrorPod(nodeanonClient))
|
||||
expectForbidden(t, createNode2MirrorPodEviction(nodeanonClient))
|
||||
expectForbidden(t, createNode2(nodeanonClient))
|
||||
expectForbidden(t, updateNode2Status(nodeanonClient))
|
||||
expectForbidden(t, deleteNode2(nodeanonClient))
|
||||
|
||||
expectForbidden(t, getSecret(node1Client))
|
||||
expectForbidden(t, getPVSecret(node1Client))
|
||||
expectForbidden(t, getConfigMap(node1Client))
|
||||
expectForbidden(t, getPVC(node1Client))
|
||||
expectForbidden(t, getPV(node1Client))
|
||||
expectForbidden(t, createNode2NormalPod(nodeanonClient))
|
||||
expectForbidden(t, createNode2MirrorPod(node1Client))
|
||||
expectNotFound(t, deleteNode2MirrorPod(node1Client))
|
||||
expectNotFound(t, createNode2MirrorPodEviction(node1Client))
|
||||
expectForbidden(t, createNode2(node1Client))
|
||||
expectForbidden(t, updateNode2Status(node1Client))
|
||||
expectForbidden(t, deleteNode2(node1Client))
|
||||
|
||||
// related object requests from node2 fail
|
||||
expectForbidden(t, getSecret(node2Client))
|
||||
expectForbidden(t, getPVSecret(node2Client))
|
||||
expectForbidden(t, getConfigMap(node2Client))
|
||||
expectForbidden(t, getPVC(node2Client))
|
||||
expectForbidden(t, getPV(node2Client))
|
||||
|
||||
expectForbidden(t, createNode2NormalPod(nodeanonClient))
|
||||
// mirror pod and self node lifecycle is allowed
|
||||
expectAllowed(t, createNode2MirrorPod(node2Client))
|
||||
expectAllowed(t, deleteNode2MirrorPod(node2Client))
|
||||
expectAllowed(t, createNode2MirrorPod(node2Client))
|
||||
expectAllowed(t, createNode2MirrorPodEviction(node2Client))
|
||||
expectAllowed(t, createNode2(node2Client))
|
||||
expectAllowed(t, updateNode2Status(node2Client))
|
||||
expectAllowed(t, deleteNode2(node2Client))
|
||||
|
||||
// create a pod as an admin to add object references
|
||||
expectAllowed(t, createNode2NormalPod(superuserClient))
|
||||
|
||||
// unidentifiable node and node1 are still forbidden
|
||||
expectForbidden(t, getSecret(nodeanonClient))
|
||||
expectForbidden(t, getPVSecret(nodeanonClient))
|
||||
expectForbidden(t, getConfigMap(nodeanonClient))
|
||||
expectForbidden(t, getPVC(nodeanonClient))
|
||||
expectForbidden(t, getPV(nodeanonClient))
|
||||
expectForbidden(t, createNode2NormalPod(nodeanonClient))
|
||||
expectForbidden(t, updateNode2NormalPodStatus(nodeanonClient))
|
||||
expectForbidden(t, deleteNode2NormalPod(nodeanonClient))
|
||||
expectForbidden(t, createNode2NormalPodEviction(nodeanonClient))
|
||||
expectForbidden(t, createNode2MirrorPod(nodeanonClient))
|
||||
expectForbidden(t, deleteNode2MirrorPod(nodeanonClient))
|
||||
expectForbidden(t, createNode2MirrorPodEviction(nodeanonClient))
|
||||
|
||||
expectForbidden(t, getSecret(node1Client))
|
||||
expectForbidden(t, getPVSecret(node1Client))
|
||||
expectForbidden(t, getConfigMap(node1Client))
|
||||
expectForbidden(t, getPVC(node1Client))
|
||||
expectForbidden(t, getPV(node1Client))
|
||||
expectForbidden(t, createNode2NormalPod(node1Client))
|
||||
expectForbidden(t, updateNode2NormalPodStatus(node1Client))
|
||||
expectForbidden(t, deleteNode2NormalPod(node1Client))
|
||||
expectForbidden(t, createNode2NormalPodEviction(node1Client))
|
||||
expectForbidden(t, createNode2MirrorPod(node1Client))
|
||||
expectNotFound(t, deleteNode2MirrorPod(node1Client))
|
||||
expectNotFound(t, createNode2MirrorPodEviction(node1Client))
|
||||
|
||||
// node2 can get referenced objects now
|
||||
expectAllowed(t, getSecret(node2Client))
|
||||
expectAllowed(t, getPVSecret(node2Client))
|
||||
expectAllowed(t, getConfigMap(node2Client))
|
||||
expectAllowed(t, getPVC(node2Client))
|
||||
expectAllowed(t, getPV(node2Client))
|
||||
|
||||
expectForbidden(t, createNode2NormalPod(node2Client))
|
||||
expectAllowed(t, updateNode2NormalPodStatus(node2Client))
|
||||
expectAllowed(t, deleteNode2NormalPod(node2Client))
|
||||
expectAllowed(t, createNode2MirrorPod(node2Client))
|
||||
expectAllowed(t, deleteNode2MirrorPod(node2Client))
|
||||
|
||||
// recreate as an admin to test eviction
|
||||
expectAllowed(t, createNode2NormalPod(superuserClient))
|
||||
expectAllowed(t, createNode2MirrorPod(superuserClient))
|
||||
expectAllowed(t, createNode2NormalPodEviction(node2Client))
|
||||
expectAllowed(t, createNode2MirrorPodEviction(node2Client))
|
||||
|
||||
// re-create a pod as an admin to add object references
|
||||
expectAllowed(t, createNode2NormalPod(superuserClient))
|
||||
|
||||
// ExpandPersistentVolumes feature disabled
|
||||
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ExpandPersistentVolumes, false)()
|
||||
expectForbidden(t, updatePVCCapacity(node1Client))
|
||||
expectForbidden(t, updatePVCCapacity(node2Client))
|
||||
|
||||
// ExpandPersistentVolumes feature enabled
|
||||
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ExpandPersistentVolumes, true)()
|
||||
expectForbidden(t, updatePVCCapacity(node1Client))
|
||||
expectAllowed(t, updatePVCCapacity(node2Client))
|
||||
expectForbidden(t, updatePVCPhase(node2Client))
|
||||
|
||||
// Disabled CSIPersistentVolume feature
|
||||
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIPersistentVolume, false)()
|
||||
expectForbidden(t, getVolumeAttachment(node1ClientExternal))
|
||||
expectForbidden(t, getVolumeAttachment(node2ClientExternal))
|
||||
// Enabled CSIPersistentVolume feature
|
||||
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIPersistentVolume, true)()
|
||||
expectForbidden(t, getVolumeAttachment(node1ClientExternal))
|
||||
expectAllowed(t, getVolumeAttachment(node2ClientExternal))
|
||||
|
||||
// create node2 again
|
||||
expectAllowed(t, createNode2(node2Client))
|
||||
// node2 can not set its own config source
|
||||
expectForbidden(t, setNode2ConfigSource(node2Client))
|
||||
// node2 can not access the configmap config source yet
|
||||
expectForbidden(t, getConfigMapConfigSource(node2Client))
|
||||
// superuser can access the configmap config source
|
||||
expectAllowed(t, getConfigMapConfigSource(superuserClient))
|
||||
// superuser can set node2's config source
|
||||
expectAllowed(t, setNode2ConfigSource(superuserClient))
|
||||
// node2 can now get the configmap assigned as its config source
|
||||
expectAllowed(t, getConfigMapConfigSource(node2Client))
|
||||
// superuser can unset node2's config source
|
||||
expectAllowed(t, unsetNode2ConfigSource(superuserClient))
|
||||
// node2 can no longer get the configmap after it is unassigned as its config source
|
||||
expectForbidden(t, getConfigMapConfigSource(node2Client))
|
||||
// clean up node2
|
||||
expectAllowed(t, deleteNode2(node2Client))
|
||||
|
||||
//TODO(mikedanese): integration test node restriction of TokenRequest
|
||||
}
|
||||
|
||||
// expect executes a function a set number of times until it either returns the
|
||||
// expected error or executes too many times. It returns if the retries timed
|
||||
// out and the last error returned by the method.
|
||||
func expect(t *testing.T, f func() error, wantErr func(error) bool) (timeout bool, lastErr error) {
|
||||
t.Helper()
|
||||
err := wait.PollImmediate(time.Second, 30*time.Second, func() (bool, error) {
|
||||
t.Helper()
|
||||
lastErr = f()
|
||||
if wantErr(lastErr) {
|
||||
return true, nil
|
||||
}
|
||||
t.Logf("unexpected response, will retry: %v", lastErr)
|
||||
return false, nil
|
||||
})
|
||||
return err == nil, lastErr
|
||||
}
|
||||
|
||||
func expectForbidden(t *testing.T, f func() error) {
|
||||
t.Helper()
|
||||
if ok, err := expect(t, f, errors.IsForbidden); !ok {
|
||||
t.Errorf("Expected forbidden error, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func expectNotFound(t *testing.T, f func() error) {
|
||||
t.Helper()
|
||||
if ok, err := expect(t, f, errors.IsNotFound); !ok {
|
||||
t.Errorf("Expected notfound error, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func expectAllowed(t *testing.T, f func() error) {
|
||||
t.Helper()
|
||||
if ok, err := expect(t, f, func(e error) bool { return e == nil }); !ok {
|
||||
t.Errorf("Expected no error, got %v", err)
|
||||
}
|
||||
}
|
||||
564
vendor/k8s.io/kubernetes/test/integration/auth/rbac_test.go
generated
vendored
Normal file
564
vendor/k8s.io/kubernetes/test/integration/auth/rbac_test.go
generated
vendored
Normal file
@@ -0,0 +1,564 @@
|
||||
/*
|
||||
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 auth
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
"k8s.io/apiserver/pkg/authentication/request/bearertoken"
|
||||
"k8s.io/apiserver/pkg/authentication/token/tokenfile"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||
"k8s.io/apiserver/pkg/registry/generic"
|
||||
externalclientset "k8s.io/client-go/kubernetes"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/transport"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
"k8s.io/kubernetes/pkg/api/testapi"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
rbacapi "k8s.io/kubernetes/pkg/apis/rbac"
|
||||
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||
"k8s.io/kubernetes/pkg/master"
|
||||
"k8s.io/kubernetes/pkg/registry/rbac/clusterrole"
|
||||
clusterrolestore "k8s.io/kubernetes/pkg/registry/rbac/clusterrole/storage"
|
||||
"k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding"
|
||||
clusterrolebindingstore "k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding/storage"
|
||||
"k8s.io/kubernetes/pkg/registry/rbac/role"
|
||||
rolestore "k8s.io/kubernetes/pkg/registry/rbac/role/storage"
|
||||
"k8s.io/kubernetes/pkg/registry/rbac/rolebinding"
|
||||
rolebindingstore "k8s.io/kubernetes/pkg/registry/rbac/rolebinding/storage"
|
||||
"k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac"
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
)
|
||||
|
||||
func clientForToken(user string) *http.Client {
|
||||
return &http.Client{
|
||||
Transport: transport.NewBearerAuthRoundTripper(
|
||||
user,
|
||||
transport.DebugWrappers(http.DefaultTransport),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
func clientsetForToken(user string, config *restclient.Config) (clientset.Interface, externalclientset.Interface) {
|
||||
configCopy := *config
|
||||
configCopy.BearerToken = user
|
||||
return clientset.NewForConfigOrDie(&configCopy), externalclientset.NewForConfigOrDie(&configCopy)
|
||||
}
|
||||
|
||||
type testRESTOptionsGetter struct {
|
||||
config *master.Config
|
||||
}
|
||||
|
||||
func (getter *testRESTOptionsGetter) GetRESTOptions(resource schema.GroupResource) (generic.RESTOptions, error) {
|
||||
storageConfig, err := getter.config.ExtraConfig.StorageFactory.NewConfig(resource)
|
||||
if err != nil {
|
||||
return generic.RESTOptions{}, fmt.Errorf("failed to get storage: %v", err)
|
||||
}
|
||||
return generic.RESTOptions{StorageConfig: storageConfig, Decorator: generic.UndecoratedStorage, ResourcePrefix: resource.Resource}, nil
|
||||
}
|
||||
|
||||
func newRBACAuthorizer(config *master.Config) authorizer.Authorizer {
|
||||
optsGetter := &testRESTOptionsGetter{config}
|
||||
roleRegistry := role.AuthorizerAdapter{Registry: role.NewRegistry(rolestore.NewREST(optsGetter))}
|
||||
roleBindingRegistry := rolebinding.AuthorizerAdapter{Registry: rolebinding.NewRegistry(rolebindingstore.NewREST(optsGetter))}
|
||||
clusterRoleRegistry := clusterrole.AuthorizerAdapter{Registry: clusterrole.NewRegistry(clusterrolestore.NewREST(optsGetter))}
|
||||
clusterRoleBindingRegistry := clusterrolebinding.AuthorizerAdapter{Registry: clusterrolebinding.NewRegistry(clusterrolebindingstore.NewREST(optsGetter))}
|
||||
return rbac.New(roleRegistry, roleBindingRegistry, clusterRoleRegistry, clusterRoleBindingRegistry)
|
||||
}
|
||||
|
||||
// bootstrapRoles are a set of RBAC roles which will be populated before the test.
|
||||
type bootstrapRoles struct {
|
||||
roles []rbacapi.Role
|
||||
roleBindings []rbacapi.RoleBinding
|
||||
clusterRoles []rbacapi.ClusterRole
|
||||
clusterRoleBindings []rbacapi.ClusterRoleBinding
|
||||
}
|
||||
|
||||
// bootstrap uses the provided client to create the bootstrap roles and role bindings.
|
||||
//
|
||||
// client should be authenticated as the RBAC super user.
|
||||
func (b bootstrapRoles) bootstrap(client clientset.Interface) error {
|
||||
for _, r := range b.clusterRoles {
|
||||
_, err := client.Rbac().ClusterRoles().Create(&r)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to make request: %v", err)
|
||||
}
|
||||
}
|
||||
for _, r := range b.roles {
|
||||
_, err := client.Rbac().Roles(r.Namespace).Create(&r)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to make request: %v", err)
|
||||
}
|
||||
}
|
||||
for _, r := range b.clusterRoleBindings {
|
||||
_, err := client.Rbac().ClusterRoleBindings().Create(&r)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to make request: %v", err)
|
||||
}
|
||||
}
|
||||
for _, r := range b.roleBindings {
|
||||
_, err := client.Rbac().RoleBindings(r.Namespace).Create(&r)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to make request: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// request is a test case which can.
|
||||
type request struct {
|
||||
// The bearer token sent as part of the request
|
||||
token string
|
||||
|
||||
// Resource metadata
|
||||
verb string
|
||||
apiGroup string
|
||||
resource string
|
||||
namespace string
|
||||
name string
|
||||
|
||||
// The actual resource.
|
||||
body string
|
||||
|
||||
// The expected return status of this request.
|
||||
expectedStatus int
|
||||
}
|
||||
|
||||
func (r request) String() string {
|
||||
return fmt.Sprintf("%s %s %s", r.token, r.verb, r.resource)
|
||||
}
|
||||
|
||||
type statusCode int
|
||||
|
||||
func (s statusCode) String() string {
|
||||
return fmt.Sprintf("%d %s", int(s), http.StatusText(int(s)))
|
||||
}
|
||||
|
||||
// Declare a set of raw objects to use.
|
||||
var (
|
||||
// Make a role binding with the version enabled in testapi.Rbac
|
||||
// This assumes testapi is using rbac.authorization.k8s.io/v1beta1 or rbac.authorization.k8s.io/v1, which are identical in structure.
|
||||
// TODO: rework or remove testapi usage to allow writing integration tests that don't depend on envvars
|
||||
writeJobsRoleBinding = `
|
||||
{
|
||||
"apiVersion": "` + testapi.Rbac.GroupVersion().String() + `",
|
||||
"kind": "RoleBinding",
|
||||
"metadata": {
|
||||
"name": "pi"%s
|
||||
},
|
||||
"roleRef": {
|
||||
"apiGroup": "rbac.authorization.k8s.io",
|
||||
"kind": "ClusterRole",
|
||||
"name": "write-jobs"
|
||||
},
|
||||
"subjects": [{
|
||||
"apiGroup": "rbac.authorization.k8s.io",
|
||||
"kind": "User",
|
||||
"name": "admin"
|
||||
}]
|
||||
}`
|
||||
|
||||
aJob = `
|
||||
{
|
||||
"apiVersion": "batch/v1",
|
||||
"kind": "Job",
|
||||
"metadata": {
|
||||
"name": "pi"%s
|
||||
},
|
||||
"spec": {
|
||||
"template": {
|
||||
"metadata": {
|
||||
"name": "a",
|
||||
"labels": {
|
||||
"name": "pijob"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"containers": [
|
||||
{
|
||||
"name": "pi",
|
||||
"image": "perl",
|
||||
"command": [
|
||||
"perl",
|
||||
"-Mbignum=bpi",
|
||||
"-wle",
|
||||
"print bpi(2000)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"restartPolicy": "Never"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
podNamespace = `
|
||||
{
|
||||
"apiVersion": "` + testapi.Groups[api.GroupName].GroupVersion().String() + `",
|
||||
"kind": "Namespace",
|
||||
"metadata": {
|
||||
"name": "pod-namespace"%s
|
||||
}
|
||||
}
|
||||
`
|
||||
jobNamespace = `
|
||||
{
|
||||
"apiVersion": "` + testapi.Groups[api.GroupName].GroupVersion().String() + `",
|
||||
"kind": "Namespace",
|
||||
"metadata": {
|
||||
"name": "job-namespace"%s
|
||||
}
|
||||
}
|
||||
`
|
||||
forbiddenNamespace = `
|
||||
{
|
||||
"apiVersion": "` + testapi.Groups[api.GroupName].GroupVersion().String() + `",
|
||||
"kind": "Namespace",
|
||||
"metadata": {
|
||||
"name": "forbidden-namespace"%s
|
||||
}
|
||||
}
|
||||
`
|
||||
)
|
||||
|
||||
// Declare some PolicyRules beforehand.
|
||||
var (
|
||||
ruleAllowAll = rbacapi.NewRule("*").Groups("*").Resources("*").RuleOrDie()
|
||||
ruleReadPods = rbacapi.NewRule("list", "get", "watch").Groups("").Resources("pods").RuleOrDie()
|
||||
ruleWriteJobs = rbacapi.NewRule("*").Groups("batch").Resources("*").RuleOrDie()
|
||||
)
|
||||
|
||||
func TestRBAC(t *testing.T) {
|
||||
superUser := "admin/system:masters"
|
||||
|
||||
tests := []struct {
|
||||
bootstrapRoles bootstrapRoles
|
||||
|
||||
requests []request
|
||||
}{
|
||||
{
|
||||
bootstrapRoles: bootstrapRoles{
|
||||
clusterRoles: []rbacapi.ClusterRole{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "allow-all"},
|
||||
Rules: []rbacapi.PolicyRule{ruleAllowAll},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "read-pods"},
|
||||
Rules: []rbacapi.PolicyRule{ruleReadPods},
|
||||
},
|
||||
},
|
||||
clusterRoleBindings: []rbacapi.ClusterRoleBinding{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "read-pods"},
|
||||
Subjects: []rbacapi.Subject{
|
||||
{Kind: "User", Name: "pod-reader"},
|
||||
},
|
||||
RoleRef: rbacapi.RoleRef{Kind: "ClusterRole", Name: "read-pods"},
|
||||
},
|
||||
},
|
||||
},
|
||||
requests: []request{
|
||||
// Create the namespace used later in the test
|
||||
{superUser, "POST", "", "namespaces", "", "", podNamespace, http.StatusCreated},
|
||||
|
||||
{superUser, "GET", "", "pods", "", "", "", http.StatusOK},
|
||||
{superUser, "GET", "", "pods", "pod-namespace", "a", "", http.StatusNotFound},
|
||||
{superUser, "POST", "", "pods", "pod-namespace", "", aPod, http.StatusCreated},
|
||||
{superUser, "GET", "", "pods", "pod-namespace", "a", "", http.StatusOK},
|
||||
|
||||
{"bob", "GET", "", "pods", "", "", "", http.StatusForbidden},
|
||||
{"bob", "GET", "", "pods", "pod-namespace", "a", "", http.StatusForbidden},
|
||||
|
||||
{"pod-reader", "GET", "", "pods", "", "", "", http.StatusOK},
|
||||
{"pod-reader", "POST", "", "pods", "pod-namespace", "", aPod, http.StatusForbidden},
|
||||
},
|
||||
},
|
||||
{
|
||||
bootstrapRoles: bootstrapRoles{
|
||||
clusterRoles: []rbacapi.ClusterRole{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "write-jobs"},
|
||||
Rules: []rbacapi.PolicyRule{ruleWriteJobs},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "create-rolebindings"},
|
||||
Rules: []rbacapi.PolicyRule{
|
||||
rbacapi.NewRule("create").Groups("rbac.authorization.k8s.io").Resources("rolebindings").RuleOrDie(),
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "bind-any-clusterrole"},
|
||||
Rules: []rbacapi.PolicyRule{
|
||||
rbacapi.NewRule("bind").Groups("rbac.authorization.k8s.io").Resources("clusterroles").RuleOrDie(),
|
||||
},
|
||||
},
|
||||
},
|
||||
clusterRoleBindings: []rbacapi.ClusterRoleBinding{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "write-jobs"},
|
||||
Subjects: []rbacapi.Subject{{Kind: "User", Name: "job-writer"}},
|
||||
RoleRef: rbacapi.RoleRef{Kind: "ClusterRole", Name: "write-jobs"},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "create-rolebindings"},
|
||||
Subjects: []rbacapi.Subject{
|
||||
{Kind: "User", Name: "job-writer"},
|
||||
{Kind: "User", Name: "nonescalating-rolebinding-writer"},
|
||||
{Kind: "User", Name: "any-rolebinding-writer"},
|
||||
},
|
||||
RoleRef: rbacapi.RoleRef{Kind: "ClusterRole", Name: "create-rolebindings"},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "bind-any-clusterrole"},
|
||||
Subjects: []rbacapi.Subject{{Kind: "User", Name: "any-rolebinding-writer"}},
|
||||
RoleRef: rbacapi.RoleRef{Kind: "ClusterRole", Name: "bind-any-clusterrole"},
|
||||
},
|
||||
},
|
||||
roleBindings: []rbacapi.RoleBinding{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "write-jobs", Namespace: "job-namespace"},
|
||||
Subjects: []rbacapi.Subject{{Kind: "User", Name: "job-writer-namespace"}},
|
||||
RoleRef: rbacapi.RoleRef{Kind: "ClusterRole", Name: "write-jobs"},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "create-rolebindings", Namespace: "job-namespace"},
|
||||
Subjects: []rbacapi.Subject{
|
||||
{Kind: "User", Name: "job-writer-namespace"},
|
||||
{Kind: "User", Name: "any-rolebinding-writer-namespace"},
|
||||
},
|
||||
RoleRef: rbacapi.RoleRef{Kind: "ClusterRole", Name: "create-rolebindings"},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "bind-any-clusterrole", Namespace: "job-namespace"},
|
||||
Subjects: []rbacapi.Subject{{Kind: "User", Name: "any-rolebinding-writer-namespace"}},
|
||||
RoleRef: rbacapi.RoleRef{Kind: "ClusterRole", Name: "bind-any-clusterrole"},
|
||||
},
|
||||
},
|
||||
},
|
||||
requests: []request{
|
||||
// Create the namespace used later in the test
|
||||
{superUser, "POST", "", "namespaces", "", "", jobNamespace, http.StatusCreated},
|
||||
{superUser, "POST", "", "namespaces", "", "", forbiddenNamespace, http.StatusCreated},
|
||||
|
||||
{"user-with-no-permissions", "POST", "batch", "jobs", "job-namespace", "", aJob, http.StatusForbidden},
|
||||
{"user-with-no-permissions", "GET", "batch", "jobs", "job-namespace", "pi", "", http.StatusForbidden},
|
||||
|
||||
// job-writer-namespace cannot write to the "forbidden-namespace"
|
||||
{"job-writer-namespace", "GET", "batch", "jobs", "forbidden-namespace", "", "", http.StatusForbidden},
|
||||
{"job-writer-namespace", "GET", "batch", "jobs", "forbidden-namespace", "pi", "", http.StatusForbidden},
|
||||
{"job-writer-namespace", "POST", "batch", "jobs", "forbidden-namespace", "", aJob, http.StatusForbidden},
|
||||
{"job-writer-namespace", "GET", "batch", "jobs", "forbidden-namespace", "pi", "", http.StatusForbidden},
|
||||
|
||||
// job-writer can write to any namespace
|
||||
{"job-writer", "GET", "batch", "jobs", "forbidden-namespace", "", "", http.StatusOK},
|
||||
{"job-writer", "GET", "batch", "jobs", "forbidden-namespace", "pi", "", http.StatusNotFound},
|
||||
{"job-writer", "POST", "batch", "jobs", "forbidden-namespace", "", aJob, http.StatusCreated},
|
||||
{"job-writer", "GET", "batch", "jobs", "forbidden-namespace", "pi", "", http.StatusOK},
|
||||
|
||||
{"job-writer-namespace", "GET", "batch", "jobs", "job-namespace", "", "", http.StatusOK},
|
||||
{"job-writer-namespace", "GET", "batch", "jobs", "job-namespace", "pi", "", http.StatusNotFound},
|
||||
{"job-writer-namespace", "POST", "batch", "jobs", "job-namespace", "", aJob, http.StatusCreated},
|
||||
{"job-writer-namespace", "GET", "batch", "jobs", "job-namespace", "pi", "", http.StatusOK},
|
||||
|
||||
// cannot bind role anywhere
|
||||
{"user-with-no-permissions", "POST", "rbac.authorization.k8s.io", "rolebindings", "job-namespace", "", writeJobsRoleBinding, http.StatusForbidden},
|
||||
// can only bind role in namespace where they have explicit bind permission
|
||||
{"any-rolebinding-writer-namespace", "POST", "rbac.authorization.k8s.io", "rolebindings", "forbidden-namespace", "", writeJobsRoleBinding, http.StatusForbidden},
|
||||
// can only bind role in namespace where they have covering permissions
|
||||
{"job-writer-namespace", "POST", "rbac.authorization.k8s.io", "rolebindings", "forbidden-namespace", "", writeJobsRoleBinding, http.StatusForbidden},
|
||||
{"job-writer-namespace", "POST", "rbac.authorization.k8s.io", "rolebindings", "job-namespace", "", writeJobsRoleBinding, http.StatusCreated},
|
||||
{superUser, "DELETE", "rbac.authorization.k8s.io", "rolebindings", "job-namespace", "pi", "", http.StatusOK},
|
||||
// can bind role in any namespace where they have covering permissions
|
||||
{"job-writer", "POST", "rbac.authorization.k8s.io", "rolebindings", "forbidden-namespace", "", writeJobsRoleBinding, http.StatusCreated},
|
||||
{superUser, "DELETE", "rbac.authorization.k8s.io", "rolebindings", "forbidden-namespace", "pi", "", http.StatusOK},
|
||||
// cannot bind role because they don't have covering permissions
|
||||
{"nonescalating-rolebinding-writer", "POST", "rbac.authorization.k8s.io", "rolebindings", "job-namespace", "", writeJobsRoleBinding, http.StatusForbidden},
|
||||
// can bind role because they have explicit bind permission
|
||||
{"any-rolebinding-writer", "POST", "rbac.authorization.k8s.io", "rolebindings", "job-namespace", "", writeJobsRoleBinding, http.StatusCreated},
|
||||
{superUser, "DELETE", "rbac.authorization.k8s.io", "rolebindings", "job-namespace", "pi", "", http.StatusOK},
|
||||
{"any-rolebinding-writer-namespace", "POST", "rbac.authorization.k8s.io", "rolebindings", "job-namespace", "", writeJobsRoleBinding, http.StatusCreated},
|
||||
{superUser, "DELETE", "rbac.authorization.k8s.io", "rolebindings", "job-namespace", "pi", "", http.StatusOK},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range tests {
|
||||
// Create an API Server.
|
||||
masterConfig := framework.NewIntegrationTestMasterConfig()
|
||||
masterConfig.GenericConfig.Authorization.Authorizer = newRBACAuthorizer(masterConfig)
|
||||
masterConfig.GenericConfig.Authentication.Authenticator = bearertoken.New(tokenfile.New(map[string]*user.DefaultInfo{
|
||||
superUser: {Name: "admin", Groups: []string{"system:masters"}},
|
||||
"any-rolebinding-writer": {Name: "any-rolebinding-writer"},
|
||||
"any-rolebinding-writer-namespace": {Name: "any-rolebinding-writer-namespace"},
|
||||
"bob": {Name: "bob"},
|
||||
"job-writer": {Name: "job-writer"},
|
||||
"job-writer-namespace": {Name: "job-writer-namespace"},
|
||||
"nonescalating-rolebinding-writer": {Name: "nonescalating-rolebinding-writer"},
|
||||
"pod-reader": {Name: "pod-reader"},
|
||||
"user-with-no-permissions": {Name: "user-with-no-permissions"},
|
||||
}))
|
||||
_, s, closeFn := framework.RunAMaster(masterConfig)
|
||||
defer closeFn()
|
||||
|
||||
clientConfig := &restclient.Config{Host: s.URL, ContentConfig: restclient.ContentConfig{NegotiatedSerializer: legacyscheme.Codecs}}
|
||||
|
||||
// Bootstrap the API Server with the test case's initial roles.
|
||||
superuserClient, _ := clientsetForToken(superUser, clientConfig)
|
||||
if err := tc.bootstrapRoles.bootstrap(superuserClient); err != nil {
|
||||
t.Errorf("case %d: failed to apply initial roles: %v", i, err)
|
||||
continue
|
||||
}
|
||||
previousResourceVersion := make(map[string]float64)
|
||||
|
||||
for j, r := range tc.requests {
|
||||
testGroup, ok := testapi.Groups[r.apiGroup]
|
||||
if !ok {
|
||||
t.Errorf("case %d %d: unknown api group %q, %s", i, j, r.apiGroup, r)
|
||||
continue
|
||||
}
|
||||
path := testGroup.ResourcePath(r.resource, r.namespace, r.name)
|
||||
|
||||
var body io.Reader
|
||||
if r.body != "" {
|
||||
sub := ""
|
||||
if r.verb == "PUT" {
|
||||
// For update operations, insert previous resource version
|
||||
if resVersion := previousResourceVersion[getPreviousResourceVersionKey(path, "")]; resVersion != 0 {
|
||||
sub += fmt.Sprintf(",\"resourceVersion\": \"%v\"", resVersion)
|
||||
}
|
||||
}
|
||||
body = strings.NewReader(fmt.Sprintf(r.body, sub))
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(r.verb, s.URL+path, body)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create request: %v", err)
|
||||
}
|
||||
|
||||
func() {
|
||||
reqDump, err := httputil.DumpRequest(req, true)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to dump request: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := clientForToken(r.token).Do(req)
|
||||
if err != nil {
|
||||
t.Errorf("case %d, req %d: failed to make request: %v", i, j, err)
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
respDump, err := httputil.DumpResponse(resp, true)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to dump response: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if resp.StatusCode != r.expectedStatus {
|
||||
// When debugging is on, dump the entire request and response. Very helpful for
|
||||
// debugging malformed test cases.
|
||||
//
|
||||
// To turn on debugging, use the '-args' flag.
|
||||
//
|
||||
// go test -v -tags integration -run RBAC -args -v 10
|
||||
//
|
||||
glog.V(8).Infof("case %d, req %d: %s\n%s\n", i, j, reqDump, respDump)
|
||||
t.Errorf("case %d, req %d: %s expected %q got %q", i, j, r, statusCode(r.expectedStatus), statusCode(resp.StatusCode))
|
||||
}
|
||||
|
||||
b, _ := ioutil.ReadAll(resp.Body)
|
||||
|
||||
if r.verb == "POST" && (resp.StatusCode/100) == 2 {
|
||||
// For successful create operations, extract resourceVersion
|
||||
id, currentResourceVersion, err := parseResourceVersion(b)
|
||||
if err == nil {
|
||||
key := getPreviousResourceVersionKey(path, id)
|
||||
previousResourceVersion[key] = currentResourceVersion
|
||||
} else {
|
||||
t.Logf("error in trying to extract resource version: %s", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBootstrapping(t *testing.T) {
|
||||
superUser := "admin/system:masters"
|
||||
|
||||
masterConfig := framework.NewIntegrationTestMasterConfig()
|
||||
masterConfig.GenericConfig.Authorization.Authorizer = newRBACAuthorizer(masterConfig)
|
||||
masterConfig.GenericConfig.Authentication.Authenticator = bearertoken.New(tokenfile.New(map[string]*user.DefaultInfo{
|
||||
superUser: {Name: "admin", Groups: []string{"system:masters"}},
|
||||
}))
|
||||
_, s, closeFn := framework.RunAMaster(masterConfig)
|
||||
defer closeFn()
|
||||
|
||||
clientset := clientset.NewForConfigOrDie(&restclient.Config{BearerToken: superUser, Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: testapi.Groups[api.GroupName].GroupVersion()}})
|
||||
|
||||
watcher, err := clientset.Rbac().ClusterRoles().Watch(metav1.ListOptions{ResourceVersion: "0"})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
_, err = watch.Until(30*time.Second, watcher, func(event watch.Event) (bool, error) {
|
||||
if event.Type != watch.Added {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
clusterRoles, err := clientset.Rbac().ClusterRoles().List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if len(clusterRoles.Items) == 0 {
|
||||
t.Fatalf("missing cluster roles")
|
||||
}
|
||||
|
||||
for _, clusterRole := range clusterRoles.Items {
|
||||
if clusterRole.Name == "cluster-admin" {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
t.Errorf("missing cluster-admin: %v", clusterRoles)
|
||||
|
||||
healthBytes, err := clientset.Discovery().RESTClient().Get().AbsPath("/healthz/poststarthook/rbac/bootstrap-roles").DoRaw()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
t.Errorf("error bootstrapping roles: %s", string(healthBytes))
|
||||
}
|
||||
558
vendor/k8s.io/kubernetes/test/integration/auth/svcaccttoken_test.go
generated
vendored
Normal file
558
vendor/k8s.io/kubernetes/test/integration/auth/svcaccttoken_test.go
generated
vendored
Normal file
@@ -0,0 +1,558 @@
|
||||
/*
|
||||
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 auth
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gopkg.in/square/go-jose.v2/jwt"
|
||||
authenticationv1 "k8s.io/api/authentication/v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apiserver/pkg/authentication/request/bearertoken"
|
||||
apiserverserviceaccount "k8s.io/apiserver/pkg/authentication/serviceaccount"
|
||||
"k8s.io/apiserver/pkg/authorization/authorizerfactory"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
externalclientset "k8s.io/client-go/kubernetes"
|
||||
certutil "k8s.io/client-go/util/cert"
|
||||
"k8s.io/kubernetes/pkg/apis/core"
|
||||
serviceaccountgetter "k8s.io/kubernetes/pkg/controller/serviceaccount"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
"k8s.io/kubernetes/pkg/serviceaccount"
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
)
|
||||
|
||||
const ecdsaPrivateKey = `-----BEGIN EC PRIVATE KEY-----
|
||||
MHcCAQEEIEZmTmUhuanLjPA2CLquXivuwBDHTt5XYwgIr/kA1LtRoAoGCCqGSM49
|
||||
AwEHoUQDQgAEH6cuzP8XuD5wal6wf9M6xDljTOPLX2i8uIp/C/ASqiIGUeeKQtX0
|
||||
/IR3qCXyThP/dbCiHrF3v1cuhBOHY8CLVg==
|
||||
-----END EC PRIVATE KEY-----`
|
||||
|
||||
func TestServiceAccountTokenCreate(t *testing.T) {
|
||||
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.TokenRequest, true)()
|
||||
|
||||
// Build client config, clientset, and informers
|
||||
sk, err := certutil.ParsePrivateKeyPEM([]byte(ecdsaPrivateKey))
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
pk := sk.(*ecdsa.PrivateKey).PublicKey
|
||||
|
||||
const iss = "https://foo.bar.example.com"
|
||||
aud := []string{"api"}
|
||||
|
||||
gcs := &clientset.Clientset{}
|
||||
|
||||
// Start the server
|
||||
masterConfig := framework.NewIntegrationTestMasterConfig()
|
||||
masterConfig.GenericConfig.Authorization.Authorizer = authorizerfactory.NewAlwaysAllowAuthorizer()
|
||||
masterConfig.GenericConfig.Authentication.Authenticator = bearertoken.New(
|
||||
serviceaccount.JWTTokenAuthenticator(
|
||||
iss,
|
||||
[]interface{}{&pk},
|
||||
serviceaccount.NewValidator(aud, serviceaccountgetter.NewGetterFromClient(gcs)),
|
||||
),
|
||||
)
|
||||
masterConfig.ExtraConfig.ServiceAccountIssuer = serviceaccount.JWTTokenGenerator(iss, sk)
|
||||
masterConfig.ExtraConfig.ServiceAccountAPIAudiences = aud
|
||||
|
||||
master, _, closeFn := framework.RunAMaster(masterConfig)
|
||||
defer closeFn()
|
||||
|
||||
cs, err := clientset.NewForConfig(master.GenericAPIServer.LoopbackClientConfig)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
*gcs = *cs
|
||||
|
||||
var (
|
||||
sa = &v1.ServiceAccount{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-svcacct",
|
||||
Namespace: "myns",
|
||||
},
|
||||
}
|
||||
pod = &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-pod",
|
||||
Namespace: sa.Namespace,
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
ServiceAccountName: sa.Name,
|
||||
Containers: []v1.Container{{Name: "test-container", Image: "nginx"}},
|
||||
},
|
||||
}
|
||||
otherpod = &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "other-test-pod",
|
||||
Namespace: sa.Namespace,
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
ServiceAccountName: "other-" + sa.Name,
|
||||
Containers: []v1.Container{{Name: "test-container", Image: "nginx"}},
|
||||
},
|
||||
}
|
||||
secret = &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-secret",
|
||||
Namespace: sa.Namespace,
|
||||
},
|
||||
}
|
||||
|
||||
wrongUID = types.UID("wrong")
|
||||
noUID = types.UID("")
|
||||
)
|
||||
|
||||
t.Run("bound to service account", func(t *testing.T) {
|
||||
treq := &authenticationv1.TokenRequest{
|
||||
Spec: authenticationv1.TokenRequestSpec{
|
||||
Audiences: []string{"api"},
|
||||
},
|
||||
}
|
||||
|
||||
if resp, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq); err == nil {
|
||||
t.Fatalf("expected err creating token for nonexistant svcacct but got: %#v", resp)
|
||||
}
|
||||
sa, delSvcAcct := createDeleteSvcAcct(t, cs, sa)
|
||||
defer delSvcAcct()
|
||||
|
||||
treq, err = cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
checkPayload(t, treq.Status.Token, `"system:serviceaccount:myns:test-svcacct"`, "sub")
|
||||
checkPayload(t, treq.Status.Token, `["api"]`, "aud")
|
||||
checkPayload(t, treq.Status.Token, "null", "kubernetes.io", "pod")
|
||||
checkPayload(t, treq.Status.Token, "null", "kubernetes.io", "secret")
|
||||
checkPayload(t, treq.Status.Token, `"myns"`, "kubernetes.io", "namespace")
|
||||
checkPayload(t, treq.Status.Token, `"test-svcacct"`, "kubernetes.io", "serviceaccount", "name")
|
||||
|
||||
doTokenReview(t, cs, treq, false)
|
||||
delSvcAcct()
|
||||
doTokenReview(t, cs, treq, true)
|
||||
})
|
||||
|
||||
t.Run("bound to service account and pod", func(t *testing.T) {
|
||||
treq := &authenticationv1.TokenRequest{
|
||||
Spec: authenticationv1.TokenRequestSpec{
|
||||
Audiences: []string{"api"},
|
||||
BoundObjectRef: &authenticationv1.BoundObjectReference{
|
||||
Kind: "Pod",
|
||||
APIVersion: "v1",
|
||||
Name: pod.Name,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if resp, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq); err == nil {
|
||||
t.Fatalf("expected err creating token for nonexistant svcacct but got: %#v", resp)
|
||||
}
|
||||
sa, del := createDeleteSvcAcct(t, cs, sa)
|
||||
defer del()
|
||||
|
||||
if resp, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq); err == nil {
|
||||
t.Fatalf("expected err creating token bound to nonexistant pod but got: %#v", resp)
|
||||
}
|
||||
pod, delPod := createDeletePod(t, cs, pod)
|
||||
defer delPod()
|
||||
|
||||
// right uid
|
||||
treq.Spec.BoundObjectRef.UID = pod.UID
|
||||
if _, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
// wrong uid
|
||||
treq.Spec.BoundObjectRef.UID = wrongUID
|
||||
if resp, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq); err == nil {
|
||||
t.Fatalf("expected err creating token bound to pod with wrong uid but got: %#v", resp)
|
||||
}
|
||||
// no uid
|
||||
treq.Spec.BoundObjectRef.UID = noUID
|
||||
treq, err = cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
checkPayload(t, treq.Status.Token, `"system:serviceaccount:myns:test-svcacct"`, "sub")
|
||||
checkPayload(t, treq.Status.Token, `["api"]`, "aud")
|
||||
checkPayload(t, treq.Status.Token, `"test-pod"`, "kubernetes.io", "pod", "name")
|
||||
checkPayload(t, treq.Status.Token, "null", "kubernetes.io", "secret")
|
||||
checkPayload(t, treq.Status.Token, `"myns"`, "kubernetes.io", "namespace")
|
||||
checkPayload(t, treq.Status.Token, `"test-svcacct"`, "kubernetes.io", "serviceaccount", "name")
|
||||
|
||||
doTokenReview(t, cs, treq, false)
|
||||
delPod()
|
||||
doTokenReview(t, cs, treq, true)
|
||||
})
|
||||
|
||||
t.Run("bound to service account and secret", func(t *testing.T) {
|
||||
treq := &authenticationv1.TokenRequest{
|
||||
Spec: authenticationv1.TokenRequestSpec{
|
||||
Audiences: []string{"api"},
|
||||
BoundObjectRef: &authenticationv1.BoundObjectReference{
|
||||
Kind: "Secret",
|
||||
APIVersion: "v1",
|
||||
Name: secret.Name,
|
||||
UID: secret.UID,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if resp, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq); err == nil {
|
||||
t.Fatalf("expected err creating token for nonexistant svcacct but got: %#v", resp)
|
||||
}
|
||||
sa, del := createDeleteSvcAcct(t, cs, sa)
|
||||
defer del()
|
||||
|
||||
if resp, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq); err == nil {
|
||||
t.Fatalf("expected err creating token bound to nonexistant secret but got: %#v", resp)
|
||||
}
|
||||
secret, delSecret := createDeleteSecret(t, cs, secret)
|
||||
defer delSecret()
|
||||
|
||||
// right uid
|
||||
treq.Spec.BoundObjectRef.UID = secret.UID
|
||||
if _, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
// wrong uid
|
||||
treq.Spec.BoundObjectRef.UID = wrongUID
|
||||
if resp, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq); err == nil {
|
||||
t.Fatalf("expected err creating token bound to secret with wrong uid but got: %#v", resp)
|
||||
}
|
||||
// no uid
|
||||
treq.Spec.BoundObjectRef.UID = noUID
|
||||
treq, err = cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
checkPayload(t, treq.Status.Token, `"system:serviceaccount:myns:test-svcacct"`, "sub")
|
||||
checkPayload(t, treq.Status.Token, `["api"]`, "aud")
|
||||
checkPayload(t, treq.Status.Token, `null`, "kubernetes.io", "pod")
|
||||
checkPayload(t, treq.Status.Token, `"test-secret"`, "kubernetes.io", "secret", "name")
|
||||
checkPayload(t, treq.Status.Token, `"myns"`, "kubernetes.io", "namespace")
|
||||
checkPayload(t, treq.Status.Token, `"test-svcacct"`, "kubernetes.io", "serviceaccount", "name")
|
||||
|
||||
doTokenReview(t, cs, treq, false)
|
||||
delSecret()
|
||||
doTokenReview(t, cs, treq, true)
|
||||
})
|
||||
|
||||
t.Run("bound to service account and pod running as different service account", func(t *testing.T) {
|
||||
treq := &authenticationv1.TokenRequest{
|
||||
Spec: authenticationv1.TokenRequestSpec{
|
||||
Audiences: []string{"api"},
|
||||
BoundObjectRef: &authenticationv1.BoundObjectReference{
|
||||
Kind: "Pod",
|
||||
APIVersion: "v1",
|
||||
Name: otherpod.Name,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
sa, del := createDeleteSvcAcct(t, cs, sa)
|
||||
defer del()
|
||||
_, del = createDeletePod(t, cs, otherpod)
|
||||
defer del()
|
||||
|
||||
if resp, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq); err == nil {
|
||||
t.Fatalf("expected err but got: %#v", resp)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("expired token", func(t *testing.T) {
|
||||
treq := &authenticationv1.TokenRequest{
|
||||
Spec: authenticationv1.TokenRequestSpec{
|
||||
Audiences: []string{"api"},
|
||||
},
|
||||
}
|
||||
|
||||
sa, del := createDeleteSvcAcct(t, cs, sa)
|
||||
defer del()
|
||||
|
||||
treq, err = cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
doTokenReview(t, cs, treq, false)
|
||||
|
||||
// backdate the token
|
||||
then := time.Now().Add(-2 * time.Hour)
|
||||
sc := &jwt.Claims{
|
||||
Subject: apiserverserviceaccount.MakeUsername(sa.Namespace, sa.Name),
|
||||
Audience: jwt.Audience([]string{"api"}),
|
||||
IssuedAt: jwt.NewNumericDate(then),
|
||||
NotBefore: jwt.NewNumericDate(then),
|
||||
Expiry: jwt.NewNumericDate(then.Add(time.Duration(60*60) * time.Second)),
|
||||
}
|
||||
coresa := core.ServiceAccount{
|
||||
ObjectMeta: sa.ObjectMeta,
|
||||
}
|
||||
_, pc := serviceaccount.Claims(coresa, nil, nil, 0, nil)
|
||||
tok, err := masterConfig.ExtraConfig.ServiceAccountIssuer.GenerateToken(sc, pc)
|
||||
if err != nil {
|
||||
t.Fatalf("err signing expired token: %v", err)
|
||||
}
|
||||
|
||||
treq.Status.Token = tok
|
||||
doTokenReview(t, cs, treq, true)
|
||||
})
|
||||
|
||||
t.Run("a token without an api audience is invalid", func(t *testing.T) {
|
||||
treq := &authenticationv1.TokenRequest{
|
||||
Spec: authenticationv1.TokenRequestSpec{
|
||||
Audiences: []string{"not-the-api"},
|
||||
},
|
||||
}
|
||||
|
||||
sa, del := createDeleteSvcAcct(t, cs, sa)
|
||||
defer del()
|
||||
|
||||
treq, err = cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
doTokenReview(t, cs, treq, true)
|
||||
})
|
||||
|
||||
t.Run("a tokenrequest without an audience is valid against the api", func(t *testing.T) {
|
||||
treq := &authenticationv1.TokenRequest{
|
||||
Spec: authenticationv1.TokenRequestSpec{},
|
||||
}
|
||||
|
||||
sa, del := createDeleteSvcAcct(t, cs, sa)
|
||||
defer del()
|
||||
|
||||
treq, err = cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
checkPayload(t, treq.Status.Token, `["api"]`, "aud")
|
||||
|
||||
doTokenReview(t, cs, treq, false)
|
||||
})
|
||||
|
||||
t.Run("a token should be invalid after recreating same name pod", func(t *testing.T) {
|
||||
treq := &authenticationv1.TokenRequest{
|
||||
Spec: authenticationv1.TokenRequestSpec{
|
||||
Audiences: []string{"api"},
|
||||
BoundObjectRef: &authenticationv1.BoundObjectReference{
|
||||
Kind: "Pod",
|
||||
APIVersion: "v1",
|
||||
Name: pod.Name,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
sa, del := createDeleteSvcAcct(t, cs, sa)
|
||||
defer del()
|
||||
originalPod, originalDelPod := createDeletePod(t, cs, pod)
|
||||
defer originalDelPod()
|
||||
|
||||
treq.Spec.BoundObjectRef.UID = originalPod.UID
|
||||
if treq, err = cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
checkPayload(t, treq.Status.Token, `"system:serviceaccount:myns:test-svcacct"`, "sub")
|
||||
checkPayload(t, treq.Status.Token, `["api"]`, "aud")
|
||||
checkPayload(t, treq.Status.Token, `"test-pod"`, "kubernetes.io", "pod", "name")
|
||||
checkPayload(t, treq.Status.Token, "null", "kubernetes.io", "secret")
|
||||
checkPayload(t, treq.Status.Token, `"myns"`, "kubernetes.io", "namespace")
|
||||
checkPayload(t, treq.Status.Token, `"test-svcacct"`, "kubernetes.io", "serviceaccount", "name")
|
||||
|
||||
doTokenReview(t, cs, treq, false)
|
||||
originalDelPod()
|
||||
doTokenReview(t, cs, treq, true)
|
||||
|
||||
_, recreateDelPod := createDeletePod(t, cs, pod)
|
||||
defer recreateDelPod()
|
||||
|
||||
doTokenReview(t, cs, treq, true)
|
||||
})
|
||||
|
||||
t.Run("a token should be invalid after recreating same name secret", func(t *testing.T) {
|
||||
treq := &authenticationv1.TokenRequest{
|
||||
Spec: authenticationv1.TokenRequestSpec{
|
||||
Audiences: []string{"api"},
|
||||
BoundObjectRef: &authenticationv1.BoundObjectReference{
|
||||
Kind: "Secret",
|
||||
APIVersion: "v1",
|
||||
Name: secret.Name,
|
||||
UID: secret.UID,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
sa, del := createDeleteSvcAcct(t, cs, sa)
|
||||
defer del()
|
||||
|
||||
originalSecret, originalDelSecret := createDeleteSecret(t, cs, secret)
|
||||
defer originalDelSecret()
|
||||
|
||||
treq.Spec.BoundObjectRef.UID = originalSecret.UID
|
||||
if treq, err = cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
checkPayload(t, treq.Status.Token, `"system:serviceaccount:myns:test-svcacct"`, "sub")
|
||||
checkPayload(t, treq.Status.Token, `["api"]`, "aud")
|
||||
checkPayload(t, treq.Status.Token, `null`, "kubernetes.io", "pod")
|
||||
checkPayload(t, treq.Status.Token, `"test-secret"`, "kubernetes.io", "secret", "name")
|
||||
checkPayload(t, treq.Status.Token, `"myns"`, "kubernetes.io", "namespace")
|
||||
checkPayload(t, treq.Status.Token, `"test-svcacct"`, "kubernetes.io", "serviceaccount", "name")
|
||||
|
||||
doTokenReview(t, cs, treq, false)
|
||||
originalDelSecret()
|
||||
doTokenReview(t, cs, treq, true)
|
||||
|
||||
_, recreateDelSecret := createDeleteSecret(t, cs, secret)
|
||||
defer recreateDelSecret()
|
||||
|
||||
doTokenReview(t, cs, treq, true)
|
||||
})
|
||||
}
|
||||
|
||||
func doTokenReview(t *testing.T, cs externalclientset.Interface, treq *authenticationv1.TokenRequest, expectErr bool) {
|
||||
t.Helper()
|
||||
trev, err := cs.AuthenticationV1().TokenReviews().Create(&authenticationv1.TokenReview{
|
||||
Spec: authenticationv1.TokenReviewSpec{
|
||||
Token: treq.Status.Token,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
t.Logf("status: %+v", trev.Status)
|
||||
if (trev.Status.Error != "") && !expectErr {
|
||||
t.Fatalf("expected no error but got: %v", trev.Status.Error)
|
||||
}
|
||||
if (trev.Status.Error == "") && expectErr {
|
||||
t.Fatalf("expected error but got: %+v", trev.Status)
|
||||
}
|
||||
if !trev.Status.Authenticated && !expectErr {
|
||||
t.Fatal("expected token to be authenticated but it wasn't")
|
||||
}
|
||||
}
|
||||
|
||||
func checkPayload(t *testing.T, tok string, want string, parts ...string) {
|
||||
t.Helper()
|
||||
got := getSubObject(t, getPayload(t, tok), parts...)
|
||||
if got != want {
|
||||
t.Errorf("unexpected payload.\nsaw:\t%v\nwant:\t%v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func getSubObject(t *testing.T, b string, parts ...string) string {
|
||||
t.Helper()
|
||||
var obj interface{}
|
||||
obj = make(map[string]interface{})
|
||||
if err := json.Unmarshal([]byte(b), &obj); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
for _, part := range parts {
|
||||
obj = obj.(map[string]interface{})[part]
|
||||
}
|
||||
out, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
return string(out)
|
||||
}
|
||||
|
||||
func getPayload(t *testing.T, b string) string {
|
||||
t.Helper()
|
||||
parts := strings.Split(b, ".")
|
||||
if len(parts) != 3 {
|
||||
t.Fatalf("token did not have three parts: %v", b)
|
||||
}
|
||||
payload, err := base64.RawURLEncoding.DecodeString(parts[1])
|
||||
if err != nil {
|
||||
t.Fatalf("failed to base64 decode token: %v", err)
|
||||
}
|
||||
return string(payload)
|
||||
}
|
||||
|
||||
func createDeleteSvcAcct(t *testing.T, cs clientset.Interface, sa *v1.ServiceAccount) (*v1.ServiceAccount, func()) {
|
||||
t.Helper()
|
||||
sa, err := cs.CoreV1().ServiceAccounts(sa.Namespace).Create(sa)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
done := false
|
||||
return sa, func() {
|
||||
t.Helper()
|
||||
if done {
|
||||
return
|
||||
}
|
||||
done = true
|
||||
if err := cs.CoreV1().ServiceAccounts(sa.Namespace).Delete(sa.Name, nil); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func createDeletePod(t *testing.T, cs clientset.Interface, pod *v1.Pod) (*v1.Pod, func()) {
|
||||
t.Helper()
|
||||
pod, err := cs.CoreV1().Pods(pod.Namespace).Create(pod)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
done := false
|
||||
return pod, func() {
|
||||
t.Helper()
|
||||
if done {
|
||||
return
|
||||
}
|
||||
done = true
|
||||
if err := cs.CoreV1().Pods(pod.Namespace).Delete(pod.Name, nil); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func createDeleteSecret(t *testing.T, cs clientset.Interface, sec *v1.Secret) (*v1.Secret, func()) {
|
||||
t.Helper()
|
||||
sec, err := cs.CoreV1().Secrets(sec.Namespace).Create(sec)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
done := false
|
||||
return sec, func() {
|
||||
t.Helper()
|
||||
if done {
|
||||
return
|
||||
}
|
||||
done = true
|
||||
if err := cs.CoreV1().Secrets(sec.Namespace).Delete(sec.Name, nil); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
25
vendor/k8s.io/kubernetes/test/integration/benchmark-controller.json
generated
vendored
Normal file
25
vendor/k8s.io/kubernetes/test/integration/benchmark-controller.json
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"kind": "ReplicationController",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "test-controller",
|
||||
"namespace": "test",
|
||||
"labels": {"name": "test-controller"}
|
||||
},
|
||||
"spec": {
|
||||
"replicas": 0,
|
||||
"selector": {"name": "test-pod"},
|
||||
"template": {
|
||||
"metadata": {
|
||||
"namespace": "test",
|
||||
"labels": {"name": "test-pod"}
|
||||
},
|
||||
"spec": {
|
||||
"containers": [{
|
||||
"name": "test-container",
|
||||
"image": "k8s.gcr.io/pause:3.1"
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
32
vendor/k8s.io/kubernetes/test/integration/benchmark/jsonify/BUILD
generated
vendored
Normal file
32
vendor/k8s.io/kubernetes/test/integration/benchmark/jsonify/BUILD
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["main.go"],
|
||||
importpath = "k8s.io/kubernetes/test/integration/benchmark/jsonify",
|
||||
visibility = ["//visibility:private"],
|
||||
deps = [
|
||||
"//test/e2e/perftype:go_default_library",
|
||||
"//vendor/golang.org/x/tools/benchmark/parse:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_binary(
|
||||
name = "jsonify",
|
||||
embed = [":go_default_library"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
77
vendor/k8s.io/kubernetes/test/integration/benchmark/jsonify/main.go
generated
vendored
Normal file
77
vendor/k8s.io/kubernetes/test/integration/benchmark/jsonify/main.go
generated
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
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 main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
benchparse "golang.org/x/tools/benchmark/parse"
|
||||
"k8s.io/kubernetes/test/e2e/perftype"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := run()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func run() error {
|
||||
if len(os.Args) < 2 {
|
||||
return fmt.Errorf("output filename is a required argument")
|
||||
}
|
||||
benchmarkSet, err := benchparse.ParseSet(os.Stdin)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data := perftype.PerfData{Version: "v1"}
|
||||
for _, benchMarks := range benchmarkSet {
|
||||
for _, benchMark := range benchMarks {
|
||||
data.DataItems = appendIfMeasured(data.DataItems, benchMark, benchparse.NsPerOp, "time", "μs", benchMark.NsPerOp/1000.0)
|
||||
data.DataItems = appendIfMeasured(data.DataItems, benchMark, benchparse.MBPerS, "throughput", "MBps", benchMark.MBPerS)
|
||||
data.DataItems = appendIfMeasured(data.DataItems, benchMark, benchparse.AllocedBytesPerOp, "allocated", "bytes", float64(benchMark.AllocedBytesPerOp))
|
||||
data.DataItems = appendIfMeasured(data.DataItems, benchMark, benchparse.AllocsPerOp, "allocations", "1", float64(benchMark.AllocsPerOp))
|
||||
data.DataItems = appendIfMeasured(data.DataItems, benchMark, 0, "iterations", "1", float64(benchMark.N))
|
||||
}
|
||||
}
|
||||
output := &bytes.Buffer{}
|
||||
if err := json.NewEncoder(output).Encode(data); err != nil {
|
||||
return err
|
||||
}
|
||||
formatted := &bytes.Buffer{}
|
||||
if err := json.Indent(formatted, output.Bytes(), "", " "); err != nil {
|
||||
return err
|
||||
}
|
||||
return ioutil.WriteFile(os.Args[1], formatted.Bytes(), 0664)
|
||||
}
|
||||
|
||||
func appendIfMeasured(items []perftype.DataItem, benchmark *benchparse.Benchmark, metricType int, metricName string, unit string, value float64) []perftype.DataItem {
|
||||
if metricType != 0 && (benchmark.Measured&metricType) == 0 {
|
||||
return items
|
||||
}
|
||||
return append(items, perftype.DataItem{
|
||||
Unit: unit,
|
||||
Labels: map[string]string{
|
||||
"benchmark": benchmark.Name,
|
||||
"metricName": metricName},
|
||||
Data: map[string]float64{
|
||||
"value": value}})
|
||||
}
|
||||
51
vendor/k8s.io/kubernetes/test/integration/client/BUILD
generated
vendored
Normal file
51
vendor/k8s.io/kubernetes/test/integration/client/BUILD
generated
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
size = "large",
|
||||
srcs = [
|
||||
"client_test.go",
|
||||
"dynamic_client_test.go",
|
||||
"main_test.go",
|
||||
],
|
||||
tags = ["integration"],
|
||||
deps = [
|
||||
"//pkg/api/legacyscheme:go_default_library",
|
||||
"//pkg/api/testapi:go_default_library",
|
||||
"//pkg/version:go_default_library",
|
||||
"//test/integration/framework:go_default_library",
|
||||
"//test/utils/image:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/fields:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/watch:go_default_library",
|
||||
"//vendor/k8s.io/client-go/dynamic:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
818
vendor/k8s.io/kubernetes/test/integration/client/client_test.go
generated
vendored
Normal file
818
vendor/k8s.io/kubernetes/test/integration/client/client_test.go
generated
vendored
Normal file
@@ -0,0 +1,818 @@
|
||||
/*
|
||||
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 client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"reflect"
|
||||
rt "runtime"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
"k8s.io/kubernetes/pkg/version"
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
imageutils "k8s.io/kubernetes/test/utils/image"
|
||||
)
|
||||
|
||||
func TestClient(t *testing.T) {
|
||||
_, s, closeFn := framework.RunAMaster(nil)
|
||||
defer closeFn()
|
||||
|
||||
client := clientset.NewForConfigOrDie(&restclient.Config{Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}}})
|
||||
|
||||
ns := framework.CreateTestingNamespace("client", s, t)
|
||||
defer framework.DeleteTestingNamespace(ns, s, t)
|
||||
|
||||
info, err := client.Discovery().ServerVersion()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if e, a := version.Get(), *info; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("expected %#v, got %#v", e, a)
|
||||
}
|
||||
|
||||
pods, err := client.Core().Pods(ns.Name).List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if len(pods.Items) != 0 {
|
||||
t.Errorf("expected no pods, got %#v", pods)
|
||||
}
|
||||
|
||||
// get a validation error
|
||||
pod := &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
GenerateName: "test",
|
||||
Namespace: ns.Name,
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Name: "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
got, err := client.Core().Pods(ns.Name).Create(pod)
|
||||
if err == nil {
|
||||
t.Fatalf("unexpected non-error: %v", got)
|
||||
}
|
||||
|
||||
// get a created pod
|
||||
pod.Spec.Containers[0].Image = "an-image"
|
||||
got, err = client.Core().Pods(ns.Name).Create(pod)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if got.Name == "" {
|
||||
t.Errorf("unexpected empty pod Name %v", got)
|
||||
}
|
||||
|
||||
// pod is shown, but not scheduled
|
||||
pods, err = client.Core().Pods(ns.Name).List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if len(pods.Items) != 1 {
|
||||
t.Errorf("expected one pod, got %#v", pods)
|
||||
}
|
||||
actual := pods.Items[0]
|
||||
if actual.Name != got.Name {
|
||||
t.Errorf("expected pod %#v, got %#v", got, actual)
|
||||
}
|
||||
if actual.Spec.NodeName != "" {
|
||||
t.Errorf("expected pod to be unscheduled, got %#v", actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAtomicPut(t *testing.T) {
|
||||
_, s, closeFn := framework.RunAMaster(nil)
|
||||
defer closeFn()
|
||||
|
||||
c := clientset.NewForConfigOrDie(&restclient.Config{Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}}})
|
||||
|
||||
ns := framework.CreateTestingNamespace("atomic-put", s, t)
|
||||
defer framework.DeleteTestingNamespace(ns, s, t)
|
||||
|
||||
rcBody := v1.ReplicationController{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: c.Core().RESTClient().APIVersion().String(),
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "atomicrc",
|
||||
Namespace: ns.Name,
|
||||
Labels: map[string]string{
|
||||
"name": "atomicrc",
|
||||
},
|
||||
},
|
||||
Spec: v1.ReplicationControllerSpec{
|
||||
Replicas: func(i int32) *int32 { return &i }(0),
|
||||
Selector: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
Template: &v1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{Name: "name", Image: "image"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
rcs := c.Core().ReplicationControllers(ns.Name)
|
||||
rc, err := rcs.Create(&rcBody)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed creating atomicRC: %v", err)
|
||||
}
|
||||
testLabels := labels.Set{
|
||||
"foo": "bar",
|
||||
}
|
||||
for i := 0; i < 5; i++ {
|
||||
// a: z, b: y, etc...
|
||||
testLabels[string([]byte{byte('a' + i)})] = string([]byte{byte('z' - i)})
|
||||
}
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(testLabels))
|
||||
for label, value := range testLabels {
|
||||
go func(l, v string) {
|
||||
defer wg.Done()
|
||||
for {
|
||||
tmpRC, err := rcs.Get(rc.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Errorf("Error getting atomicRC: %v", err)
|
||||
continue
|
||||
}
|
||||
if tmpRC.Spec.Selector == nil {
|
||||
tmpRC.Spec.Selector = map[string]string{l: v}
|
||||
tmpRC.Spec.Template.Labels = map[string]string{l: v}
|
||||
} else {
|
||||
tmpRC.Spec.Selector[l] = v
|
||||
tmpRC.Spec.Template.Labels[l] = v
|
||||
}
|
||||
tmpRC, err = rcs.Update(tmpRC)
|
||||
if err != nil {
|
||||
if apierrors.IsConflict(err) {
|
||||
// This is what we expect.
|
||||
continue
|
||||
}
|
||||
t.Errorf("Unexpected error putting atomicRC: %v", err)
|
||||
continue
|
||||
}
|
||||
return
|
||||
}
|
||||
}(label, value)
|
||||
}
|
||||
wg.Wait()
|
||||
rc, err = rcs.Get(rc.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed getting atomicRC after writers are complete: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(testLabels, labels.Set(rc.Spec.Selector)) {
|
||||
t.Errorf("Selector PUTs were not atomic: wanted %v, got %v", testLabels, rc.Spec.Selector)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPatch(t *testing.T) {
|
||||
_, s, closeFn := framework.RunAMaster(nil)
|
||||
defer closeFn()
|
||||
|
||||
c := clientset.NewForConfigOrDie(&restclient.Config{Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}}})
|
||||
|
||||
ns := framework.CreateTestingNamespace("patch", s, t)
|
||||
defer framework.DeleteTestingNamespace(ns, s, t)
|
||||
|
||||
name := "patchpod"
|
||||
resource := "pods"
|
||||
podBody := v1.Pod{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: c.Core().RESTClient().APIVersion().String(),
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: ns.Name,
|
||||
Labels: map[string]string{},
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{Name: "name", Image: "image"},
|
||||
},
|
||||
},
|
||||
}
|
||||
pods := c.Core().Pods(ns.Name)
|
||||
pod, err := pods.Create(&podBody)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed creating patchpods: %v", err)
|
||||
}
|
||||
|
||||
patchBodies := map[schema.GroupVersion]map[types.PatchType]struct {
|
||||
AddLabelBody []byte
|
||||
RemoveLabelBody []byte
|
||||
RemoveAllLabelsBody []byte
|
||||
}{
|
||||
v1.SchemeGroupVersion: {
|
||||
types.JSONPatchType: {
|
||||
[]byte(`[{"op":"add","path":"/metadata/labels","value":{"foo":"bar","baz":"qux"}}]`),
|
||||
[]byte(`[{"op":"remove","path":"/metadata/labels/foo"}]`),
|
||||
[]byte(`[{"op":"remove","path":"/metadata/labels"}]`),
|
||||
},
|
||||
types.MergePatchType: {
|
||||
[]byte(`{"metadata":{"labels":{"foo":"bar","baz":"qux"}}}`),
|
||||
[]byte(`{"metadata":{"labels":{"foo":null}}}`),
|
||||
[]byte(`{"metadata":{"labels":null}}`),
|
||||
},
|
||||
types.StrategicMergePatchType: {
|
||||
[]byte(`{"metadata":{"labels":{"foo":"bar","baz":"qux"}}}`),
|
||||
[]byte(`{"metadata":{"labels":{"foo":null}}}`),
|
||||
[]byte(`{"metadata":{"labels":{"$patch":"replace"}}}`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
pb := patchBodies[c.Core().RESTClient().APIVersion()]
|
||||
|
||||
execPatch := func(pt types.PatchType, body []byte) error {
|
||||
result := c.Core().RESTClient().Patch(pt).
|
||||
Resource(resource).
|
||||
Namespace(ns.Name).
|
||||
Name(name).
|
||||
Body(body).
|
||||
Do()
|
||||
if result.Error() != nil {
|
||||
return result.Error()
|
||||
}
|
||||
|
||||
// trying to chase flakes, this should give us resource versions of objects as we step through
|
||||
jsonObj, err := result.Raw()
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
} else {
|
||||
t.Logf("%v", string(jsonObj))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
for k, v := range pb {
|
||||
// add label
|
||||
err := execPatch(k, v.AddLabelBody)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed updating patchpod with patch type %s: %v", k, err)
|
||||
}
|
||||
pod, err = pods.Get(name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed getting patchpod: %v", err)
|
||||
}
|
||||
if len(pod.Labels) != 2 || pod.Labels["foo"] != "bar" || pod.Labels["baz"] != "qux" {
|
||||
t.Errorf("Failed updating patchpod with patch type %s: labels are: %v", k, pod.Labels)
|
||||
}
|
||||
|
||||
// remove one label
|
||||
err = execPatch(k, v.RemoveLabelBody)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed updating patchpod with patch type %s: %v", k, err)
|
||||
}
|
||||
pod, err = pods.Get(name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed getting patchpod: %v", err)
|
||||
}
|
||||
if len(pod.Labels) != 1 || pod.Labels["baz"] != "qux" {
|
||||
t.Errorf("Failed updating patchpod with patch type %s: labels are: %v", k, pod.Labels)
|
||||
}
|
||||
|
||||
// remove all labels
|
||||
err = execPatch(k, v.RemoveAllLabelsBody)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed updating patchpod with patch type %s: %v", k, err)
|
||||
}
|
||||
pod, err = pods.Get(name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed getting patchpod: %v", err)
|
||||
}
|
||||
if pod.Labels != nil {
|
||||
t.Errorf("Failed remove all labels from patchpod with patch type %s: %v", k, pod.Labels)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPatchWithCreateOnUpdate(t *testing.T) {
|
||||
_, s, closeFn := framework.RunAMaster(nil)
|
||||
defer closeFn()
|
||||
|
||||
c := clientset.NewForConfigOrDie(&restclient.Config{Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}}})
|
||||
|
||||
ns := framework.CreateTestingNamespace("patch-with-create", s, t)
|
||||
defer framework.DeleteTestingNamespace(ns, s, t)
|
||||
|
||||
endpointTemplate := &v1.Endpoints{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "patchendpoint",
|
||||
Namespace: ns.Name,
|
||||
},
|
||||
Subsets: []v1.EndpointSubset{
|
||||
{
|
||||
Addresses: []v1.EndpointAddress{{IP: "1.2.3.4"}},
|
||||
Ports: []v1.EndpointPort{{Port: 80, Protocol: v1.ProtocolTCP}},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
patchEndpoint := func(json []byte) (runtime.Object, error) {
|
||||
return c.Core().RESTClient().Patch(types.MergePatchType).Resource("endpoints").Namespace(ns.Name).Name("patchendpoint").Body(json).Do().Get()
|
||||
}
|
||||
|
||||
// Make sure patch doesn't get to CreateOnUpdate
|
||||
{
|
||||
endpointJSON, err := runtime.Encode(legacyscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), endpointTemplate)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed creating endpoint JSON: %v", err)
|
||||
}
|
||||
if obj, err := patchEndpoint(endpointJSON); !apierrors.IsNotFound(err) {
|
||||
t.Errorf("Expected notfound creating from patch, got error=%v and object: %#v", err, obj)
|
||||
}
|
||||
}
|
||||
|
||||
// Create the endpoint (endpoints set AllowCreateOnUpdate=true) to get a UID and resource version
|
||||
createdEndpoint, err := c.Core().Endpoints(ns.Name).Update(endpointTemplate)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed creating endpoint: %v", err)
|
||||
}
|
||||
|
||||
// Make sure identity patch is accepted
|
||||
{
|
||||
endpointJSON, err := runtime.Encode(legacyscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), createdEndpoint)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed creating endpoint JSON: %v", err)
|
||||
}
|
||||
if _, err := patchEndpoint(endpointJSON); err != nil {
|
||||
t.Errorf("Failed patching endpoint: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure patch complains about a mismatched resourceVersion
|
||||
{
|
||||
endpointTemplate.Name = ""
|
||||
endpointTemplate.UID = ""
|
||||
endpointTemplate.ResourceVersion = "1"
|
||||
endpointJSON, err := runtime.Encode(legacyscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), endpointTemplate)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed creating endpoint JSON: %v", err)
|
||||
}
|
||||
if _, err := patchEndpoint(endpointJSON); !apierrors.IsConflict(err) {
|
||||
t.Errorf("Expected error, got %#v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure patch complains about mutating the UID
|
||||
{
|
||||
endpointTemplate.Name = ""
|
||||
endpointTemplate.UID = "abc"
|
||||
endpointTemplate.ResourceVersion = ""
|
||||
endpointJSON, err := runtime.Encode(legacyscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), endpointTemplate)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed creating endpoint JSON: %v", err)
|
||||
}
|
||||
if _, err := patchEndpoint(endpointJSON); !apierrors.IsInvalid(err) {
|
||||
t.Errorf("Expected error, got %#v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure patch complains about a mismatched name
|
||||
{
|
||||
endpointTemplate.Name = "changedname"
|
||||
endpointTemplate.UID = ""
|
||||
endpointTemplate.ResourceVersion = ""
|
||||
endpointJSON, err := runtime.Encode(legacyscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), endpointTemplate)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed creating endpoint JSON: %v", err)
|
||||
}
|
||||
if _, err := patchEndpoint(endpointJSON); !apierrors.IsBadRequest(err) {
|
||||
t.Errorf("Expected error, got %#v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure patch containing originally submitted JSON is accepted
|
||||
{
|
||||
endpointTemplate.Name = ""
|
||||
endpointTemplate.UID = ""
|
||||
endpointTemplate.ResourceVersion = ""
|
||||
endpointJSON, err := runtime.Encode(legacyscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), endpointTemplate)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed creating endpoint JSON: %v", err)
|
||||
}
|
||||
if _, err := patchEndpoint(endpointJSON); err != nil {
|
||||
t.Errorf("Failed patching endpoint: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAPIVersions(t *testing.T) {
|
||||
_, s, closeFn := framework.RunAMaster(nil)
|
||||
defer closeFn()
|
||||
|
||||
c := clientset.NewForConfigOrDie(&restclient.Config{Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}}})
|
||||
|
||||
clientVersion := c.Core().RESTClient().APIVersion().String()
|
||||
g, err := c.Discovery().ServerGroups()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get api versions: %v", err)
|
||||
}
|
||||
versions := metav1.ExtractGroupVersions(g)
|
||||
|
||||
// Verify that the server supports the API version used by the client.
|
||||
for _, version := range versions {
|
||||
if version == clientVersion {
|
||||
return
|
||||
}
|
||||
}
|
||||
t.Errorf("Server does not support APIVersion used by client. Server supported APIVersions: '%v', client APIVersion: '%v'", versions, clientVersion)
|
||||
}
|
||||
|
||||
func TestSingleWatch(t *testing.T) {
|
||||
_, s, closeFn := framework.RunAMaster(nil)
|
||||
defer closeFn()
|
||||
|
||||
ns := framework.CreateTestingNamespace("single-watch", s, t)
|
||||
defer framework.DeleteTestingNamespace(ns, s, t)
|
||||
|
||||
client := clientset.NewForConfigOrDie(&restclient.Config{Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}}})
|
||||
|
||||
mkEvent := func(i int) *v1.Event {
|
||||
name := fmt.Sprintf("event-%v", i)
|
||||
return &v1.Event{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: ns.Name,
|
||||
Name: name,
|
||||
},
|
||||
InvolvedObject: v1.ObjectReference{
|
||||
Namespace: ns.Name,
|
||||
Name: name,
|
||||
},
|
||||
Reason: fmt.Sprintf("event %v", i),
|
||||
}
|
||||
}
|
||||
|
||||
rv1 := ""
|
||||
for i := 0; i < 10; i++ {
|
||||
event := mkEvent(i)
|
||||
got, err := client.Core().Events(ns.Name).Create(event)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed creating event %#q: %v", event, err)
|
||||
}
|
||||
if rv1 == "" {
|
||||
rv1 = got.ResourceVersion
|
||||
if rv1 == "" {
|
||||
t.Fatal("did not get a resource version.")
|
||||
}
|
||||
}
|
||||
t.Logf("Created event %#v", got.ObjectMeta)
|
||||
}
|
||||
|
||||
w, err := client.Core().RESTClient().Get().
|
||||
Namespace(ns.Name).
|
||||
Resource("events").
|
||||
VersionedParams(&metav1.ListOptions{
|
||||
ResourceVersion: rv1,
|
||||
Watch: true,
|
||||
FieldSelector: fields.OneTermEqualSelector("metadata.name", "event-9").String(),
|
||||
}, metav1.ParameterCodec).
|
||||
Watch()
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Failed watch: %v", err)
|
||||
}
|
||||
defer w.Stop()
|
||||
|
||||
select {
|
||||
case <-time.After(wait.ForeverTestTimeout):
|
||||
t.Fatalf("watch took longer than %s", wait.ForeverTestTimeout.String())
|
||||
case got, ok := <-w.ResultChan():
|
||||
if !ok {
|
||||
t.Fatal("Watch channel closed unexpectedly.")
|
||||
}
|
||||
|
||||
// We expect to see an ADD of event-9 and only event-9. (This
|
||||
// catches a bug where all the events would have been sent down
|
||||
// the channel.)
|
||||
if e, a := watch.Added, got.Type; e != a {
|
||||
t.Errorf("Wanted %v, got %v", e, a)
|
||||
}
|
||||
switch o := got.Object.(type) {
|
||||
case *v1.Event:
|
||||
if e, a := "event-9", o.Name; e != a {
|
||||
t.Errorf("Wanted %v, got %v", e, a)
|
||||
}
|
||||
default:
|
||||
t.Fatalf("Unexpected watch event containing object %#q", got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultiWatch(t *testing.T) {
|
||||
// Disable this test as long as it demonstrates a problem.
|
||||
// TODO: Re-enable this test when we get #6059 resolved.
|
||||
t.Skip()
|
||||
|
||||
const watcherCount = 50
|
||||
rt.GOMAXPROCS(watcherCount)
|
||||
|
||||
_, s, closeFn := framework.RunAMaster(nil)
|
||||
defer closeFn()
|
||||
|
||||
ns := framework.CreateTestingNamespace("multi-watch", s, t)
|
||||
defer framework.DeleteTestingNamespace(ns, s, t)
|
||||
|
||||
client := clientset.NewForConfigOrDie(&restclient.Config{Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}}})
|
||||
|
||||
dummyEvent := func(i int) *v1.Event {
|
||||
name := fmt.Sprintf("unrelated-%v", i)
|
||||
return &v1.Event{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: fmt.Sprintf("%v.%x", name, time.Now().UnixNano()),
|
||||
Namespace: ns.Name,
|
||||
},
|
||||
InvolvedObject: v1.ObjectReference{
|
||||
Name: name,
|
||||
Namespace: ns.Name,
|
||||
},
|
||||
Reason: fmt.Sprintf("unrelated change %v", i),
|
||||
}
|
||||
}
|
||||
|
||||
type timePair struct {
|
||||
t time.Time
|
||||
name string
|
||||
}
|
||||
|
||||
receivedTimes := make(chan timePair, watcherCount*2)
|
||||
watchesStarted := sync.WaitGroup{}
|
||||
|
||||
// make a bunch of pods and watch them
|
||||
for i := 0; i < watcherCount; i++ {
|
||||
watchesStarted.Add(1)
|
||||
name := fmt.Sprintf("multi-watch-%v", i)
|
||||
got, err := client.Core().Pods(ns.Name).Create(&v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Labels: labels.Set{"watchlabel": name},
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{{
|
||||
Name: "pause",
|
||||
Image: imageutils.GetPauseImageName(),
|
||||
}},
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't make %v: %v", name, err)
|
||||
}
|
||||
go func(name, rv string) {
|
||||
options := metav1.ListOptions{
|
||||
LabelSelector: labels.Set{"watchlabel": name}.AsSelector().String(),
|
||||
ResourceVersion: rv,
|
||||
}
|
||||
w, err := client.Core().Pods(ns.Name).Watch(options)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("watch error for %v: %v", name, err))
|
||||
}
|
||||
defer w.Stop()
|
||||
watchesStarted.Done()
|
||||
e, ok := <-w.ResultChan() // should get the update (that we'll do below)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("%v ended early?", name))
|
||||
}
|
||||
if e.Type != watch.Modified {
|
||||
panic(fmt.Sprintf("Got unexpected watch notification:\n%v: %+v %+v", name, e, e.Object))
|
||||
}
|
||||
receivedTimes <- timePair{time.Now(), name}
|
||||
}(name, got.ObjectMeta.ResourceVersion)
|
||||
}
|
||||
log.Printf("%v: %v pods made and watchers started", time.Now(), watcherCount)
|
||||
|
||||
// wait for watches to start before we start spamming the system with
|
||||
// objects below, otherwise we'll hit the watch window restriction.
|
||||
watchesStarted.Wait()
|
||||
|
||||
const (
|
||||
useEventsAsUnrelatedType = false
|
||||
usePodsAsUnrelatedType = true
|
||||
)
|
||||
|
||||
// make a bunch of unrelated changes in parallel
|
||||
if useEventsAsUnrelatedType {
|
||||
const unrelatedCount = 3000
|
||||
var wg sync.WaitGroup
|
||||
defer wg.Wait()
|
||||
changeToMake := make(chan int, unrelatedCount*2)
|
||||
changeMade := make(chan int, unrelatedCount*2)
|
||||
go func() {
|
||||
for i := 0; i < unrelatedCount; i++ {
|
||||
changeToMake <- i
|
||||
}
|
||||
close(changeToMake)
|
||||
}()
|
||||
for i := 0; i < 50; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for {
|
||||
i, ok := <-changeToMake
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if _, err := client.Core().Events(ns.Name).Create(dummyEvent(i)); err != nil {
|
||||
panic(fmt.Sprintf("couldn't make an event: %v", err))
|
||||
}
|
||||
changeMade <- i
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
for i := 0; i < 2000; i++ {
|
||||
<-changeMade
|
||||
if (i+1)%50 == 0 {
|
||||
log.Printf("%v: %v unrelated changes made", time.Now(), i+1)
|
||||
}
|
||||
}
|
||||
}
|
||||
if usePodsAsUnrelatedType {
|
||||
const unrelatedCount = 3000
|
||||
var wg sync.WaitGroup
|
||||
defer wg.Wait()
|
||||
changeToMake := make(chan int, unrelatedCount*2)
|
||||
changeMade := make(chan int, unrelatedCount*2)
|
||||
go func() {
|
||||
for i := 0; i < unrelatedCount; i++ {
|
||||
changeToMake <- i
|
||||
}
|
||||
close(changeToMake)
|
||||
}()
|
||||
for i := 0; i < 50; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for {
|
||||
i, ok := <-changeToMake
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
name := fmt.Sprintf("unrelated-%v", i)
|
||||
_, err := client.Core().Pods(ns.Name).Create(&v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{{
|
||||
Name: "nothing",
|
||||
Image: imageutils.GetPauseImageName(),
|
||||
}},
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("couldn't make unrelated pod: %v", err))
|
||||
}
|
||||
changeMade <- i
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
for i := 0; i < 2000; i++ {
|
||||
<-changeMade
|
||||
if (i+1)%50 == 0 {
|
||||
log.Printf("%v: %v unrelated changes made", time.Now(), i+1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now we still have changes being made in parallel, but at least 1000 have been made.
|
||||
// Make some updates to send down the watches.
|
||||
sentTimes := make(chan timePair, watcherCount*2)
|
||||
for i := 0; i < watcherCount; i++ {
|
||||
go func(i int) {
|
||||
name := fmt.Sprintf("multi-watch-%v", i)
|
||||
pod, err := client.Core().Pods(ns.Name).Get(name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Couldn't get %v: %v", name, err))
|
||||
}
|
||||
pod.Spec.Containers[0].Image = imageutils.GetPauseImageName()
|
||||
sentTimes <- timePair{time.Now(), name}
|
||||
if _, err := client.Core().Pods(ns.Name).Update(pod); err != nil {
|
||||
panic(fmt.Sprintf("Couldn't make %v: %v", name, err))
|
||||
}
|
||||
}(i)
|
||||
}
|
||||
|
||||
sent := map[string]time.Time{}
|
||||
for i := 0; i < watcherCount; i++ {
|
||||
tp := <-sentTimes
|
||||
sent[tp.name] = tp.t
|
||||
}
|
||||
log.Printf("all changes made")
|
||||
dur := map[string]time.Duration{}
|
||||
for i := 0; i < watcherCount; i++ {
|
||||
tp := <-receivedTimes
|
||||
delta := tp.t.Sub(sent[tp.name])
|
||||
dur[tp.name] = delta
|
||||
log.Printf("%v: %v", tp.name, delta)
|
||||
}
|
||||
log.Printf("all watches ended")
|
||||
t.Errorf("durations: %v", dur)
|
||||
}
|
||||
|
||||
func runSelfLinkTestOnNamespace(t *testing.T, c clientset.Interface, namespace string) {
|
||||
podBody := v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "selflinktest",
|
||||
Namespace: namespace,
|
||||
Labels: map[string]string{
|
||||
"name": "selflinktest",
|
||||
},
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{Name: "name", Image: "image"},
|
||||
},
|
||||
},
|
||||
}
|
||||
pod, err := c.CoreV1().Pods(namespace).Create(&podBody)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed creating selflinktest pod: %v", err)
|
||||
}
|
||||
if err = c.CoreV1().RESTClient().Get().RequestURI(pod.SelfLink).Do().Into(pod); err != nil {
|
||||
t.Errorf("Failed listing pod with supplied self link '%v': %v", pod.SelfLink, err)
|
||||
}
|
||||
|
||||
podList, err := c.CoreV1().Pods(namespace).List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
t.Errorf("Failed listing pods: %v", err)
|
||||
}
|
||||
|
||||
if err = c.CoreV1().RESTClient().Get().RequestURI(podList.SelfLink).Do().Into(podList); err != nil {
|
||||
t.Errorf("Failed listing pods with supplied self link '%v': %v", podList.SelfLink, err)
|
||||
}
|
||||
|
||||
found := false
|
||||
for i := range podList.Items {
|
||||
item := &podList.Items[i]
|
||||
if item.Name != "selflinktest" {
|
||||
continue
|
||||
}
|
||||
found = true
|
||||
err = c.CoreV1().RESTClient().Get().RequestURI(item.SelfLink).Do().Into(pod)
|
||||
if err != nil {
|
||||
t.Errorf("Failed listing pod with supplied self link '%v': %v", item.SelfLink, err)
|
||||
}
|
||||
break
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("never found selflinktest pod in namespace %s", namespace)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSelfLinkOnNamespace(t *testing.T) {
|
||||
_, s, closeFn := framework.RunAMaster(nil)
|
||||
defer closeFn()
|
||||
|
||||
ns := framework.CreateTestingNamespace("selflink", s, t)
|
||||
defer framework.DeleteTestingNamespace(ns, s, t)
|
||||
|
||||
c := clientset.NewForConfigOrDie(&restclient.Config{Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}}})
|
||||
|
||||
runSelfLinkTestOnNamespace(t, c, ns.Name)
|
||||
}
|
||||
136
vendor/k8s.io/kubernetes/test/integration/client/dynamic_client_test.go
generated
vendored
Normal file
136
vendor/k8s.io/kubernetes/test/integration/client/dynamic_client_test.go
generated
vendored
Normal file
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
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 client
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/dynamic"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/kubernetes/pkg/api/testapi"
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
)
|
||||
|
||||
func TestDynamicClient(t *testing.T) {
|
||||
_, s, closeFn := framework.RunAMaster(nil)
|
||||
defer closeFn()
|
||||
|
||||
ns := framework.CreateTestingNamespace("dynamic-client", s, t)
|
||||
defer framework.DeleteTestingNamespace(ns, s, t)
|
||||
|
||||
gv := &schema.GroupVersion{Group: "", Version: "v1"}
|
||||
config := &restclient.Config{
|
||||
Host: s.URL,
|
||||
ContentConfig: restclient.ContentConfig{GroupVersion: gv},
|
||||
}
|
||||
|
||||
client := clientset.NewForConfigOrDie(config)
|
||||
dynamicClient, err := dynamic.NewForConfig(config)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error creating dynamic client: %v", err)
|
||||
}
|
||||
|
||||
resource := schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}
|
||||
|
||||
// Create a Pod with the normal client
|
||||
pod := &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
GenerateName: "test",
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Name: "test",
|
||||
Image: "test-image",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
actual, err := client.Core().Pods(ns.Name).Create(pod)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error when creating pod: %v", err)
|
||||
}
|
||||
|
||||
// check dynamic list
|
||||
unstructuredList, err := dynamicClient.Resource(resource).Namespace(ns.Name).List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error when listing pods: %v", err)
|
||||
}
|
||||
|
||||
if len(unstructuredList.Items) != 1 {
|
||||
t.Fatalf("expected one pod, got %d", len(unstructuredList.Items))
|
||||
}
|
||||
|
||||
got, err := unstructuredToPod(&unstructuredList.Items[0])
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error converting Unstructured to v1.Pod: %v", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(actual, got) {
|
||||
t.Fatalf("unexpected pod in list. wanted %#v, got %#v", actual, got)
|
||||
}
|
||||
|
||||
// check dynamic get
|
||||
unstruct, err := dynamicClient.Resource(resource).Namespace(ns.Name).Get(actual.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error when getting pod %q: %v", actual.Name, err)
|
||||
}
|
||||
|
||||
got, err = unstructuredToPod(unstruct)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error converting Unstructured to v1.Pod: %v", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(actual, got) {
|
||||
t.Fatalf("unexpected pod in list. wanted %#v, got %#v", actual, got)
|
||||
}
|
||||
|
||||
// delete the pod dynamically
|
||||
err = dynamicClient.Resource(resource).Namespace(ns.Name).Delete(actual.Name, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error when deleting pod: %v", err)
|
||||
}
|
||||
|
||||
list, err := client.Core().Pods(ns.Name).List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error when listing pods: %v", err)
|
||||
}
|
||||
|
||||
if len(list.Items) != 0 {
|
||||
t.Fatalf("expected zero pods, got %d", len(list.Items))
|
||||
}
|
||||
}
|
||||
|
||||
func unstructuredToPod(obj *unstructured.Unstructured) (*v1.Pod, error) {
|
||||
json, err := runtime.Encode(unstructured.UnstructuredJSONScheme, obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pod := new(v1.Pod)
|
||||
err = runtime.DecodeInto(testapi.Default.Codec(), json, pod)
|
||||
pod.Kind = ""
|
||||
pod.APIVersion = ""
|
||||
return pod, err
|
||||
}
|
||||
27
vendor/k8s.io/kubernetes/test/integration/client/main_test.go
generated
vendored
Normal file
27
vendor/k8s.io/kubernetes/test/integration/client/main_test.go
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
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 client
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
framework.EtcdMain(m.Run)
|
||||
}
|
||||
38
vendor/k8s.io/kubernetes/test/integration/configmap/BUILD
generated
vendored
Normal file
38
vendor/k8s.io/kubernetes/test/integration/configmap/BUILD
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
size = "large",
|
||||
srcs = [
|
||||
"configmap_test.go",
|
||||
"main_test.go",
|
||||
],
|
||||
tags = ["integration"],
|
||||
deps = [
|
||||
"//test/integration:go_default_library",
|
||||
"//test/integration/framework:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
124
vendor/k8s.io/kubernetes/test/integration/configmap/configmap_test.go
generated
vendored
Normal file
124
vendor/k8s.io/kubernetes/test/integration/configmap/configmap_test.go
generated
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
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 configmap
|
||||
|
||||
// This file tests use of the configMap API resource.
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/kubernetes/test/integration"
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
)
|
||||
|
||||
// TestConfigMap tests apiserver-side behavior of creation of ConfigMaps and pods that consume them.
|
||||
func TestConfigMap(t *testing.T) {
|
||||
_, s, closeFn := framework.RunAMaster(nil)
|
||||
defer closeFn()
|
||||
|
||||
client := clientset.NewForConfigOrDie(&restclient.Config{Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}}})
|
||||
|
||||
ns := framework.CreateTestingNamespace("config-map", s, t)
|
||||
defer framework.DeleteTestingNamespace(ns, s, t)
|
||||
|
||||
DoTestConfigMap(t, client, ns)
|
||||
}
|
||||
|
||||
func DoTestConfigMap(t *testing.T, client clientset.Interface, ns *v1.Namespace) {
|
||||
cfg := v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "configmap",
|
||||
Namespace: ns.Name,
|
||||
},
|
||||
Data: map[string]string{
|
||||
"data-1": "value-1",
|
||||
"data-2": "value-2",
|
||||
"data-3": "value-3",
|
||||
},
|
||||
}
|
||||
|
||||
if _, err := client.CoreV1().ConfigMaps(cfg.Namespace).Create(&cfg); err != nil {
|
||||
t.Errorf("unable to create test configMap: %v", err)
|
||||
}
|
||||
defer deleteConfigMapOrErrorf(t, client, cfg.Namespace, cfg.Name)
|
||||
|
||||
pod := &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "XXX",
|
||||
Namespace: ns.Name,
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Name: "fake-name",
|
||||
Image: "fakeimage",
|
||||
Env: []v1.EnvVar{
|
||||
{
|
||||
Name: "CONFIG_DATA_1",
|
||||
ValueFrom: &v1.EnvVarSource{
|
||||
ConfigMapKeyRef: &v1.ConfigMapKeySelector{
|
||||
LocalObjectReference: v1.LocalObjectReference{
|
||||
Name: "configmap",
|
||||
},
|
||||
Key: "data-1",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "CONFIG_DATA_2",
|
||||
ValueFrom: &v1.EnvVarSource{
|
||||
ConfigMapKeyRef: &v1.ConfigMapKeySelector{
|
||||
LocalObjectReference: v1.LocalObjectReference{
|
||||
Name: "configmap",
|
||||
},
|
||||
Key: "data-2",
|
||||
},
|
||||
},
|
||||
}, {
|
||||
Name: "CONFIG_DATA_3",
|
||||
ValueFrom: &v1.EnvVarSource{
|
||||
ConfigMapKeyRef: &v1.ConfigMapKeySelector{
|
||||
LocalObjectReference: v1.LocalObjectReference{
|
||||
Name: "configmap",
|
||||
},
|
||||
Key: "data-3",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
pod.ObjectMeta.Name = "uses-configmap"
|
||||
if _, err := client.CoreV1().Pods(ns.Name).Create(pod); err != nil {
|
||||
t.Errorf("Failed to create pod: %v", err)
|
||||
}
|
||||
defer integration.DeletePodOrErrorf(t, client, ns.Name, pod.Name)
|
||||
}
|
||||
|
||||
func deleteConfigMapOrErrorf(t *testing.T, c clientset.Interface, ns, name string) {
|
||||
if err := c.CoreV1().ConfigMaps(ns).Delete(name, nil); err != nil {
|
||||
t.Errorf("unable to delete ConfigMap %v: %v", name, err)
|
||||
}
|
||||
}
|
||||
27
vendor/k8s.io/kubernetes/test/integration/configmap/main_test.go
generated
vendored
Normal file
27
vendor/k8s.io/kubernetes/test/integration/configmap/main_test.go
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
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 configmap
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
framework.EtcdMain(m.Run)
|
||||
}
|
||||
57
vendor/k8s.io/kubernetes/test/integration/daemonset/BUILD
generated
vendored
Normal file
57
vendor/k8s.io/kubernetes/test/integration/daemonset/BUILD
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
size = "large",
|
||||
srcs = [
|
||||
"daemonset_test.go",
|
||||
"main_test.go",
|
||||
],
|
||||
tags = ["integration"],
|
||||
deps = [
|
||||
"//pkg/api/legacyscheme:go_default_library",
|
||||
"//pkg/api/v1/pod:go_default_library",
|
||||
"//pkg/controller/daemon:go_default_library",
|
||||
"//pkg/features:go_default_library",
|
||||
"//pkg/scheduler:go_default_library",
|
||||
"//pkg/scheduler/algorithm:go_default_library",
|
||||
"//pkg/scheduler/algorithmprovider:go_default_library",
|
||||
"//pkg/scheduler/factory:go_default_library",
|
||||
"//pkg/util/metrics:go_default_library",
|
||||
"//test/integration/framework:go_default_library",
|
||||
"//vendor/k8s.io/api/apps/v1:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/uuid:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||
"//vendor/k8s.io/client-go/informers:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes/typed/apps/v1:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes/typed/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/cache:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/record:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
9
vendor/k8s.io/kubernetes/test/integration/daemonset/OWNERS
generated
vendored
Executable file
9
vendor/k8s.io/kubernetes/test/integration/daemonset/OWNERS
generated
vendored
Executable file
@@ -0,0 +1,9 @@
|
||||
approvers:
|
||||
- mikedanese
|
||||
- kow3ns
|
||||
reviewers:
|
||||
- mikedanese
|
||||
- kargakis
|
||||
- lukaszo
|
||||
- janetkuo
|
||||
- kow3ns
|
||||
742
vendor/k8s.io/kubernetes/test/integration/daemonset/daemonset_test.go
generated
vendored
Normal file
742
vendor/k8s.io/kubernetes/test/integration/daemonset/daemonset_test.go
generated
vendored
Normal file
@@ -0,0 +1,742 @@
|
||||
/*
|
||||
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 daemonset
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
apps "k8s.io/api/apps/v1"
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"k8s.io/apimachinery/pkg/util/uuid"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/client-go/informers"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
appstyped "k8s.io/client-go/kubernetes/typed/apps/v1"
|
||||
clientv1core "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
corev1typed "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"k8s.io/client-go/tools/record"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
podutil "k8s.io/kubernetes/pkg/api/v1/pod"
|
||||
"k8s.io/kubernetes/pkg/controller/daemon"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
"k8s.io/kubernetes/pkg/scheduler"
|
||||
"k8s.io/kubernetes/pkg/scheduler/algorithm"
|
||||
"k8s.io/kubernetes/pkg/scheduler/algorithmprovider"
|
||||
_ "k8s.io/kubernetes/pkg/scheduler/algorithmprovider"
|
||||
"k8s.io/kubernetes/pkg/scheduler/factory"
|
||||
"k8s.io/kubernetes/pkg/util/metrics"
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
)
|
||||
|
||||
var zero = int64(0)
|
||||
|
||||
func setup(t *testing.T) (*httptest.Server, framework.CloseFunc, *daemon.DaemonSetsController, informers.SharedInformerFactory, clientset.Interface) {
|
||||
masterConfig := framework.NewIntegrationTestMasterConfig()
|
||||
_, server, closeFn := framework.RunAMaster(masterConfig)
|
||||
|
||||
config := restclient.Config{Host: server.URL}
|
||||
clientSet, err := clientset.NewForConfig(&config)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in creating clientset: %v", err)
|
||||
}
|
||||
resyncPeriod := 12 * time.Hour
|
||||
informers := informers.NewSharedInformerFactory(clientset.NewForConfigOrDie(restclient.AddUserAgent(&config, "daemonset-informers")), resyncPeriod)
|
||||
metrics.UnregisterMetricAndUntrackRateLimiterUsage("daemon_controller")
|
||||
dc, err := daemon.NewDaemonSetsController(
|
||||
informers.Apps().V1().DaemonSets(),
|
||||
informers.Apps().V1().ControllerRevisions(),
|
||||
informers.Core().V1().Pods(),
|
||||
informers.Core().V1().Nodes(),
|
||||
clientset.NewForConfigOrDie(restclient.AddUserAgent(&config, "daemonset-controller")),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("error creating DaemonSets controller: %v", err)
|
||||
}
|
||||
|
||||
return server, closeFn, dc, informers, clientSet
|
||||
}
|
||||
|
||||
func setupScheduler(
|
||||
t *testing.T,
|
||||
cs clientset.Interface,
|
||||
informerFactory informers.SharedInformerFactory,
|
||||
stopCh chan struct{},
|
||||
) {
|
||||
// If ScheduleDaemonSetPods is disabled, do not start scheduler.
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.ScheduleDaemonSetPods) {
|
||||
return
|
||||
}
|
||||
|
||||
schedulerConfigFactory := factory.NewConfigFactory(
|
||||
v1.DefaultSchedulerName,
|
||||
cs,
|
||||
informerFactory.Core().V1().Nodes(),
|
||||
informerFactory.Core().V1().Pods(),
|
||||
informerFactory.Core().V1().PersistentVolumes(),
|
||||
informerFactory.Core().V1().PersistentVolumeClaims(),
|
||||
informerFactory.Core().V1().ReplicationControllers(),
|
||||
informerFactory.Extensions().V1beta1().ReplicaSets(),
|
||||
informerFactory.Apps().V1beta1().StatefulSets(),
|
||||
informerFactory.Core().V1().Services(),
|
||||
informerFactory.Policy().V1beta1().PodDisruptionBudgets(),
|
||||
informerFactory.Storage().V1().StorageClasses(),
|
||||
v1.DefaultHardPodAffinitySymmetricWeight,
|
||||
true,
|
||||
false,
|
||||
)
|
||||
|
||||
schedulerConfig, err := schedulerConfigFactory.Create()
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't create scheduler config: %v", err)
|
||||
}
|
||||
|
||||
schedulerConfig.StopEverything = stopCh
|
||||
|
||||
eventBroadcaster := record.NewBroadcaster()
|
||||
schedulerConfig.Recorder = eventBroadcaster.NewRecorder(
|
||||
legacyscheme.Scheme,
|
||||
v1.EventSource{Component: v1.DefaultSchedulerName},
|
||||
)
|
||||
eventBroadcaster.StartRecordingToSink(&clientv1core.EventSinkImpl{
|
||||
Interface: cs.CoreV1().Events(""),
|
||||
})
|
||||
|
||||
sched, err := scheduler.NewFromConfigurator(
|
||||
&scheduler.FakeConfigurator{Config: schedulerConfig}, nil...)
|
||||
if err != nil {
|
||||
t.Fatalf("error creating scheduler: %v", err)
|
||||
}
|
||||
|
||||
algorithmprovider.ApplyFeatureGates()
|
||||
|
||||
go sched.Run()
|
||||
}
|
||||
|
||||
func testLabels() map[string]string {
|
||||
return map[string]string{"name": "test"}
|
||||
}
|
||||
|
||||
func newDaemonSet(name, namespace string) *apps.DaemonSet {
|
||||
two := int32(2)
|
||||
return &apps.DaemonSet{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "DaemonSet",
|
||||
APIVersion: "apps/v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: namespace,
|
||||
Name: name,
|
||||
},
|
||||
Spec: apps.DaemonSetSpec{
|
||||
RevisionHistoryLimit: &two,
|
||||
Selector: &metav1.LabelSelector{MatchLabels: testLabels()},
|
||||
UpdateStrategy: apps.DaemonSetUpdateStrategy{
|
||||
Type: apps.OnDeleteDaemonSetStrategyType,
|
||||
},
|
||||
Template: v1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: testLabels(),
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{{Name: "foo", Image: "bar"}},
|
||||
TerminationGracePeriodSeconds: &zero,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func cleanupDaemonSets(t *testing.T, cs clientset.Interface, ds *apps.DaemonSet) {
|
||||
ds, err := cs.AppsV1().DaemonSets(ds.Namespace).Get(ds.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Errorf("Failed to get DaemonSet %s/%s: %v", ds.Namespace, ds.Name, err)
|
||||
return
|
||||
}
|
||||
|
||||
// We set the nodeSelector to a random label. This label is nearly guaranteed
|
||||
// to not be set on any node so the DameonSetController will start deleting
|
||||
// daemon pods. Once it's done deleting the daemon pods, it's safe to delete
|
||||
// the DaemonSet.
|
||||
ds.Spec.Template.Spec.NodeSelector = map[string]string{
|
||||
string(uuid.NewUUID()): string(uuid.NewUUID()),
|
||||
}
|
||||
// force update to avoid version conflict
|
||||
ds.ResourceVersion = ""
|
||||
|
||||
if ds, err = cs.AppsV1().DaemonSets(ds.Namespace).Update(ds); err != nil {
|
||||
t.Errorf("Failed to update DaemonSet %s/%s: %v", ds.Namespace, ds.Name, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Wait for the daemon set controller to kill all the daemon pods.
|
||||
if err := wait.Poll(100*time.Millisecond, 30*time.Second, func() (bool, error) {
|
||||
updatedDS, err := cs.AppsV1().DaemonSets(ds.Namespace).Get(ds.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
return updatedDS.Status.CurrentNumberScheduled+updatedDS.Status.NumberMisscheduled == 0, nil
|
||||
}); err != nil {
|
||||
t.Errorf("Failed to kill the pods of DaemonSet %s/%s: %v", ds.Namespace, ds.Name, err)
|
||||
return
|
||||
}
|
||||
|
||||
falseVar := false
|
||||
deleteOptions := &metav1.DeleteOptions{OrphanDependents: &falseVar}
|
||||
if err := cs.AppsV1().DaemonSets(ds.Namespace).Delete(ds.Name, deleteOptions); err != nil {
|
||||
t.Errorf("Failed to delete DaemonSet %s/%s: %v", ds.Namespace, ds.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
func newRollbackStrategy() *apps.DaemonSetUpdateStrategy {
|
||||
one := intstr.FromInt(1)
|
||||
return &apps.DaemonSetUpdateStrategy{
|
||||
Type: apps.RollingUpdateDaemonSetStrategyType,
|
||||
RollingUpdate: &apps.RollingUpdateDaemonSet{MaxUnavailable: &one},
|
||||
}
|
||||
}
|
||||
|
||||
func newOnDeleteStrategy() *apps.DaemonSetUpdateStrategy {
|
||||
return &apps.DaemonSetUpdateStrategy{
|
||||
Type: apps.OnDeleteDaemonSetStrategyType,
|
||||
}
|
||||
}
|
||||
|
||||
func updateStrategies() []*apps.DaemonSetUpdateStrategy {
|
||||
return []*apps.DaemonSetUpdateStrategy{newOnDeleteStrategy(), newRollbackStrategy()}
|
||||
}
|
||||
|
||||
func featureGates() []utilfeature.Feature {
|
||||
return []utilfeature.Feature{
|
||||
features.ScheduleDaemonSetPods,
|
||||
}
|
||||
}
|
||||
|
||||
func allocatableResources(memory, cpu string) v1.ResourceList {
|
||||
return v1.ResourceList{
|
||||
v1.ResourceMemory: resource.MustParse(memory),
|
||||
v1.ResourceCPU: resource.MustParse(cpu),
|
||||
v1.ResourcePods: resource.MustParse("100"),
|
||||
}
|
||||
}
|
||||
|
||||
func resourcePodSpec(nodeName, memory, cpu string) v1.PodSpec {
|
||||
return v1.PodSpec{
|
||||
NodeName: nodeName,
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Name: "foo",
|
||||
Image: "bar",
|
||||
Resources: v1.ResourceRequirements{
|
||||
Requests: v1.ResourceList{
|
||||
v1.ResourceMemory: resource.MustParse(memory),
|
||||
v1.ResourceCPU: resource.MustParse(cpu),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
TerminationGracePeriodSeconds: &zero,
|
||||
}
|
||||
}
|
||||
|
||||
func newNode(name string, label map[string]string) *v1.Node {
|
||||
return &v1.Node{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Node",
|
||||
APIVersion: "v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Labels: label,
|
||||
Namespace: metav1.NamespaceDefault,
|
||||
},
|
||||
Status: v1.NodeStatus{
|
||||
Conditions: []v1.NodeCondition{{Type: v1.NodeReady, Status: v1.ConditionTrue}},
|
||||
Allocatable: v1.ResourceList{v1.ResourcePods: resource.MustParse("100")},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func addNodes(nodeClient corev1typed.NodeInterface, startIndex, numNodes int, label map[string]string, t *testing.T) {
|
||||
for i := startIndex; i < startIndex+numNodes; i++ {
|
||||
_, err := nodeClient.Create(newNode(fmt.Sprintf("node-%d", i), label))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create node: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func validateDaemonSetPodsAndMarkReady(
|
||||
podClient corev1typed.PodInterface,
|
||||
podInformer cache.SharedIndexInformer,
|
||||
numberPods int,
|
||||
t *testing.T) {
|
||||
if err := wait.Poll(10*time.Second, 60*time.Second, func() (bool, error) {
|
||||
objects := podInformer.GetIndexer().List()
|
||||
if len(objects) != numberPods {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
for _, object := range objects {
|
||||
pod := object.(*v1.Pod)
|
||||
|
||||
ownerReferences := pod.ObjectMeta.OwnerReferences
|
||||
if len(ownerReferences) != 1 {
|
||||
return false, fmt.Errorf("Pod %s has %d OwnerReferences, expected only 1", pod.Name, len(ownerReferences))
|
||||
}
|
||||
controllerRef := ownerReferences[0]
|
||||
if got, want := controllerRef.Kind, "DaemonSet"; got != want {
|
||||
t.Errorf("controllerRef.Kind = %q, want %q", got, want)
|
||||
}
|
||||
if controllerRef.Controller == nil || *controllerRef.Controller != true {
|
||||
t.Errorf("controllerRef.Controller is not set to true")
|
||||
}
|
||||
|
||||
if !podutil.IsPodReady(pod) && len(pod.Spec.NodeName) != 0 {
|
||||
podCopy := pod.DeepCopy()
|
||||
podCopy.Status = v1.PodStatus{
|
||||
Phase: v1.PodRunning,
|
||||
Conditions: []v1.PodCondition{{Type: v1.PodReady, Status: v1.ConditionTrue}},
|
||||
}
|
||||
_, err := podClient.UpdateStatus(podCopy)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// podUnschedulable returns a condition function that returns true if the given pod
|
||||
// gets unschedulable status.
|
||||
func podUnschedulable(c clientset.Interface, podNamespace, podName string) wait.ConditionFunc {
|
||||
return func() (bool, error) {
|
||||
pod, err := c.CoreV1().Pods(podNamespace).Get(podName, metav1.GetOptions{})
|
||||
if errors.IsNotFound(err) {
|
||||
return false, nil
|
||||
}
|
||||
if err != nil {
|
||||
// This could be a connection error so we want to retry.
|
||||
return false, nil
|
||||
}
|
||||
_, cond := podutil.GetPodCondition(&pod.Status, v1.PodScheduled)
|
||||
return cond != nil && cond.Status == v1.ConditionFalse &&
|
||||
cond.Reason == v1.PodReasonUnschedulable, nil
|
||||
}
|
||||
}
|
||||
|
||||
// waitForPodUnscheduleWithTimeout waits for a pod to fail scheduling and returns
|
||||
// an error if it does not become unschedulable within the given timeout.
|
||||
func waitForPodUnschedulableWithTimeout(cs clientset.Interface, pod *v1.Pod, timeout time.Duration) error {
|
||||
return wait.Poll(100*time.Millisecond, timeout, podUnschedulable(cs, pod.Namespace, pod.Name))
|
||||
}
|
||||
|
||||
// waitForPodUnschedule waits for a pod to fail scheduling and returns
|
||||
// an error if it does not become unschedulable within the timeout duration (30 seconds).
|
||||
func waitForPodUnschedulable(cs clientset.Interface, pod *v1.Pod) error {
|
||||
return waitForPodUnschedulableWithTimeout(cs, pod, 10*time.Second)
|
||||
}
|
||||
|
||||
// waitForPodsCreated waits for number of pods are created.
|
||||
func waitForPodsCreated(podInformer cache.SharedIndexInformer, num int) error {
|
||||
return wait.Poll(100*time.Millisecond, 10*time.Second, func() (bool, error) {
|
||||
objects := podInformer.GetIndexer().List()
|
||||
return len(objects) == num, nil
|
||||
})
|
||||
}
|
||||
|
||||
func validateDaemonSetStatus(
|
||||
dsClient appstyped.DaemonSetInterface,
|
||||
dsName string,
|
||||
expectedNumberReady int32,
|
||||
t *testing.T) {
|
||||
if err := wait.Poll(5*time.Second, 60*time.Second, func() (bool, error) {
|
||||
ds, err := dsClient.Get(dsName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return ds.Status.NumberReady == expectedNumberReady, nil
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func validateFailedPlacementEvent(eventClient corev1typed.EventInterface, t *testing.T) {
|
||||
if err := wait.Poll(5*time.Second, 60*time.Second, func() (bool, error) {
|
||||
eventList, err := eventClient.List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if len(eventList.Items) == 0 {
|
||||
return false, nil
|
||||
}
|
||||
if len(eventList.Items) > 1 {
|
||||
t.Errorf("Expected 1 event got %d", len(eventList.Items))
|
||||
}
|
||||
event := eventList.Items[0]
|
||||
if event.Type != v1.EventTypeWarning {
|
||||
t.Errorf("Event type expected %s got %s", v1.EventTypeWarning, event.Type)
|
||||
}
|
||||
if event.Reason != daemon.FailedPlacementReason {
|
||||
t.Errorf("Event reason expected %s got %s", daemon.FailedPlacementReason, event.Reason)
|
||||
}
|
||||
return true, nil
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func forEachFeatureGate(t *testing.T, tf func(t *testing.T)) {
|
||||
for _, fg := range featureGates() {
|
||||
func() {
|
||||
enabled := utilfeature.DefaultFeatureGate.Enabled(fg)
|
||||
defer func() {
|
||||
utilfeature.DefaultFeatureGate.Set(fmt.Sprintf("%v=%t", fg, enabled))
|
||||
}()
|
||||
|
||||
for _, f := range []bool{true, false} {
|
||||
utilfeature.DefaultFeatureGate.Set(fmt.Sprintf("%v=%t", fg, f))
|
||||
t.Run(fmt.Sprintf("%v (%t)", fg, f), tf)
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
func forEachStrategy(t *testing.T, tf func(t *testing.T, strategy *apps.DaemonSetUpdateStrategy)) {
|
||||
for _, strategy := range updateStrategies() {
|
||||
t.Run(fmt.Sprintf("%s (%v)", t.Name(), strategy),
|
||||
func(tt *testing.T) { tf(tt, strategy) })
|
||||
}
|
||||
}
|
||||
|
||||
func TestOneNodeDaemonLaunchesPod(t *testing.T) {
|
||||
forEachFeatureGate(t, func(t *testing.T) {
|
||||
forEachStrategy(t, func(t *testing.T, strategy *apps.DaemonSetUpdateStrategy) {
|
||||
server, closeFn, dc, informers, clientset := setup(t)
|
||||
defer closeFn()
|
||||
ns := framework.CreateTestingNamespace("one-node-daemonset-test", server, t)
|
||||
defer framework.DeleteTestingNamespace(ns, server, t)
|
||||
|
||||
dsClient := clientset.AppsV1().DaemonSets(ns.Name)
|
||||
podClient := clientset.CoreV1().Pods(ns.Name)
|
||||
nodeClient := clientset.CoreV1().Nodes()
|
||||
podInformer := informers.Core().V1().Pods().Informer()
|
||||
|
||||
stopCh := make(chan struct{})
|
||||
defer close(stopCh)
|
||||
|
||||
informers.Start(stopCh)
|
||||
go dc.Run(5, stopCh)
|
||||
|
||||
// Start Scheduler
|
||||
setupScheduler(t, clientset, informers, stopCh)
|
||||
|
||||
ds := newDaemonSet("foo", ns.Name)
|
||||
ds.Spec.UpdateStrategy = *strategy
|
||||
_, err := dsClient.Create(ds)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create DaemonSet: %v", err)
|
||||
}
|
||||
defer cleanupDaemonSets(t, clientset, ds)
|
||||
|
||||
_, err = nodeClient.Create(newNode("single-node", nil))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create node: %v", err)
|
||||
}
|
||||
|
||||
validateDaemonSetPodsAndMarkReady(podClient, podInformer, 1, t)
|
||||
validateDaemonSetStatus(dsClient, ds.Name, 1, t)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSimpleDaemonSetLaunchesPods(t *testing.T) {
|
||||
forEachFeatureGate(t, func(t *testing.T) {
|
||||
forEachStrategy(t, func(t *testing.T, strategy *apps.DaemonSetUpdateStrategy) {
|
||||
server, closeFn, dc, informers, clientset := setup(t)
|
||||
defer closeFn()
|
||||
ns := framework.CreateTestingNamespace("simple-daemonset-test", server, t)
|
||||
defer framework.DeleteTestingNamespace(ns, server, t)
|
||||
|
||||
dsClient := clientset.AppsV1().DaemonSets(ns.Name)
|
||||
podClient := clientset.CoreV1().Pods(ns.Name)
|
||||
nodeClient := clientset.CoreV1().Nodes()
|
||||
podInformer := informers.Core().V1().Pods().Informer()
|
||||
|
||||
stopCh := make(chan struct{})
|
||||
defer close(stopCh)
|
||||
|
||||
informers.Start(stopCh)
|
||||
go dc.Run(5, stopCh)
|
||||
|
||||
// Start Scheduler
|
||||
setupScheduler(t, clientset, informers, stopCh)
|
||||
|
||||
ds := newDaemonSet("foo", ns.Name)
|
||||
ds.Spec.UpdateStrategy = *strategy
|
||||
_, err := dsClient.Create(ds)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create DaemonSet: %v", err)
|
||||
}
|
||||
defer cleanupDaemonSets(t, clientset, ds)
|
||||
|
||||
addNodes(nodeClient, 0, 5, nil, t)
|
||||
|
||||
validateDaemonSetPodsAndMarkReady(podClient, podInformer, 5, t)
|
||||
validateDaemonSetStatus(dsClient, ds.Name, 5, t)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestDaemonSetWithNodeSelectorLaunchesPods(t *testing.T) {
|
||||
forEachFeatureGate(t, func(t *testing.T) {
|
||||
forEachStrategy(t, func(t *testing.T, strategy *apps.DaemonSetUpdateStrategy) {
|
||||
server, closeFn, dc, informers, clientset := setup(t)
|
||||
defer closeFn()
|
||||
ns := framework.CreateTestingNamespace("simple-daemonset-test", server, t)
|
||||
defer framework.DeleteTestingNamespace(ns, server, t)
|
||||
|
||||
dsClient := clientset.AppsV1().DaemonSets(ns.Name)
|
||||
podClient := clientset.CoreV1().Pods(ns.Name)
|
||||
nodeClient := clientset.CoreV1().Nodes()
|
||||
podInformer := informers.Core().V1().Pods().Informer()
|
||||
|
||||
stopCh := make(chan struct{})
|
||||
defer close(stopCh)
|
||||
|
||||
informers.Start(stopCh)
|
||||
go dc.Run(5, stopCh)
|
||||
|
||||
// Start Scheduler
|
||||
setupScheduler(t, clientset, informers, stopCh)
|
||||
|
||||
ds := newDaemonSet("foo", ns.Name)
|
||||
ds.Spec.UpdateStrategy = *strategy
|
||||
|
||||
ds.Spec.Template.Spec.Affinity = &v1.Affinity{
|
||||
NodeAffinity: &v1.NodeAffinity{
|
||||
RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
|
||||
NodeSelectorTerms: []v1.NodeSelectorTerm{
|
||||
{
|
||||
MatchExpressions: []v1.NodeSelectorRequirement{
|
||||
{
|
||||
Key: "zone",
|
||||
Operator: v1.NodeSelectorOpIn,
|
||||
Values: []string{"test"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
MatchFields: []v1.NodeSelectorRequirement{
|
||||
{
|
||||
Key: algorithm.NodeFieldSelectorKeyNodeName,
|
||||
Operator: v1.NodeSelectorOpIn,
|
||||
Values: []string{"node-1"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
_, err := dsClient.Create(ds)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create DaemonSet: %v", err)
|
||||
}
|
||||
defer cleanupDaemonSets(t, clientset, ds)
|
||||
|
||||
addNodes(nodeClient, 0, 2, nil, t)
|
||||
// Two nodes with labels
|
||||
addNodes(nodeClient, 2, 2, map[string]string{
|
||||
"zone": "test",
|
||||
}, t)
|
||||
addNodes(nodeClient, 4, 2, nil, t)
|
||||
|
||||
validateDaemonSetPodsAndMarkReady(podClient, podInformer, 3, t)
|
||||
validateDaemonSetStatus(dsClient, ds.Name, 3, t)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestNotReadyNodeDaemonDoesLaunchPod(t *testing.T) {
|
||||
forEachStrategy(t, func(t *testing.T, strategy *apps.DaemonSetUpdateStrategy) {
|
||||
server, closeFn, dc, informers, clientset := setup(t)
|
||||
defer closeFn()
|
||||
ns := framework.CreateTestingNamespace("simple-daemonset-test", server, t)
|
||||
defer framework.DeleteTestingNamespace(ns, server, t)
|
||||
|
||||
dsClient := clientset.AppsV1().DaemonSets(ns.Name)
|
||||
podClient := clientset.CoreV1().Pods(ns.Name)
|
||||
nodeClient := clientset.CoreV1().Nodes()
|
||||
podInformer := informers.Core().V1().Pods().Informer()
|
||||
|
||||
stopCh := make(chan struct{})
|
||||
defer close(stopCh)
|
||||
|
||||
informers.Start(stopCh)
|
||||
go dc.Run(5, stopCh)
|
||||
|
||||
// Start Scheduler
|
||||
setupScheduler(t, clientset, informers, stopCh)
|
||||
|
||||
ds := newDaemonSet("foo", ns.Name)
|
||||
ds.Spec.UpdateStrategy = *strategy
|
||||
_, err := dsClient.Create(ds)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create DaemonSet: %v", err)
|
||||
}
|
||||
|
||||
defer cleanupDaemonSets(t, clientset, ds)
|
||||
|
||||
node := newNode("single-node", nil)
|
||||
node.Status.Conditions = []v1.NodeCondition{
|
||||
{Type: v1.NodeReady, Status: v1.ConditionFalse},
|
||||
}
|
||||
_, err = nodeClient.Create(node)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create node: %v", err)
|
||||
}
|
||||
|
||||
validateDaemonSetPodsAndMarkReady(podClient, podInformer, 1, t)
|
||||
validateDaemonSetStatus(dsClient, ds.Name, 1, t)
|
||||
})
|
||||
}
|
||||
|
||||
func TestInsufficientCapacityNodeDaemonDoesNotLaunchPod(t *testing.T) {
|
||||
forEachStrategy(t, func(t *testing.T, strategy *apps.DaemonSetUpdateStrategy) {
|
||||
server, closeFn, dc, informers, clientset := setup(t)
|
||||
defer closeFn()
|
||||
ns := framework.CreateTestingNamespace("insufficient-capacity", server, t)
|
||||
defer framework.DeleteTestingNamespace(ns, server, t)
|
||||
|
||||
dsClient := clientset.AppsV1().DaemonSets(ns.Name)
|
||||
nodeClient := clientset.CoreV1().Nodes()
|
||||
eventClient := clientset.CoreV1().Events(ns.Namespace)
|
||||
|
||||
stopCh := make(chan struct{})
|
||||
defer close(stopCh)
|
||||
|
||||
informers.Start(stopCh)
|
||||
go dc.Run(5, stopCh)
|
||||
|
||||
ds := newDaemonSet("foo", ns.Name)
|
||||
ds.Spec.Template.Spec = resourcePodSpec("node-with-limited-memory", "120M", "75m")
|
||||
ds.Spec.UpdateStrategy = *strategy
|
||||
_, err := dsClient.Create(ds)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create DaemonSet: %v", err)
|
||||
}
|
||||
defer cleanupDaemonSets(t, clientset, ds)
|
||||
|
||||
node := newNode("node-with-limited-memory", nil)
|
||||
node.Status.Allocatable = allocatableResources("100M", "200m")
|
||||
_, err = nodeClient.Create(node)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create node: %v", err)
|
||||
}
|
||||
|
||||
validateFailedPlacementEvent(eventClient, t)
|
||||
})
|
||||
}
|
||||
|
||||
// TestInsufficientCapacityNodeDaemonSetCreateButNotLaunchPod tests that when "ScheduleDaemonSetPods"
|
||||
// feature is enabled, the DaemonSet should create Pods for all the nodes regardless of available resource
|
||||
// on the nodes, and kube-scheduler should not schedule Pods onto the nodes with insufficient resource.
|
||||
func TestInsufficientCapacityNodeWhenScheduleDaemonSetPodsEnabled(t *testing.T) {
|
||||
enabled := utilfeature.DefaultFeatureGate.Enabled(features.ScheduleDaemonSetPods)
|
||||
defer func() {
|
||||
utilfeature.DefaultFeatureGate.Set(fmt.Sprintf("%s=%t",
|
||||
features.ScheduleDaemonSetPods, enabled))
|
||||
}()
|
||||
|
||||
utilfeature.DefaultFeatureGate.Set(fmt.Sprintf("%s=%t", features.ScheduleDaemonSetPods, true))
|
||||
|
||||
forEachStrategy(t, func(t *testing.T, strategy *apps.DaemonSetUpdateStrategy) {
|
||||
server, closeFn, dc, informers, clientset := setup(t)
|
||||
defer closeFn()
|
||||
ns := framework.CreateTestingNamespace("insufficient-capacity", server, t)
|
||||
defer framework.DeleteTestingNamespace(ns, server, t)
|
||||
|
||||
dsClient := clientset.AppsV1().DaemonSets(ns.Name)
|
||||
podClient := clientset.CoreV1().Pods(ns.Name)
|
||||
podInformer := informers.Core().V1().Pods().Informer()
|
||||
nodeClient := clientset.CoreV1().Nodes()
|
||||
stopCh := make(chan struct{})
|
||||
defer close(stopCh)
|
||||
|
||||
informers.Start(stopCh)
|
||||
go dc.Run(5, stopCh)
|
||||
|
||||
// Start Scheduler
|
||||
setupScheduler(t, clientset, informers, stopCh)
|
||||
|
||||
ds := newDaemonSet("foo", ns.Name)
|
||||
ds.Spec.Template.Spec = resourcePodSpec("", "120M", "75m")
|
||||
ds.Spec.UpdateStrategy = *strategy
|
||||
ds, err := dsClient.Create(ds)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create DaemonSet: %v", err)
|
||||
}
|
||||
|
||||
defer cleanupDaemonSets(t, clientset, ds)
|
||||
|
||||
node := newNode("node-with-limited-memory", nil)
|
||||
node.Status.Allocatable = allocatableResources("100M", "200m")
|
||||
_, err = nodeClient.Create(node)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create node: %v", err)
|
||||
}
|
||||
|
||||
if err := waitForPodsCreated(podInformer, 1); err != nil {
|
||||
t.Errorf("Failed to wait for pods created: %v", err)
|
||||
}
|
||||
|
||||
objects := podInformer.GetIndexer().List()
|
||||
for _, object := range objects {
|
||||
pod := object.(*v1.Pod)
|
||||
if err := waitForPodUnschedulable(clientset, pod); err != nil {
|
||||
t.Errorf("Failed to wait for unschedulable status of pod %+v", pod)
|
||||
}
|
||||
}
|
||||
|
||||
node1 := newNode("node-with-enough-memory", nil)
|
||||
node1.Status.Allocatable = allocatableResources("200M", "2000m")
|
||||
_, err = nodeClient.Create(node1)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create node: %v", err)
|
||||
}
|
||||
|
||||
// When ScheduleDaemonSetPods enabled, 2 pods are created. But only one
|
||||
// of two Pods is scheduled by default scheduler.
|
||||
validateDaemonSetPodsAndMarkReady(podClient, podInformer, 2, t)
|
||||
validateDaemonSetStatus(dsClient, ds.Name, 1, t)
|
||||
})
|
||||
}
|
||||
27
vendor/k8s.io/kubernetes/test/integration/daemonset/main_test.go
generated
vendored
Normal file
27
vendor/k8s.io/kubernetes/test/integration/daemonset/main_test.go
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
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 daemonset
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
framework.EtcdMain(m.Run)
|
||||
}
|
||||
43
vendor/k8s.io/kubernetes/test/integration/defaulttolerationseconds/BUILD
generated
vendored
Normal file
43
vendor/k8s.io/kubernetes/test/integration/defaulttolerationseconds/BUILD
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
size = "large",
|
||||
srcs = [
|
||||
"defaulttolerationseconds_test.go",
|
||||
"main_test.go",
|
||||
],
|
||||
tags = [
|
||||
"etcd",
|
||||
"integration",
|
||||
],
|
||||
deps = [
|
||||
"//pkg/apis/core/helper:go_default_library",
|
||||
"//pkg/scheduler/algorithm:go_default_library",
|
||||
"//plugin/pkg/admission/defaulttolerationseconds:go_default_library",
|
||||
"//test/integration/framework:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
103
vendor/k8s.io/kubernetes/test/integration/defaulttolerationseconds/defaulttolerationseconds_test.go
generated
vendored
Normal file
103
vendor/k8s.io/kubernetes/test/integration/defaulttolerationseconds/defaulttolerationseconds_test.go
generated
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
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 defaulttolerationseconds
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/kubernetes/pkg/apis/core/helper"
|
||||
"k8s.io/kubernetes/pkg/scheduler/algorithm"
|
||||
"k8s.io/kubernetes/plugin/pkg/admission/defaulttolerationseconds"
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
)
|
||||
|
||||
func TestAdmission(t *testing.T) {
|
||||
masterConfig := framework.NewMasterConfig()
|
||||
masterConfig.GenericConfig.EnableProfiling = true
|
||||
masterConfig.GenericConfig.AdmissionControl = defaulttolerationseconds.NewDefaultTolerationSeconds()
|
||||
_, s, closeFn := framework.RunAMaster(masterConfig)
|
||||
defer closeFn()
|
||||
|
||||
client := clientset.NewForConfigOrDie(&restclient.Config{Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}}})
|
||||
|
||||
ns := framework.CreateTestingNamespace("default-toleration-seconds", s, t)
|
||||
defer framework.DeleteTestingNamespace(ns, s, t)
|
||||
|
||||
pod := v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: ns.Name,
|
||||
Name: "foo",
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Name: "test",
|
||||
Image: "an-image",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
updatedPod, err := client.Core().Pods(pod.Namespace).Create(&pod)
|
||||
if err != nil {
|
||||
t.Fatalf("error creating pod: %v", err)
|
||||
}
|
||||
|
||||
var defaultSeconds int64 = 300
|
||||
nodeNotReady := v1.Toleration{
|
||||
Key: algorithm.TaintNodeNotReady,
|
||||
Operator: v1.TolerationOpExists,
|
||||
Effect: v1.TaintEffectNoExecute,
|
||||
TolerationSeconds: &defaultSeconds,
|
||||
}
|
||||
|
||||
nodeUnreachable := v1.Toleration{
|
||||
Key: algorithm.TaintNodeUnreachable,
|
||||
Operator: v1.TolerationOpExists,
|
||||
Effect: v1.TaintEffectNoExecute,
|
||||
TolerationSeconds: &defaultSeconds,
|
||||
}
|
||||
|
||||
found := 0
|
||||
tolerations := updatedPod.Spec.Tolerations
|
||||
for i := range tolerations {
|
||||
if found == 2 {
|
||||
break
|
||||
}
|
||||
if tolerations[i].MatchToleration(&nodeNotReady) {
|
||||
if helper.Semantic.DeepEqual(tolerations[i], nodeNotReady) {
|
||||
found++
|
||||
continue
|
||||
}
|
||||
}
|
||||
if tolerations[i].MatchToleration(&nodeUnreachable) {
|
||||
if helper.Semantic.DeepEqual(tolerations[i], nodeUnreachable) {
|
||||
found++
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if found != 2 {
|
||||
t.Fatalf("unexpected tolerations: %v\n", updatedPod.Spec.Tolerations)
|
||||
}
|
||||
}
|
||||
27
vendor/k8s.io/kubernetes/test/integration/defaulttolerationseconds/main_test.go
generated
vendored
Normal file
27
vendor/k8s.io/kubernetes/test/integration/defaulttolerationseconds/main_test.go
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
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 defaulttolerationseconds
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
framework.EtcdMain(m.Run)
|
||||
}
|
||||
67
vendor/k8s.io/kubernetes/test/integration/deployment/BUILD
generated
vendored
Normal file
67
vendor/k8s.io/kubernetes/test/integration/deployment/BUILD
generated
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
size = "large",
|
||||
srcs = [
|
||||
"deployment_test.go",
|
||||
"main_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
tags = ["integration"],
|
||||
deps = [
|
||||
"//pkg/controller/deployment/util:go_default_library",
|
||||
"//pkg/util/pointer:go_default_library",
|
||||
"//test/integration/framework:go_default_library",
|
||||
"//vendor/k8s.io/api/apps/v1:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/uuid:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||
"//vendor/k8s.io/client-go/util/retry:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["util.go"],
|
||||
importpath = "k8s.io/kubernetes/test/integration/deployment",
|
||||
deps = [
|
||||
"//pkg/api/v1/pod:go_default_library",
|
||||
"//pkg/controller/deployment:go_default_library",
|
||||
"//pkg/controller/deployment/util:go_default_library",
|
||||
"//pkg/controller/replicaset:go_default_library",
|
||||
"//pkg/util/metrics:go_default_library",
|
||||
"//test/integration/framework:go_default_library",
|
||||
"//test/utils:go_default_library",
|
||||
"//vendor/k8s.io/api/apps/v1:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/api/extensions/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||
"//vendor/k8s.io/client-go/informers:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
7
vendor/k8s.io/kubernetes/test/integration/deployment/OWNERS
generated
vendored
Normal file
7
vendor/k8s.io/kubernetes/test/integration/deployment/OWNERS
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
reviewers:
|
||||
- janetkuo
|
||||
- kargakis
|
||||
- tnozicka
|
||||
approvers:
|
||||
- janetkuo
|
||||
- kargakis
|
||||
1455
vendor/k8s.io/kubernetes/test/integration/deployment/deployment_test.go
generated
vendored
Normal file
1455
vendor/k8s.io/kubernetes/test/integration/deployment/deployment_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
27
vendor/k8s.io/kubernetes/test/integration/deployment/main_test.go
generated
vendored
Normal file
27
vendor/k8s.io/kubernetes/test/integration/deployment/main_test.go
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
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 deployment
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
framework.EtcdMain(m.Run)
|
||||
}
|
||||
509
vendor/k8s.io/kubernetes/test/integration/deployment/util.go
generated
vendored
Normal file
509
vendor/k8s.io/kubernetes/test/integration/deployment/util.go
generated
vendored
Normal file
@@ -0,0 +1,509 @@
|
||||
/*
|
||||
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 deployment
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http/httptest"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
apps "k8s.io/api/apps/v1"
|
||||
"k8s.io/api/core/v1"
|
||||
extensions "k8s.io/api/extensions/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/client-go/informers"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
podutil "k8s.io/kubernetes/pkg/api/v1/pod"
|
||||
"k8s.io/kubernetes/pkg/controller/deployment"
|
||||
deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util"
|
||||
"k8s.io/kubernetes/pkg/controller/replicaset"
|
||||
"k8s.io/kubernetes/pkg/util/metrics"
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
testutil "k8s.io/kubernetes/test/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
pollInterval = 100 * time.Millisecond
|
||||
pollTimeout = 60 * time.Second
|
||||
|
||||
fakeContainerName = "fake-name"
|
||||
fakeImage = "fakeimage"
|
||||
)
|
||||
|
||||
var pauseFn = func(update *apps.Deployment) {
|
||||
update.Spec.Paused = true
|
||||
}
|
||||
|
||||
var resumeFn = func(update *apps.Deployment) {
|
||||
update.Spec.Paused = false
|
||||
}
|
||||
|
||||
type deploymentTester struct {
|
||||
t *testing.T
|
||||
c clientset.Interface
|
||||
deployment *apps.Deployment
|
||||
}
|
||||
|
||||
func testLabels() map[string]string {
|
||||
return map[string]string{"name": "test"}
|
||||
}
|
||||
|
||||
// newDeployment returns a RollingUpdate Deployment with with a fake container image
|
||||
func newDeployment(name, ns string, replicas int32) *apps.Deployment {
|
||||
return &apps.Deployment{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Deployment",
|
||||
APIVersion: "apps/v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: ns,
|
||||
Name: name,
|
||||
},
|
||||
Spec: apps.DeploymentSpec{
|
||||
Replicas: &replicas,
|
||||
Selector: &metav1.LabelSelector{MatchLabels: testLabels()},
|
||||
Strategy: apps.DeploymentStrategy{
|
||||
Type: apps.RollingUpdateDeploymentStrategyType,
|
||||
RollingUpdate: new(apps.RollingUpdateDeployment),
|
||||
},
|
||||
Template: v1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: testLabels(),
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Name: fakeContainerName,
|
||||
Image: fakeImage,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newReplicaSet(name, ns string, replicas int32) *apps.ReplicaSet {
|
||||
return &apps.ReplicaSet{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "ReplicaSet",
|
||||
APIVersion: "extensions/v1beta1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: ns,
|
||||
Name: name,
|
||||
Labels: testLabels(),
|
||||
},
|
||||
Spec: apps.ReplicaSetSpec{
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: testLabels(),
|
||||
},
|
||||
Replicas: &replicas,
|
||||
Template: v1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: testLabels(),
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Name: fakeContainerName,
|
||||
Image: fakeImage,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newDeploymentRollback(name string, annotations map[string]string, revision int64) *extensions.DeploymentRollback {
|
||||
return &extensions.DeploymentRollback{
|
||||
Name: name,
|
||||
UpdatedAnnotations: annotations,
|
||||
RollbackTo: extensions.RollbackConfig{Revision: revision},
|
||||
}
|
||||
}
|
||||
|
||||
// dcSetup sets up necessities for Deployment integration test, including master, apiserver, informers, and clientset
|
||||
func dcSetup(t *testing.T) (*httptest.Server, framework.CloseFunc, *replicaset.ReplicaSetController, *deployment.DeploymentController, informers.SharedInformerFactory, clientset.Interface) {
|
||||
masterConfig := framework.NewIntegrationTestMasterConfig()
|
||||
_, s, closeFn := framework.RunAMaster(masterConfig)
|
||||
|
||||
config := restclient.Config{Host: s.URL}
|
||||
clientSet, err := clientset.NewForConfig(&config)
|
||||
if err != nil {
|
||||
t.Fatalf("error in create clientset: %v", err)
|
||||
}
|
||||
resyncPeriod := 12 * time.Hour
|
||||
informers := informers.NewSharedInformerFactory(clientset.NewForConfigOrDie(restclient.AddUserAgent(&config, "deployment-informers")), resyncPeriod)
|
||||
|
||||
metrics.UnregisterMetricAndUntrackRateLimiterUsage("deployment_controller")
|
||||
dc, err := deployment.NewDeploymentController(
|
||||
informers.Apps().V1().Deployments(),
|
||||
informers.Apps().V1().ReplicaSets(),
|
||||
informers.Core().V1().Pods(),
|
||||
clientset.NewForConfigOrDie(restclient.AddUserAgent(&config, "deployment-controller")),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("error creating Deployment controller: %v", err)
|
||||
}
|
||||
rm := replicaset.NewReplicaSetController(
|
||||
informers.Apps().V1().ReplicaSets(),
|
||||
informers.Core().V1().Pods(),
|
||||
clientset.NewForConfigOrDie(restclient.AddUserAgent(&config, "replicaset-controller")),
|
||||
replicaset.BurstReplicas,
|
||||
)
|
||||
return s, closeFn, rm, dc, informers, clientSet
|
||||
}
|
||||
|
||||
// dcSimpleSetup sets up necessities for Deployment integration test, including master, apiserver,
|
||||
// and clientset, but not controllers and informers
|
||||
func dcSimpleSetup(t *testing.T) (*httptest.Server, framework.CloseFunc, clientset.Interface) {
|
||||
masterConfig := framework.NewIntegrationTestMasterConfig()
|
||||
_, s, closeFn := framework.RunAMaster(masterConfig)
|
||||
|
||||
config := restclient.Config{Host: s.URL}
|
||||
clientSet, err := clientset.NewForConfig(&config)
|
||||
if err != nil {
|
||||
t.Fatalf("error in create clientset: %v", err)
|
||||
}
|
||||
return s, closeFn, clientSet
|
||||
}
|
||||
|
||||
// addPodConditionReady sets given pod status to ready at given time
|
||||
func addPodConditionReady(pod *v1.Pod, time metav1.Time) {
|
||||
pod.Status = v1.PodStatus{
|
||||
Phase: v1.PodRunning,
|
||||
Conditions: []v1.PodCondition{
|
||||
{
|
||||
Type: v1.PodReady,
|
||||
Status: v1.ConditionTrue,
|
||||
LastTransitionTime: time,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (d *deploymentTester) waitForDeploymentRevisionAndImage(revision, image string) error {
|
||||
if err := testutil.WaitForDeploymentRevisionAndImage(d.c, d.deployment.Namespace, d.deployment.Name, revision, image, d.t.Logf, pollInterval, pollTimeout); err != nil {
|
||||
return fmt.Errorf("failed to wait for Deployment revision %s: %v", d.deployment.Name, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func markPodReady(c clientset.Interface, ns string, pod *v1.Pod) error {
|
||||
addPodConditionReady(pod, metav1.Now())
|
||||
_, err := c.CoreV1().Pods(ns).UpdateStatus(pod)
|
||||
return err
|
||||
}
|
||||
|
||||
func intOrStrP(num int) *intstr.IntOrString {
|
||||
intstr := intstr.FromInt(num)
|
||||
return &intstr
|
||||
}
|
||||
|
||||
// markUpdatedPodsReady manually marks updated Deployment pods status to ready,
|
||||
// until the deployment is complete
|
||||
func (d *deploymentTester) markUpdatedPodsReady(wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
ns := d.deployment.Namespace
|
||||
err := wait.PollImmediate(pollInterval, pollTimeout, func() (bool, error) {
|
||||
// We're done when the deployment is complete
|
||||
if completed, err := d.deploymentComplete(); err != nil {
|
||||
return false, err
|
||||
} else if completed {
|
||||
return true, nil
|
||||
}
|
||||
// Otherwise, mark remaining pods as ready
|
||||
pods, err := d.listUpdatedPods()
|
||||
if err != nil {
|
||||
d.t.Log(err)
|
||||
return false, nil
|
||||
}
|
||||
d.t.Logf("%d/%d of deployment pods are created", len(pods), *d.deployment.Spec.Replicas)
|
||||
for i := range pods {
|
||||
pod := pods[i]
|
||||
if podutil.IsPodReady(&pod) {
|
||||
continue
|
||||
}
|
||||
if err = markPodReady(d.c, ns, &pod); err != nil {
|
||||
d.t.Logf("failed to update Deployment pod %s, will retry later: %v", pod.Name, err)
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
})
|
||||
if err != nil {
|
||||
d.t.Fatalf("failed to mark updated Deployment pods to ready: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *deploymentTester) deploymentComplete() (bool, error) {
|
||||
latest, err := d.c.AppsV1().Deployments(d.deployment.Namespace).Get(d.deployment.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return deploymentutil.DeploymentComplete(d.deployment, &latest.Status), nil
|
||||
}
|
||||
|
||||
// Waits for the deployment to complete, and check rolling update strategy isn't broken at any times.
|
||||
// Rolling update strategy should not be broken during a rolling update.
|
||||
func (d *deploymentTester) waitForDeploymentCompleteAndCheckRolling() error {
|
||||
return testutil.WaitForDeploymentCompleteAndCheckRolling(d.c, d.deployment, d.t.Logf, pollInterval, pollTimeout)
|
||||
}
|
||||
|
||||
// Waits for the deployment to complete, and don't check if rolling update strategy is broken.
|
||||
// Rolling update strategy is used only during a rolling update, and can be violated in other situations,
|
||||
// such as shortly after a scaling event or the deployment is just created.
|
||||
func (d *deploymentTester) waitForDeploymentComplete() error {
|
||||
return testutil.WaitForDeploymentComplete(d.c, d.deployment, d.t.Logf, pollInterval, pollTimeout)
|
||||
}
|
||||
|
||||
// waitForDeploymentCompleteAndCheckRollingAndMarkPodsReady waits for the Deployment to complete
|
||||
// while marking updated Deployment pods as ready at the same time.
|
||||
// Uses hard check to make sure rolling update strategy is not violated at any times.
|
||||
func (d *deploymentTester) waitForDeploymentCompleteAndCheckRollingAndMarkPodsReady() error {
|
||||
var wg sync.WaitGroup
|
||||
|
||||
// Manually mark updated Deployment pods as ready in a separate goroutine
|
||||
wg.Add(1)
|
||||
go d.markUpdatedPodsReady(&wg)
|
||||
// Wait for goroutine to finish, for all return paths.
|
||||
defer wg.Wait()
|
||||
|
||||
// Wait for the Deployment status to complete while Deployment pods are becoming ready
|
||||
err := d.waitForDeploymentCompleteAndCheckRolling()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to wait for Deployment %s to complete: %v", d.deployment.Name, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// waitForDeploymentCompleteAndMarkPodsReady waits for the Deployment to complete
|
||||
// while marking updated Deployment pods as ready at the same time.
|
||||
func (d *deploymentTester) waitForDeploymentCompleteAndMarkPodsReady() error {
|
||||
var wg sync.WaitGroup
|
||||
|
||||
// Manually mark updated Deployment pods as ready in a separate goroutine
|
||||
wg.Add(1)
|
||||
go d.markUpdatedPodsReady(&wg)
|
||||
|
||||
// Wait for the Deployment status to complete using soft check, while Deployment pods are becoming ready
|
||||
err := d.waitForDeploymentComplete()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to wait for Deployment status %s: %v", d.deployment.Name, err)
|
||||
}
|
||||
|
||||
// Wait for goroutine to finish
|
||||
wg.Wait()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *deploymentTester) updateDeployment(applyUpdate testutil.UpdateDeploymentFunc) (*apps.Deployment, error) {
|
||||
return testutil.UpdateDeploymentWithRetries(d.c, d.deployment.Namespace, d.deployment.Name, applyUpdate, d.t.Logf, pollInterval, pollTimeout)
|
||||
}
|
||||
|
||||
func (d *deploymentTester) waitForObservedDeployment(desiredGeneration int64) error {
|
||||
if err := testutil.WaitForObservedDeployment(d.c, d.deployment.Namespace, d.deployment.Name, desiredGeneration); err != nil {
|
||||
return fmt.Errorf("failed waiting for ObservedGeneration of deployment %s to become %d: %v", d.deployment.Name, desiredGeneration, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *deploymentTester) getNewReplicaSet() (*apps.ReplicaSet, error) {
|
||||
deployment, err := d.c.AppsV1().Deployments(d.deployment.Namespace).Get(d.deployment.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed retrieving deployment %s: %v", d.deployment.Name, err)
|
||||
}
|
||||
rs, err := deploymentutil.GetNewReplicaSet(deployment, d.c.AppsV1())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed retrieving new replicaset of deployment %s: %v", d.deployment.Name, err)
|
||||
}
|
||||
return rs, nil
|
||||
}
|
||||
|
||||
func (d *deploymentTester) expectNoNewReplicaSet() error {
|
||||
rs, err := d.getNewReplicaSet()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rs != nil {
|
||||
return fmt.Errorf("expected deployment %s not to create a new replicaset, got %v", d.deployment.Name, rs)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *deploymentTester) expectNewReplicaSet() (*apps.ReplicaSet, error) {
|
||||
rs, err := d.getNewReplicaSet()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if rs == nil {
|
||||
return nil, fmt.Errorf("expected deployment %s to create a new replicaset, got nil", d.deployment.Name)
|
||||
}
|
||||
return rs, nil
|
||||
}
|
||||
|
||||
func (d *deploymentTester) updateReplicaSet(name string, applyUpdate testutil.UpdateReplicaSetFunc) (*apps.ReplicaSet, error) {
|
||||
return testutil.UpdateReplicaSetWithRetries(d.c, d.deployment.Namespace, name, applyUpdate, d.t.Logf, pollInterval, pollTimeout)
|
||||
}
|
||||
|
||||
func (d *deploymentTester) updateReplicaSetStatus(name string, applyStatusUpdate testutil.UpdateReplicaSetFunc) (*apps.ReplicaSet, error) {
|
||||
return testutil.UpdateReplicaSetStatusWithRetries(d.c, d.deployment.Namespace, name, applyStatusUpdate, d.t.Logf, pollInterval, pollTimeout)
|
||||
}
|
||||
|
||||
// waitForDeploymentRollbackCleared waits for deployment either started rolling back or doesn't need to rollback.
|
||||
func (d *deploymentTester) waitForDeploymentRollbackCleared() error {
|
||||
return testutil.WaitForDeploymentRollbackCleared(d.c, d.deployment.Namespace, d.deployment.Name, pollInterval, pollTimeout)
|
||||
}
|
||||
|
||||
// checkDeploymentRevisionAndImage checks if the input deployment's and its new replica set's revision and image are as expected.
|
||||
func (d *deploymentTester) checkDeploymentRevisionAndImage(revision, image string) error {
|
||||
return testutil.CheckDeploymentRevisionAndImage(d.c, d.deployment.Namespace, d.deployment.Name, revision, image)
|
||||
}
|
||||
|
||||
func (d *deploymentTester) waitForDeploymentUpdatedReplicasGTE(minUpdatedReplicas int32) error {
|
||||
return testutil.WaitForDeploymentUpdatedReplicasGTE(d.c, d.deployment.Namespace, d.deployment.Name, minUpdatedReplicas, d.deployment.Generation, pollInterval, pollTimeout)
|
||||
}
|
||||
|
||||
func (d *deploymentTester) waitForDeploymentWithCondition(reason string, condType apps.DeploymentConditionType) error {
|
||||
return testutil.WaitForDeploymentWithCondition(d.c, d.deployment.Namespace, d.deployment.Name, reason, condType, d.t.Logf, pollInterval, pollTimeout)
|
||||
}
|
||||
|
||||
func (d *deploymentTester) listUpdatedPods() ([]v1.Pod, error) {
|
||||
selector, err := metav1.LabelSelectorAsSelector(d.deployment.Spec.Selector)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse deployment selector: %v", err)
|
||||
}
|
||||
pods, err := d.c.CoreV1().Pods(d.deployment.Namespace).List(metav1.ListOptions{LabelSelector: selector.String()})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list deployment pods, will retry later: %v", err)
|
||||
}
|
||||
newRS, err := d.getNewReplicaSet()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get new replicaset of deployment %q: %v", d.deployment.Name, err)
|
||||
}
|
||||
if newRS == nil {
|
||||
return nil, fmt.Errorf("unable to find new replicaset of deployment %q", d.deployment.Name)
|
||||
}
|
||||
|
||||
var ownedPods []v1.Pod
|
||||
for _, pod := range pods.Items {
|
||||
rs := metav1.GetControllerOf(&pod)
|
||||
if rs.UID == newRS.UID {
|
||||
ownedPods = append(ownedPods, pod)
|
||||
}
|
||||
}
|
||||
return ownedPods, nil
|
||||
}
|
||||
|
||||
func (d *deploymentTester) waitRSStable(replicaset *apps.ReplicaSet) error {
|
||||
return testutil.WaitRSStable(d.t, d.c, replicaset, pollInterval, pollTimeout)
|
||||
}
|
||||
|
||||
func (d *deploymentTester) scaleDeployment(newReplicas int32) error {
|
||||
var err error
|
||||
d.deployment, err = d.updateDeployment(func(update *apps.Deployment) {
|
||||
update.Spec.Replicas = &newReplicas
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed updating deployment %q: %v", d.deployment.Name, err)
|
||||
}
|
||||
|
||||
if err := d.waitForDeploymentCompleteAndMarkPodsReady(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rs, err := d.expectNewReplicaSet()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if *rs.Spec.Replicas != newReplicas {
|
||||
return fmt.Errorf("expected new replicaset replicas = %d, got %d", newReplicas, *rs.Spec.Replicas)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// waitForReadyReplicas waits for number of ready replicas to equal number of replicas.
|
||||
func (d *deploymentTester) waitForReadyReplicas() error {
|
||||
if err := wait.PollImmediate(pollInterval, pollTimeout, func() (bool, error) {
|
||||
deployment, err := d.c.AppsV1().Deployments(d.deployment.Namespace).Get(d.deployment.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to get deployment %q: %v", d.deployment.Name, err)
|
||||
}
|
||||
return deployment.Status.ReadyReplicas == *deployment.Spec.Replicas, nil
|
||||
}); err != nil {
|
||||
return fmt.Errorf("failed to wait for .readyReplicas to equal .replicas: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// markUpdatedPodsReadyWithoutComplete marks updated Deployment pods as ready without waiting for deployment to complete.
|
||||
func (d *deploymentTester) markUpdatedPodsReadyWithoutComplete() error {
|
||||
if err := wait.PollImmediate(pollInterval, pollTimeout, func() (bool, error) {
|
||||
pods, err := d.listUpdatedPods()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
for i := range pods {
|
||||
pod := pods[i]
|
||||
if podutil.IsPodReady(&pod) {
|
||||
continue
|
||||
}
|
||||
if err = markPodReady(d.c, d.deployment.Namespace, &pod); err != nil {
|
||||
d.t.Logf("failed to update Deployment pod %q, will retry later: %v", pod.Name, err)
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}); err != nil {
|
||||
return fmt.Errorf("failed to mark all updated pods as ready: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Verify all replicas fields of DeploymentStatus have desired count.
|
||||
// Immediately return an error when found a non-matching replicas field.
|
||||
func (d *deploymentTester) checkDeploymentStatusReplicasFields(replicas, updatedReplicas, readyReplicas, availableReplicas, unavailableReplicas int32) error {
|
||||
deployment, err := d.c.AppsV1().Deployments(d.deployment.Namespace).Get(d.deployment.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get deployment %q: %v", d.deployment.Name, err)
|
||||
}
|
||||
if deployment.Status.Replicas != replicas {
|
||||
return fmt.Errorf("unexpected .replicas: expect %d, got %d", replicas, deployment.Status.Replicas)
|
||||
}
|
||||
if deployment.Status.UpdatedReplicas != updatedReplicas {
|
||||
return fmt.Errorf("unexpected .updatedReplicas: expect %d, got %d", updatedReplicas, deployment.Status.UpdatedReplicas)
|
||||
}
|
||||
if deployment.Status.ReadyReplicas != readyReplicas {
|
||||
return fmt.Errorf("unexpected .readyReplicas: expect %d, got %d", readyReplicas, deployment.Status.ReadyReplicas)
|
||||
}
|
||||
if deployment.Status.AvailableReplicas != availableReplicas {
|
||||
return fmt.Errorf("unexpected .replicas: expect %d, got %d", availableReplicas, deployment.Status.AvailableReplicas)
|
||||
}
|
||||
if deployment.Status.UnavailableReplicas != unavailableReplicas {
|
||||
return fmt.Errorf("unexpected .replicas: expect %d, got %d", unavailableReplicas, deployment.Status.UnavailableReplicas)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
19
vendor/k8s.io/kubernetes/test/integration/doc.go
generated
vendored
Normal file
19
vendor/k8s.io/kubernetes/test/integration/doc.go
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
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 integration provides integration tests for Kubernetes.Some tests require a
|
||||
// running etcd or Docker installation on the system.
|
||||
package integration // import "k8s.io/kubernetes/test/integration"
|
||||
59
vendor/k8s.io/kubernetes/test/integration/etcd/BUILD
generated
vendored
Normal file
59
vendor/k8s.io/kubernetes/test/integration/etcd/BUILD
generated
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
size = "large",
|
||||
srcs = [
|
||||
"etcd_storage_path_test.go",
|
||||
"main_test.go",
|
||||
],
|
||||
tags = [
|
||||
"etcd",
|
||||
"integration",
|
||||
],
|
||||
deps = [
|
||||
"//cmd/kube-apiserver/app:go_default_library",
|
||||
"//cmd/kube-apiserver/app/options:go_default_library",
|
||||
"//pkg/api/legacyscheme:go_default_library",
|
||||
"//pkg/apis/core:go_default_library",
|
||||
"//pkg/master:go_default_library",
|
||||
"//test/integration:go_default_library",
|
||||
"//test/integration/framework:go_default_library",
|
||||
"//vendor/github.com/coreos/etcd/clientv3:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/diff:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/server:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/server/options:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/storage/storagebackend:go_default_library",
|
||||
"//vendor/k8s.io/client-go/discovery/cached:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||
"//vendor/k8s.io/client-go/restmapper:go_default_library",
|
||||
"//vendor/k8s.io/client-go/util/flowcontrol:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
1134
vendor/k8s.io/kubernetes/test/integration/etcd/etcd_storage_path_test.go
generated
vendored
Normal file
1134
vendor/k8s.io/kubernetes/test/integration/etcd/etcd_storage_path_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
27
vendor/k8s.io/kubernetes/test/integration/etcd/main_test.go
generated
vendored
Normal file
27
vendor/k8s.io/kubernetes/test/integration/etcd/main_test.go
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
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 etcd
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
framework.EtcdMain(m.Run)
|
||||
}
|
||||
44
vendor/k8s.io/kubernetes/test/integration/evictions/BUILD
generated
vendored
Normal file
44
vendor/k8s.io/kubernetes/test/integration/evictions/BUILD
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
size = "large",
|
||||
srcs = [
|
||||
"evictions_test.go",
|
||||
"main_test.go",
|
||||
],
|
||||
tags = ["integration"],
|
||||
deps = [
|
||||
"//pkg/controller/disruption:go_default_library",
|
||||
"//test/integration/framework:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/api/policy/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||
"//vendor/k8s.io/client-go/informers:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/cache:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
280
vendor/k8s.io/kubernetes/test/integration/evictions/evictions_test.go
generated
vendored
Normal file
280
vendor/k8s.io/kubernetes/test/integration/evictions/evictions_test.go
generated
vendored
Normal file
@@ -0,0 +1,280 @@
|
||||
/*
|
||||
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 evictions
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http/httptest"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/api/policy/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/client-go/informers"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"k8s.io/kubernetes/pkg/controller/disruption"
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
)
|
||||
|
||||
const (
|
||||
numOfEvictions = 10
|
||||
)
|
||||
|
||||
// TestConcurrentEvictionRequests is to make sure pod disruption budgets (PDB) controller is able to
|
||||
// handle concurrent eviction requests. Original issue:#37605
|
||||
func TestConcurrentEvictionRequests(t *testing.T) {
|
||||
podNameFormat := "test-pod-%d"
|
||||
|
||||
s, closeFn, rm, informers, clientSet := rmSetup(t)
|
||||
defer closeFn()
|
||||
|
||||
ns := framework.CreateTestingNamespace("concurrent-eviction-requests", s, t)
|
||||
defer framework.DeleteTestingNamespace(ns, s, t)
|
||||
|
||||
stopCh := make(chan struct{})
|
||||
informers.Start(stopCh)
|
||||
go rm.Run(stopCh)
|
||||
defer close(stopCh)
|
||||
|
||||
config := restclient.Config{Host: s.URL}
|
||||
clientSet, err := clientset.NewForConfig(&config)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create clientset: %v", err)
|
||||
}
|
||||
|
||||
var gracePeriodSeconds int64 = 30
|
||||
deleteOption := &metav1.DeleteOptions{
|
||||
GracePeriodSeconds: &gracePeriodSeconds,
|
||||
}
|
||||
|
||||
// Generate numOfEvictions pods to evict
|
||||
for i := 0; i < numOfEvictions; i++ {
|
||||
podName := fmt.Sprintf(podNameFormat, i)
|
||||
pod := newPod(podName)
|
||||
|
||||
if _, err := clientSet.CoreV1().Pods(ns.Name).Create(pod); err != nil {
|
||||
t.Errorf("Failed to create pod: %v", err)
|
||||
}
|
||||
|
||||
addPodConditionReady(pod)
|
||||
if _, err := clientSet.CoreV1().Pods(ns.Name).UpdateStatus(pod); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
waitToObservePods(t, informers.Core().V1().Pods().Informer(), numOfEvictions)
|
||||
|
||||
pdb := newPDB()
|
||||
if _, err := clientSet.Policy().PodDisruptionBudgets(ns.Name).Create(pdb); err != nil {
|
||||
t.Errorf("Failed to create PodDisruptionBudget: %v", err)
|
||||
}
|
||||
|
||||
waitPDBStable(t, clientSet, numOfEvictions, ns.Name, pdb.Name)
|
||||
|
||||
var numberPodsEvicted uint32 = 0
|
||||
errCh := make(chan error, 3*numOfEvictions)
|
||||
var wg sync.WaitGroup
|
||||
// spawn numOfEvictions goroutines to concurrently evict the pods
|
||||
for i := 0; i < numOfEvictions; i++ {
|
||||
wg.Add(1)
|
||||
go func(id int, errCh chan error) {
|
||||
defer wg.Done()
|
||||
podName := fmt.Sprintf(podNameFormat, id)
|
||||
eviction := newEviction(ns.Name, podName, deleteOption)
|
||||
|
||||
err := wait.PollImmediate(5*time.Second, 60*time.Second, func() (bool, error) {
|
||||
e := clientSet.Policy().Evictions(ns.Name).Evict(eviction)
|
||||
switch {
|
||||
case errors.IsTooManyRequests(e):
|
||||
return false, nil
|
||||
case errors.IsConflict(e):
|
||||
return false, fmt.Errorf("Unexpected Conflict (409) error caused by failing to handle concurrent PDB updates: %v", e)
|
||||
case e == nil:
|
||||
return true, nil
|
||||
default:
|
||||
return false, e
|
||||
}
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
errCh <- err
|
||||
// should not return here otherwise we would leak the pod
|
||||
}
|
||||
|
||||
_, err = clientSet.CoreV1().Pods(ns.Name).Get(podName, metav1.GetOptions{})
|
||||
switch {
|
||||
case errors.IsNotFound(err):
|
||||
atomic.AddUint32(&numberPodsEvicted, 1)
|
||||
// pod was evicted and deleted so return from goroutine immediately
|
||||
return
|
||||
case err == nil:
|
||||
// this shouldn't happen if the pod was evicted successfully
|
||||
errCh <- fmt.Errorf("Pod %q is expected to be evicted", podName)
|
||||
default:
|
||||
errCh <- err
|
||||
}
|
||||
|
||||
// delete pod which still exists due to error
|
||||
e := clientSet.CoreV1().Pods(ns.Name).Delete(podName, deleteOption)
|
||||
if e != nil {
|
||||
errCh <- e
|
||||
}
|
||||
|
||||
}(i, errCh)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
close(errCh)
|
||||
var errList []error
|
||||
if err := clientSet.Policy().PodDisruptionBudgets(ns.Name).Delete(pdb.Name, deleteOption); err != nil {
|
||||
errList = append(errList, fmt.Errorf("Failed to delete PodDisruptionBudget: %v", err))
|
||||
}
|
||||
for err := range errCh {
|
||||
errList = append(errList, err)
|
||||
}
|
||||
if len(errList) > 0 {
|
||||
t.Fatal(utilerrors.NewAggregate(errList))
|
||||
}
|
||||
|
||||
if atomic.LoadUint32(&numberPodsEvicted) != numOfEvictions {
|
||||
t.Fatalf("fewer number of successful evictions than expected : %d", numberPodsEvicted)
|
||||
}
|
||||
}
|
||||
|
||||
func newPod(podName string) *v1.Pod {
|
||||
return &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: podName,
|
||||
Labels: map[string]string{"app": "test-evictions"},
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Name: "fake-name",
|
||||
Image: "fakeimage",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func addPodConditionReady(pod *v1.Pod) {
|
||||
pod.Status = v1.PodStatus{
|
||||
Phase: v1.PodRunning,
|
||||
Conditions: []v1.PodCondition{
|
||||
{
|
||||
Type: v1.PodReady,
|
||||
Status: v1.ConditionTrue,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newPDB() *v1beta1.PodDisruptionBudget {
|
||||
return &v1beta1.PodDisruptionBudget{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-pdb",
|
||||
},
|
||||
Spec: v1beta1.PodDisruptionBudgetSpec{
|
||||
MinAvailable: &intstr.IntOrString{
|
||||
Type: intstr.Int,
|
||||
IntVal: 0,
|
||||
},
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{"app": "test-evictions"},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newEviction(ns, evictionName string, deleteOption *metav1.DeleteOptions) *v1beta1.Eviction {
|
||||
return &v1beta1.Eviction{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "Policy/v1beta1",
|
||||
Kind: "Eviction",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: evictionName,
|
||||
Namespace: ns,
|
||||
},
|
||||
DeleteOptions: deleteOption,
|
||||
}
|
||||
}
|
||||
|
||||
func rmSetup(t *testing.T) (*httptest.Server, framework.CloseFunc, *disruption.DisruptionController, informers.SharedInformerFactory, clientset.Interface) {
|
||||
masterConfig := framework.NewIntegrationTestMasterConfig()
|
||||
_, s, closeFn := framework.RunAMaster(masterConfig)
|
||||
|
||||
config := restclient.Config{Host: s.URL}
|
||||
clientSet, err := clientset.NewForConfig(&config)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in create clientset: %v", err)
|
||||
}
|
||||
resyncPeriod := 12 * time.Hour
|
||||
informers := informers.NewSharedInformerFactory(clientset.NewForConfigOrDie(restclient.AddUserAgent(&config, "pdb-informers")), resyncPeriod)
|
||||
|
||||
rm := disruption.NewDisruptionController(
|
||||
informers.Core().V1().Pods(),
|
||||
informers.Policy().V1beta1().PodDisruptionBudgets(),
|
||||
informers.Core().V1().ReplicationControllers(),
|
||||
informers.Extensions().V1beta1().ReplicaSets(),
|
||||
informers.Extensions().V1beta1().Deployments(),
|
||||
informers.Apps().V1beta1().StatefulSets(),
|
||||
clientset.NewForConfigOrDie(restclient.AddUserAgent(&config, "disruption-controller")),
|
||||
)
|
||||
return s, closeFn, rm, informers, clientSet
|
||||
}
|
||||
|
||||
// wait for the podInformer to observe the pods. Call this function before
|
||||
// running the RS controller to prevent the rc manager from creating new pods
|
||||
// rather than adopting the existing ones.
|
||||
func waitToObservePods(t *testing.T, podInformer cache.SharedIndexInformer, podNum int) {
|
||||
if err := wait.PollImmediate(2*time.Second, 60*time.Second, func() (bool, error) {
|
||||
objects := podInformer.GetIndexer().List()
|
||||
if len(objects) == podNum {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func waitPDBStable(t *testing.T, clientSet clientset.Interface, podNum int32, ns, pdbName string) {
|
||||
if err := wait.PollImmediate(2*time.Second, 60*time.Second, func() (bool, error) {
|
||||
pdb, err := clientSet.Policy().PodDisruptionBudgets(ns).Get(pdbName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if pdb.Status.CurrentHealthy != podNum {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
27
vendor/k8s.io/kubernetes/test/integration/evictions/main_test.go
generated
vendored
Normal file
27
vendor/k8s.io/kubernetes/test/integration/evictions/main_test.go
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
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 evictions
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
framework.EtcdMain(m.Run)
|
||||
}
|
||||
59
vendor/k8s.io/kubernetes/test/integration/examples/BUILD
generated
vendored
Normal file
59
vendor/k8s.io/kubernetes/test/integration/examples/BUILD
generated
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
size = "large",
|
||||
srcs = [
|
||||
"apiserver_test.go",
|
||||
"main_test.go",
|
||||
"setup_test.go",
|
||||
"webhook_test.go",
|
||||
],
|
||||
tags = ["integration"],
|
||||
deps = [
|
||||
"//cmd/kube-apiserver/app:go_default_library",
|
||||
"//cmd/kube-apiserver/app/options:go_default_library",
|
||||
"//pkg/master:go_default_library",
|
||||
"//pkg/master/reconcilers:go_default_library",
|
||||
"//test/integration/framework:go_default_library",
|
||||
"//vendor/github.com/pborman/uuid:go_default_library",
|
||||
"//vendor/github.com/stretchr/testify/assert:go_default_library",
|
||||
"//vendor/k8s.io/api/admissionregistration/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/apis/audit:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/server:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/server/options:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/clientcmd:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/clientcmd/api:go_default_library",
|
||||
"//vendor/k8s.io/client-go/util/cert:go_default_library",
|
||||
"//vendor/k8s.io/kube-aggregator/pkg/apis/apiregistration/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset:go_default_library",
|
||||
"//vendor/k8s.io/kube-aggregator/pkg/cmd/server:go_default_library",
|
||||
"//vendor/k8s.io/sample-apiserver/pkg/apis/wardle/v1alpha1:go_default_library",
|
||||
"//vendor/k8s.io/sample-apiserver/pkg/apis/wardle/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/sample-apiserver/pkg/cmd/server:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
472
vendor/k8s.io/kubernetes/test/integration/examples/apiserver_test.go
generated
vendored
Normal file
472
vendor/k8s.io/kubernetes/test/integration/examples/apiserver_test.go
generated
vendored
Normal file
@@ -0,0 +1,472 @@
|
||||
/*
|
||||
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 apiserver
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
genericapiserver "k8s.io/apiserver/pkg/server"
|
||||
genericapiserveroptions "k8s.io/apiserver/pkg/server/options"
|
||||
client "k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
"k8s.io/client-go/util/cert"
|
||||
apiregistrationv1beta1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1beta1"
|
||||
aggregatorclient "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset"
|
||||
kubeaggregatorserver "k8s.io/kube-aggregator/pkg/cmd/server"
|
||||
"k8s.io/kubernetes/cmd/kube-apiserver/app"
|
||||
"k8s.io/kubernetes/cmd/kube-apiserver/app/options"
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
wardlev1alpha1 "k8s.io/sample-apiserver/pkg/apis/wardle/v1alpha1"
|
||||
wardlev1beta1 "k8s.io/sample-apiserver/pkg/apis/wardle/v1beta1"
|
||||
sampleserver "k8s.io/sample-apiserver/pkg/cmd/server"
|
||||
)
|
||||
|
||||
func TestAggregatedAPIServer(t *testing.T) {
|
||||
stopCh := make(chan struct{})
|
||||
defer close(stopCh)
|
||||
|
||||
certDir, _ := ioutil.TempDir("", "test-integration-apiserver")
|
||||
defer os.RemoveAll(certDir)
|
||||
_, defaultServiceClusterIPRange, _ := net.ParseCIDR("10.0.0.0/24")
|
||||
proxySigningKey, err := cert.NewPrivateKey()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
proxySigningCert, err := cert.NewSelfSignedCACert(cert.Config{CommonName: "front-proxy-ca"}, proxySigningKey)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
proxyCACertFile, _ := ioutil.TempFile(certDir, "proxy-ca.crt")
|
||||
if err := ioutil.WriteFile(proxyCACertFile.Name(), cert.EncodeCertPEM(proxySigningCert), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
clientSigningKey, err := cert.NewPrivateKey()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
clientSigningCert, err := cert.NewSelfSignedCACert(cert.Config{CommonName: "client-ca"}, clientSigningKey)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
clientCACertFile, _ := ioutil.TempFile(certDir, "client-ca.crt")
|
||||
if err := ioutil.WriteFile(clientCACertFile.Name(), cert.EncodeCertPEM(clientSigningCert), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
kubeClientConfigValue := atomic.Value{}
|
||||
go func() {
|
||||
listener, _, err := genericapiserveroptions.CreateListener("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
kubeAPIServerOptions := options.NewServerRunOptions()
|
||||
kubeAPIServerOptions.SecureServing.Listener = listener
|
||||
kubeAPIServerOptions.SecureServing.BindAddress = net.ParseIP("127.0.0.1")
|
||||
kubeAPIServerOptions.SecureServing.ServerCert.CertDirectory = certDir
|
||||
kubeAPIServerOptions.InsecureServing.BindPort = 0
|
||||
kubeAPIServerOptions.Etcd.StorageConfig.ServerList = []string{framework.GetEtcdURL()}
|
||||
kubeAPIServerOptions.ServiceClusterIPRange = *defaultServiceClusterIPRange
|
||||
kubeAPIServerOptions.Authentication.RequestHeader.UsernameHeaders = []string{"X-Remote-User"}
|
||||
kubeAPIServerOptions.Authentication.RequestHeader.GroupHeaders = []string{"X-Remote-Group"}
|
||||
kubeAPIServerOptions.Authentication.RequestHeader.ExtraHeaderPrefixes = []string{"X-Remote-Extra-"}
|
||||
kubeAPIServerOptions.Authentication.RequestHeader.AllowedNames = []string{"kube-aggregator"}
|
||||
kubeAPIServerOptions.Authentication.RequestHeader.ClientCAFile = proxyCACertFile.Name()
|
||||
kubeAPIServerOptions.Authentication.ClientCert.ClientCA = clientCACertFile.Name()
|
||||
kubeAPIServerOptions.Authorization.Modes = []string{"RBAC"}
|
||||
completedOptions, err := app.Complete(kubeAPIServerOptions)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
tunneler, proxyTransport, err := app.CreateNodeDialer(completedOptions)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
kubeAPIServerConfig, sharedInformers, versionedInformers, _, _, _, admissionPostStartHook, err := app.CreateKubeAPIServerConfig(completedOptions, tunneler, proxyTransport)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Adjust the loopback config for external use (external server name and CA)
|
||||
kubeAPIServerClientConfig := rest.CopyConfig(kubeAPIServerConfig.GenericConfig.LoopbackClientConfig)
|
||||
kubeAPIServerClientConfig.CAFile = path.Join(certDir, "apiserver.crt")
|
||||
kubeAPIServerClientConfig.CAData = nil
|
||||
kubeAPIServerClientConfig.ServerName = ""
|
||||
kubeClientConfigValue.Store(kubeAPIServerClientConfig)
|
||||
|
||||
kubeAPIServer, err := app.CreateKubeAPIServer(kubeAPIServerConfig, genericapiserver.NewEmptyDelegate(), sharedInformers, versionedInformers, admissionPostStartHook)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := kubeAPIServer.GenericAPIServer.PrepareRun().Run(wait.NeverStop); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}()
|
||||
|
||||
// just use json because everyone speaks it
|
||||
err = wait.PollImmediate(time.Second, time.Minute, func() (done bool, err error) {
|
||||
obj := kubeClientConfigValue.Load()
|
||||
if obj == nil {
|
||||
return false, nil
|
||||
}
|
||||
kubeClientConfig := kubeClientConfigValue.Load().(*rest.Config)
|
||||
kubeClientConfig.ContentType = ""
|
||||
kubeClientConfig.AcceptContentTypes = ""
|
||||
kubeClient, err := client.NewForConfig(kubeClientConfig)
|
||||
if err != nil {
|
||||
// this happens because we race the API server start
|
||||
t.Log(err)
|
||||
return false, nil
|
||||
}
|
||||
|
||||
healthStatus := 0
|
||||
kubeClient.Discovery().RESTClient().Get().AbsPath("/healthz").Do().StatusCode(&healthStatus)
|
||||
if healthStatus != http.StatusOK {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// after this point we won't be mutating, so the race detector will be fine
|
||||
kubeClientConfig := kubeClientConfigValue.Load().(*rest.Config)
|
||||
|
||||
// write a kubeconfig out for starting other API servers with delegated auth. remember, no in-cluster config
|
||||
adminKubeConfig := createKubeConfig(kubeClientConfig)
|
||||
kubeconfigFile, _ := ioutil.TempFile("", "")
|
||||
defer os.Remove(kubeconfigFile.Name())
|
||||
clientcmd.WriteToFile(*adminKubeConfig, kubeconfigFile.Name())
|
||||
wardleCertDir, _ := ioutil.TempDir("", "test-integration-wardle-server")
|
||||
defer os.RemoveAll(wardleCertDir)
|
||||
wardlePort := new(int32)
|
||||
|
||||
// start the wardle server to prove we can aggregate it
|
||||
go func() {
|
||||
listener, port, err := genericapiserveroptions.CreateListener("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
atomic.StoreInt32(wardlePort, int32(port))
|
||||
|
||||
o := sampleserver.NewWardleServerOptions(os.Stdout, os.Stderr)
|
||||
o.RecommendedOptions.SecureServing.Listener = listener
|
||||
o.RecommendedOptions.SecureServing.BindAddress = net.ParseIP("127.0.0.1")
|
||||
wardleCmd := sampleserver.NewCommandStartWardleServer(o, stopCh)
|
||||
wardleCmd.SetArgs([]string{
|
||||
"--requestheader-username-headers=X-Remote-User",
|
||||
"--requestheader-group-headers=X-Remote-Group",
|
||||
"--requestheader-extra-headers-prefix=X-Remote-Extra-",
|
||||
"--requestheader-client-ca-file=" + proxyCACertFile.Name(),
|
||||
"--requestheader-allowed-names=kube-aggregator",
|
||||
"--authentication-kubeconfig", kubeconfigFile.Name(),
|
||||
"--authorization-kubeconfig", kubeconfigFile.Name(),
|
||||
"--etcd-servers", framework.GetEtcdURL(),
|
||||
"--cert-dir", wardleCertDir,
|
||||
"--kubeconfig", kubeconfigFile.Name(),
|
||||
})
|
||||
if err := wardleCmd.Execute(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}()
|
||||
|
||||
wardleClientConfig := rest.AnonymousClientConfig(kubeClientConfig)
|
||||
wardleClientConfig.CAFile = path.Join(wardleCertDir, "apiserver.crt")
|
||||
wardleClientConfig.CAData = nil
|
||||
wardleClientConfig.ServerName = ""
|
||||
wardleClientConfig.BearerToken = kubeClientConfig.BearerToken
|
||||
var wardleClient client.Interface
|
||||
err = wait.PollImmediate(100*time.Millisecond, 10*time.Second, func() (done bool, err error) {
|
||||
wardleClientConfig.Host = fmt.Sprintf("https://127.0.0.1:%d", atomic.LoadInt32(wardlePort))
|
||||
wardleClient, err = client.NewForConfig(wardleClientConfig)
|
||||
if err != nil {
|
||||
// this happens because we race the API server start
|
||||
t.Log(err)
|
||||
return false, nil
|
||||
}
|
||||
healthStatus := 0
|
||||
wardleClient.Discovery().RESTClient().Get().AbsPath("/healthz").Do().StatusCode(&healthStatus)
|
||||
if healthStatus != http.StatusOK {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// start the aggregator
|
||||
aggregatorCertDir, _ := ioutil.TempDir("", "test-integration-aggregator")
|
||||
defer os.RemoveAll(aggregatorCertDir)
|
||||
proxyClientKey, err := cert.NewPrivateKey()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
proxyClientCert, err := cert.NewSignedCert(
|
||||
cert.Config{
|
||||
CommonName: "kube-aggregator",
|
||||
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
||||
},
|
||||
proxyClientKey, proxySigningCert, proxySigningKey,
|
||||
)
|
||||
proxyClientCertFile, _ := ioutil.TempFile(aggregatorCertDir, "proxy-client.crt")
|
||||
proxyClientKeyFile, _ := ioutil.TempFile(aggregatorCertDir, "proxy-client.key")
|
||||
if err := ioutil.WriteFile(proxyClientCertFile.Name(), cert.EncodeCertPEM(proxyClientCert), 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := ioutil.WriteFile(proxyClientKeyFile.Name(), cert.EncodePrivateKeyPEM(proxyClientKey), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
aggregatorPort := new(int32)
|
||||
|
||||
go func() {
|
||||
listener, port, err := genericapiserveroptions.CreateListener("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
atomic.StoreInt32(aggregatorPort, int32(port))
|
||||
|
||||
o := kubeaggregatorserver.NewDefaultOptions(os.Stdout, os.Stderr)
|
||||
o.RecommendedOptions.SecureServing.Listener = listener
|
||||
o.RecommendedOptions.SecureServing.BindAddress = net.ParseIP("127.0.0.1")
|
||||
aggregatorCmd := kubeaggregatorserver.NewCommandStartAggregator(o, stopCh)
|
||||
aggregatorCmd.SetArgs([]string{
|
||||
"--requestheader-username-headers", "",
|
||||
"--proxy-client-cert-file", proxyClientCertFile.Name(),
|
||||
"--proxy-client-key-file", proxyClientKeyFile.Name(),
|
||||
"--kubeconfig", kubeconfigFile.Name(),
|
||||
"--authentication-kubeconfig", kubeconfigFile.Name(),
|
||||
"--authorization-kubeconfig", kubeconfigFile.Name(),
|
||||
"--etcd-servers", framework.GetEtcdURL(),
|
||||
"--cert-dir", aggregatorCertDir,
|
||||
})
|
||||
|
||||
if err := aggregatorCmd.Execute(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}()
|
||||
|
||||
aggregatorClientConfig := rest.AnonymousClientConfig(kubeClientConfig)
|
||||
aggregatorClientConfig.CAFile = path.Join(aggregatorCertDir, "apiserver.crt")
|
||||
aggregatorClientConfig.CAData = nil
|
||||
aggregatorClientConfig.ServerName = ""
|
||||
aggregatorClientConfig.BearerToken = kubeClientConfig.BearerToken
|
||||
var aggregatorDiscoveryClient client.Interface
|
||||
err = wait.PollImmediate(100*time.Millisecond, 10*time.Second, func() (done bool, err error) {
|
||||
aggregatorClientConfig.Host = fmt.Sprintf("https://127.0.0.1:%d", atomic.LoadInt32(aggregatorPort))
|
||||
aggregatorDiscoveryClient, err = client.NewForConfig(aggregatorClientConfig)
|
||||
if err != nil {
|
||||
// this happens if we race the API server for writing the cert
|
||||
return false, nil
|
||||
}
|
||||
healthStatus := 0
|
||||
aggregatorDiscoveryClient.Discovery().RESTClient().Get().AbsPath("/healthz").Do().StatusCode(&healthStatus)
|
||||
if healthStatus != http.StatusOK {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// now we're finally ready to test. These are what's run by default now
|
||||
testAPIGroupList(t, wardleClient.Discovery().RESTClient())
|
||||
testAPIGroup(t, wardleClient.Discovery().RESTClient())
|
||||
testAPIResourceList(t, wardleClient.Discovery().RESTClient())
|
||||
|
||||
wardleCA, err := ioutil.ReadFile(wardleClientConfig.CAFile)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
aggregatorClient := aggregatorclient.NewForConfigOrDie(aggregatorClientConfig)
|
||||
_, err = aggregatorClient.ApiregistrationV1beta1().APIServices().Create(&apiregistrationv1beta1.APIService{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "v1alpha1.wardle.k8s.io"},
|
||||
Spec: apiregistrationv1beta1.APIServiceSpec{
|
||||
Service: &apiregistrationv1beta1.ServiceReference{
|
||||
Namespace: "kube-wardle",
|
||||
Name: "api",
|
||||
},
|
||||
Group: "wardle.k8s.io",
|
||||
Version: "v1alpha1",
|
||||
CABundle: wardleCA,
|
||||
GroupPriorityMinimum: 200,
|
||||
VersionPriority: 200,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// this is ugly, but sleep just a little bit so that the watch is probably observed. Since nothing will actually be added to discovery
|
||||
// (the service is missing), we don't have an external signal.
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
if _, err := aggregatorDiscoveryClient.Discovery().ServerResources(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = aggregatorClient.ApiregistrationV1beta1().APIServices().Create(&apiregistrationv1beta1.APIService{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "v1."},
|
||||
Spec: apiregistrationv1beta1.APIServiceSpec{
|
||||
// register this as a local service so it doesn't try to lookup the default kubernetes service
|
||||
// which will have an unroutable IP address since it's fake.
|
||||
Group: "",
|
||||
Version: "v1",
|
||||
GroupPriorityMinimum: 100,
|
||||
VersionPriority: 100,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// this is ugly, but sleep just a little bit so that the watch is probably observed. Since nothing will actually be added to discovery
|
||||
// (the service is missing), we don't have an external signal.
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
_, err = aggregatorDiscoveryClient.Discovery().ServerResources()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// TODO figure out how to turn on enough of services and dns to run more
|
||||
}
|
||||
|
||||
func createKubeConfig(clientCfg *rest.Config) *clientcmdapi.Config {
|
||||
clusterNick := "cluster"
|
||||
userNick := "user"
|
||||
contextNick := "context"
|
||||
|
||||
config := clientcmdapi.NewConfig()
|
||||
|
||||
credentials := clientcmdapi.NewAuthInfo()
|
||||
credentials.Token = clientCfg.BearerToken
|
||||
credentials.ClientCertificate = clientCfg.TLSClientConfig.CertFile
|
||||
if len(credentials.ClientCertificate) == 0 {
|
||||
credentials.ClientCertificateData = clientCfg.TLSClientConfig.CertData
|
||||
}
|
||||
credentials.ClientKey = clientCfg.TLSClientConfig.KeyFile
|
||||
if len(credentials.ClientKey) == 0 {
|
||||
credentials.ClientKeyData = clientCfg.TLSClientConfig.KeyData
|
||||
}
|
||||
config.AuthInfos[userNick] = credentials
|
||||
|
||||
cluster := clientcmdapi.NewCluster()
|
||||
cluster.Server = clientCfg.Host
|
||||
cluster.CertificateAuthority = clientCfg.CAFile
|
||||
if len(cluster.CertificateAuthority) == 0 {
|
||||
cluster.CertificateAuthorityData = clientCfg.CAData
|
||||
}
|
||||
cluster.InsecureSkipTLSVerify = clientCfg.Insecure
|
||||
config.Clusters[clusterNick] = cluster
|
||||
|
||||
context := clientcmdapi.NewContext()
|
||||
context.Cluster = clusterNick
|
||||
context.AuthInfo = userNick
|
||||
config.Contexts[contextNick] = context
|
||||
config.CurrentContext = contextNick
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
func readResponse(client rest.Interface, location string) ([]byte, error) {
|
||||
return client.Get().AbsPath(location).DoRaw()
|
||||
}
|
||||
|
||||
func testAPIGroupList(t *testing.T, client rest.Interface) {
|
||||
contents, err := readResponse(client, "/apis")
|
||||
if err != nil {
|
||||
t.Fatalf("%v", err)
|
||||
}
|
||||
t.Log(string(contents))
|
||||
var apiGroupList metav1.APIGroupList
|
||||
err = json.Unmarshal(contents, &apiGroupList)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in unmarshalling response from server %s: %v", "/apis", err)
|
||||
}
|
||||
assert.Equal(t, 1, len(apiGroupList.Groups))
|
||||
assert.Equal(t, wardlev1alpha1.GroupName, apiGroupList.Groups[0].Name)
|
||||
assert.Equal(t, 2, len(apiGroupList.Groups[0].Versions))
|
||||
|
||||
v1alpha1 := metav1.GroupVersionForDiscovery{
|
||||
GroupVersion: wardlev1alpha1.SchemeGroupVersion.String(),
|
||||
Version: wardlev1alpha1.SchemeGroupVersion.Version,
|
||||
}
|
||||
v1beta1 := metav1.GroupVersionForDiscovery{
|
||||
GroupVersion: wardlev1beta1.SchemeGroupVersion.String(),
|
||||
Version: wardlev1beta1.SchemeGroupVersion.Version,
|
||||
}
|
||||
|
||||
assert.Equal(t, v1beta1, apiGroupList.Groups[0].Versions[0])
|
||||
assert.Equal(t, v1alpha1, apiGroupList.Groups[0].Versions[1])
|
||||
assert.Equal(t, v1beta1, apiGroupList.Groups[0].PreferredVersion)
|
||||
}
|
||||
|
||||
func testAPIGroup(t *testing.T, client rest.Interface) {
|
||||
contents, err := readResponse(client, "/apis/wardle.k8s.io")
|
||||
if err != nil {
|
||||
t.Fatalf("%v", err)
|
||||
}
|
||||
t.Log(string(contents))
|
||||
var apiGroup metav1.APIGroup
|
||||
err = json.Unmarshal(contents, &apiGroup)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in unmarshalling response from server %s: %v", "/apis/wardle.k8s.io", err)
|
||||
}
|
||||
assert.Equal(t, wardlev1alpha1.SchemeGroupVersion.Group, apiGroup.Name)
|
||||
assert.Equal(t, 2, len(apiGroup.Versions))
|
||||
assert.Equal(t, wardlev1alpha1.SchemeGroupVersion.String(), apiGroup.Versions[1].GroupVersion)
|
||||
assert.Equal(t, wardlev1alpha1.SchemeGroupVersion.Version, apiGroup.Versions[1].Version)
|
||||
assert.Equal(t, apiGroup.PreferredVersion, apiGroup.Versions[0])
|
||||
}
|
||||
|
||||
func testAPIResourceList(t *testing.T, client rest.Interface) {
|
||||
contents, err := readResponse(client, "/apis/wardle.k8s.io/v1alpha1")
|
||||
if err != nil {
|
||||
t.Fatalf("%v", err)
|
||||
}
|
||||
t.Log(string(contents))
|
||||
var apiResourceList metav1.APIResourceList
|
||||
err = json.Unmarshal(contents, &apiResourceList)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in unmarshalling response from server %s: %v", "/apis/wardle.k8s.io/v1alpha1", err)
|
||||
}
|
||||
assert.Equal(t, wardlev1alpha1.SchemeGroupVersion.String(), apiResourceList.GroupVersion)
|
||||
assert.Equal(t, 2, len(apiResourceList.APIResources))
|
||||
assert.Equal(t, "fischers", apiResourceList.APIResources[0].Name)
|
||||
assert.False(t, apiResourceList.APIResources[0].Namespaced)
|
||||
assert.Equal(t, "flunders", apiResourceList.APIResources[1].Name)
|
||||
assert.True(t, apiResourceList.APIResources[1].Namespaced)
|
||||
}
|
||||
27
vendor/k8s.io/kubernetes/test/integration/examples/main_test.go
generated
vendored
Normal file
27
vendor/k8s.io/kubernetes/test/integration/examples/main_test.go
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
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 apiserver
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
framework.EtcdMain(m.Run)
|
||||
}
|
||||
168
vendor/k8s.io/kubernetes/test/integration/examples/setup_test.go
generated
vendored
Normal file
168
vendor/k8s.io/kubernetes/test/integration/examples/setup_test.go
generated
vendored
Normal file
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
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 apiserver
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/pborman/uuid"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
genericapiserver "k8s.io/apiserver/pkg/server"
|
||||
genericapiserveroptions "k8s.io/apiserver/pkg/server/options"
|
||||
client "k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/util/cert"
|
||||
"k8s.io/kubernetes/cmd/kube-apiserver/app"
|
||||
"k8s.io/kubernetes/cmd/kube-apiserver/app/options"
|
||||
"k8s.io/kubernetes/pkg/master"
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
)
|
||||
|
||||
type TestServerSetup struct {
|
||||
ModifyServerRunOptions func(*options.ServerRunOptions)
|
||||
ModifyServerConfig func(*master.Config)
|
||||
}
|
||||
|
||||
// startTestServer runs a kube-apiserver, optionally calling out to the setup.ModifyServerRunOptions and setup.ModifyServerConfig functions
|
||||
func startTestServer(t *testing.T, stopCh <-chan struct{}, setup TestServerSetup) (client.Interface, *rest.Config) {
|
||||
certDir, _ := ioutil.TempDir("", "test-integration-"+t.Name())
|
||||
go func() {
|
||||
<-stopCh
|
||||
os.RemoveAll(certDir)
|
||||
}()
|
||||
|
||||
_, defaultServiceClusterIPRange, _ := net.ParseCIDR("10.0.0.0/24")
|
||||
proxySigningKey, err := cert.NewPrivateKey()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
proxySigningCert, err := cert.NewSelfSignedCACert(cert.Config{CommonName: "front-proxy-ca"}, proxySigningKey)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
proxyCACertFile, _ := ioutil.TempFile(certDir, "proxy-ca.crt")
|
||||
if err := ioutil.WriteFile(proxyCACertFile.Name(), cert.EncodeCertPEM(proxySigningCert), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
clientSigningKey, err := cert.NewPrivateKey()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
clientSigningCert, err := cert.NewSelfSignedCACert(cert.Config{CommonName: "client-ca"}, clientSigningKey)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
clientCACertFile, _ := ioutil.TempFile(certDir, "client-ca.crt")
|
||||
if err := ioutil.WriteFile(clientCACertFile.Name(), cert.EncodeCertPEM(clientSigningCert), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
listener, _, err := genericapiserveroptions.CreateListener("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
kubeAPIServerOptions := options.NewServerRunOptions()
|
||||
kubeAPIServerOptions.SecureServing.Listener = listener
|
||||
kubeAPIServerOptions.SecureServing.BindAddress = net.ParseIP("127.0.0.1")
|
||||
kubeAPIServerOptions.SecureServing.ServerCert.CertDirectory = certDir
|
||||
kubeAPIServerOptions.InsecureServing.BindPort = 0
|
||||
kubeAPIServerOptions.Etcd.StorageConfig.Prefix = path.Join("/", uuid.New(), "registry")
|
||||
kubeAPIServerOptions.Etcd.StorageConfig.ServerList = []string{framework.GetEtcdURL()}
|
||||
kubeAPIServerOptions.ServiceClusterIPRange = *defaultServiceClusterIPRange
|
||||
kubeAPIServerOptions.Authentication.RequestHeader.UsernameHeaders = []string{"X-Remote-User"}
|
||||
kubeAPIServerOptions.Authentication.RequestHeader.GroupHeaders = []string{"X-Remote-Group"}
|
||||
kubeAPIServerOptions.Authentication.RequestHeader.ExtraHeaderPrefixes = []string{"X-Remote-Extra-"}
|
||||
kubeAPIServerOptions.Authentication.RequestHeader.AllowedNames = []string{"kube-aggregator"}
|
||||
kubeAPIServerOptions.Authentication.RequestHeader.ClientCAFile = proxyCACertFile.Name()
|
||||
kubeAPIServerOptions.Authentication.ClientCert.ClientCA = clientCACertFile.Name()
|
||||
kubeAPIServerOptions.Authorization.Modes = []string{"Node", "RBAC"}
|
||||
|
||||
if setup.ModifyServerRunOptions != nil {
|
||||
setup.ModifyServerRunOptions(kubeAPIServerOptions)
|
||||
}
|
||||
|
||||
completedOptions, err := app.Complete(kubeAPIServerOptions)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tunneler, proxyTransport, err := app.CreateNodeDialer(completedOptions)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
kubeAPIServerConfig, sharedInformers, versionedInformers, _, _, _, admissionPostStartHook, err := app.CreateKubeAPIServerConfig(completedOptions, tunneler, proxyTransport)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if setup.ModifyServerConfig != nil {
|
||||
setup.ModifyServerConfig(kubeAPIServerConfig)
|
||||
}
|
||||
kubeAPIServer, err := app.CreateKubeAPIServer(kubeAPIServerConfig, genericapiserver.NewEmptyDelegate(), sharedInformers, versionedInformers, admissionPostStartHook)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
go func() {
|
||||
if err := kubeAPIServer.GenericAPIServer.PrepareRun().Run(stopCh); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Adjust the loopback config for external use (external server name and CA)
|
||||
kubeAPIServerClientConfig := rest.CopyConfig(kubeAPIServerConfig.GenericConfig.LoopbackClientConfig)
|
||||
kubeAPIServerClientConfig.CAFile = path.Join(certDir, "apiserver.crt")
|
||||
kubeAPIServerClientConfig.CAData = nil
|
||||
kubeAPIServerClientConfig.ServerName = ""
|
||||
|
||||
// wait for health
|
||||
err = wait.PollImmediate(100*time.Millisecond, 10*time.Second, func() (done bool, err error) {
|
||||
healthzConfig := rest.CopyConfig(kubeAPIServerClientConfig)
|
||||
healthzConfig.ContentType = ""
|
||||
healthzConfig.AcceptContentTypes = ""
|
||||
kubeClient, err := client.NewForConfig(healthzConfig)
|
||||
if err != nil {
|
||||
// this happens because we race the API server start
|
||||
t.Log(err)
|
||||
return false, nil
|
||||
}
|
||||
|
||||
healthStatus := 0
|
||||
kubeClient.Discovery().RESTClient().Get().AbsPath("/healthz").Do().StatusCode(&healthStatus)
|
||||
if healthStatus != http.StatusOK {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return true, nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
kubeAPIServerClient, err := client.NewForConfig(kubeAPIServerClientConfig)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
return kubeAPIServerClient, kubeAPIServerClientConfig
|
||||
}
|
||||
118
vendor/k8s.io/kubernetes/test/integration/examples/webhook_test.go
generated
vendored
Normal file
118
vendor/k8s.io/kubernetes/test/integration/examples/webhook_test.go
generated
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
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 apiserver
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
admissionv1beta1 "k8s.io/api/admissionregistration/v1beta1"
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
auditinternal "k8s.io/apiserver/pkg/apis/audit"
|
||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||
"k8s.io/kubernetes/cmd/kube-apiserver/app/options"
|
||||
"k8s.io/kubernetes/pkg/master"
|
||||
"k8s.io/kubernetes/pkg/master/reconcilers"
|
||||
)
|
||||
|
||||
func TestWebhookLoopback(t *testing.T) {
|
||||
stopCh := make(chan struct{})
|
||||
defer close(stopCh)
|
||||
|
||||
webhookPath := "/webhook-test"
|
||||
|
||||
called := int32(0)
|
||||
|
||||
client, _ := startTestServer(t, stopCh, TestServerSetup{
|
||||
ModifyServerRunOptions: func(opts *options.ServerRunOptions) {
|
||||
},
|
||||
ModifyServerConfig: func(config *master.Config) {
|
||||
// Avoid resolveable kubernetes service
|
||||
config.ExtraConfig.EndpointReconcilerType = reconcilers.NoneEndpointReconcilerType
|
||||
|
||||
// Hook into audit to watch requests
|
||||
config.GenericConfig.AuditBackend = auditSinkFunc(func(events ...*auditinternal.Event) {})
|
||||
config.GenericConfig.AuditPolicyChecker = auditChecker(func(attrs authorizer.Attributes) (auditinternal.Level, []auditinternal.Stage) {
|
||||
if attrs.GetPath() == webhookPath {
|
||||
if attrs.GetUser().GetName() != "system:apiserver" {
|
||||
t.Errorf("expected user %q, got %q", "system:apiserver", attrs.GetUser().GetName())
|
||||
}
|
||||
atomic.AddInt32(&called, 1)
|
||||
}
|
||||
return auditinternal.LevelNone, nil
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
fail := admissionv1beta1.Fail
|
||||
_, err := client.AdmissionregistrationV1beta1().MutatingWebhookConfigurations().Create(&admissionv1beta1.MutatingWebhookConfiguration{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "webhooktest.example.com"},
|
||||
Webhooks: []admissionv1beta1.Webhook{{
|
||||
Name: "webhooktest.example.com",
|
||||
ClientConfig: admissionv1beta1.WebhookClientConfig{
|
||||
Service: &admissionv1beta1.ServiceReference{Namespace: "default", Name: "kubernetes", Path: &webhookPath},
|
||||
},
|
||||
Rules: []admissionv1beta1.RuleWithOperations{{
|
||||
Operations: []admissionv1beta1.OperationType{admissionv1beta1.OperationAll},
|
||||
Rule: admissionv1beta1.Rule{APIGroups: []string{""}, APIVersions: []string{"v1"}, Resources: []string{"configmaps"}},
|
||||
}},
|
||||
FailurePolicy: &fail,
|
||||
}},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = wait.PollImmediate(100*time.Millisecond, 30*time.Second, func() (done bool, err error) {
|
||||
_, err = client.CoreV1().ConfigMaps("default").Create(&v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "webhook-test"},
|
||||
Data: map[string]string{"invalid key": "value"},
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatal("Unexpected success")
|
||||
}
|
||||
if called > 0 {
|
||||
return true, nil
|
||||
}
|
||||
t.Logf("%v", err)
|
||||
t.Logf("webhook not called yet, continuing...")
|
||||
return false, nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
type auditChecker func(authorizer.Attributes) (auditinternal.Level, []auditinternal.Stage)
|
||||
|
||||
func (f auditChecker) LevelAndStages(attrs authorizer.Attributes) (auditinternal.Level, []auditinternal.Stage) {
|
||||
return f(attrs)
|
||||
}
|
||||
|
||||
type auditSinkFunc func(events ...*auditinternal.Event)
|
||||
|
||||
func (f auditSinkFunc) ProcessEvents(events ...*auditinternal.Event) {
|
||||
f(events...)
|
||||
}
|
||||
func (auditSinkFunc) Run(stopCh <-chan struct{}) error {
|
||||
return nil
|
||||
}
|
||||
func (auditSinkFunc) Shutdown() {
|
||||
}
|
||||
78
vendor/k8s.io/kubernetes/test/integration/framework/BUILD
generated
vendored
Normal file
78
vendor/k8s.io/kubernetes/test/integration/framework/BUILD
generated
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"etcd.go",
|
||||
"master_utils.go",
|
||||
"perf_utils.go",
|
||||
"serializer.go",
|
||||
"util.go",
|
||||
],
|
||||
data = [
|
||||
"@com_coreos_etcd//:etcd",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/test/integration/framework",
|
||||
deps = [
|
||||
"//pkg/api/legacyscheme:go_default_library",
|
||||
"//pkg/api/testapi:go_default_library",
|
||||
"//pkg/apis/batch:go_default_library",
|
||||
"//pkg/apis/policy/v1beta1:go_default_library",
|
||||
"//pkg/generated/openapi:go_default_library",
|
||||
"//pkg/kubelet/client:go_default_library",
|
||||
"//pkg/master:go_default_library",
|
||||
"//pkg/util/env:go_default_library",
|
||||
"//pkg/version:go_default_library",
|
||||
"//test/e2e/framework:go_default_library",
|
||||
"//test/utils:go_default_library",
|
||||
"//vendor/github.com/go-openapi/spec:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/github.com/pborman/uuid:go_default_library",
|
||||
"//vendor/k8s.io/api/apps/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/api/autoscaling/v1:go_default_library",
|
||||
"//vendor/k8s.io/api/certificates/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/api/extensions/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/api/rbac/v1alpha1:go_default_library",
|
||||
"//vendor/k8s.io/api/storage/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer/versioning:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authentication/authenticator:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authentication/authenticatorfactory:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authentication/request/union:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authentication/user:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authorization/authorizerfactory:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authorization/union:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/endpoints/openapi:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/server:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/server/options:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/server/storage:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/storage/storagebackend:go_default_library",
|
||||
"//vendor/k8s.io/client-go/informers:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
109
vendor/k8s.io/kubernetes/test/integration/framework/etcd.go
generated
vendored
Normal file
109
vendor/k8s.io/kubernetes/test/integration/framework/etcd.go
generated
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
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 framework
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"hash/adler32"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
"k8s.io/kubernetes/pkg/util/env"
|
||||
)
|
||||
|
||||
var (
|
||||
etcdSetup sync.Once
|
||||
etcdURL = ""
|
||||
)
|
||||
|
||||
func setupETCD() {
|
||||
etcdSetup.Do(func() {
|
||||
if os.Getenv("RUNFILES_DIR") == "" {
|
||||
etcdURL = env.GetEnvAsStringOrFallback("KUBE_INTEGRATION_ETCD_URL", "http://127.0.0.1:2379")
|
||||
return
|
||||
}
|
||||
etcdPath := filepath.Join(os.Getenv("RUNFILES_DIR"), "com_coreos_etcd/etcd")
|
||||
// give every test the same random port each run
|
||||
etcdPort := 20000 + rand.New(rand.NewSource(int64(adler32.Checksum([]byte(os.Args[0]))))).Intn(5000)
|
||||
etcdURL = fmt.Sprintf("http://127.0.0.1:%d", etcdPort)
|
||||
|
||||
info, err := os.Stat(etcdPath)
|
||||
if err != nil {
|
||||
glog.Fatalf("Unable to stat etcd: %v", err)
|
||||
}
|
||||
if info.IsDir() {
|
||||
glog.Fatalf("Did not expect %q to be a directory", etcdPath)
|
||||
}
|
||||
|
||||
etcdDataDir, err := ioutil.TempDir(os.TempDir(), "integration_test_etcd_data")
|
||||
if err != nil {
|
||||
glog.Fatalf("Unable to make temp etcd data dir: %v", err)
|
||||
}
|
||||
glog.Infof("storing etcd data in: %v", etcdDataDir)
|
||||
|
||||
etcdCmd := exec.Command(
|
||||
etcdPath,
|
||||
"--data-dir",
|
||||
etcdDataDir,
|
||||
"--listen-client-urls",
|
||||
GetEtcdURL(),
|
||||
"--advertise-client-urls",
|
||||
GetEtcdURL(),
|
||||
"--listen-peer-urls",
|
||||
"http://127.0.0.1:0",
|
||||
)
|
||||
|
||||
stdout, err := etcdCmd.StdoutPipe()
|
||||
if err != nil {
|
||||
glog.Fatalf("Failed to run etcd: %v", err)
|
||||
}
|
||||
stderr, err := etcdCmd.StderrPipe()
|
||||
if err != nil {
|
||||
glog.Fatalf("Failed to run etcd: %v", err)
|
||||
}
|
||||
if err := etcdCmd.Start(); err != nil {
|
||||
glog.Fatalf("Failed to run etcd: %v", err)
|
||||
}
|
||||
|
||||
go io.Copy(os.Stdout, stdout)
|
||||
go io.Copy(os.Stderr, stderr)
|
||||
|
||||
go func() {
|
||||
if err := etcdCmd.Wait(); err != nil {
|
||||
glog.Fatalf("Failed to run etcd: %v", err)
|
||||
}
|
||||
glog.Fatalf("etcd should not have succeeded")
|
||||
}()
|
||||
})
|
||||
}
|
||||
|
||||
func EtcdMain(tests func() int) {
|
||||
setupETCD()
|
||||
os.Exit(tests())
|
||||
}
|
||||
|
||||
// return the EtcdURL
|
||||
func GetEtcdURL() string {
|
||||
return etcdURL
|
||||
}
|
||||
331
vendor/k8s.io/kubernetes/test/integration/framework/master_utils.go
generated
vendored
Normal file
331
vendor/k8s.io/kubernetes/test/integration/framework/master_utils.go
generated
vendored
Normal file
@@ -0,0 +1,331 @@
|
||||
/*
|
||||
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 framework
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/go-openapi/spec"
|
||||
"github.com/golang/glog"
|
||||
"github.com/pborman/uuid"
|
||||
|
||||
apps "k8s.io/api/apps/v1beta1"
|
||||
autoscaling "k8s.io/api/autoscaling/v1"
|
||||
certificates "k8s.io/api/certificates/v1beta1"
|
||||
"k8s.io/api/core/v1"
|
||||
extensions "k8s.io/api/extensions/v1beta1"
|
||||
rbac "k8s.io/api/rbac/v1alpha1"
|
||||
storage "k8s.io/api/storage/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
authauthenticator "k8s.io/apiserver/pkg/authentication/authenticator"
|
||||
"k8s.io/apiserver/pkg/authentication/authenticatorfactory"
|
||||
authenticatorunion "k8s.io/apiserver/pkg/authentication/request/union"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||
"k8s.io/apiserver/pkg/authorization/authorizerfactory"
|
||||
authorizerunion "k8s.io/apiserver/pkg/authorization/union"
|
||||
openapinamer "k8s.io/apiserver/pkg/endpoints/openapi"
|
||||
genericapiserver "k8s.io/apiserver/pkg/server"
|
||||
"k8s.io/apiserver/pkg/server/options"
|
||||
serverstorage "k8s.io/apiserver/pkg/server/storage"
|
||||
"k8s.io/apiserver/pkg/storage/storagebackend"
|
||||
"k8s.io/client-go/informers"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
"k8s.io/kubernetes/pkg/api/testapi"
|
||||
"k8s.io/kubernetes/pkg/apis/batch"
|
||||
policy "k8s.io/kubernetes/pkg/apis/policy/v1beta1"
|
||||
"k8s.io/kubernetes/pkg/generated/openapi"
|
||||
kubeletclient "k8s.io/kubernetes/pkg/kubelet/client"
|
||||
"k8s.io/kubernetes/pkg/master"
|
||||
"k8s.io/kubernetes/pkg/version"
|
||||
)
|
||||
|
||||
// Config is a struct of configuration directives for NewMasterComponents.
|
||||
type Config struct {
|
||||
// If nil, a default is used, partially filled configs will not get populated.
|
||||
MasterConfig *master.Config
|
||||
StartReplicationManager bool
|
||||
// Client throttling qps
|
||||
QPS float32
|
||||
// Client burst qps, also burst replicas allowed in rc manager
|
||||
Burst int
|
||||
// TODO: Add configs for endpoints controller, scheduler etc
|
||||
}
|
||||
|
||||
// alwaysAllow always allows an action
|
||||
type alwaysAllow struct{}
|
||||
|
||||
func (alwaysAllow) Authorize(requestAttributes authorizer.Attributes) (authorizer.Decision, string, error) {
|
||||
return authorizer.DecisionAllow, "always allow", nil
|
||||
}
|
||||
|
||||
// alwaysEmpty simulates "no authentication" for old tests
|
||||
func alwaysEmpty(req *http.Request) (user.Info, bool, error) {
|
||||
return &user.DefaultInfo{
|
||||
Name: "",
|
||||
}, true, nil
|
||||
}
|
||||
|
||||
// MasterReceiver can be used to provide the master to a custom incoming server function
|
||||
type MasterReceiver interface {
|
||||
SetMaster(m *master.Master)
|
||||
}
|
||||
|
||||
// MasterHolder implements
|
||||
type MasterHolder struct {
|
||||
Initialized chan struct{}
|
||||
M *master.Master
|
||||
}
|
||||
|
||||
func (h *MasterHolder) SetMaster(m *master.Master) {
|
||||
h.M = m
|
||||
close(h.Initialized)
|
||||
}
|
||||
|
||||
// startMasterOrDie starts a kubernetes master and an httpserver to handle api requests
|
||||
func startMasterOrDie(masterConfig *master.Config, incomingServer *httptest.Server, masterReceiver MasterReceiver) (*master.Master, *httptest.Server, CloseFunc) {
|
||||
var m *master.Master
|
||||
var s *httptest.Server
|
||||
|
||||
if incomingServer != nil {
|
||||
s = incomingServer
|
||||
} else {
|
||||
s = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
m.GenericAPIServer.Handler.ServeHTTP(w, req)
|
||||
}))
|
||||
}
|
||||
|
||||
stopCh := make(chan struct{})
|
||||
closeFn := func() {
|
||||
close(stopCh)
|
||||
s.Close()
|
||||
}
|
||||
|
||||
if masterConfig == nil {
|
||||
masterConfig = NewMasterConfig()
|
||||
masterConfig.GenericConfig.OpenAPIConfig = genericapiserver.DefaultOpenAPIConfig(openapi.GetOpenAPIDefinitions, openapinamer.NewDefinitionNamer(legacyscheme.Scheme))
|
||||
masterConfig.GenericConfig.OpenAPIConfig.Info = &spec.Info{
|
||||
InfoProps: spec.InfoProps{
|
||||
Title: "Kubernetes",
|
||||
Version: "unversioned",
|
||||
},
|
||||
}
|
||||
masterConfig.GenericConfig.OpenAPIConfig.DefaultResponse = &spec.Response{
|
||||
ResponseProps: spec.ResponseProps{
|
||||
Description: "Default Response.",
|
||||
},
|
||||
}
|
||||
masterConfig.GenericConfig.OpenAPIConfig.GetDefinitions = openapi.GetOpenAPIDefinitions
|
||||
masterConfig.GenericConfig.SwaggerConfig = genericapiserver.DefaultSwaggerConfig()
|
||||
}
|
||||
|
||||
// set the loopback client config
|
||||
if masterConfig.GenericConfig.LoopbackClientConfig == nil {
|
||||
masterConfig.GenericConfig.LoopbackClientConfig = &restclient.Config{QPS: 50, Burst: 100, ContentConfig: restclient.ContentConfig{NegotiatedSerializer: legacyscheme.Codecs}}
|
||||
}
|
||||
masterConfig.GenericConfig.LoopbackClientConfig.Host = s.URL
|
||||
|
||||
privilegedLoopbackToken := uuid.NewRandom().String()
|
||||
// wrap any available authorizer
|
||||
tokens := make(map[string]*user.DefaultInfo)
|
||||
tokens[privilegedLoopbackToken] = &user.DefaultInfo{
|
||||
Name: user.APIServerUser,
|
||||
UID: uuid.NewRandom().String(),
|
||||
Groups: []string{user.SystemPrivilegedGroup},
|
||||
}
|
||||
|
||||
tokenAuthenticator := authenticatorfactory.NewFromTokens(tokens)
|
||||
if masterConfig.GenericConfig.Authentication.Authenticator == nil {
|
||||
masterConfig.GenericConfig.Authentication.Authenticator = authenticatorunion.New(tokenAuthenticator, authauthenticator.RequestFunc(alwaysEmpty))
|
||||
} else {
|
||||
masterConfig.GenericConfig.Authentication.Authenticator = authenticatorunion.New(tokenAuthenticator, masterConfig.GenericConfig.Authentication.Authenticator)
|
||||
}
|
||||
|
||||
if masterConfig.GenericConfig.Authorization.Authorizer != nil {
|
||||
tokenAuthorizer := authorizerfactory.NewPrivilegedGroups(user.SystemPrivilegedGroup)
|
||||
masterConfig.GenericConfig.Authorization.Authorizer = authorizerunion.New(tokenAuthorizer, masterConfig.GenericConfig.Authorization.Authorizer)
|
||||
} else {
|
||||
masterConfig.GenericConfig.Authorization.Authorizer = alwaysAllow{}
|
||||
}
|
||||
|
||||
masterConfig.GenericConfig.LoopbackClientConfig.BearerToken = privilegedLoopbackToken
|
||||
|
||||
clientset, err := clientset.NewForConfig(masterConfig.GenericConfig.LoopbackClientConfig)
|
||||
if err != nil {
|
||||
glog.Fatal(err)
|
||||
}
|
||||
|
||||
sharedInformers := informers.NewSharedInformerFactory(clientset, masterConfig.GenericConfig.LoopbackClientConfig.Timeout)
|
||||
m, err = masterConfig.Complete(sharedInformers).New(genericapiserver.NewEmptyDelegate())
|
||||
if err != nil {
|
||||
closeFn()
|
||||
glog.Fatalf("error in bringing up the master: %v", err)
|
||||
}
|
||||
if masterReceiver != nil {
|
||||
masterReceiver.SetMaster(m)
|
||||
}
|
||||
|
||||
// TODO have this start method actually use the normal start sequence for the API server
|
||||
// this method never actually calls the `Run` method for the API server
|
||||
// fire the post hooks ourselves
|
||||
m.GenericAPIServer.PrepareRun()
|
||||
m.GenericAPIServer.RunPostStartHooks(stopCh)
|
||||
|
||||
cfg := *masterConfig.GenericConfig.LoopbackClientConfig
|
||||
cfg.ContentConfig.GroupVersion = &schema.GroupVersion{}
|
||||
privilegedClient, err := restclient.RESTClientFor(&cfg)
|
||||
if err != nil {
|
||||
closeFn()
|
||||
glog.Fatal(err)
|
||||
}
|
||||
var lastHealthContent []byte
|
||||
err = wait.PollImmediate(100*time.Millisecond, 30*time.Second, func() (bool, error) {
|
||||
result := privilegedClient.Get().AbsPath("/healthz").Do()
|
||||
status := 0
|
||||
result.StatusCode(&status)
|
||||
if status == 200 {
|
||||
return true, nil
|
||||
}
|
||||
lastHealthContent, _ = result.Raw()
|
||||
return false, nil
|
||||
})
|
||||
if err != nil {
|
||||
closeFn()
|
||||
glog.Errorf("last health content: %q", string(lastHealthContent))
|
||||
glog.Fatal(err)
|
||||
}
|
||||
|
||||
return m, s, closeFn
|
||||
}
|
||||
|
||||
// Returns the master config appropriate for most integration tests.
|
||||
func NewIntegrationTestMasterConfig() *master.Config {
|
||||
masterConfig := NewMasterConfig()
|
||||
masterConfig.GenericConfig.PublicAddress = net.ParseIP("192.168.10.4")
|
||||
masterConfig.ExtraConfig.APIResourceConfigSource = master.DefaultAPIResourceConfigSource()
|
||||
return masterConfig
|
||||
}
|
||||
|
||||
// Returns a basic master config.
|
||||
func NewMasterConfig() *master.Config {
|
||||
// This causes the integration tests to exercise the etcd
|
||||
// prefix code, so please don't change without ensuring
|
||||
// sufficient coverage in other ways.
|
||||
etcdOptions := options.NewEtcdOptions(storagebackend.NewDefaultConfig(uuid.New(), nil))
|
||||
etcdOptions.StorageConfig.ServerList = []string{GetEtcdURL()}
|
||||
|
||||
info, _ := runtime.SerializerInfoForMediaType(legacyscheme.Codecs.SupportedMediaTypes(), runtime.ContentTypeJSON)
|
||||
ns := NewSingleContentTypeSerializer(legacyscheme.Scheme, info)
|
||||
|
||||
resourceEncoding := serverstorage.NewDefaultResourceEncodingConfig(legacyscheme.Scheme)
|
||||
// FIXME (soltysh): this GroupVersionResource override should be configurable
|
||||
// we need to set both for the whole group and for cronjobs, separately
|
||||
resourceEncoding.SetVersionEncoding(batch.GroupName, *testapi.Batch.GroupVersion(), schema.GroupVersion{Group: batch.GroupName, Version: runtime.APIVersionInternal})
|
||||
resourceEncoding.SetResourceEncoding(schema.GroupResource{Group: batch.GroupName, Resource: "cronjobs"}, schema.GroupVersion{Group: batch.GroupName, Version: "v1beta1"}, schema.GroupVersion{Group: batch.GroupName, Version: runtime.APIVersionInternal})
|
||||
// we also need to set both for the storage group and for volumeattachments, separately
|
||||
resourceEncoding.SetVersionEncoding(storage.GroupName, *testapi.Storage.GroupVersion(), schema.GroupVersion{Group: storage.GroupName, Version: runtime.APIVersionInternal})
|
||||
resourceEncoding.SetResourceEncoding(schema.GroupResource{Group: storage.GroupName, Resource: "volumeattachments"}, schema.GroupVersion{Group: storage.GroupName, Version: "v1beta1"}, schema.GroupVersion{Group: storage.GroupName, Version: runtime.APIVersionInternal})
|
||||
|
||||
storageFactory := serverstorage.NewDefaultStorageFactory(etcdOptions.StorageConfig, runtime.ContentTypeJSON, ns, resourceEncoding, master.DefaultAPIResourceConfigSource(), nil)
|
||||
storageFactory.SetSerializer(
|
||||
schema.GroupResource{Group: v1.GroupName, Resource: serverstorage.AllResources},
|
||||
"",
|
||||
ns)
|
||||
storageFactory.SetSerializer(
|
||||
schema.GroupResource{Group: autoscaling.GroupName, Resource: serverstorage.AllResources},
|
||||
"",
|
||||
ns)
|
||||
storageFactory.SetSerializer(
|
||||
schema.GroupResource{Group: batch.GroupName, Resource: serverstorage.AllResources},
|
||||
"",
|
||||
ns)
|
||||
storageFactory.SetSerializer(
|
||||
schema.GroupResource{Group: apps.GroupName, Resource: serverstorage.AllResources},
|
||||
"",
|
||||
ns)
|
||||
storageFactory.SetSerializer(
|
||||
schema.GroupResource{Group: extensions.GroupName, Resource: serverstorage.AllResources},
|
||||
"",
|
||||
ns)
|
||||
storageFactory.SetSerializer(
|
||||
schema.GroupResource{Group: policy.GroupName, Resource: serverstorage.AllResources},
|
||||
"",
|
||||
ns)
|
||||
storageFactory.SetSerializer(
|
||||
schema.GroupResource{Group: rbac.GroupName, Resource: serverstorage.AllResources},
|
||||
"",
|
||||
ns)
|
||||
storageFactory.SetSerializer(
|
||||
schema.GroupResource{Group: certificates.GroupName, Resource: serverstorage.AllResources},
|
||||
"",
|
||||
ns)
|
||||
storageFactory.SetSerializer(
|
||||
schema.GroupResource{Group: storage.GroupName, Resource: serverstorage.AllResources},
|
||||
"",
|
||||
ns)
|
||||
|
||||
genericConfig := genericapiserver.NewConfig(legacyscheme.Codecs)
|
||||
kubeVersion := version.Get()
|
||||
genericConfig.Version = &kubeVersion
|
||||
genericConfig.Authorization.Authorizer = authorizerfactory.NewAlwaysAllowAuthorizer()
|
||||
|
||||
err := etcdOptions.ApplyWithStorageFactoryTo(storageFactory, genericConfig)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return &master.Config{
|
||||
GenericConfig: genericConfig,
|
||||
ExtraConfig: master.ExtraConfig{
|
||||
APIResourceConfigSource: master.DefaultAPIResourceConfigSource(),
|
||||
StorageFactory: storageFactory,
|
||||
KubeletClientConfig: kubeletclient.KubeletClientConfig{Port: 10250},
|
||||
APIServerServicePort: 443,
|
||||
MasterCount: 1,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// CloseFunc can be called to cleanup the master
|
||||
type CloseFunc func()
|
||||
|
||||
func RunAMaster(masterConfig *master.Config) (*master.Master, *httptest.Server, CloseFunc) {
|
||||
if masterConfig == nil {
|
||||
masterConfig = NewMasterConfig()
|
||||
masterConfig.GenericConfig.EnableProfiling = true
|
||||
}
|
||||
return startMasterOrDie(masterConfig, nil, nil)
|
||||
}
|
||||
|
||||
func RunAMasterUsingServer(masterConfig *master.Config, s *httptest.Server, masterReceiver MasterReceiver) (*master.Master, *httptest.Server, CloseFunc) {
|
||||
return startMasterOrDie(masterConfig, s, masterReceiver)
|
||||
}
|
||||
|
||||
// SharedEtcd creates a storage config for a shared etcd instance, with a unique prefix.
|
||||
func SharedEtcd() *storagebackend.Config {
|
||||
cfg := storagebackend.NewDefaultConfig(path.Join(uuid.New(), "registry"), nil)
|
||||
cfg.ServerList = []string{GetEtcdURL()}
|
||||
return cfg
|
||||
}
|
||||
107
vendor/k8s.io/kubernetes/test/integration/framework/perf_utils.go
generated
vendored
Normal file
107
vendor/k8s.io/kubernetes/test/integration/framework/perf_utils.go
generated
vendored
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
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 framework
|
||||
|
||||
import (
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
e2eframework "k8s.io/kubernetes/test/e2e/framework"
|
||||
testutils "k8s.io/kubernetes/test/utils"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
const (
|
||||
retries = 5
|
||||
)
|
||||
|
||||
type IntegrationTestNodePreparer struct {
|
||||
client clientset.Interface
|
||||
countToStrategy []testutils.CountToStrategy
|
||||
nodeNamePrefix string
|
||||
}
|
||||
|
||||
func NewIntegrationTestNodePreparer(client clientset.Interface, countToStrategy []testutils.CountToStrategy, nodeNamePrefix string) testutils.TestNodePreparer {
|
||||
return &IntegrationTestNodePreparer{
|
||||
client: client,
|
||||
countToStrategy: countToStrategy,
|
||||
nodeNamePrefix: nodeNamePrefix,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *IntegrationTestNodePreparer) PrepareNodes() error {
|
||||
numNodes := 0
|
||||
for _, v := range p.countToStrategy {
|
||||
numNodes += v.Count
|
||||
}
|
||||
|
||||
glog.Infof("Making %d nodes", numNodes)
|
||||
baseNode := &v1.Node{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
GenerateName: p.nodeNamePrefix,
|
||||
},
|
||||
Status: v1.NodeStatus{
|
||||
Capacity: v1.ResourceList{
|
||||
v1.ResourcePods: *resource.NewQuantity(110, resource.DecimalSI),
|
||||
v1.ResourceCPU: resource.MustParse("4"),
|
||||
v1.ResourceMemory: resource.MustParse("32Gi"),
|
||||
},
|
||||
Phase: v1.NodeRunning,
|
||||
Conditions: []v1.NodeCondition{
|
||||
{Type: v1.NodeReady, Status: v1.ConditionTrue},
|
||||
},
|
||||
},
|
||||
}
|
||||
for i := 0; i < numNodes; i++ {
|
||||
var err error
|
||||
for retry := 0; retry < retries; retry++ {
|
||||
_, err = p.client.CoreV1().Nodes().Create(baseNode)
|
||||
if err == nil || !testutils.IsRetryableAPIError(err) {
|
||||
break
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
glog.Fatalf("Error creating node: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
nodes := e2eframework.GetReadySchedulableNodesOrDie(p.client)
|
||||
index := 0
|
||||
sum := 0
|
||||
for _, v := range p.countToStrategy {
|
||||
sum += v.Count
|
||||
for ; index < sum; index++ {
|
||||
if err := testutils.DoPrepareNode(p.client, &nodes.Items[index], v.Strategy); err != nil {
|
||||
glog.Errorf("Aborting node preparation: %v", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *IntegrationTestNodePreparer) CleanupNodes() error {
|
||||
nodes := e2eframework.GetReadySchedulableNodesOrDie(p.client)
|
||||
for i := range nodes.Items {
|
||||
if err := p.client.CoreV1().Nodes().Delete(nodes.Items[i].Name, &metav1.DeleteOptions{}); err != nil {
|
||||
glog.Errorf("Error while deleting Node: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
53
vendor/k8s.io/kubernetes/test/integration/framework/serializer.go
generated
vendored
Normal file
53
vendor/k8s.io/kubernetes/test/integration/framework/serializer.go
generated
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
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 framework
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer/versioning"
|
||||
)
|
||||
|
||||
// NewSingleContentTypeSerializer wraps a serializer in a NegotiatedSerializer that handles one content type
|
||||
func NewSingleContentTypeSerializer(scheme *runtime.Scheme, info runtime.SerializerInfo) runtime.StorageSerializer {
|
||||
return &wrappedSerializer{
|
||||
scheme: scheme,
|
||||
info: info,
|
||||
}
|
||||
}
|
||||
|
||||
type wrappedSerializer struct {
|
||||
scheme *runtime.Scheme
|
||||
info runtime.SerializerInfo
|
||||
}
|
||||
|
||||
var _ runtime.StorageSerializer = &wrappedSerializer{}
|
||||
|
||||
func (s *wrappedSerializer) SupportedMediaTypes() []runtime.SerializerInfo {
|
||||
return []runtime.SerializerInfo{s.info}
|
||||
}
|
||||
|
||||
func (s *wrappedSerializer) UniversalDeserializer() runtime.Decoder {
|
||||
return s.info.Serializer
|
||||
}
|
||||
|
||||
func (s *wrappedSerializer) EncoderForVersion(encoder runtime.Encoder, gv runtime.GroupVersioner) runtime.Encoder {
|
||||
return versioning.NewCodec(encoder, nil, s.scheme, s.scheme, s.scheme, s.scheme, gv, nil)
|
||||
}
|
||||
|
||||
func (s *wrappedSerializer) DecoderToVersion(decoder runtime.Decoder, gv runtime.GroupVersioner) runtime.Decoder {
|
||||
return versioning.NewCodec(nil, decoder, s.scheme, s.scheme, s.scheme, s.scheme, nil, gv)
|
||||
}
|
||||
50
vendor/k8s.io/kubernetes/test/integration/framework/util.go
generated
vendored
Normal file
50
vendor/k8s.io/kubernetes/test/integration/framework/util.go
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
// TODO: This file can potentially be moved to a common place used by both e2e and integration tests.
|
||||
|
||||
package framework
|
||||
|
||||
import (
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
// When these values are updated, also update cmd/kubelet/app/options/container_runtime.go
|
||||
// A copy of these values exist in test/utils/image/manifest.go
|
||||
currentPodInfraContainerImageName = "k8s.gcr.io/pause"
|
||||
currentPodInfraContainerImageVersion = "3.1"
|
||||
)
|
||||
|
||||
func CreateTestingNamespace(baseName string, apiserver *httptest.Server, t *testing.T) *v1.Namespace {
|
||||
// TODO: Create a namespace with a given basename.
|
||||
// Currently we neither create the namespace nor delete all of its contents at the end.
|
||||
// But as long as tests are not using the same namespaces, this should work fine.
|
||||
return &v1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
// TODO: Once we start creating namespaces, switch to GenerateName.
|
||||
Name: baseName,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func DeleteTestingNamespace(ns *v1.Namespace, apiserver *httptest.Server, t *testing.T) {
|
||||
// TODO: Remove all resources from a given namespace once we implement CreateTestingNamespace.
|
||||
}
|
||||
51
vendor/k8s.io/kubernetes/test/integration/garbagecollector/BUILD
generated
vendored
Normal file
51
vendor/k8s.io/kubernetes/test/integration/garbagecollector/BUILD
generated
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_test")
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
size = "large",
|
||||
srcs = [
|
||||
"cluster_scoped_owner_test.go",
|
||||
"garbage_collector_test.go",
|
||||
"main_test.go",
|
||||
],
|
||||
tags = ["integration"],
|
||||
deps = [
|
||||
"//cmd/kube-apiserver/app/testing:go_default_library",
|
||||
"//pkg/controller/garbagecollector:go_default_library",
|
||||
"//test/integration:go_default_library",
|
||||
"//test/integration/framework:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset:go_default_library",
|
||||
"//vendor/k8s.io/apiextensions-apiserver/test/integration/testserver:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library",
|
||||
"//vendor/k8s.io/client-go/discovery/cached:go_default_library",
|
||||
"//vendor/k8s.io/client-go/dynamic:go_default_library",
|
||||
"//vendor/k8s.io/client-go/informers:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
"//vendor/k8s.io/client-go/restmapper:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/cache:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
147
vendor/k8s.io/kubernetes/test/integration/garbagecollector/cluster_scoped_owner_test.go
generated
vendored
Normal file
147
vendor/k8s.io/kubernetes/test/integration/garbagecollector/cluster_scoped_owner_test.go
generated
vendored
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
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 garbagecollector
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing"
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
)
|
||||
|
||||
type roundTripFunc func(req *http.Request) (*http.Response, error)
|
||||
|
||||
func (w roundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
return w(req)
|
||||
}
|
||||
|
||||
type readDelayer struct {
|
||||
delay time.Duration
|
||||
io.ReadCloser
|
||||
}
|
||||
|
||||
func (b *readDelayer) Read(p []byte) (n int, err error) {
|
||||
defer time.Sleep(b.delay)
|
||||
return b.ReadCloser.Read(p)
|
||||
}
|
||||
|
||||
func TestClusterScopedOwners(t *testing.T) {
|
||||
// Start the test server and wrap the client to delay PV watch responses
|
||||
server := kubeapiservertesting.StartTestServerOrDie(t, nil, nil, framework.SharedEtcd())
|
||||
server.ClientConfig.WrapTransport = func(rt http.RoundTripper) http.RoundTripper {
|
||||
return roundTripFunc(func(req *http.Request) (*http.Response, error) {
|
||||
if req.URL.Query().Get("watch") != "true" || !strings.Contains(req.URL.String(), "persistentvolumes") {
|
||||
return rt.RoundTrip(req)
|
||||
}
|
||||
resp, err := rt.RoundTrip(req)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
resp.Body = &readDelayer{30 * time.Second, resp.Body}
|
||||
return resp, err
|
||||
})
|
||||
}
|
||||
ctx := setupWithServer(t, server, 5)
|
||||
defer ctx.tearDown()
|
||||
|
||||
_, clientSet := ctx.gc, ctx.clientSet
|
||||
|
||||
ns := createNamespaceOrDie("gc-cluster-scope-deletion", clientSet, t)
|
||||
defer deleteNamespaceOrDie(ns.Name, clientSet, t)
|
||||
|
||||
t.Log("Create a pair of objects")
|
||||
pv, err := clientSet.CoreV1().PersistentVolumes().Create(&v1.PersistentVolume{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "pv-valid"},
|
||||
Spec: v1.PersistentVolumeSpec{
|
||||
PersistentVolumeSource: v1.PersistentVolumeSource{HostPath: &v1.HostPathVolumeSource{Path: "/foo"}},
|
||||
Capacity: v1.ResourceList{v1.ResourceStorage: resource.MustParse("1Gi")},
|
||||
AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteMany},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := clientSet.CoreV1().ConfigMaps(ns.Name).Create(&v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "cm-valid",
|
||||
OwnerReferences: []metav1.OwnerReference{{Kind: "PersistentVolume", APIVersion: "v1", Name: pv.Name, UID: pv.UID}},
|
||||
},
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Log("Create a namespaced object with a missing parent")
|
||||
if _, err := clientSet.CoreV1().ConfigMaps(ns.Name).Create(&v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "cm-missing",
|
||||
Labels: map[string]string{"missing": "true"},
|
||||
OwnerReferences: []metav1.OwnerReference{{Kind: "PersistentVolume", APIVersion: "v1", Name: "missing-name", UID: types.UID("missing-uid")}},
|
||||
},
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Log("Create a namespaced object with a missing type parent")
|
||||
if _, err := clientSet.CoreV1().ConfigMaps(ns.Name).Create(&v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "cm-invalid",
|
||||
OwnerReferences: []metav1.OwnerReference{{Kind: "UnknownType", APIVersion: "unknown.group/v1", Name: "invalid-name", UID: types.UID("invalid-uid")}},
|
||||
},
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// wait for deletable children to go away
|
||||
if err := wait.Poll(5*time.Second, 300*time.Second, func() (bool, error) {
|
||||
_, err := clientSet.CoreV1().ConfigMaps(ns.Name).Get("cm-missing", metav1.GetOptions{})
|
||||
switch {
|
||||
case errors.IsNotFound(err):
|
||||
return true, nil
|
||||
case err != nil:
|
||||
return false, err
|
||||
default:
|
||||
t.Logf("cm with missing parent still exists, retrying")
|
||||
return false, nil
|
||||
}
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Logf("deletable children removed")
|
||||
|
||||
// Give time for blocked children to be incorrectly cleaned up
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
// ensure children with unverifiable parents don't get reaped
|
||||
if _, err := clientSet.CoreV1().ConfigMaps(ns.Name).Get("cm-invalid", metav1.GetOptions{}); err != nil {
|
||||
t.Fatalf("child with invalid ownerRef is unexpectedly missing: %v", err)
|
||||
}
|
||||
|
||||
// ensure children with present parents don't get reaped
|
||||
if _, err := clientSet.CoreV1().ConfigMaps(ns.Name).Get("cm-valid", metav1.GetOptions{}); err != nil {
|
||||
t.Fatalf("child with valid ownerRef is unexpectedly missing: %v", err)
|
||||
}
|
||||
}
|
||||
1011
vendor/k8s.io/kubernetes/test/integration/garbagecollector/garbage_collector_test.go
generated
vendored
Normal file
1011
vendor/k8s.io/kubernetes/test/integration/garbagecollector/garbage_collector_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
27
vendor/k8s.io/kubernetes/test/integration/garbagecollector/main_test.go
generated
vendored
Normal file
27
vendor/k8s.io/kubernetes/test/integration/garbagecollector/main_test.go
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
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 garbagecollector
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
framework.EtcdMain(m.Run)
|
||||
}
|
||||
70
vendor/k8s.io/kubernetes/test/integration/ipamperf/BUILD
generated
vendored
Normal file
70
vendor/k8s.io/kubernetes/test/integration/ipamperf/BUILD
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
size = "large",
|
||||
srcs = [
|
||||
"ipam_test.go",
|
||||
"main_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
tags = ["integration"],
|
||||
deps = [
|
||||
"//pkg/controller/nodeipam:go_default_library",
|
||||
"//pkg/controller/nodeipam/ipam:go_default_library",
|
||||
"//test/integration/framework:go_default_library",
|
||||
"//test/integration/util:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/client-go/informers:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"cloud.go",
|
||||
"results.go",
|
||||
"util.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/test/integration/ipamperf",
|
||||
deps = [
|
||||
"//pkg/cloudprovider:go_default_library",
|
||||
"//pkg/cloudprovider/providers/gce/cloud:go_default_library",
|
||||
"//pkg/cloudprovider/providers/gce/cloud/meta:go_default_library",
|
||||
"//pkg/controller/nodeipam/ipam:go_default_library",
|
||||
"//pkg/controller/nodeipam/ipam/cidrset:go_default_library",
|
||||
"//pkg/controller/util/node:go_default_library",
|
||||
"//test/integration/util:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/google.golang.org/api/compute/v0.beta:go_default_library",
|
||||
"//vendor/google.golang.org/api/compute/v1:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/client-go/informers:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/cache:go_default_library",
|
||||
],
|
||||
)
|
||||
85
vendor/k8s.io/kubernetes/test/integration/ipamperf/README.md
generated
vendored
Normal file
85
vendor/k8s.io/kubernetes/test/integration/ipamperf/README.md
generated
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
IPAM Performance Test
|
||||
=====
|
||||
|
||||
Motivation
|
||||
-----
|
||||
We wanted to be able to test the behavior of the IPAM controller's under various scenarios,
|
||||
by mocking and monitoring the edges that the controller interacts with. This has the following goals:
|
||||
|
||||
- Save time on testing
|
||||
- To simulate various behaviors cheaply
|
||||
- To observe and model the ideal behavior of the IPAM controller code
|
||||
|
||||
Currently the test runs through the 4 different IPAM controller modes for cases where the kube API QPS is a)
|
||||
equal to and b) significantly less than the number of nodes being added to observe and quantify behavior.
|
||||
|
||||
How to run
|
||||
-------
|
||||
|
||||
```shell
|
||||
# In kubernetes root path
|
||||
make generated_files
|
||||
|
||||
cd test/integration/ipamperf
|
||||
./test-performance.sh
|
||||
```
|
||||
|
||||
The runner scripts support a few different options:
|
||||
|
||||
```shell
|
||||
./test-performance.sh -h
|
||||
usage: ./test-performance.sh [-h] [-d] [-r <pattern>] [-o <filename>]
|
||||
usage: ./test-performance.sh <options>
|
||||
-h display this help message
|
||||
-d enable debug logs in tests
|
||||
-r <pattern> regex pattern to match for tests
|
||||
-o <filename> file to write JSON formatted results to
|
||||
-p <id> enable cpu and memory profiles, output written to mem-<id>.out and cpu-<id>.out
|
||||
-c enable custom test configuration
|
||||
-a <name> allocator name, one of RangeAllocator, CloudAllocator, IPAMFromCluster, IPAMFromCloud
|
||||
-k <num> api server qps for allocator
|
||||
-n <num> number of nodes to simulate
|
||||
-m <num> api server qps for node creation
|
||||
-l <num> gce cloud endpoint qps
|
||||
```
|
||||
|
||||
The tests follow the pattern TestPerformance/{AllocatorType}-KubeQPS{X}-Nodes{Y}, where AllocatorType
|
||||
is one of
|
||||
|
||||
- RangeAllocator
|
||||
- IPAMFromCluster
|
||||
- CloudAllocator
|
||||
- IPAMFromCloud
|
||||
|
||||
and X represents the QPS configured for the kubernetes API client, and Y is the number of nodes to create.
|
||||
|
||||
The -d flags set the -v level for glog to 6, enabling nearly all of the debug logs in the code.
|
||||
|
||||
So to run the test for CloudAllocator with 10 nodes, one can run
|
||||
|
||||
```shell
|
||||
./test-performance.sh -r /CloudAllocator.*Nodes10$
|
||||
```
|
||||
|
||||
At the end of the test, a JSON format of the results for all the tests run is printed. Passing the -o option
|
||||
allows for also saving this JSON to a named file.
|
||||
|
||||
### Profiling the code
|
||||
It's possible to get the CPU and memory profiles of code during test execution by using the ```-p``` option.
|
||||
The CPU and memory profiles are generated in the same directory with the file names set to ```cpu-<id>.out```
|
||||
and ```cpu-<id>.out```, where ```<id>``` is the argument value. Typicall pattern is to put in the number
|
||||
of nodes being simulated as the id, or 'all' in case running the full suite.
|
||||
|
||||
### Custom Test Configuration
|
||||
It's also possible to run a custom test configuration by passing the -c option. With this option, it then
|
||||
possible to specify the number of nodes to simulate and the API server qps values for creation,
|
||||
IPAM allocation and cloud endpoint, along with the allocator name to run. The defaults values for the
|
||||
qps parmeters are 30 for IPAM allocation, 100 for node creation and 30 for the cloud endpoint, and the
|
||||
default allocator is the RangeAllocator.
|
||||
|
||||
Code Organization
|
||||
-----
|
||||
The core of the tests are defined in [ipam_test.go](ipam_test.go), using the t.Run() helper to control parallelism
|
||||
as we want to able to start the master once. [cloud.go](cloud.go) contains the mock of the cloud server endpoint
|
||||
and can be configured to behave differently as needed by the various modes. The tracking of the node behavior and
|
||||
creation of the test results data is in [results.go](results.go).
|
||||
154
vendor/k8s.io/kubernetes/test/integration/ipamperf/cloud.go
generated
vendored
Normal file
154
vendor/k8s.io/kubernetes/test/integration/ipamperf/cloud.go
generated
vendored
Normal file
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
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 ipamperf
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
beta "google.golang.org/api/compute/v0.beta"
|
||||
ga "google.golang.org/api/compute/v1"
|
||||
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud"
|
||||
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/meta"
|
||||
"k8s.io/kubernetes/pkg/controller/nodeipam/ipam/cidrset"
|
||||
"k8s.io/kubernetes/test/integration/util"
|
||||
)
|
||||
|
||||
// implemntation note:
|
||||
// ------------------
|
||||
// cloud.go implements hooks and handler functions for the MockGCE cloud in order to meet expectations
|
||||
// of cloud behavior from the IPAM controllers. The key constraint is that the IPAM code is spread
|
||||
// across both GA and Beta instances, which are distinct objects in the mock. We need to solve for
|
||||
//
|
||||
// 1. When a GET is called on an instance, we lazy create the instance with or without an assigned
|
||||
// ip alias as needed by the IPAM controller type
|
||||
// 2. When we assign an IP alias for an instance, both the GA and Beta instance have to agree on the
|
||||
// assigned alias range
|
||||
//
|
||||
// We solve both the problems by using a baseInstanceList which maintains a list of known instances,
|
||||
// and their pre-assigned ip-alias ranges (if needed). We then create GetHook for GA and Beta GetInstance
|
||||
// calls as closures over this betaInstanceList that can lookup base instance data.
|
||||
//
|
||||
// This has the advantage that once the Get hook pouplates the GCEMock with the base data, we then let the
|
||||
// rest of the mock code run as is.
|
||||
|
||||
// baseInstance tracks basic instance data needed by the IPAM controllers
|
||||
type baseInstance struct {
|
||||
name string
|
||||
zone string
|
||||
aliasRange string
|
||||
}
|
||||
|
||||
// baseInstanceList tracks a set of base instances
|
||||
type baseInstanceList struct {
|
||||
allocateCIDR bool
|
||||
clusterCIDR *net.IPNet
|
||||
subnetMaskSize int
|
||||
cidrSet *cidrset.CidrSet
|
||||
|
||||
lock sync.Mutex // protect access to instances
|
||||
instances map[meta.Key]*baseInstance
|
||||
}
|
||||
|
||||
// toGA is an utility method to return the baseInstance data as a GA Instance object
|
||||
func (bi *baseInstance) toGA() *ga.Instance {
|
||||
inst := &ga.Instance{Name: bi.name, Zone: bi.zone, NetworkInterfaces: []*ga.NetworkInterface{{}}}
|
||||
if bi.aliasRange != "" {
|
||||
inst.NetworkInterfaces[0].AliasIpRanges = []*ga.AliasIpRange{
|
||||
{IpCidrRange: bi.aliasRange, SubnetworkRangeName: util.TestSecondaryRangeName},
|
||||
}
|
||||
}
|
||||
return inst
|
||||
}
|
||||
|
||||
// toGA is an utility method to return the baseInstance data as a beta Instance object
|
||||
func (bi *baseInstance) toBeta() *beta.Instance {
|
||||
inst := &beta.Instance{Name: bi.name, Zone: bi.zone, NetworkInterfaces: []*beta.NetworkInterface{{}}}
|
||||
if bi.aliasRange != "" {
|
||||
inst.NetworkInterfaces[0].AliasIpRanges = []*beta.AliasIpRange{
|
||||
{IpCidrRange: bi.aliasRange, SubnetworkRangeName: util.TestSecondaryRangeName},
|
||||
}
|
||||
}
|
||||
return inst
|
||||
}
|
||||
|
||||
// newBaseInstanceList is the baseInstanceList constructor
|
||||
func newBaseInstanceList(allocateCIDR bool, clusterCIDR *net.IPNet, subnetMaskSize int) *baseInstanceList {
|
||||
cidrSet, _ := cidrset.NewCIDRSet(clusterCIDR, subnetMaskSize)
|
||||
return &baseInstanceList{
|
||||
allocateCIDR: allocateCIDR,
|
||||
clusterCIDR: clusterCIDR,
|
||||
subnetMaskSize: subnetMaskSize,
|
||||
cidrSet: cidrSet,
|
||||
instances: make(map[meta.Key]*baseInstance),
|
||||
}
|
||||
}
|
||||
|
||||
// getOrCreateBaseInstance lazily creates a new base instance, assigning if allocateCIDR is true
|
||||
func (bil *baseInstanceList) getOrCreateBaseInstance(key *meta.Key) *baseInstance {
|
||||
bil.lock.Lock()
|
||||
defer bil.lock.Unlock()
|
||||
|
||||
inst, found := bil.instances[*key]
|
||||
if !found {
|
||||
inst = &baseInstance{name: key.Name, zone: key.Zone}
|
||||
if bil.allocateCIDR {
|
||||
nextRange, _ := bil.cidrSet.AllocateNext()
|
||||
inst.aliasRange = nextRange.String()
|
||||
}
|
||||
bil.instances[*key] = inst
|
||||
}
|
||||
return inst
|
||||
}
|
||||
|
||||
// newGAGetHook creates a new closure with the current baseInstanceList to be used as a MockInstances.GetHook
|
||||
func (bil *baseInstanceList) newGAGetHook() func(ctx context.Context, key *meta.Key, m *cloud.MockInstances) (bool, *ga.Instance, error) {
|
||||
return func(ctx context.Context, key *meta.Key, m *cloud.MockInstances) (bool, *ga.Instance, error) {
|
||||
m.Lock.Lock()
|
||||
defer m.Lock.Unlock()
|
||||
|
||||
if _, found := m.Objects[*key]; !found {
|
||||
m.Objects[*key] = &cloud.MockInstancesObj{Obj: bil.getOrCreateBaseInstance(key).toGA()}
|
||||
}
|
||||
return false, nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
// newBetaGetHook creates a new closure with the current baseInstanceList to be used as a MockBetaInstances.GetHook
|
||||
func (bil *baseInstanceList) newBetaGetHook() func(ctx context.Context, key *meta.Key, m *cloud.MockBetaInstances) (bool, *beta.Instance, error) {
|
||||
return func(ctx context.Context, key *meta.Key, m *cloud.MockBetaInstances) (bool, *beta.Instance, error) {
|
||||
m.Lock.Lock()
|
||||
defer m.Lock.Unlock()
|
||||
|
||||
if _, found := m.Objects[*key]; !found {
|
||||
m.Objects[*key] = &cloud.MockInstancesObj{Obj: bil.getOrCreateBaseInstance(key).toBeta()}
|
||||
}
|
||||
return false, nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
// newMockCloud returns a mock GCE instance with the appropriate handlers hooks
|
||||
func (bil *baseInstanceList) newMockCloud() cloud.Cloud {
|
||||
c := cloud.NewMockGCE(nil)
|
||||
|
||||
// insert hooks to lazy create a instance when needed
|
||||
c.MockInstances.GetHook = bil.newGAGetHook()
|
||||
c.MockBetaInstances.GetHook = bil.newBetaGetHook()
|
||||
|
||||
return c
|
||||
}
|
||||
158
vendor/k8s.io/kubernetes/test/integration/ipamperf/ipam_test.go
generated
vendored
Normal file
158
vendor/k8s.io/kubernetes/test/integration/ipamperf/ipam_test.go
generated
vendored
Normal file
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
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 ipamperf
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/informers"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/kubernetes/pkg/controller/nodeipam"
|
||||
"k8s.io/kubernetes/pkg/controller/nodeipam/ipam"
|
||||
"k8s.io/kubernetes/test/integration/util"
|
||||
)
|
||||
|
||||
func setupAllocator(apiURL string, config *Config, clusterCIDR, serviceCIDR *net.IPNet, subnetMaskSize int) (*clientset.Clientset, util.ShutdownFunc, error) {
|
||||
controllerStopChan := make(chan struct{})
|
||||
shutdownFunc := func() {
|
||||
close(controllerStopChan)
|
||||
}
|
||||
|
||||
clientSet := clientset.NewForConfigOrDie(&restclient.Config{
|
||||
Host: apiURL,
|
||||
ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}},
|
||||
QPS: float32(config.KubeQPS),
|
||||
Burst: config.KubeQPS,
|
||||
})
|
||||
|
||||
sharedInformer := informers.NewSharedInformerFactory(clientSet, 1*time.Hour)
|
||||
ipamController, err := nodeipam.NewNodeIpamController(
|
||||
sharedInformer.Core().V1().Nodes(), config.Cloud, clientSet,
|
||||
clusterCIDR, serviceCIDR, subnetMaskSize, config.AllocatorType,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, shutdownFunc, err
|
||||
}
|
||||
go ipamController.Run(controllerStopChan)
|
||||
sharedInformer.Start(controllerStopChan)
|
||||
|
||||
return clientSet, shutdownFunc, nil
|
||||
}
|
||||
|
||||
func runTest(t *testing.T, apiURL string, config *Config, clusterCIDR, serviceCIDR *net.IPNet, subnetMaskSize int) (*Results, error) {
|
||||
t.Helper()
|
||||
glog.Infof("Running test %s", t.Name())
|
||||
|
||||
defer deleteNodes(apiURL, config) // cleanup nodes on after controller shutdown
|
||||
|
||||
clientSet, shutdownFunc, err := setupAllocator(apiURL, config, clusterCIDR, serviceCIDR, subnetMaskSize)
|
||||
if err != nil {
|
||||
t.Fatalf("Error starting IPAM allocator: %v", err)
|
||||
}
|
||||
defer shutdownFunc()
|
||||
|
||||
o := NewObserver(clientSet, config.NumNodes)
|
||||
if err := o.StartObserving(); err != nil {
|
||||
t.Fatalf("Could not start test observer: %v", err)
|
||||
}
|
||||
|
||||
if err := createNodes(apiURL, config); err != nil {
|
||||
t.Fatalf("Could not create nodes: %v", err)
|
||||
}
|
||||
|
||||
results := o.Results(t.Name(), config)
|
||||
glog.Infof("Results: %s", results)
|
||||
if !results.Succeeded {
|
||||
t.Errorf("%s: Not allocations succeeded", t.Name())
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func logResults(allResults []*Results) {
|
||||
jStr, err := json.MarshalIndent(allResults, "", " ")
|
||||
if err != nil {
|
||||
glog.Errorf("Error formating results: %v", err)
|
||||
return
|
||||
}
|
||||
if resultsLogFile != "" {
|
||||
glog.Infof("Logging results to %s", resultsLogFile)
|
||||
if err := ioutil.WriteFile(resultsLogFile, jStr, os.FileMode(0644)); err != nil {
|
||||
glog.Errorf("Error logging results to %s: %v", resultsLogFile, err)
|
||||
}
|
||||
}
|
||||
glog.Infof("AllResults:\n%s", string(jStr))
|
||||
}
|
||||
|
||||
func TestPerformance(t *testing.T) {
|
||||
if testing.Short() {
|
||||
// TODO (#61854) find why flakiness is caused by etcd connectivity before enabling always
|
||||
t.Skip("Skipping because we want to run short tests")
|
||||
}
|
||||
|
||||
apiURL, masterShutdown := util.StartApiserver()
|
||||
defer masterShutdown()
|
||||
|
||||
_, clusterCIDR, _ := net.ParseCIDR("10.96.0.0/11") // allows up to 8K nodes
|
||||
_, serviceCIDR, _ := net.ParseCIDR("10.94.0.0/24") // does not matter for test - pick upto 250 services
|
||||
subnetMaskSize := 24
|
||||
|
||||
var (
|
||||
allResults []*Results
|
||||
tests []*Config
|
||||
)
|
||||
|
||||
if isCustom {
|
||||
tests = append(tests, customConfig)
|
||||
} else {
|
||||
for _, numNodes := range []int{10, 100} {
|
||||
for _, alloc := range []ipam.CIDRAllocatorType{ipam.RangeAllocatorType, ipam.CloudAllocatorType, ipam.IPAMFromClusterAllocatorType, ipam.IPAMFromCloudAllocatorType} {
|
||||
tests = append(tests, &Config{AllocatorType: alloc, NumNodes: numNodes, CreateQPS: numNodes, KubeQPS: 10, CloudQPS: 10})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
testName := fmt.Sprintf("%s-KubeQPS%d-Nodes%d", test.AllocatorType, test.KubeQPS, test.NumNodes)
|
||||
t.Run(testName, func(t *testing.T) {
|
||||
allocateCIDR := false
|
||||
if test.AllocatorType == ipam.IPAMFromCloudAllocatorType || test.AllocatorType == ipam.CloudAllocatorType {
|
||||
allocateCIDR = true
|
||||
}
|
||||
bil := newBaseInstanceList(allocateCIDR, clusterCIDR, subnetMaskSize)
|
||||
cloud, err := util.NewMockGCECloud(bil.newMockCloud())
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to create mock cloud: %v", err)
|
||||
}
|
||||
test.Cloud = cloud
|
||||
if results, err := runTest(t, apiURL, test, clusterCIDR, serviceCIDR, subnetMaskSize); err == nil {
|
||||
allResults = append(allResults, results)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
logResults(allResults)
|
||||
}
|
||||
66
vendor/k8s.io/kubernetes/test/integration/ipamperf/main_test.go
generated
vendored
Normal file
66
vendor/k8s.io/kubernetes/test/integration/ipamperf/main_test.go
generated
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package ipamperf
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"testing"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/kubernetes/pkg/controller/nodeipam/ipam"
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
)
|
||||
|
||||
var (
|
||||
resultsLogFile string
|
||||
isCustom bool
|
||||
customConfig = &Config{
|
||||
NumNodes: 10,
|
||||
KubeQPS: 30,
|
||||
CloudQPS: 30,
|
||||
CreateQPS: 100,
|
||||
AllocatorType: ipam.RangeAllocatorType,
|
||||
}
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
allocator := string(ipam.RangeAllocatorType)
|
||||
|
||||
flag.StringVar(&resultsLogFile, "log", "", "log file to write JSON results to")
|
||||
flag.BoolVar(&isCustom, "custom", false, "enable custom test configuration")
|
||||
flag.StringVar(&allocator, "allocator", allocator, "allocator to use")
|
||||
flag.IntVar(&customConfig.KubeQPS, "kube-qps", customConfig.KubeQPS, "API server qps for allocations")
|
||||
flag.IntVar(&customConfig.NumNodes, "num-nodes", 10, "number of nodes to simulate")
|
||||
flag.IntVar(&customConfig.CreateQPS, "create-qps", customConfig.CreateQPS, "API server qps for node creation")
|
||||
flag.IntVar(&customConfig.CloudQPS, "cloud-qps", customConfig.CloudQPS, "GCE Cloud qps limit")
|
||||
flag.Parse()
|
||||
|
||||
switch allocator {
|
||||
case string(ipam.RangeAllocatorType):
|
||||
customConfig.AllocatorType = ipam.RangeAllocatorType
|
||||
case string(ipam.CloudAllocatorType):
|
||||
customConfig.AllocatorType = ipam.CloudAllocatorType
|
||||
case string(ipam.IPAMFromCloudAllocatorType):
|
||||
customConfig.AllocatorType = ipam.IPAMFromCloudAllocatorType
|
||||
case string(ipam.IPAMFromClusterAllocatorType):
|
||||
customConfig.AllocatorType = ipam.IPAMFromClusterAllocatorType
|
||||
default:
|
||||
glog.Fatalf("Unknown allocator type: %s", allocator)
|
||||
}
|
||||
|
||||
framework.EtcdMain(m.Run)
|
||||
}
|
||||
221
vendor/k8s.io/kubernetes/test/integration/ipamperf/results.go
generated
vendored
Normal file
221
vendor/k8s.io/kubernetes/test/integration/ipamperf/results.go
generated
vendored
Normal file
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
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 ipamperf
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/client-go/informers"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"k8s.io/kubernetes/pkg/cloudprovider"
|
||||
"k8s.io/kubernetes/pkg/controller/nodeipam/ipam"
|
||||
nodeutil "k8s.io/kubernetes/pkg/controller/util/node"
|
||||
)
|
||||
|
||||
// Config represents the test configuration that is being run
|
||||
type Config struct {
|
||||
CreateQPS int // rate at which nodes are created
|
||||
KubeQPS int // rate for communication with kubernetes API
|
||||
CloudQPS int // rate for communication with cloud endpoint
|
||||
NumNodes int // number of nodes to created and monitored
|
||||
AllocatorType ipam.CIDRAllocatorType // type of allocator to run
|
||||
Cloud cloudprovider.Interface // cloud provider
|
||||
}
|
||||
|
||||
type nodeTime struct {
|
||||
added time.Time // observed time for when node was added
|
||||
allocated time.Time // observed time for when node was assigned podCIDR
|
||||
podCIDR string // the allocated podCIDR range
|
||||
}
|
||||
|
||||
// Observer represents the handle to test observer that watches for node changes
|
||||
// and tracks behavior
|
||||
type Observer struct {
|
||||
numAdded int // number of nodes observed added
|
||||
numAllocated int // number of nodes observed allocated podCIDR
|
||||
timing map[string]*nodeTime // per node timing
|
||||
numNodes int // the number of nodes to expect
|
||||
stopChan chan struct{} // for the shared informer
|
||||
wg sync.WaitGroup
|
||||
clientSet *clientset.Clientset
|
||||
}
|
||||
|
||||
// JSONDuration is an alias of time.Duration to support custom Marshal code
|
||||
type JSONDuration time.Duration
|
||||
|
||||
// NodeDuration represents the CIDR allocation time for each node
|
||||
type NodeDuration struct {
|
||||
Name string // node name
|
||||
PodCIDR string // the podCIDR that was assigned to the node
|
||||
Duration JSONDuration // how long it took to assign podCIDR
|
||||
}
|
||||
|
||||
// Results represents the observed test results.
|
||||
type Results struct {
|
||||
Name string // name for the test
|
||||
Config *Config // handle to the test config
|
||||
Succeeded bool // whether all nodes were assigned podCIDR
|
||||
MaxAllocTime JSONDuration // the maximum time take for assignment per node
|
||||
TotalAllocTime JSONDuration // duration between first addition and last assignment
|
||||
NodeAllocTime []NodeDuration // assignment time by node name
|
||||
}
|
||||
|
||||
// NewObserver creates a new observer given a handle to the Clientset
|
||||
func NewObserver(clientSet *clientset.Clientset, numNodes int) *Observer {
|
||||
o := &Observer{
|
||||
timing: map[string]*nodeTime{},
|
||||
numNodes: numNodes,
|
||||
clientSet: clientSet,
|
||||
stopChan: make(chan struct{}),
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
||||
// StartObserving starts an asynchronous loop to monitor for node changes.
|
||||
// Call Results() to get the test results after starting observer.
|
||||
func (o *Observer) StartObserving() error {
|
||||
o.monitor()
|
||||
glog.Infof("Test observer started")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Results returns the test results. It waits for the observer to finish
|
||||
// and returns the computed results of the observations.
|
||||
func (o *Observer) Results(name string, config *Config) *Results {
|
||||
var (
|
||||
firstAdd time.Time // earliest time any node was added (first node add)
|
||||
lastAssignment time.Time // latest time any node was assignged CIDR (last node assignment)
|
||||
)
|
||||
o.wg.Wait()
|
||||
close(o.stopChan) // shutdown the shared informer
|
||||
|
||||
results := &Results{
|
||||
Name: name,
|
||||
Config: config,
|
||||
Succeeded: o.numAdded == o.numNodes && o.numAllocated == o.numNodes,
|
||||
MaxAllocTime: 0,
|
||||
NodeAllocTime: []NodeDuration{},
|
||||
}
|
||||
for name, nTime := range o.timing {
|
||||
addFound := !nTime.added.IsZero()
|
||||
if addFound && (firstAdd.IsZero() || nTime.added.Before(firstAdd)) {
|
||||
firstAdd = nTime.added
|
||||
}
|
||||
cidrFound := !nTime.allocated.IsZero()
|
||||
if cidrFound && nTime.allocated.After(lastAssignment) {
|
||||
lastAssignment = nTime.allocated
|
||||
}
|
||||
if addFound && cidrFound {
|
||||
allocTime := nTime.allocated.Sub(nTime.added)
|
||||
if allocTime > time.Duration(results.MaxAllocTime) {
|
||||
results.MaxAllocTime = JSONDuration(allocTime)
|
||||
}
|
||||
results.NodeAllocTime = append(results.NodeAllocTime, NodeDuration{
|
||||
Name: name, PodCIDR: nTime.podCIDR, Duration: JSONDuration(allocTime),
|
||||
})
|
||||
}
|
||||
}
|
||||
results.TotalAllocTime = JSONDuration(lastAssignment.Sub(firstAdd))
|
||||
sort.Slice(results.NodeAllocTime, func(i, j int) bool {
|
||||
return results.NodeAllocTime[i].Duration > results.NodeAllocTime[j].Duration
|
||||
})
|
||||
return results
|
||||
}
|
||||
|
||||
func (o *Observer) monitor() {
|
||||
o.wg.Add(1)
|
||||
|
||||
sharedInformer := informers.NewSharedInformerFactory(o.clientSet, 1*time.Second)
|
||||
nodeInformer := sharedInformer.Core().V1().Nodes().Informer()
|
||||
|
||||
nodeInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: nodeutil.CreateAddNodeHandler(func(node *v1.Node) (err error) {
|
||||
name := node.GetName()
|
||||
if node.Spec.PodCIDR != "" {
|
||||
// ignore nodes that have PodCIDR (might be hold over from previous runs that did not get cleaned up)
|
||||
return
|
||||
}
|
||||
nTime := &nodeTime{}
|
||||
o.timing[name] = nTime
|
||||
nTime.added = time.Now()
|
||||
o.numAdded = o.numAdded + 1
|
||||
return
|
||||
}),
|
||||
UpdateFunc: nodeutil.CreateUpdateNodeHandler(func(oldNode, newNode *v1.Node) (err error) {
|
||||
name := newNode.GetName()
|
||||
nTime, found := o.timing[name]
|
||||
if !found {
|
||||
return // consistency check - ignore nodes we have not seen the add event for
|
||||
}
|
||||
// check if CIDR assigned and ignore redundant updates
|
||||
if newNode.Spec.PodCIDR != "" && nTime.podCIDR == "" {
|
||||
nTime.allocated = time.Now()
|
||||
nTime.podCIDR = newNode.Spec.PodCIDR
|
||||
o.numAllocated++
|
||||
if o.numAllocated%10 == 0 {
|
||||
glog.Infof("progress: %d/%d - %.2d%%", o.numAllocated, o.numNodes, (o.numAllocated * 100.0 / o.numNodes))
|
||||
}
|
||||
// do following check only if numAllocated is modified, as otherwise, redundant updates
|
||||
// can cause wg.Done() to be called multiple times, causing a panic
|
||||
if o.numAdded == o.numNodes && o.numAllocated == o.numNodes {
|
||||
glog.Info("All nodes assigned podCIDR")
|
||||
o.wg.Done()
|
||||
}
|
||||
}
|
||||
return
|
||||
}),
|
||||
})
|
||||
sharedInformer.Start(o.stopChan)
|
||||
}
|
||||
|
||||
// String implements the Stringer interface and returns a multi-line representation
|
||||
// of the test results.
|
||||
func (results *Results) String() string {
|
||||
var b bytes.Buffer
|
||||
fmt.Fprintf(&b, "\n TestName: %s", results.Name)
|
||||
fmt.Fprintf(&b, "\n NumNodes: %d, CreateQPS: %d, KubeQPS: %d, CloudQPS: %d, Allocator: %v",
|
||||
results.Config.NumNodes, results.Config.CreateQPS, results.Config.KubeQPS,
|
||||
results.Config.CloudQPS, results.Config.AllocatorType)
|
||||
fmt.Fprintf(&b, "\n Succeeded: %v, TotalAllocTime: %v, MaxAllocTime: %v",
|
||||
results.Succeeded, time.Duration(results.TotalAllocTime), time.Duration(results.MaxAllocTime))
|
||||
fmt.Fprintf(&b, "\n %5s %-20s %-20s %s", "Num", "Node", "PodCIDR", "Duration (s)")
|
||||
for i, d := range results.NodeAllocTime {
|
||||
fmt.Fprintf(&b, "\n %5d %-20s %-20s %10.3f", i+1, d.Name, d.PodCIDR, time.Duration(d.Duration).Seconds())
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json.Marshaler interface
|
||||
func (jDuration *JSONDuration) MarshalJSON() ([]byte, error) {
|
||||
return []byte(fmt.Sprintf("\"%s\"", time.Duration(*jDuration).String())), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshaler interface
|
||||
func (jDuration *JSONDuration) UnmarshalJSON(b []byte) (err error) {
|
||||
var d time.Duration
|
||||
if d, err = time.ParseDuration(string(b[1 : len(b)-1])); err == nil {
|
||||
*jDuration = JSONDuration(d)
|
||||
}
|
||||
return
|
||||
}
|
||||
91
vendor/k8s.io/kubernetes/test/integration/ipamperf/test-performance.sh
generated
vendored
Executable file
91
vendor/k8s.io/kubernetes/test/integration/ipamperf/test-performance.sh
generated
vendored
Executable file
@@ -0,0 +1,91 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# 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.
|
||||
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
set -o pipefail
|
||||
|
||||
TEST_ARGS=""
|
||||
RUN_PATTERN=".*"
|
||||
PROFILE_OPTS=""
|
||||
|
||||
function usage() {
|
||||
echo "usage: $0 <options>"
|
||||
echo " -h display this help message"
|
||||
echo " -d enable debug logs in tests"
|
||||
echo " -r <pattern> regex pattern to match for tests"
|
||||
echo " -o <filename> file to write JSON formatted results to"
|
||||
echo " -p <id> enable cpu and memory profiles, output written to mem-<id>.out and cpu-<id>.out"
|
||||
echo " -c enable custom test configuration"
|
||||
echo " -a <name> allocator name, one of RangeAllocator, CloudAllocator, IPAMFromCluster, IPAMFromCloud"
|
||||
echo " -k <num> api server qps for allocator"
|
||||
echo " -n <num> number of nodes to simulate"
|
||||
echo " -m <num> api server qps for node creation"
|
||||
echo " -l <num> gce cloud endpoint qps"
|
||||
exit 1
|
||||
}
|
||||
|
||||
while getopts ":hdr:o:p:ca:k:n:m:l:" opt; do
|
||||
case ${opt} in
|
||||
d) TEST_ARGS="${TEST_ARGS} -v=6"
|
||||
;;
|
||||
r) RUN_PATTERN="${OPTARG}"
|
||||
;;
|
||||
o) TEST_ARGS="${TEST_ARGS} -log ${OPTARG}"
|
||||
;;
|
||||
p) PROFILE_OPTS="-memprofile mem-${OPTARG}.out -cpuprofile cpu-${OPTARG}.out"
|
||||
;;
|
||||
c) TEST_ARGS="${TEST_ARGS} -custom"
|
||||
;;
|
||||
a) TEST_ARGS="${TEST_ARGS} -allocator ${OPTARG}"
|
||||
;;
|
||||
k) TEST_ARGS="${TEST_ARGS} -kube-qps ${OPTARG}"
|
||||
;;
|
||||
n) TEST_ARGS="${TEST_ARGS} -num-nodes ${OPTARG}"
|
||||
;;
|
||||
m) TEST_ARGS="${TEST_ARGS} -create-qps ${OPTARG}"
|
||||
;;
|
||||
l) TEST_ARGS="${TEST_ARGS} -cloud-qps ${OPTARG}"
|
||||
;;
|
||||
h) usage
|
||||
;;
|
||||
\?) usage
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
KUBE_ROOT=$(dirname "${BASH_SOURCE}")/../../../
|
||||
source "${KUBE_ROOT}/hack/lib/init.sh"
|
||||
|
||||
kube::golang::setup_env
|
||||
|
||||
DIR_BASENAME=$(dirname "${BASH_SOURCE}")
|
||||
pushd ${DIR_BASENAME}
|
||||
|
||||
cleanup() {
|
||||
popd 2> /dev/null
|
||||
kube::etcd::cleanup
|
||||
kube::log::status "performance test cleanup complete"
|
||||
}
|
||||
|
||||
trap cleanup EXIT
|
||||
|
||||
kube::etcd::start
|
||||
|
||||
# Running IPAM tests. It might take a long time.
|
||||
kube::log::status "performance test (IPAM) start"
|
||||
go test ${PROFILE_OPTS} -test.run=${RUN_PATTERN} -test.timeout=60m -test.short=false -v -args ${TEST_ARGS}
|
||||
kube::log::status "... IPAM tests finished."
|
||||
95
vendor/k8s.io/kubernetes/test/integration/ipamperf/util.go
generated
vendored
Normal file
95
vendor/k8s.io/kubernetes/test/integration/ipamperf/util.go
generated
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
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 ipamperf
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
)
|
||||
|
||||
const (
|
||||
maxCreateRetries = 10
|
||||
retryDelay = 10 * time.Second
|
||||
)
|
||||
|
||||
var (
|
||||
baseNodeTemplate = &v1.Node{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
GenerateName: "sample-node-",
|
||||
},
|
||||
Status: v1.NodeStatus{
|
||||
Capacity: v1.ResourceList{
|
||||
v1.ResourcePods: *resource.NewQuantity(110, resource.DecimalSI),
|
||||
v1.ResourceCPU: resource.MustParse("4"),
|
||||
v1.ResourceMemory: resource.MustParse("32Gi"),
|
||||
},
|
||||
Phase: v1.NodeRunning,
|
||||
Conditions: []v1.NodeCondition{
|
||||
{Type: v1.NodeReady, Status: v1.ConditionTrue},
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func deleteNodes(apiURL string, config *Config) {
|
||||
glog.Info("Deleting nodes")
|
||||
clientSet := clientset.NewForConfigOrDie(&restclient.Config{
|
||||
Host: apiURL,
|
||||
ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}},
|
||||
QPS: float32(config.CreateQPS),
|
||||
Burst: config.CreateQPS,
|
||||
})
|
||||
noGrace := int64(0)
|
||||
if err := clientSet.CoreV1().Nodes().DeleteCollection(&metav1.DeleteOptions{GracePeriodSeconds: &noGrace}, metav1.ListOptions{}); err != nil {
|
||||
glog.Errorf("Error deleting node: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func createNodes(apiURL string, config *Config) error {
|
||||
clientSet := clientset.NewForConfigOrDie(&restclient.Config{
|
||||
Host: apiURL,
|
||||
ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}},
|
||||
QPS: float32(config.CreateQPS),
|
||||
Burst: config.CreateQPS,
|
||||
})
|
||||
glog.Infof("Creating %d nodes", config.NumNodes)
|
||||
for i := 0; i < config.NumNodes; i++ {
|
||||
var err error
|
||||
for j := 0; j < maxCreateRetries; j++ {
|
||||
if _, err = clientSet.CoreV1().Nodes().Create(baseNodeTemplate); err != nil && errors.IsServerTimeout(err) {
|
||||
glog.Infof("Server timeout creating nodes, retrying after %v", retryDelay)
|
||||
time.Sleep(retryDelay)
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
glog.Errorf("Error creating nodes: %v", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
glog.Infof("%d nodes created", config.NumNodes)
|
||||
return nil
|
||||
}
|
||||
255
vendor/k8s.io/kubernetes/test/integration/master/BUILD
generated
vendored
Normal file
255
vendor/k8s.io/kubernetes/test/integration/master/BUILD
generated
vendored
Normal file
@@ -0,0 +1,255 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
size = "large",
|
||||
srcs = [
|
||||
"crd_test.go",
|
||||
"kube_apiserver_test.go",
|
||||
"main_test.go",
|
||||
"secrets_transformation_test.go",
|
||||
"synthetic_master_test.go",
|
||||
] + select({
|
||||
"@io_bazel_rules_go//go/platform:android": [
|
||||
"kms_transformation_test.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:darwin": [
|
||||
"kms_transformation_test.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:dragonfly": [
|
||||
"kms_transformation_test.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:freebsd": [
|
||||
"kms_transformation_test.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:linux": [
|
||||
"kms_transformation_test.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:nacl": [
|
||||
"kms_transformation_test.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:netbsd": [
|
||||
"kms_transformation_test.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:openbsd": [
|
||||
"kms_transformation_test.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:plan9": [
|
||||
"kms_transformation_test.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:solaris": [
|
||||
"kms_transformation_test.go",
|
||||
],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
embed = [":go_default_library"],
|
||||
tags = ["integration"],
|
||||
deps = [
|
||||
"//cmd/kube-apiserver/app/testing:go_default_library",
|
||||
"//pkg/api/testapi:go_default_library",
|
||||
"//pkg/apis/core:go_default_library",
|
||||
"//pkg/client/clientset_generated/internalclientset:go_default_library",
|
||||
"//pkg/master:go_default_library",
|
||||
"//test/integration:go_default_library",
|
||||
"//test/integration/framework:go_default_library",
|
||||
"//vendor/github.com/ghodss/yaml:go_default_library",
|
||||
"//vendor/k8s.io/api/admissionregistration/v1alpha1:go_default_library",
|
||||
"//vendor/k8s.io/api/apps/v1:go_default_library",
|
||||
"//vendor/k8s.io/api/apps/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/api/networking/v1:go_default_library",
|
||||
"//vendor/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions:go_default_library",
|
||||
"//vendor/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authentication/group:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authentication/request/bearertoken:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authentication/user:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authorization/authorizerfactory:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/features:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/registry/generic/registry:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/server/options/encryptionconfig:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/storage/value:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/storage/value/encrypt/aes:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/util/feature/testing:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/plugin/pkg/authenticator/token/tokentest:go_default_library",
|
||||
"//vendor/k8s.io/client-go/dynamic:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes/typed/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||
"//vendor/k8s.io/kube-aggregator/pkg/apis/apiregistration:go_default_library",
|
||||
] + select({
|
||||
"@io_bazel_rules_go//go/platform:android": [
|
||||
"//vendor/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1:go_default_library",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:darwin": [
|
||||
"//vendor/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1:go_default_library",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:dragonfly": [
|
||||
"//vendor/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1:go_default_library",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:freebsd": [
|
||||
"//vendor/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1:go_default_library",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:linux": [
|
||||
"//vendor/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1:go_default_library",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:nacl": [
|
||||
"//vendor/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1:go_default_library",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:netbsd": [
|
||||
"//vendor/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1:go_default_library",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:openbsd": [
|
||||
"//vendor/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1:go_default_library",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:plan9": [
|
||||
"//vendor/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1:go_default_library",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:solaris": [
|
||||
"//vendor/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1:go_default_library",
|
||||
],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"transformation_testcase.go",
|
||||
] + select({
|
||||
"@io_bazel_rules_go//go/platform:android": [
|
||||
"kms_plugin_mock.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:darwin": [
|
||||
"kms_plugin_mock.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:dragonfly": [
|
||||
"kms_plugin_mock.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:freebsd": [
|
||||
"kms_plugin_mock.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:linux": [
|
||||
"kms_plugin_mock.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:nacl": [
|
||||
"kms_plugin_mock.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:netbsd": [
|
||||
"kms_plugin_mock.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:openbsd": [
|
||||
"kms_plugin_mock.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:plan9": [
|
||||
"kms_plugin_mock.go",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:solaris": [
|
||||
"kms_plugin_mock.go",
|
||||
],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
importpath = "k8s.io/kubernetes/test/integration/master",
|
||||
deps = [
|
||||
"//cmd/kube-apiserver/app/testing:go_default_library",
|
||||
"//test/integration:go_default_library",
|
||||
"//test/integration/framework:go_default_library",
|
||||
"//vendor/github.com/coreos/etcd/clientv3:go_default_library",
|
||||
"//vendor/github.com/ghodss/yaml:go_default_library",
|
||||
"//vendor/github.com/prometheus/client_golang/prometheus:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/server/options/encryptionconfig:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/storage/storagebackend:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/storage/value:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
] + select({
|
||||
"@io_bazel_rules_go//go/platform:android": [
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/golang.org/x/sys/unix:go_default_library",
|
||||
"//vendor/google.golang.org/grpc:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1:go_default_library",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:darwin": [
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/golang.org/x/sys/unix:go_default_library",
|
||||
"//vendor/google.golang.org/grpc:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1:go_default_library",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:dragonfly": [
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/golang.org/x/sys/unix:go_default_library",
|
||||
"//vendor/google.golang.org/grpc:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1:go_default_library",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:freebsd": [
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/golang.org/x/sys/unix:go_default_library",
|
||||
"//vendor/google.golang.org/grpc:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1:go_default_library",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:linux": [
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/golang.org/x/sys/unix:go_default_library",
|
||||
"//vendor/google.golang.org/grpc:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1:go_default_library",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:nacl": [
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/golang.org/x/sys/unix:go_default_library",
|
||||
"//vendor/google.golang.org/grpc:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1:go_default_library",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:netbsd": [
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/golang.org/x/sys/unix:go_default_library",
|
||||
"//vendor/google.golang.org/grpc:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1:go_default_library",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:openbsd": [
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/golang.org/x/sys/unix:go_default_library",
|
||||
"//vendor/google.golang.org/grpc:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1:go_default_library",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:plan9": [
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/golang.org/x/sys/unix:go_default_library",
|
||||
"//vendor/google.golang.org/grpc:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1:go_default_library",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:solaris": [
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/golang.org/x/sys/unix:go_default_library",
|
||||
"//vendor/google.golang.org/grpc:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1:go_default_library",
|
||||
],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
)
|
||||
343
vendor/k8s.io/kubernetes/test/integration/master/crd_test.go
generated
vendored
Normal file
343
vendor/k8s.io/kubernetes/test/integration/master/crd_test.go
generated
vendored
Normal file
@@ -0,0 +1,343 @@
|
||||
/*
|
||||
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 master
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
admissionregistrationv1alpha1 "k8s.io/api/admissionregistration/v1alpha1"
|
||||
networkingv1 "k8s.io/api/networking/v1"
|
||||
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||
apiextensionsclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/apiserver/pkg/features"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing"
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
)
|
||||
|
||||
func TestCRDShadowGroup(t *testing.T) {
|
||||
result := kubeapiservertesting.StartTestServerOrDie(t, nil, nil, framework.SharedEtcd())
|
||||
defer result.TearDownFn()
|
||||
|
||||
kubeclient, err := kubernetes.NewForConfig(result.ClientConfig)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
apiextensionsclient, err := apiextensionsclientset.NewForConfig(result.ClientConfig)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
t.Logf("Creating a NetworkPolicy")
|
||||
nwPolicy, err := kubeclient.NetworkingV1().NetworkPolicies("default").Create(&networkingv1.NetworkPolicy{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
|
||||
Spec: networkingv1.NetworkPolicySpec{
|
||||
PodSelector: metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
|
||||
Ingress: []networkingv1.NetworkPolicyIngressRule{},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create NetworkPolicy: %v", err)
|
||||
}
|
||||
|
||||
t.Logf("Trying to shadow networking group")
|
||||
crd := &apiextensionsv1beta1.CustomResourceDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foos." + networkingv1.GroupName,
|
||||
},
|
||||
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
|
||||
Group: networkingv1.GroupName,
|
||||
Version: networkingv1.SchemeGroupVersion.Version,
|
||||
Scope: apiextensionsv1beta1.ClusterScoped,
|
||||
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
|
||||
Plural: "foos",
|
||||
Kind: "Foo",
|
||||
},
|
||||
},
|
||||
}
|
||||
if _, err = apiextensionsclient.ApiextensionsV1beta1().CustomResourceDefinitions().Create(crd); err != nil {
|
||||
t.Fatalf("Failed to create networking group CRD: %v", err)
|
||||
}
|
||||
if err := waitForEstablishedCRD(apiextensionsclient, crd.Name); err != nil {
|
||||
t.Fatalf("Failed to establish networking group CRD: %v", err)
|
||||
}
|
||||
// wait to give aggregator time to update
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
t.Logf("Checking that we still see the NetworkPolicy")
|
||||
_, err = kubeclient.NetworkingV1().NetworkPolicies(nwPolicy.Namespace).Get(nwPolicy.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Errorf("Failed to get NetworkPolocy: %v", err)
|
||||
}
|
||||
|
||||
t.Logf("Checking that crd resource does not show up in networking group")
|
||||
found, err := crdExistsInDiscovery(apiextensionsclient, crd)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected discovery error: %v", err)
|
||||
}
|
||||
if found {
|
||||
t.Errorf("CRD resource shows up in discovery, but shouldn't.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCRD(t *testing.T) {
|
||||
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.Initializers, true)()
|
||||
|
||||
result := kubeapiservertesting.StartTestServerOrDie(t, nil, []string{"--admission-control", "Initializers"}, framework.SharedEtcd())
|
||||
defer result.TearDownFn()
|
||||
|
||||
kubeclient, err := kubernetes.NewForConfig(result.ClientConfig)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
apiextensionsclient, err := apiextensionsclientset.NewForConfig(result.ClientConfig)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
t.Logf("Trying to create a custom resource without conflict")
|
||||
crd := &apiextensionsv1beta1.CustomResourceDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foos.cr.bar.com",
|
||||
},
|
||||
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
|
||||
Group: "cr.bar.com",
|
||||
Version: "v1",
|
||||
Scope: apiextensionsv1beta1.NamespaceScoped,
|
||||
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
|
||||
Plural: "foos",
|
||||
Kind: "Foo",
|
||||
},
|
||||
},
|
||||
}
|
||||
if _, err = apiextensionsclient.ApiextensionsV1beta1().CustomResourceDefinitions().Create(crd); err != nil {
|
||||
t.Fatalf("Failed to create foos.cr.bar.com CRD; %v", err)
|
||||
}
|
||||
if err := waitForEstablishedCRD(apiextensionsclient, crd.Name); err != nil {
|
||||
t.Fatalf("Failed to establish foos.cr.bar.com CRD: %v", err)
|
||||
}
|
||||
if err := wait.PollImmediate(500*time.Millisecond, 30*time.Second, func() (bool, error) {
|
||||
return crdExistsInDiscovery(apiextensionsclient, crd)
|
||||
}); err != nil {
|
||||
t.Fatalf("Failed to see foos.cr.bar.com in discovery: %v", err)
|
||||
}
|
||||
|
||||
t.Logf("Trying to access foos.cr.bar.com with dynamic client")
|
||||
dynamicClient, err := dynamic.NewForConfig(result.ClientConfig)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
fooResource := schema.GroupVersionResource{Group: "cr.bar.com", Version: "v1", Resource: "foos"}
|
||||
_, err = dynamicClient.Resource(fooResource).Namespace("default").List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
t.Errorf("Failed to list foos.cr.bar.com instances: %v", err)
|
||||
}
|
||||
|
||||
t.Logf("Creating InitializerConfiguration")
|
||||
_, err = kubeclient.AdmissionregistrationV1alpha1().InitializerConfigurations().Create(&admissionregistrationv1alpha1.InitializerConfiguration{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foos.cr.bar.com",
|
||||
},
|
||||
Initializers: []admissionregistrationv1alpha1.Initializer{
|
||||
{
|
||||
Name: "cr.bar.com",
|
||||
Rules: []admissionregistrationv1alpha1.Rule{
|
||||
{
|
||||
APIGroups: []string{"cr.bar.com"},
|
||||
APIVersions: []string{"*"},
|
||||
Resources: []string{"*"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create InitializerConfiguration: %v", err)
|
||||
}
|
||||
|
||||
// TODO DO NOT MERGE THIS
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
t.Logf("Creating Foo instance")
|
||||
foo := &Foo{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "cr.bar.com/v1",
|
||||
Kind: "Foo",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "foo"},
|
||||
}
|
||||
unstructuredFoo, err := unstructuredFoo(foo)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to create Foo: %v", err)
|
||||
}
|
||||
createErr := make(chan error, 1)
|
||||
go func() {
|
||||
_, err := dynamicClient.Resource(fooResource).Namespace("default").Create(unstructuredFoo)
|
||||
t.Logf("Foo instance create returned: %v", err)
|
||||
if err != nil {
|
||||
createErr <- err
|
||||
}
|
||||
}()
|
||||
|
||||
err = wait.PollImmediate(100*time.Millisecond, wait.ForeverTestTimeout, func() (bool, error) {
|
||||
select {
|
||||
case createErr := <-createErr:
|
||||
return true, createErr
|
||||
default:
|
||||
}
|
||||
|
||||
t.Logf("Checking that Foo instance is visible with IncludeUninitialized=true")
|
||||
_, err := dynamicClient.Resource(fooResource).Namespace("default").Get(foo.ObjectMeta.Name, metav1.GetOptions{
|
||||
IncludeUninitialized: true,
|
||||
})
|
||||
switch {
|
||||
case err == nil:
|
||||
return true, nil
|
||||
case errors.IsNotFound(err):
|
||||
return false, nil
|
||||
default:
|
||||
return false, err
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
t.Logf("Removing initializer from Foo instance")
|
||||
success := false
|
||||
for i := 0; i < 10; i++ {
|
||||
// would love to replace the following with a patch, but removing strings from the intitializer array
|
||||
// is not what JSON (Merge) patch authors had in mind.
|
||||
fooUnstructured, err := dynamicClient.Resource(fooResource).Namespace("default").Get(foo.ObjectMeta.Name, metav1.GetOptions{
|
||||
IncludeUninitialized: true,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error getting Foo instance: %v", err)
|
||||
}
|
||||
bs, _ := fooUnstructured.MarshalJSON()
|
||||
t.Logf("Got Foo instance: %v", string(bs))
|
||||
foo := Foo{}
|
||||
if err := json.Unmarshal(bs, &foo); err != nil {
|
||||
t.Fatalf("Error parsing Foo instance: %v", err)
|
||||
}
|
||||
|
||||
// remove initialize
|
||||
if foo.ObjectMeta.Initializers == nil {
|
||||
t.Fatalf("Expected initializers to be set in Foo instance")
|
||||
}
|
||||
found := false
|
||||
for i := range foo.ObjectMeta.Initializers.Pending {
|
||||
if foo.ObjectMeta.Initializers.Pending[i].Name == "cr.bar.com" {
|
||||
foo.ObjectMeta.Initializers.Pending = append(foo.ObjectMeta.Initializers.Pending[:i], foo.ObjectMeta.Initializers.Pending[i+1:]...)
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatalf("Expected cr.bar.com as initializer on Foo instance")
|
||||
}
|
||||
if len(foo.ObjectMeta.Initializers.Pending) == 0 && foo.ObjectMeta.Initializers.Result == nil {
|
||||
foo.ObjectMeta.Initializers = nil
|
||||
}
|
||||
bs, err = json.Marshal(&foo)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
fooUnstructured.UnmarshalJSON(bs)
|
||||
|
||||
_, err = dynamicClient.Resource(fooResource).Namespace("default").Update(fooUnstructured)
|
||||
if err != nil && !errors.IsConflict(err) {
|
||||
t.Fatalf("Failed to update Foo instance: %v", err)
|
||||
} else if err == nil {
|
||||
success = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !success {
|
||||
t.Fatalf("Failed to remove initializer from Foo object")
|
||||
}
|
||||
|
||||
t.Logf("Checking that Foo instance is visible after removing the initializer")
|
||||
if _, err := dynamicClient.Resource(fooResource).Namespace("default").Get(foo.ObjectMeta.Name, metav1.GetOptions{}); err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
type Foo struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
|
||||
}
|
||||
|
||||
func unstructuredFoo(foo *Foo) (*unstructured.Unstructured, error) {
|
||||
bs, err := json.Marshal(foo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := &unstructured.Unstructured{}
|
||||
if err = ret.UnmarshalJSON(bs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func waitForEstablishedCRD(client apiextensionsclientset.Interface, name string) error {
|
||||
return wait.PollImmediate(500*time.Millisecond, wait.ForeverTestTimeout, func() (bool, error) {
|
||||
crd, err := client.ApiextensionsV1beta1().CustomResourceDefinitions().Get(name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
for _, cond := range crd.Status.Conditions {
|
||||
switch cond.Type {
|
||||
case apiextensionsv1beta1.Established:
|
||||
if cond.Status == apiextensionsv1beta1.ConditionTrue {
|
||||
return true, err
|
||||
}
|
||||
case apiextensionsv1beta1.NamesAccepted:
|
||||
if cond.Status == apiextensionsv1beta1.ConditionFalse {
|
||||
fmt.Printf("Name conflict: %v\n", cond.Reason)
|
||||
}
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
})
|
||||
}
|
||||
|
||||
func crdExistsInDiscovery(client apiextensionsclientset.Interface, crd *apiextensionsv1beta1.CustomResourceDefinition) (bool, error) {
|
||||
resourceList, err := client.Discovery().ServerResourcesForGroupVersion(crd.Spec.Group + "/" + crd.Spec.Version)
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
for _, resource := range resourceList.APIResources {
|
||||
if resource.Name == crd.Spec.Names.Plural {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
115
vendor/k8s.io/kubernetes/test/integration/master/kms_plugin_mock.go
generated
vendored
Normal file
115
vendor/k8s.io/kubernetes/test/integration/master/kms_plugin_mock.go
generated
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||
// +build !windows
|
||||
|
||||
/*
|
||||
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 master
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/golang/glog"
|
||||
kmsapi "k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1"
|
||||
)
|
||||
|
||||
const (
|
||||
kmsAPIVersion = "v1beta1"
|
||||
sockFile = "/tmp/kms-provider.sock"
|
||||
unixProtocol = "unix"
|
||||
)
|
||||
|
||||
// base64Plugin gRPC sever for a mock KMS provider.
|
||||
// Uses base64 to simulate encrypt and decrypt.
|
||||
type base64Plugin struct {
|
||||
grpcServer *grpc.Server
|
||||
listener net.Listener
|
||||
|
||||
// Allow users of the plugin to sense requests that were passed to KMS.
|
||||
encryptRequest chan *kmsapi.EncryptRequest
|
||||
}
|
||||
|
||||
func NewBase64Plugin() (*base64Plugin, error) {
|
||||
if err := cleanSockFile(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
listener, err := net.Listen(unixProtocol, sockFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to listen on the unix socket, error: %v", err)
|
||||
}
|
||||
glog.Infof("Listening on %s", sockFile)
|
||||
|
||||
server := grpc.NewServer()
|
||||
|
||||
result := &base64Plugin{
|
||||
grpcServer: server,
|
||||
listener: listener,
|
||||
encryptRequest: make(chan *kmsapi.EncryptRequest, 1),
|
||||
}
|
||||
|
||||
kmsapi.RegisterKeyManagementServiceServer(server, result)
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s *base64Plugin) cleanUp() {
|
||||
s.grpcServer.Stop()
|
||||
s.listener.Close()
|
||||
cleanSockFile()
|
||||
}
|
||||
|
||||
var testProviderAPIVersion = kmsAPIVersion
|
||||
|
||||
func (s *base64Plugin) Version(ctx context.Context, request *kmsapi.VersionRequest) (*kmsapi.VersionResponse, error) {
|
||||
return &kmsapi.VersionResponse{Version: testProviderAPIVersion, RuntimeName: "testKMS", RuntimeVersion: "0.0.1"}, nil
|
||||
}
|
||||
|
||||
func (s *base64Plugin) Decrypt(ctx context.Context, request *kmsapi.DecryptRequest) (*kmsapi.DecryptResponse, error) {
|
||||
glog.Infof("Received Decrypt Request for DEK: %s", string(request.Cipher))
|
||||
|
||||
buf := make([]byte, base64.StdEncoding.DecodedLen(len(request.Cipher)))
|
||||
n, err := base64.StdEncoding.Decode(buf, request.Cipher)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &kmsapi.DecryptResponse{Plain: buf[:n]}, nil
|
||||
}
|
||||
|
||||
func (s *base64Plugin) Encrypt(ctx context.Context, request *kmsapi.EncryptRequest) (*kmsapi.EncryptResponse, error) {
|
||||
glog.Infof("Received Encrypt Request for DEK: %x", request.Plain)
|
||||
s.encryptRequest <- request
|
||||
|
||||
buf := make([]byte, base64.StdEncoding.EncodedLen(len(request.Plain)))
|
||||
base64.StdEncoding.Encode(buf, request.Plain)
|
||||
|
||||
return &kmsapi.EncryptResponse{Cipher: buf}, nil
|
||||
}
|
||||
|
||||
func cleanSockFile() error {
|
||||
err := unix.Unlink(sockFile)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return fmt.Errorf("failed to delete the socket file, error: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
175
vendor/k8s.io/kubernetes/test/integration/master/kms_transformation_test.go
generated
vendored
Normal file
175
vendor/k8s.io/kubernetes/test/integration/master/kms_transformation_test.go
generated
vendored
Normal file
@@ -0,0 +1,175 @@
|
||||
// +build !windows
|
||||
|
||||
/*
|
||||
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 master
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/aes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apiserver/pkg/storage/value"
|
||||
aestransformer "k8s.io/apiserver/pkg/storage/value/encrypt/aes"
|
||||
|
||||
kmsapi "k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1"
|
||||
)
|
||||
|
||||
const (
|
||||
kmsPrefix = "k8s:enc:kms:v1:grpc-kms-provider:"
|
||||
dekKeySizeLen = 2
|
||||
|
||||
kmsConfigYAML = `
|
||||
kind: EncryptionConfig
|
||||
apiVersion: v1
|
||||
resources:
|
||||
- resources:
|
||||
- secrets
|
||||
providers:
|
||||
- kms:
|
||||
name: grpc-kms-provider
|
||||
cachesize: 1000
|
||||
endpoint: unix:///tmp/kms-provider.sock
|
||||
`
|
||||
)
|
||||
|
||||
// rawDEKKEKSecret provides operations for working with secrets transformed with Data Encryption Key(DEK) Key Encryption Kye(KEK) envelop.
|
||||
type rawDEKKEKSecret []byte
|
||||
|
||||
func (r rawDEKKEKSecret) getDEKLen() int {
|
||||
// DEK's length is stored in the two bytes that follow the prefix.
|
||||
return int(binary.BigEndian.Uint16(r[len(kmsPrefix) : len(kmsPrefix)+dekKeySizeLen]))
|
||||
}
|
||||
|
||||
func (r rawDEKKEKSecret) getDEK() []byte {
|
||||
return r[len(kmsPrefix)+dekKeySizeLen : len(kmsPrefix)+dekKeySizeLen+r.getDEKLen()]
|
||||
}
|
||||
|
||||
func (r rawDEKKEKSecret) getStartOfPayload() int {
|
||||
return len(kmsPrefix) + dekKeySizeLen + r.getDEKLen()
|
||||
}
|
||||
|
||||
func (r rawDEKKEKSecret) getPayload() []byte {
|
||||
return r[r.getStartOfPayload():]
|
||||
}
|
||||
|
||||
// TestKMSProvider is an integration test between KubAPI, ETCD and KMS Plugin
|
||||
// Concretely, this test verifies the following integration contracts:
|
||||
// 1. Raw records in ETCD that were processed by KMS Provider should be prefixed with k8s:enc:kms:v1:grpc-kms-provider-name:
|
||||
// 2. Data Encryption Key (DEK) should be generated by envelopeTransformer and passed to KMS gRPC Plugin
|
||||
// 3. KMS gRPC Plugin should encrypt the DEK with a Key Encryption Key (KEK) and pass it back to envelopeTransformer
|
||||
// 4. The payload (ex. Secret) should be encrypted via AES CBC transform
|
||||
// 5. Prefix-EncryptedDEK-EncryptedPayload structure should be deposited to ETCD
|
||||
func TestKMSProvider(t *testing.T) {
|
||||
pluginMock, err := NewBase64Plugin()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create mock of KMS Plugin: %v", err)
|
||||
}
|
||||
defer pluginMock.cleanUp()
|
||||
serveErr := make(chan error, 1)
|
||||
go func() {
|
||||
serveErr <- pluginMock.grpcServer.Serve(pluginMock.listener)
|
||||
}()
|
||||
|
||||
test, err := newTransformTest(t, kmsConfigYAML)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to start KUBE API Server with encryptionConfig\n %s", kmsConfigYAML)
|
||||
}
|
||||
defer test.cleanUp()
|
||||
|
||||
// As part of newTransformTest a new secret was created, so KMS Mock should have been exercised by this point.
|
||||
if len(serveErr) != 0 {
|
||||
t.Fatalf("KMSPlugin failed while serving requests: %v", <-serveErr)
|
||||
}
|
||||
|
||||
secretETCDPath := test.getETCDPath()
|
||||
var rawSecretAsSeenByETCD rawDEKKEKSecret
|
||||
rawSecretAsSeenByETCD, err = test.getRawSecretFromETCD()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read %s from etcd: %v", secretETCDPath, err)
|
||||
}
|
||||
|
||||
if !bytes.HasPrefix(rawSecretAsSeenByETCD, []byte(kmsPrefix)) {
|
||||
t.Fatalf("expected secret to be prefixed with %s, but got %s", kmsPrefix, rawSecretAsSeenByETCD)
|
||||
}
|
||||
|
||||
// Since Data Encryption Key (DEK) is randomly generated (per encryption operation), we need to ask KMS Mock for it.
|
||||
dekPlainAsSeenByKMS, err := getDEKFromKMSPlugin(pluginMock)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get DEK from KMS: %v", err)
|
||||
}
|
||||
|
||||
decryptResponse, err := pluginMock.Decrypt(context.Background(),
|
||||
&kmsapi.DecryptRequest{Version: kmsAPIVersion, Cipher: rawSecretAsSeenByETCD.getDEK()})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to decrypt DEK, %v", err)
|
||||
}
|
||||
dekPlainAsWouldBeSeenByETCD := decryptResponse.Plain
|
||||
|
||||
if !bytes.Equal(dekPlainAsSeenByKMS, dekPlainAsWouldBeSeenByETCD) {
|
||||
t.Fatalf("expected dekPlainAsSeenByKMS %v to be passed to KMS Plugin, but got %s",
|
||||
dekPlainAsSeenByKMS, dekPlainAsWouldBeSeenByETCD)
|
||||
}
|
||||
|
||||
plainSecret, err := decryptPayload(dekPlainAsWouldBeSeenByETCD, rawSecretAsSeenByETCD, secretETCDPath)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to transform from storage via AESCBC, err: %v", err)
|
||||
}
|
||||
|
||||
if !strings.Contains(string(plainSecret), secretVal) {
|
||||
t.Fatalf("expected %q after decryption, but got %q", secretVal, string(plainSecret))
|
||||
}
|
||||
|
||||
// Secrets should be un-enveloped on direct reads from Kube API Server.
|
||||
s, err := test.restClient.CoreV1().Secrets(testNamespace).Get(testSecret, metav1.GetOptions{})
|
||||
if secretVal != string(s.Data[secretKey]) {
|
||||
t.Fatalf("expected %s from KubeAPI, but got %s", secretVal, string(s.Data[secretKey]))
|
||||
}
|
||||
test.printMetrics()
|
||||
}
|
||||
|
||||
func getDEKFromKMSPlugin(pluginMock *base64Plugin) ([]byte, error) {
|
||||
// We expect KMS to already have seen an encryptRequest. Hence non-blocking call.
|
||||
e, ok := <-pluginMock.encryptRequest
|
||||
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("failed to sense encryptRequest from KMS Plugin Mock")
|
||||
}
|
||||
|
||||
return e.Plain, nil
|
||||
}
|
||||
|
||||
func decryptPayload(key []byte, secret rawDEKKEKSecret, secretETCDPath string) ([]byte, error) {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to initialize AES Cipher: %v", err)
|
||||
}
|
||||
// etcd path of the key is used as the authenticated context - need to pass it to decrypt
|
||||
ctx := value.DefaultContext([]byte(secretETCDPath))
|
||||
aescbcTransformer := aestransformer.NewCBCTransformer(block)
|
||||
plainSecret, _, err := aescbcTransformer.TransformFromStorage(secret.getPayload(), ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to transform from storage via AESCBC, err: %v", err)
|
||||
}
|
||||
|
||||
return plainSecret, nil
|
||||
}
|
||||
252
vendor/k8s.io/kubernetes/test/integration/master/kube_apiserver_test.go
generated
vendored
Normal file
252
vendor/k8s.io/kubernetes/test/integration/master/kube_apiserver_test.go
generated
vendored
Normal file
@@ -0,0 +1,252 @@
|
||||
/*
|
||||
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 master
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
appsv1beta1 "k8s.io/api/apps/v1beta1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/apiserver/pkg/registry/generic/registry"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/kube-aggregator/pkg/apis/apiregistration"
|
||||
kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing"
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
)
|
||||
|
||||
func TestRun(t *testing.T) {
|
||||
server := kubeapiservertesting.StartTestServerOrDie(t, nil, nil, framework.SharedEtcd())
|
||||
defer server.TearDownFn()
|
||||
|
||||
client, err := kubernetes.NewForConfig(server.ClientConfig)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// test whether the server is really healthy after /healthz told us so
|
||||
t.Logf("Creating Deployment directly after being healthy")
|
||||
var replicas int32 = 1
|
||||
_, err = client.AppsV1beta1().Deployments("default").Create(&appsv1beta1.Deployment{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Deployment",
|
||||
APIVersion: "apps/v1beta1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "default",
|
||||
Name: "test",
|
||||
},
|
||||
Spec: appsv1beta1.DeploymentSpec{
|
||||
Replicas: &replicas,
|
||||
Strategy: appsv1beta1.DeploymentStrategy{
|
||||
Type: appsv1beta1.RollingUpdateDeploymentStrategyType,
|
||||
},
|
||||
Template: corev1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: map[string]string{"foo": "bar"},
|
||||
},
|
||||
Spec: corev1.PodSpec{
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Name: "foo",
|
||||
Image: "foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create deployment: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestOpenAPIDelegationChainPlumbing is a smoke test that checks for
|
||||
// the existence of some representative paths from the
|
||||
// apiextensions-server and the kube-aggregator server, both part of
|
||||
// the delegation chain in kube-apiserver.
|
||||
func TestOpenAPIDelegationChainPlumbing(t *testing.T) {
|
||||
server := kubeapiservertesting.StartTestServerOrDie(t, nil, nil, framework.SharedEtcd())
|
||||
defer server.TearDownFn()
|
||||
|
||||
kubeclient, err := kubernetes.NewForConfig(server.ClientConfig)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
result := kubeclient.RESTClient().Get().AbsPath("/swagger.json").Do()
|
||||
status := 0
|
||||
result.StatusCode(&status)
|
||||
if status != 200 {
|
||||
t.Fatalf("GET /swagger.json failed: expected status=%d, got=%d", 200, status)
|
||||
}
|
||||
|
||||
raw, err := result.Raw()
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
type openAPISchema struct {
|
||||
Paths map[string]interface{} `json:"paths"`
|
||||
}
|
||||
|
||||
var doc openAPISchema
|
||||
err = json.Unmarshal(raw, &doc)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to unmarshal: %v", err)
|
||||
}
|
||||
|
||||
matchedExtension := false
|
||||
extensionsPrefix := "/apis/" + apiextensions.GroupName
|
||||
|
||||
matchedRegistration := false
|
||||
registrationPrefix := "/apis/" + apiregistration.GroupName
|
||||
|
||||
for path := range doc.Paths {
|
||||
if strings.HasPrefix(path, extensionsPrefix) {
|
||||
matchedExtension = true
|
||||
}
|
||||
if strings.HasPrefix(path, registrationPrefix) {
|
||||
matchedRegistration = true
|
||||
}
|
||||
if matchedExtension && matchedRegistration {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if !matchedExtension {
|
||||
t.Errorf("missing path: %q", extensionsPrefix)
|
||||
}
|
||||
|
||||
if !matchedRegistration {
|
||||
t.Errorf("missing path: %q", registrationPrefix)
|
||||
}
|
||||
}
|
||||
|
||||
// return the unique endpoint IPs
|
||||
func getEndpointIPs(endpoints *corev1.Endpoints) []string {
|
||||
endpointMap := make(map[string]bool)
|
||||
ips := make([]string, 0)
|
||||
for _, subset := range endpoints.Subsets {
|
||||
for _, address := range subset.Addresses {
|
||||
if _, ok := endpointMap[address.IP]; !ok {
|
||||
endpointMap[address.IP] = true
|
||||
ips = append(ips, address.IP)
|
||||
}
|
||||
}
|
||||
}
|
||||
return ips
|
||||
}
|
||||
|
||||
func verifyEndpointsWithIPs(servers []*kubeapiservertesting.TestServer, ips []string) bool {
|
||||
listenAddresses := make([]string, 0)
|
||||
for _, server := range servers {
|
||||
listenAddresses = append(listenAddresses, server.ServerOpts.GenericServerRunOptions.AdvertiseAddress.String())
|
||||
}
|
||||
return reflect.DeepEqual(listenAddresses, ips)
|
||||
}
|
||||
|
||||
func testReconcilersMasterLease(t *testing.T, leaseCount int, masterCount int) {
|
||||
var leaseServers []*kubeapiservertesting.TestServer
|
||||
var masterCountServers []*kubeapiservertesting.TestServer
|
||||
etcd := framework.SharedEtcd()
|
||||
|
||||
instanceOptions := &kubeapiservertesting.TestServerInstanceOptions{
|
||||
DisableStorageCleanup: true,
|
||||
}
|
||||
|
||||
// cleanup the registry storage
|
||||
defer registry.CleanupStorage()
|
||||
|
||||
// 1. start masterCount api servers
|
||||
for i := 0; i < masterCount; i++ {
|
||||
// start master count api server
|
||||
server := kubeapiservertesting.StartTestServerOrDie(t, instanceOptions, []string{
|
||||
"--endpoint-reconciler-type", "master-count",
|
||||
"--advertise-address", fmt.Sprintf("10.0.1.%v", i+1),
|
||||
"--apiserver-count", fmt.Sprintf("%v", masterCount),
|
||||
}, etcd)
|
||||
masterCountServers = append(masterCountServers, server)
|
||||
}
|
||||
|
||||
// 2. verify master count servers have registered
|
||||
if err := wait.PollImmediate(3*time.Second, 2*time.Minute, func() (bool, error) {
|
||||
client, err := kubernetes.NewForConfig(masterCountServers[0].ClientConfig)
|
||||
endpoints, err := client.CoreV1().Endpoints("default").Get("kubernetes", metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Logf("error fetching endpoints: %v", err)
|
||||
return false, nil
|
||||
}
|
||||
return verifyEndpointsWithIPs(masterCountServers, getEndpointIPs(endpoints)), nil
|
||||
}); err != nil {
|
||||
t.Fatalf("master count endpoints failed to register: %v", err)
|
||||
}
|
||||
|
||||
// 3. start lease api servers
|
||||
for i := 0; i < leaseCount; i++ {
|
||||
options := []string{
|
||||
"--endpoint-reconciler-type", "lease",
|
||||
"--advertise-address", fmt.Sprintf("10.0.1.%v", i+10),
|
||||
}
|
||||
server := kubeapiservertesting.StartTestServerOrDie(t, instanceOptions, options, etcd)
|
||||
defer server.TearDownFn()
|
||||
leaseServers = append(leaseServers, server)
|
||||
}
|
||||
|
||||
time.Sleep(3 * time.Second)
|
||||
|
||||
// 4. Shutdown the masterCount server
|
||||
for _, server := range masterCountServers {
|
||||
server.TearDownFn()
|
||||
}
|
||||
|
||||
// 5. verify only leaseEndpoint servers left
|
||||
if err := wait.PollImmediate(3*time.Second, 2*time.Minute, func() (bool, error) {
|
||||
client, err := kubernetes.NewForConfig(leaseServers[0].ClientConfig)
|
||||
if err != nil {
|
||||
t.Logf("create client error: %v", err)
|
||||
return false, nil
|
||||
}
|
||||
endpoints, err := client.CoreV1().Endpoints("default").Get("kubernetes", metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Logf("error fetching endpoints: %v", err)
|
||||
return false, nil
|
||||
}
|
||||
return verifyEndpointsWithIPs(leaseServers, getEndpointIPs(endpoints)), nil
|
||||
}); err != nil {
|
||||
t.Fatalf("did not find only lease endpoints: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReconcilerMasterLeaseCombined(t *testing.T) {
|
||||
testReconcilersMasterLease(t, 1, 3)
|
||||
}
|
||||
|
||||
func TestReconcilerMasterLeaseMultiMoreMasters(t *testing.T) {
|
||||
testReconcilersMasterLease(t, 3, 2)
|
||||
}
|
||||
|
||||
func TestReconcilerMasterLeaseMultiCombined(t *testing.T) {
|
||||
testReconcilersMasterLease(t, 3, 3)
|
||||
}
|
||||
27
vendor/k8s.io/kubernetes/test/integration/master/main_test.go
generated
vendored
Normal file
27
vendor/k8s.io/kubernetes/test/integration/master/main_test.go
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
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 master
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
framework.EtcdMain(m.Run)
|
||||
}
|
||||
178
vendor/k8s.io/kubernetes/test/integration/master/secrets_transformation_test.go
generated
vendored
Normal file
178
vendor/k8s.io/kubernetes/test/integration/master/secrets_transformation_test.go
generated
vendored
Normal file
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
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 master
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"k8s.io/apiserver/pkg/server/options/encryptionconfig"
|
||||
"k8s.io/apiserver/pkg/storage/value"
|
||||
aestransformer "k8s.io/apiserver/pkg/storage/value/encrypt/aes"
|
||||
)
|
||||
|
||||
const (
|
||||
aesGCMPrefix = "k8s:enc:aesgcm:v1:key1:"
|
||||
aesCBCPrefix = "k8s:enc:aescbc:v1:key1:"
|
||||
|
||||
aesGCMConfigYAML = `
|
||||
kind: EncryptionConfig
|
||||
apiVersion: v1
|
||||
resources:
|
||||
- resources:
|
||||
- secrets
|
||||
providers:
|
||||
- aesgcm:
|
||||
keys:
|
||||
- name: key1
|
||||
secret: c2VjcmV0IGlzIHNlY3VyZQ==
|
||||
`
|
||||
|
||||
aesCBCConfigYAML = `
|
||||
kind: EncryptionConfig
|
||||
apiVersion: v1
|
||||
resources:
|
||||
- resources:
|
||||
- secrets
|
||||
providers:
|
||||
- aescbc:
|
||||
keys:
|
||||
- name: key1
|
||||
secret: c2VjcmV0IGlzIHNlY3VyZQ==
|
||||
`
|
||||
|
||||
identityConfigYAML = `
|
||||
kind: EncryptionConfig
|
||||
apiVersion: v1
|
||||
resources:
|
||||
- resources:
|
||||
- secrets
|
||||
providers:
|
||||
- identity: {}
|
||||
`
|
||||
)
|
||||
|
||||
// TestSecretsShouldBeEnveloped is an integration test between KubeAPI and etcd that checks:
|
||||
// 1. Secrets are encrypted on write
|
||||
// 2. Secrets are decrypted on read
|
||||
// when EncryptionConfig is passed to KubeAPI server.
|
||||
func TestSecretsShouldBeTransformed(t *testing.T) {
|
||||
var testCases = []struct {
|
||||
transformerConfigContent string
|
||||
transformerPrefix string
|
||||
unSealFunc unSealSecret
|
||||
}{
|
||||
{aesGCMConfigYAML, aesGCMPrefix, unSealWithGCMTransformer},
|
||||
{aesCBCConfigYAML, aesCBCPrefix, unSealWithCBCTransformer},
|
||||
// TODO: add secretbox
|
||||
}
|
||||
for _, tt := range testCases {
|
||||
test, err := newTransformTest(t, tt.transformerConfigContent)
|
||||
if err != nil {
|
||||
test.cleanUp()
|
||||
t.Errorf("failed to setup test for envelop %s, error was %v", tt.transformerPrefix, err)
|
||||
continue
|
||||
}
|
||||
test.run(tt.unSealFunc, tt.transformerPrefix)
|
||||
test.cleanUp()
|
||||
}
|
||||
}
|
||||
|
||||
// Baseline (no enveloping) - use to contrast with enveloping benchmarks.
|
||||
func BenchmarkBase(b *testing.B) {
|
||||
runBenchmark(b, "")
|
||||
}
|
||||
|
||||
// Identity transformer is a NOOP (crypto-wise) - use to contrast with AESGCM and AESCBC benchmark results.
|
||||
func BenchmarkIdentityWrite(b *testing.B) {
|
||||
runBenchmark(b, identityConfigYAML)
|
||||
}
|
||||
|
||||
func BenchmarkAESGCMEnvelopeWrite(b *testing.B) {
|
||||
runBenchmark(b, aesGCMConfigYAML)
|
||||
}
|
||||
|
||||
func BenchmarkAESCBCEnvelopeWrite(b *testing.B) {
|
||||
runBenchmark(b, aesCBCConfigYAML)
|
||||
}
|
||||
|
||||
func runBenchmark(b *testing.B, transformerConfig string) {
|
||||
b.StopTimer()
|
||||
test, err := newTransformTest(b, transformerConfig)
|
||||
defer test.cleanUp()
|
||||
if err != nil {
|
||||
b.Fatalf("failed to setup benchmark for config %s, error was %v", transformerConfig, err)
|
||||
}
|
||||
|
||||
b.StartTimer()
|
||||
test.benchmark(b)
|
||||
b.StopTimer()
|
||||
test.printMetrics()
|
||||
}
|
||||
|
||||
func unSealWithGCMTransformer(cipherText []byte, ctx value.Context,
|
||||
transformerConfig encryptionconfig.ProviderConfig) ([]byte, error) {
|
||||
|
||||
block, err := newAESCipher(transformerConfig.AESGCM.Keys[0].Secret)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create block cipher: %v", err)
|
||||
}
|
||||
|
||||
gcmTransformer := aestransformer.NewGCMTransformer(block)
|
||||
|
||||
clearText, _, err := gcmTransformer.TransformFromStorage(cipherText, ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decypt secret: %v", err)
|
||||
}
|
||||
|
||||
return clearText, nil
|
||||
}
|
||||
|
||||
func unSealWithCBCTransformer(cipherText []byte, ctx value.Context,
|
||||
transformerConfig encryptionconfig.ProviderConfig) ([]byte, error) {
|
||||
|
||||
block, err := newAESCipher(transformerConfig.AESCBC.Keys[0].Secret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cbcTransformer := aestransformer.NewCBCTransformer(block)
|
||||
|
||||
clearText, _, err := cbcTransformer.TransformFromStorage(cipherText, ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decypt secret: %v", err)
|
||||
}
|
||||
|
||||
return clearText, nil
|
||||
}
|
||||
|
||||
func newAESCipher(key string) (cipher.Block, error) {
|
||||
k, err := base64.StdEncoding.DecodeString(key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode config secret: %v", err)
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(k)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create AES cipher: %v", err)
|
||||
}
|
||||
|
||||
return block, nil
|
||||
}
|
||||
882
vendor/k8s.io/kubernetes/test/integration/master/synthetic_master_test.go
generated
vendored
Normal file
882
vendor/k8s.io/kubernetes/test/integration/master/synthetic_master_test.go
generated
vendored
Normal file
@@ -0,0 +1,882 @@
|
||||
/*
|
||||
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 master
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
"k8s.io/api/core/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/apiserver/pkg/authentication/group"
|
||||
"k8s.io/apiserver/pkg/authentication/request/bearertoken"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||
"k8s.io/apiserver/pkg/authorization/authorizerfactory"
|
||||
"k8s.io/apiserver/plugin/pkg/authenticator/token/tokentest"
|
||||
clientsetv1 "k8s.io/client-go/kubernetes"
|
||||
clienttypedv1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/kubernetes/pkg/api/testapi"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||
"k8s.io/kubernetes/pkg/master"
|
||||
"k8s.io/kubernetes/test/integration"
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
)
|
||||
|
||||
const (
|
||||
AliceToken string = "abc123" // username: alice. Present in token file.
|
||||
BobToken string = "xyz987" // username: bob. Present in token file.
|
||||
)
|
||||
|
||||
type allowAliceAuthorizer struct{}
|
||||
|
||||
func (allowAliceAuthorizer) Authorize(a authorizer.Attributes) (authorizer.Decision, string, error) {
|
||||
if a.GetUser() != nil && a.GetUser().GetName() == "alice" {
|
||||
return authorizer.DecisionAllow, "", nil
|
||||
}
|
||||
return authorizer.DecisionNoOpinion, "I can't allow that. Go ask alice.", nil
|
||||
}
|
||||
|
||||
func testPrefix(t *testing.T, prefix string) {
|
||||
_, s, closeFn := framework.RunAMaster(nil)
|
||||
defer closeFn()
|
||||
|
||||
resp, err := http.Get(s.URL + prefix)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error getting %s prefix: %v", prefix, err)
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
t.Fatalf("got status %v instead of 200 OK", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAutoscalingPrefix(t *testing.T) {
|
||||
testPrefix(t, "/apis/autoscaling/")
|
||||
}
|
||||
|
||||
func TestBatchPrefix(t *testing.T) {
|
||||
testPrefix(t, "/apis/batch/")
|
||||
}
|
||||
|
||||
func TestAppsPrefix(t *testing.T) {
|
||||
testPrefix(t, "/apis/apps/")
|
||||
}
|
||||
|
||||
func TestExtensionsPrefix(t *testing.T) {
|
||||
testPrefix(t, "/apis/extensions/")
|
||||
}
|
||||
|
||||
func TestKubernetesService(t *testing.T) {
|
||||
config := framework.NewMasterConfig()
|
||||
_, _, closeFn := framework.RunAMaster(config)
|
||||
defer closeFn()
|
||||
coreClient := clientset.NewForConfigOrDie(config.GenericConfig.LoopbackClientConfig)
|
||||
if _, err := coreClient.Core().Services(metav1.NamespaceDefault).Get("kubernetes", metav1.GetOptions{}); err != nil {
|
||||
t.Fatalf("Expected kubernetes service to exists, got: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmptyList(t *testing.T) {
|
||||
_, s, closeFn := framework.RunAMaster(nil)
|
||||
defer closeFn()
|
||||
|
||||
u := s.URL + "/api/v1/namespaces/default/pods"
|
||||
resp, err := http.Get(u)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error getting %s: %v", u, err)
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
t.Fatalf("got status %v instead of 200 OK", resp.StatusCode)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
data, _ := ioutil.ReadAll(resp.Body)
|
||||
decodedData := map[string]interface{}{}
|
||||
if err := json.Unmarshal(data, &decodedData); err != nil {
|
||||
t.Logf("body: %s", string(data))
|
||||
t.Fatalf("got error decoding data: %v", err)
|
||||
}
|
||||
if items, ok := decodedData["items"]; !ok {
|
||||
t.Logf("body: %s", string(data))
|
||||
t.Fatalf("missing items field in empty list (all lists should return an items field)")
|
||||
} else if items == nil {
|
||||
t.Logf("body: %s", string(data))
|
||||
t.Fatalf("nil items field from empty list (all lists should return non-nil empty items lists)")
|
||||
}
|
||||
}
|
||||
|
||||
func initStatusForbiddenMasterCongfig() *master.Config {
|
||||
masterConfig := framework.NewIntegrationTestMasterConfig()
|
||||
masterConfig.GenericConfig.Authorization.Authorizer = authorizerfactory.NewAlwaysDenyAuthorizer()
|
||||
return masterConfig
|
||||
}
|
||||
|
||||
func initUnauthorizedMasterCongfig() *master.Config {
|
||||
masterConfig := framework.NewIntegrationTestMasterConfig()
|
||||
tokenAuthenticator := tokentest.New()
|
||||
tokenAuthenticator.Tokens[AliceToken] = &user.DefaultInfo{Name: "alice", UID: "1"}
|
||||
tokenAuthenticator.Tokens[BobToken] = &user.DefaultInfo{Name: "bob", UID: "2"}
|
||||
masterConfig.GenericConfig.Authentication.Authenticator = group.NewGroupAdder(bearertoken.New(tokenAuthenticator), []string{user.AllAuthenticated})
|
||||
masterConfig.GenericConfig.Authorization.Authorizer = allowAliceAuthorizer{}
|
||||
return masterConfig
|
||||
}
|
||||
|
||||
func TestStatus(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
masterConfig *master.Config
|
||||
statusCode int
|
||||
reqPath string
|
||||
reason string
|
||||
message string
|
||||
}{
|
||||
{
|
||||
name: "404",
|
||||
masterConfig: nil,
|
||||
statusCode: http.StatusNotFound,
|
||||
reqPath: "/apis/batch/v1/namespaces/default/jobs/foo",
|
||||
reason: "NotFound",
|
||||
message: `jobs.batch "foo" not found`,
|
||||
},
|
||||
{
|
||||
name: "403",
|
||||
masterConfig: initStatusForbiddenMasterCongfig(),
|
||||
statusCode: http.StatusForbidden,
|
||||
reqPath: "/apis",
|
||||
reason: "Forbidden",
|
||||
message: `forbidden: User "" cannot get path "/apis": Everything is forbidden.`,
|
||||
},
|
||||
{
|
||||
name: "401",
|
||||
masterConfig: initUnauthorizedMasterCongfig(),
|
||||
statusCode: http.StatusUnauthorized,
|
||||
reqPath: "/apis",
|
||||
reason: "Unauthorized",
|
||||
message: `Unauthorized`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
_, s, closeFn := framework.RunAMaster(tc.masterConfig)
|
||||
defer closeFn()
|
||||
|
||||
u := s.URL + tc.reqPath
|
||||
resp, err := http.Get(u)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error getting %s: %v", u, err)
|
||||
}
|
||||
if resp.StatusCode != tc.statusCode {
|
||||
t.Fatalf("got status %v instead of %s", resp.StatusCode, tc.name)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
data, _ := ioutil.ReadAll(resp.Body)
|
||||
decodedData := map[string]interface{}{}
|
||||
if err := json.Unmarshal(data, &decodedData); err != nil {
|
||||
t.Logf("body: %s", string(data))
|
||||
t.Fatalf("got error decoding data: %v", err)
|
||||
}
|
||||
t.Logf("body: %s", string(data))
|
||||
|
||||
if got, expected := decodedData["apiVersion"], "v1"; got != expected {
|
||||
t.Errorf("unexpected apiVersion %q, expected %q", got, expected)
|
||||
}
|
||||
if got, expected := decodedData["kind"], "Status"; got != expected {
|
||||
t.Errorf("unexpected kind %q, expected %q", got, expected)
|
||||
}
|
||||
if got, expected := decodedData["status"], "Failure"; got != expected {
|
||||
t.Errorf("unexpected status %q, expected %q", got, expected)
|
||||
}
|
||||
if got, expected := decodedData["code"], float64(tc.statusCode); got != expected {
|
||||
t.Errorf("unexpected code %v, expected %v", got, expected)
|
||||
}
|
||||
if got, expected := decodedData["reason"], tc.reason; got != expected {
|
||||
t.Errorf("unexpected reason %v, expected %v", got, expected)
|
||||
}
|
||||
if got, expected := decodedData["message"], tc.message; got != expected {
|
||||
t.Errorf("unexpected message %v, expected %v", got, expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func constructBody(val string, size int, field string, t *testing.T) *appsv1.Deployment {
|
||||
var replicas int32 = 1
|
||||
deploymentObject := &appsv1.Deployment{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Deployment",
|
||||
APIVersion: "apps/v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "default",
|
||||
Name: "test",
|
||||
},
|
||||
Spec: appsv1.DeploymentSpec{
|
||||
Replicas: &replicas,
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
Strategy: appsv1.DeploymentStrategy{
|
||||
Type: appsv1.RollingUpdateDeploymentStrategyType,
|
||||
},
|
||||
Template: corev1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: map[string]string{"foo": "bar"},
|
||||
},
|
||||
Spec: corev1.PodSpec{
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Name: "foo",
|
||||
Image: "foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
switch field {
|
||||
case "labels":
|
||||
labelsMap := map[string]string{}
|
||||
for i := 0; i < size; i++ {
|
||||
key := val + strconv.Itoa(i)
|
||||
labelsMap[key] = val
|
||||
}
|
||||
deploymentObject.ObjectMeta.Labels = labelsMap
|
||||
case "annotations":
|
||||
annotationsMap := map[string]string{}
|
||||
for i := 0; i < size; i++ {
|
||||
key := val + strconv.Itoa(i)
|
||||
annotationsMap[key] = val
|
||||
}
|
||||
deploymentObject.ObjectMeta.Annotations = annotationsMap
|
||||
case "finalizers":
|
||||
finalizerString := []string{}
|
||||
for i := 0; i < size; i++ {
|
||||
finalizerString = append(finalizerString, val)
|
||||
}
|
||||
deploymentObject.ObjectMeta.Finalizers = finalizerString
|
||||
default:
|
||||
t.Fatalf("Unexpected field: %s used for making large deployment object value", field)
|
||||
}
|
||||
|
||||
return deploymentObject
|
||||
}
|
||||
|
||||
func TestObjectSizeResponses(t *testing.T) {
|
||||
_, s, closeFn := framework.RunAMaster(nil)
|
||||
defer closeFn()
|
||||
|
||||
client := clientsetv1.NewForConfigOrDie(&restclient.Config{Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: testapi.Groups[api.GroupName].GroupVersion()}})
|
||||
|
||||
const DeploymentMegabyteSize = 100000
|
||||
const DeploymentTwoMegabyteSize = 1000000
|
||||
|
||||
expectedMsgFor1MB := `etcdserver: request is too large`
|
||||
expectedMsgFor2MB := `rpc error: code = ResourceExhausted desc = grpc: trying to send message larger than max`
|
||||
expectedMsgForLargeAnnotation := `metadata.annotations: Too long: must have at most 262144 characters`
|
||||
|
||||
deployment1 := constructBody("a", DeploymentMegabyteSize, "labels", t) // >1 MB file
|
||||
deployment2 := constructBody("a", DeploymentTwoMegabyteSize, "labels", t) // >2 MB file
|
||||
|
||||
deployment3 := constructBody("a", DeploymentMegabyteSize, "annotations", t)
|
||||
|
||||
deployment4 := constructBody("sample/sample", DeploymentMegabyteSize, "finalizers", t) // >1 MB file
|
||||
deployment5 := constructBody("sample/sample", DeploymentTwoMegabyteSize, "finalizers", t) // >2 MB file
|
||||
|
||||
requests := []struct {
|
||||
size string
|
||||
deploymentObject *appsv1.Deployment
|
||||
expectedMessage string
|
||||
}{
|
||||
{"1 MB", deployment1, expectedMsgFor1MB},
|
||||
{"2 MB", deployment2, expectedMsgFor2MB},
|
||||
{"1 MB", deployment3, expectedMsgForLargeAnnotation},
|
||||
{"1 MB", deployment4, expectedMsgFor1MB},
|
||||
{"2 MB", deployment5, expectedMsgFor2MB},
|
||||
}
|
||||
|
||||
for _, r := range requests {
|
||||
t.Run(r.size, func(t *testing.T) {
|
||||
_, err := client.AppsV1().Deployments(metav1.NamespaceDefault).Create(r.deploymentObject)
|
||||
if err != nil {
|
||||
if !strings.Contains(err.Error(), r.expectedMessage) {
|
||||
t.Errorf("got: %s;want: %s", err.Error(), r.expectedMessage)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWatchSucceedsWithoutArgs(t *testing.T) {
|
||||
_, s, closeFn := framework.RunAMaster(nil)
|
||||
defer closeFn()
|
||||
|
||||
resp, err := http.Get(s.URL + "/api/v1/namespaces?watch=1")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error getting experimental prefix: %v", err)
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
t.Fatalf("got status %v instead of 200 OK", resp.StatusCode)
|
||||
}
|
||||
resp.Body.Close()
|
||||
}
|
||||
|
||||
var hpaV1 string = `
|
||||
{
|
||||
"apiVersion": "autoscaling/v1",
|
||||
"kind": "HorizontalPodAutoscaler",
|
||||
"metadata": {
|
||||
"name": "test-hpa",
|
||||
"namespace": "default"
|
||||
},
|
||||
"spec": {
|
||||
"scaleTargetRef": {
|
||||
"kind": "ReplicationController",
|
||||
"name": "test-hpa",
|
||||
"namespace": "default"
|
||||
},
|
||||
"minReplicas": 1,
|
||||
"maxReplicas": 10,
|
||||
"targetCPUUtilizationPercentage": 50
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
var deploymentExtensions string = `
|
||||
{
|
||||
"apiVersion": "extensions/v1beta1",
|
||||
"kind": "Deployment",
|
||||
"metadata": {
|
||||
"name": "test-deployment1",
|
||||
"namespace": "default"
|
||||
},
|
||||
"spec": {
|
||||
"replicas": 1,
|
||||
"template": {
|
||||
"metadata": {
|
||||
"labels": {
|
||||
"app": "nginx0"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"containers": [{
|
||||
"name": "nginx",
|
||||
"image": "k8s.gcr.io/nginx:1.7.9"
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
var deploymentApps string = `
|
||||
{
|
||||
"apiVersion": "apps/v1beta1",
|
||||
"kind": "Deployment",
|
||||
"metadata": {
|
||||
"name": "test-deployment2",
|
||||
"namespace": "default"
|
||||
},
|
||||
"spec": {
|
||||
"replicas": 1,
|
||||
"template": {
|
||||
"metadata": {
|
||||
"labels": {
|
||||
"app": "nginx0"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"containers": [{
|
||||
"name": "nginx",
|
||||
"image": "k8s.gcr.io/nginx:1.7.9"
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
func autoscalingPath(resource, namespace, name string) string {
|
||||
return testapi.Autoscaling.ResourcePath(resource, namespace, name)
|
||||
}
|
||||
|
||||
func batchPath(resource, namespace, name string) string {
|
||||
return testapi.Batch.ResourcePath(resource, namespace, name)
|
||||
}
|
||||
|
||||
func extensionsPath(resource, namespace, name string) string {
|
||||
return testapi.Extensions.ResourcePath(resource, namespace, name)
|
||||
}
|
||||
|
||||
func appsPath(resource, namespace, name string) string {
|
||||
return testapi.Apps.ResourcePath(resource, namespace, name)
|
||||
}
|
||||
|
||||
func TestAutoscalingGroupBackwardCompatibility(t *testing.T) {
|
||||
_, s, closeFn := framework.RunAMaster(nil)
|
||||
defer closeFn()
|
||||
transport := http.DefaultTransport
|
||||
|
||||
requests := []struct {
|
||||
verb string
|
||||
URL string
|
||||
body string
|
||||
expectedStatusCodes map[int]bool
|
||||
expectedVersion string
|
||||
}{
|
||||
{"POST", autoscalingPath("horizontalpodautoscalers", metav1.NamespaceDefault, ""), hpaV1, integration.Code201, ""},
|
||||
{"GET", autoscalingPath("horizontalpodautoscalers", metav1.NamespaceDefault, ""), "", integration.Code200, testapi.Autoscaling.GroupVersion().String()},
|
||||
}
|
||||
|
||||
for _, r := range requests {
|
||||
bodyBytes := bytes.NewReader([]byte(r.body))
|
||||
req, err := http.NewRequest(r.verb, s.URL+r.URL, bodyBytes)
|
||||
if err != nil {
|
||||
t.Logf("case %v", r)
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
func() {
|
||||
resp, err := transport.RoundTrip(req)
|
||||
defer resp.Body.Close()
|
||||
if err != nil {
|
||||
t.Logf("case %v", r)
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
b, _ := ioutil.ReadAll(resp.Body)
|
||||
body := string(b)
|
||||
if _, ok := r.expectedStatusCodes[resp.StatusCode]; !ok {
|
||||
t.Logf("case %v", r)
|
||||
t.Errorf("Expected status one of %v, but got %v", r.expectedStatusCodes, resp.StatusCode)
|
||||
t.Errorf("Body: %v", body)
|
||||
}
|
||||
if !strings.Contains(body, "\"apiVersion\":\""+r.expectedVersion) {
|
||||
t.Logf("case %v", r)
|
||||
t.Errorf("Expected version %v, got body %v", r.expectedVersion, body)
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppsGroupBackwardCompatibility(t *testing.T) {
|
||||
_, s, closeFn := framework.RunAMaster(nil)
|
||||
defer closeFn()
|
||||
transport := http.DefaultTransport
|
||||
|
||||
requests := []struct {
|
||||
verb string
|
||||
URL string
|
||||
body string
|
||||
expectedStatusCodes map[int]bool
|
||||
expectedVersion string
|
||||
}{
|
||||
// Post to extensions endpoint and get back from both: extensions and apps
|
||||
{"POST", extensionsPath("deployments", metav1.NamespaceDefault, ""), deploymentExtensions, integration.Code201, ""},
|
||||
{"GET", extensionsPath("deployments", metav1.NamespaceDefault, "test-deployment1"), "", integration.Code200, testapi.Extensions.GroupVersion().String()},
|
||||
{"GET", appsPath("deployments", metav1.NamespaceDefault, "test-deployment1"), "", integration.Code200, testapi.Apps.GroupVersion().String()},
|
||||
{"DELETE", extensionsPath("deployments", metav1.NamespaceDefault, "test-deployment1"), "", integration.Code200, testapi.Extensions.GroupVersion().String()},
|
||||
// Post to apps endpoint and get back from both: apps and extensions
|
||||
{"POST", appsPath("deployments", metav1.NamespaceDefault, ""), deploymentApps, integration.Code201, ""},
|
||||
{"GET", appsPath("deployments", metav1.NamespaceDefault, "test-deployment2"), "", integration.Code200, testapi.Apps.GroupVersion().String()},
|
||||
{"GET", extensionsPath("deployments", metav1.NamespaceDefault, "test-deployment2"), "", integration.Code200, testapi.Extensions.GroupVersion().String()},
|
||||
{"DELETE", appsPath("deployments", metav1.NamespaceDefault, "test-deployment2"), "", integration.Code200, testapi.Apps.GroupVersion().String()},
|
||||
}
|
||||
|
||||
for _, r := range requests {
|
||||
bodyBytes := bytes.NewReader([]byte(r.body))
|
||||
req, err := http.NewRequest(r.verb, s.URL+r.URL, bodyBytes)
|
||||
if err != nil {
|
||||
t.Logf("case %v", r)
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
func() {
|
||||
resp, err := transport.RoundTrip(req)
|
||||
defer resp.Body.Close()
|
||||
if err != nil {
|
||||
t.Logf("case %v", r)
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
b, _ := ioutil.ReadAll(resp.Body)
|
||||
body := string(b)
|
||||
if _, ok := r.expectedStatusCodes[resp.StatusCode]; !ok {
|
||||
t.Logf("case %v", r)
|
||||
t.Errorf("Expected status one of %v, but got %v", r.expectedStatusCodes, resp.StatusCode)
|
||||
t.Errorf("Body: %v", body)
|
||||
}
|
||||
if !strings.Contains(body, "\"apiVersion\":\""+r.expectedVersion) {
|
||||
t.Logf("case %v", r)
|
||||
t.Errorf("Expected version %v, got body %v", r.expectedVersion, body)
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
func TestAccept(t *testing.T) {
|
||||
_, s, closeFn := framework.RunAMaster(nil)
|
||||
defer closeFn()
|
||||
|
||||
resp, err := http.Get(s.URL + "/api/")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error getting api: %v", err)
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
t.Fatalf("got status %v instead of 200 OK", resp.StatusCode)
|
||||
}
|
||||
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
if resp.Header.Get("Content-Type") != "application/json" {
|
||||
t.Errorf("unexpected content: %s", body)
|
||||
}
|
||||
if err := json.Unmarshal(body, &map[string]interface{}{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("GET", s.URL+"/api/", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
req.Header.Set("Accept", "application/yaml")
|
||||
resp, err = http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
body, _ = ioutil.ReadAll(resp.Body)
|
||||
if resp.Header.Get("Content-Type") != "application/yaml" {
|
||||
t.Errorf("unexpected content: %s", body)
|
||||
}
|
||||
t.Logf("body: %s", body)
|
||||
if err := yaml.Unmarshal(body, &map[string]interface{}{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
req, err = http.NewRequest("GET", s.URL+"/api/", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
req.Header.Set("Accept", "application/json, application/yaml")
|
||||
resp, err = http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
body, _ = ioutil.ReadAll(resp.Body)
|
||||
if resp.Header.Get("Content-Type") != "application/json" {
|
||||
t.Errorf("unexpected content: %s", body)
|
||||
}
|
||||
t.Logf("body: %s", body)
|
||||
if err := yaml.Unmarshal(body, &map[string]interface{}{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
req, err = http.NewRequest("GET", s.URL+"/api/", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
req.Header.Set("Accept", "application") // not a valid media type
|
||||
resp, err = http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if resp.StatusCode != http.StatusNotAcceptable {
|
||||
t.Errorf("unexpected error from the server")
|
||||
}
|
||||
}
|
||||
|
||||
func countEndpoints(eps *api.Endpoints) int {
|
||||
count := 0
|
||||
for i := range eps.Subsets {
|
||||
count += len(eps.Subsets[i].Addresses) * len(eps.Subsets[i].Ports)
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
func TestMasterService(t *testing.T) {
|
||||
_, s, closeFn := framework.RunAMaster(framework.NewIntegrationTestMasterConfig())
|
||||
defer closeFn()
|
||||
|
||||
client := clientset.NewForConfigOrDie(&restclient.Config{Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: testapi.Groups[api.GroupName].GroupVersion()}})
|
||||
|
||||
err := wait.Poll(time.Second, time.Minute, func() (bool, error) {
|
||||
svcList, err := client.Core().Services(metav1.NamespaceDefault).List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
return false, nil
|
||||
}
|
||||
found := false
|
||||
for i := range svcList.Items {
|
||||
if svcList.Items[i].Name == "kubernetes" {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if found {
|
||||
ep, err := client.Core().Endpoints(metav1.NamespaceDefault).Get("kubernetes", metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
if countEndpoints(ep) == 0 {
|
||||
return false, fmt.Errorf("no endpoints for kubernetes service: %v", ep)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestServiceAlloc(t *testing.T) {
|
||||
cfg := framework.NewIntegrationTestMasterConfig()
|
||||
_, cidr, err := net.ParseCIDR("192.168.0.0/29")
|
||||
if err != nil {
|
||||
t.Fatalf("bad cidr: %v", err)
|
||||
}
|
||||
cfg.ExtraConfig.ServiceIPRange = *cidr
|
||||
_, s, closeFn := framework.RunAMaster(cfg)
|
||||
defer closeFn()
|
||||
|
||||
client := clientset.NewForConfigOrDie(&restclient.Config{Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: testapi.Groups[api.GroupName].GroupVersion()}})
|
||||
|
||||
svc := func(i int) *api.Service {
|
||||
return &api.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: fmt.Sprintf("svc-%v", i),
|
||||
},
|
||||
Spec: api.ServiceSpec{
|
||||
Type: api.ServiceTypeClusterIP,
|
||||
Ports: []api.ServicePort{
|
||||
{Port: 80},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Wait until the default "kubernetes" service is created.
|
||||
if err = wait.Poll(250*time.Millisecond, time.Minute, func() (bool, error) {
|
||||
_, err := client.Core().Services(metav1.NamespaceDefault).Get("kubernetes", metav1.GetOptions{})
|
||||
if err != nil && !errors.IsNotFound(err) {
|
||||
return false, err
|
||||
}
|
||||
return !errors.IsNotFound(err), nil
|
||||
}); err != nil {
|
||||
t.Fatalf("creating kubernetes service timed out")
|
||||
}
|
||||
|
||||
// make 5 more services to take up all IPs
|
||||
for i := 0; i < 5; i++ {
|
||||
if _, err := client.Core().Services(metav1.NamespaceDefault).Create(svc(i)); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Make another service. It will fail because we're out of cluster IPs
|
||||
if _, err := client.Core().Services(metav1.NamespaceDefault).Create(svc(8)); err != nil {
|
||||
if !strings.Contains(err.Error(), "range is full") {
|
||||
t.Errorf("unexpected error text: %v", err)
|
||||
}
|
||||
} else {
|
||||
svcs, err := client.Core().Services(metav1.NamespaceAll).List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected success, and error getting the services: %v", err)
|
||||
}
|
||||
allIPs := []string{}
|
||||
for _, s := range svcs.Items {
|
||||
allIPs = append(allIPs, s.Spec.ClusterIP)
|
||||
}
|
||||
t.Fatalf("unexpected creation success. The following IPs exist: %#v. It should only be possible to allocate 2 IP addresses in this cluster.\n\n%#v", allIPs, svcs)
|
||||
}
|
||||
|
||||
// Delete the first service.
|
||||
if err := client.Core().Services(metav1.NamespaceDefault).Delete(svc(1).ObjectMeta.Name, nil); err != nil {
|
||||
t.Fatalf("got unexpected error: %v", err)
|
||||
}
|
||||
|
||||
// This time creating the second service should work.
|
||||
if _, err := client.Core().Services(metav1.NamespaceDefault).Create(svc(8)); err != nil {
|
||||
t.Fatalf("got unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestUpdateNodeObjects represents a simple version of the behavior of node checkins at steady
|
||||
// state. This test allows for easy profiling of a realistic master scenario for baseline CPU
|
||||
// in very large clusters. It is disabled by default - start a kube-apiserver and pass
|
||||
// UPDATE_NODE_APISERVER as the host value.
|
||||
func TestUpdateNodeObjects(t *testing.T) {
|
||||
server := os.Getenv("UPDATE_NODE_APISERVER")
|
||||
if len(server) == 0 {
|
||||
t.Skip("UPDATE_NODE_APISERVER is not set")
|
||||
}
|
||||
c := clienttypedv1.NewForConfigOrDie(&restclient.Config{
|
||||
QPS: 10000,
|
||||
Host: server,
|
||||
ContentConfig: restclient.ContentConfig{
|
||||
AcceptContentTypes: "application/vnd.kubernetes.protobuf",
|
||||
ContentType: "application/vnd.kubernetes.protobuf",
|
||||
},
|
||||
})
|
||||
|
||||
nodes := 400
|
||||
listers := 5
|
||||
watchers := 50
|
||||
iterations := 10000
|
||||
|
||||
for i := 0; i < nodes*6; i++ {
|
||||
c.Nodes().Delete(fmt.Sprintf("node-%d", i), nil)
|
||||
_, err := c.Nodes().Create(&v1.Node{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: fmt.Sprintf("node-%d", i),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
for k := 0; k < listers; k++ {
|
||||
go func(lister int) {
|
||||
for i := 0; i < iterations; i++ {
|
||||
_, err := c.Nodes().List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
fmt.Printf("[list:%d] error after %d: %v\n", lister, i, err)
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Duration(lister)*10*time.Millisecond + 1500*time.Millisecond)
|
||||
}
|
||||
}(k)
|
||||
}
|
||||
|
||||
for k := 0; k < watchers; k++ {
|
||||
go func(lister int) {
|
||||
w, err := c.Nodes().Watch(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
fmt.Printf("[watch:%d] error: %v", lister, err)
|
||||
return
|
||||
}
|
||||
i := 0
|
||||
for r := range w.ResultChan() {
|
||||
i++
|
||||
if _, ok := r.Object.(*v1.Node); !ok {
|
||||
fmt.Printf("[watch:%d] unexpected object after %d: %#v\n", lister, i, r)
|
||||
}
|
||||
if i%100 == 0 {
|
||||
fmt.Printf("[watch:%d] iteration %d ...\n", lister, i)
|
||||
}
|
||||
}
|
||||
fmt.Printf("[watch:%d] done\n", lister)
|
||||
}(k)
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(nodes - listers)
|
||||
|
||||
for j := 0; j < nodes; j++ {
|
||||
go func(node int) {
|
||||
var lastCount int
|
||||
for i := 0; i < iterations; i++ {
|
||||
if i%100 == 0 {
|
||||
fmt.Printf("[%d] iteration %d ...\n", node, i)
|
||||
}
|
||||
if i%20 == 0 {
|
||||
_, err := c.Nodes().List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
fmt.Printf("[%d] error after %d: %v\n", node, i, err)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
r, err := c.Nodes().List(metav1.ListOptions{
|
||||
FieldSelector: fmt.Sprintf("metadata.name=node-%d", node),
|
||||
ResourceVersion: "0",
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Printf("[%d] error after %d: %v\n", node, i, err)
|
||||
break
|
||||
}
|
||||
if len(r.Items) != 1 {
|
||||
fmt.Printf("[%d] error after %d: unexpected list count\n", node, i)
|
||||
break
|
||||
}
|
||||
|
||||
n, err := c.Nodes().Get(fmt.Sprintf("node-%d", node), metav1.GetOptions{})
|
||||
if err != nil {
|
||||
fmt.Printf("[%d] error after %d: %v\n", node, i, err)
|
||||
break
|
||||
}
|
||||
if len(n.Status.Conditions) != lastCount {
|
||||
fmt.Printf("[%d] worker set %d, read %d conditions\n", node, lastCount, len(n.Status.Conditions))
|
||||
break
|
||||
}
|
||||
previousCount := lastCount
|
||||
switch {
|
||||
case i%4 == 0:
|
||||
lastCount = 1
|
||||
n.Status.Conditions = []v1.NodeCondition{
|
||||
{
|
||||
Type: v1.NodeReady,
|
||||
Status: v1.ConditionTrue,
|
||||
Reason: "foo",
|
||||
},
|
||||
}
|
||||
case i%4 == 1:
|
||||
lastCount = 2
|
||||
n.Status.Conditions = []v1.NodeCondition{
|
||||
{
|
||||
Type: v1.NodeReady,
|
||||
Status: v1.ConditionFalse,
|
||||
Reason: "foo",
|
||||
},
|
||||
{
|
||||
Type: v1.NodeDiskPressure,
|
||||
Status: v1.ConditionTrue,
|
||||
Reason: "bar",
|
||||
},
|
||||
}
|
||||
case i%4 == 1:
|
||||
lastCount = 0
|
||||
n.Status.Conditions = nil
|
||||
}
|
||||
if _, err := c.Nodes().UpdateStatus(n); err != nil {
|
||||
if !errors.IsConflict(err) {
|
||||
fmt.Printf("[%d] error after %d: %v\n", node, i, err)
|
||||
break
|
||||
}
|
||||
lastCount = previousCount
|
||||
}
|
||||
}
|
||||
wg.Done()
|
||||
fmt.Printf("[%d] done\n", node)
|
||||
}(j)
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
269
vendor/k8s.io/kubernetes/test/integration/master/transformation_testcase.go
generated
vendored
Normal file
269
vendor/k8s.io/kubernetes/test/integration/master/transformation_testcase.go
generated
vendored
Normal file
@@ -0,0 +1,269 @@
|
||||
/*
|
||||
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 master
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/coreos/etcd/clientv3"
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apiserver/pkg/server/options/encryptionconfig"
|
||||
"k8s.io/apiserver/pkg/storage/storagebackend"
|
||||
"k8s.io/apiserver/pkg/storage/value"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing"
|
||||
"k8s.io/kubernetes/test/integration"
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
)
|
||||
|
||||
const (
|
||||
secretKey = "api_key"
|
||||
secretVal = "086a7ffc-0225-11e8-ba89-0ed5f89f718b"
|
||||
encryptionConfigFileName = "encryption.conf"
|
||||
testNamespace = "secret-encryption-test"
|
||||
testSecret = "test-secret"
|
||||
metricsPrefix = "apiserver_storage_"
|
||||
)
|
||||
|
||||
type unSealSecret func(cipherText []byte, ctx value.Context, config encryptionconfig.ProviderConfig) ([]byte, error)
|
||||
|
||||
type transformTest struct {
|
||||
logger kubeapiservertesting.Logger
|
||||
storageConfig *storagebackend.Config
|
||||
configDir string
|
||||
transformerConfig string
|
||||
kubeAPIServer kubeapiservertesting.TestServer
|
||||
restClient *kubernetes.Clientset
|
||||
ns *corev1.Namespace
|
||||
secret *corev1.Secret
|
||||
}
|
||||
|
||||
func newTransformTest(l kubeapiservertesting.Logger, transformerConfigYAML string) (*transformTest, error) {
|
||||
e := transformTest{
|
||||
logger: l,
|
||||
transformerConfig: transformerConfigYAML,
|
||||
storageConfig: framework.SharedEtcd(),
|
||||
}
|
||||
|
||||
var err error
|
||||
if transformerConfigYAML != "" {
|
||||
if e.configDir, err = e.createEncryptionConfig(); err != nil {
|
||||
return nil, fmt.Errorf("error while creating KubeAPIServer encryption config: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if e.kubeAPIServer, err = kubeapiservertesting.StartTestServer(l, nil, e.getEncryptionOptions(), e.storageConfig); err != nil {
|
||||
return nil, fmt.Errorf("failed to start KubeAPI server: %v", err)
|
||||
}
|
||||
|
||||
if e.restClient, err = kubernetes.NewForConfig(e.kubeAPIServer.ClientConfig); err != nil {
|
||||
return nil, fmt.Errorf("error while creating rest client: %v", err)
|
||||
}
|
||||
|
||||
if e.ns, err = e.createNamespace(testNamespace); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if e.secret, err = e.createSecret(testSecret, e.ns.Name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &e, nil
|
||||
}
|
||||
|
||||
func (e *transformTest) cleanUp() {
|
||||
os.RemoveAll(e.configDir)
|
||||
e.restClient.CoreV1().Namespaces().Delete(e.ns.Name, metav1.NewDeleteOptions(0))
|
||||
e.kubeAPIServer.TearDownFn()
|
||||
}
|
||||
|
||||
func (e *transformTest) run(unSealSecretFunc unSealSecret, expectedEnvelopePrefix string) {
|
||||
response, err := e.readRawRecordFromETCD(e.getETCDPath())
|
||||
if err != nil {
|
||||
e.logger.Errorf("failed to read from etcd: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if !bytes.HasPrefix(response.Kvs[0].Value, []byte(expectedEnvelopePrefix)) {
|
||||
e.logger.Errorf("expected secret to be prefixed with %s, but got %s",
|
||||
expectedEnvelopePrefix, response.Kvs[0].Value)
|
||||
return
|
||||
}
|
||||
|
||||
// etcd path of the key is used as the authenticated context - need to pass it to decrypt
|
||||
ctx := value.DefaultContext([]byte(e.getETCDPath()))
|
||||
// Envelope header precedes the payload
|
||||
sealedData := response.Kvs[0].Value[len(expectedEnvelopePrefix):]
|
||||
transformerConfig, err := e.getEncryptionConfig()
|
||||
if err != nil {
|
||||
e.logger.Errorf("failed to parse transformer config: %v", err)
|
||||
}
|
||||
v, err := unSealSecretFunc(sealedData, ctx, *transformerConfig)
|
||||
if err != nil {
|
||||
e.logger.Errorf("failed to unseal secret: %v", err)
|
||||
return
|
||||
}
|
||||
if !strings.Contains(string(v), secretVal) {
|
||||
e.logger.Errorf("expected %q after decryption, but got %q", secretVal, string(v))
|
||||
}
|
||||
|
||||
// Secrets should be un-enveloped on direct reads from Kube API Server.
|
||||
s, err := e.restClient.CoreV1().Secrets(testNamespace).Get(testSecret, metav1.GetOptions{})
|
||||
if secretVal != string(s.Data[secretKey]) {
|
||||
e.logger.Errorf("expected %s from KubeAPI, but got %s", secretVal, string(s.Data[secretKey]))
|
||||
}
|
||||
}
|
||||
|
||||
func (e *transformTest) benchmark(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := e.createSecret(e.secret.Name+strconv.Itoa(i), e.ns.Name)
|
||||
if err != nil {
|
||||
b.Fatalf("failed to create a secret: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (e *transformTest) getETCDPath() string {
|
||||
return fmt.Sprintf("/%s/secrets/%s/%s", e.storageConfig.Prefix, e.ns.Name, e.secret.Name)
|
||||
}
|
||||
|
||||
func (e *transformTest) getRawSecretFromETCD() ([]byte, error) {
|
||||
secretETCDPath := e.getETCDPath()
|
||||
etcdResponse, err := e.readRawRecordFromETCD(secretETCDPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read %s from etcd: %v", secretETCDPath, err)
|
||||
}
|
||||
return etcdResponse.Kvs[0].Value, nil
|
||||
}
|
||||
|
||||
func (e *transformTest) getEncryptionOptions() []string {
|
||||
if e.transformerConfig != "" {
|
||||
return []string{"--experimental-encryption-provider-config", path.Join(e.configDir, encryptionConfigFileName)}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *transformTest) createEncryptionConfig() (string, error) {
|
||||
tempDir, err := ioutil.TempDir("", "secrets-encryption-test")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to create temp directory: %v", err)
|
||||
}
|
||||
|
||||
encryptionConfig := path.Join(tempDir, encryptionConfigFileName)
|
||||
|
||||
if err := ioutil.WriteFile(encryptionConfig, []byte(e.transformerConfig), 0644); err != nil {
|
||||
os.RemoveAll(tempDir)
|
||||
return "", fmt.Errorf("error while writing encryption config: %v", err)
|
||||
}
|
||||
|
||||
return tempDir, nil
|
||||
}
|
||||
|
||||
func (e *transformTest) getEncryptionConfig() (*encryptionconfig.ProviderConfig, error) {
|
||||
var config encryptionconfig.EncryptionConfig
|
||||
err := yaml.Unmarshal([]byte(e.transformerConfig), &config)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to extract transformer key: %v", err)
|
||||
}
|
||||
|
||||
return &config.Resources[0].Providers[0], nil
|
||||
}
|
||||
|
||||
func (e *transformTest) createNamespace(name string) (*corev1.Namespace, error) {
|
||||
ns := &corev1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
}
|
||||
|
||||
if _, err := e.restClient.CoreV1().Namespaces().Create(ns); err != nil {
|
||||
return nil, fmt.Errorf("unable to create testing namespace %v", err)
|
||||
}
|
||||
|
||||
return ns, nil
|
||||
}
|
||||
|
||||
func (e *transformTest) createSecret(name, namespace string) (*corev1.Secret, error) {
|
||||
secret := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
secretKey: []byte(secretVal),
|
||||
},
|
||||
}
|
||||
if _, err := e.restClient.CoreV1().Secrets(secret.Namespace).Create(secret); err != nil {
|
||||
return nil, fmt.Errorf("error while writing secret: %v", err)
|
||||
}
|
||||
|
||||
return secret, nil
|
||||
}
|
||||
|
||||
func (e *transformTest) readRawRecordFromETCD(path string) (*clientv3.GetResponse, error) {
|
||||
etcdClient, err := integration.GetEtcdKVClient(e.kubeAPIServer.ServerOpts.Etcd.StorageConfig)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create etcd client: %v", err)
|
||||
}
|
||||
response, err := etcdClient.Get(context.Background(), path, clientv3.WithPrefix())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to retrieve secret from etcd %v", err)
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (e *transformTest) printMetrics() error {
|
||||
e.logger.Logf("Transformation Metrics:")
|
||||
metrics, err := prometheus.DefaultGatherer.Gather()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to gather metrics: %s", err)
|
||||
}
|
||||
|
||||
for _, mf := range metrics {
|
||||
if strings.HasPrefix(*mf.Name, metricsPrefix) {
|
||||
e.logger.Logf("%s", *mf.Name)
|
||||
for _, metric := range mf.GetMetric() {
|
||||
e.logger.Logf("%v", metric)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func contains(s []string, e string) bool {
|
||||
for _, a := range s {
|
||||
if a == e {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
47
vendor/k8s.io/kubernetes/test/integration/metrics/BUILD
generated
vendored
Normal file
47
vendor/k8s.io/kubernetes/test/integration/metrics/BUILD
generated
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["doc.go"],
|
||||
importpath = "k8s.io/kubernetes/test/integration/metrics",
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
size = "large",
|
||||
srcs = [
|
||||
"main_test.go",
|
||||
"metrics_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
tags = ["integration"],
|
||||
deps = [
|
||||
"//test/integration/framework:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/github.com/golang/protobuf/proto:go_default_library",
|
||||
"//vendor/github.com/prometheus/client_model/go:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||
],
|
||||
)
|
||||
17
vendor/k8s.io/kubernetes/test/integration/metrics/doc.go
generated
vendored
Normal file
17
vendor/k8s.io/kubernetes/test/integration/metrics/doc.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
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 metrics
|
||||
27
vendor/k8s.io/kubernetes/test/integration/metrics/main_test.go
generated
vendored
Normal file
27
vendor/k8s.io/kubernetes/test/integration/metrics/main_test.go
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
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 metrics
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
framework.EtcdMain(m.Run)
|
||||
}
|
||||
126
vendor/k8s.io/kubernetes/test/integration/metrics/metrics_test.go
generated
vendored
Normal file
126
vendor/k8s.io/kubernetes/test/integration/metrics/metrics_test.go
generated
vendored
Normal file
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
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 metrics
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/golang/protobuf/proto"
|
||||
prometheuspb "github.com/prometheus/client_model/go"
|
||||
)
|
||||
|
||||
const scrapeRequestHeader = "application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=compact-text"
|
||||
|
||||
func scrapeMetrics(s *httptest.Server) ([]*prometheuspb.MetricFamily, error) {
|
||||
req, err := http.NewRequest("GET", s.URL+"/metrics", nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Unable to create http request: %v", err)
|
||||
}
|
||||
// Ask the prometheus exporter for its text protocol buffer format, since it's
|
||||
// much easier to parse than its plain-text format. Don't use the serialized
|
||||
// proto representation since it uses a non-standard varint delimiter between
|
||||
// metric families.
|
||||
req.Header.Add("Accept", scrapeRequestHeader)
|
||||
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Unable to contact metrics endpoint of master: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != 200 {
|
||||
return nil, fmt.Errorf("Non-200 response trying to scrape metrics from master: %v", resp)
|
||||
}
|
||||
|
||||
// Each line in the response body should contain all the data for a single metric.
|
||||
var metrics []*prometheuspb.MetricFamily
|
||||
scanner := bufio.NewScanner(resp.Body)
|
||||
for scanner.Scan() {
|
||||
var metric prometheuspb.MetricFamily
|
||||
if err := proto.UnmarshalText(scanner.Text(), &metric); err != nil {
|
||||
return nil, fmt.Errorf("Failed to unmarshal line of metrics response: %v", err)
|
||||
}
|
||||
glog.V(4).Infof("Got metric %q", metric.GetName())
|
||||
metrics = append(metrics, &metric)
|
||||
}
|
||||
return metrics, nil
|
||||
}
|
||||
|
||||
func checkForExpectedMetrics(t *testing.T, metrics []*prometheuspb.MetricFamily, expectedMetrics []string) {
|
||||
foundMetrics := make(map[string]bool)
|
||||
for _, metric := range metrics {
|
||||
foundMetrics[metric.GetName()] = true
|
||||
}
|
||||
for _, expected := range expectedMetrics {
|
||||
if _, found := foundMetrics[expected]; !found {
|
||||
t.Errorf("Master metrics did not include expected metric %q", expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMasterProcessMetrics(t *testing.T) {
|
||||
if runtime.GOOS == "darwin" || runtime.GOOS == "windows" {
|
||||
t.Skipf("not supported on GOOS=%s", runtime.GOOS)
|
||||
}
|
||||
|
||||
_, s, closeFn := framework.RunAMaster(nil)
|
||||
defer closeFn()
|
||||
|
||||
metrics, err := scrapeMetrics(s)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
checkForExpectedMetrics(t, metrics, []string{
|
||||
"process_start_time_seconds",
|
||||
"process_cpu_seconds_total",
|
||||
"process_open_fds",
|
||||
"process_resident_memory_bytes",
|
||||
})
|
||||
}
|
||||
|
||||
func TestApiserverMetrics(t *testing.T) {
|
||||
_, s, closeFn := framework.RunAMaster(nil)
|
||||
defer closeFn()
|
||||
|
||||
// Make a request to the apiserver to ensure there's at least one data point
|
||||
// for the metrics we're expecting -- otherwise, they won't be exported.
|
||||
client := clientset.NewForConfigOrDie(&restclient.Config{Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}}})
|
||||
if _, err := client.Core().Pods(metav1.NamespaceDefault).List(metav1.ListOptions{}); err != nil {
|
||||
t.Fatalf("unexpected error getting pods: %v", err)
|
||||
}
|
||||
|
||||
metrics, err := scrapeMetrics(s)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
checkForExpectedMetrics(t, metrics, []string{
|
||||
"apiserver_request_count",
|
||||
"apiserver_request_latencies",
|
||||
})
|
||||
}
|
||||
38
vendor/k8s.io/kubernetes/test/integration/objectmeta/BUILD
generated
vendored
Normal file
38
vendor/k8s.io/kubernetes/test/integration/objectmeta/BUILD
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
size = "large",
|
||||
srcs = [
|
||||
"main_test.go",
|
||||
"objectmeta_test.go",
|
||||
],
|
||||
tags = ["integration"],
|
||||
deps = [
|
||||
"//test/integration/framework:go_default_library",
|
||||
"//vendor/github.com/stretchr/testify/assert:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
27
vendor/k8s.io/kubernetes/test/integration/objectmeta/main_test.go
generated
vendored
Normal file
27
vendor/k8s.io/kubernetes/test/integration/objectmeta/main_test.go
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
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 objectmeta
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
framework.EtcdMain(m.Run)
|
||||
}
|
||||
53
vendor/k8s.io/kubernetes/test/integration/objectmeta/objectmeta_test.go
generated
vendored
Normal file
53
vendor/k8s.io/kubernetes/test/integration/objectmeta/objectmeta_test.go
generated
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
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 objectmeta
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
)
|
||||
|
||||
func TestIgnoreClusterName(t *testing.T) {
|
||||
config := framework.NewMasterConfig()
|
||||
_, s, closeFn := framework.RunAMaster(config)
|
||||
defer closeFn()
|
||||
|
||||
client := clientset.NewForConfigOrDie(&restclient.Config{Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}}})
|
||||
ns := v1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-namespace",
|
||||
ClusterName: "cluster-name-to-ignore",
|
||||
},
|
||||
}
|
||||
nsNew, err := client.Core().Namespaces().Create(&ns)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, ns.Name, nsNew.Name)
|
||||
assert.Empty(t, nsNew.ClusterName)
|
||||
|
||||
nsNew, err = client.Core().Namespaces().Update(&ns)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, ns.Name, nsNew.Name)
|
||||
assert.Empty(t, nsNew.ClusterName)
|
||||
}
|
||||
34
vendor/k8s.io/kubernetes/test/integration/openshift/BUILD
generated
vendored
Normal file
34
vendor/k8s.io/kubernetes/test/integration/openshift/BUILD
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
size = "large",
|
||||
srcs = [
|
||||
"main_test.go",
|
||||
"openshift_test.go",
|
||||
],
|
||||
tags = ["integration"],
|
||||
deps = [
|
||||
"//pkg/master:go_default_library",
|
||||
"//test/integration/framework:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/server:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
27
vendor/k8s.io/kubernetes/test/integration/openshift/main_test.go
generated
vendored
Normal file
27
vendor/k8s.io/kubernetes/test/integration/openshift/main_test.go
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
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 openshift
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
framework.EtcdMain(m.Run)
|
||||
}
|
||||
40
vendor/k8s.io/kubernetes/test/integration/openshift/openshift_test.go
generated
vendored
Normal file
40
vendor/k8s.io/kubernetes/test/integration/openshift/openshift_test.go
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
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 openshift
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
genericapiserver "k8s.io/apiserver/pkg/server"
|
||||
"k8s.io/kubernetes/pkg/master"
|
||||
)
|
||||
|
||||
// This test references methods that OpenShift uses to customize the master on startup, that
|
||||
// are not referenced directly by a master.
|
||||
func TestMasterExportsSymbols(t *testing.T) {
|
||||
_ = &master.Config{
|
||||
GenericConfig: &genericapiserver.Config{
|
||||
EnableMetrics: true,
|
||||
},
|
||||
ExtraConfig: master.ExtraConfig{
|
||||
EnableLogsSupport: false,
|
||||
},
|
||||
}
|
||||
_ = &master.Master{
|
||||
GenericAPIServer: &genericapiserver.GenericAPIServer{},
|
||||
}
|
||||
}
|
||||
38
vendor/k8s.io/kubernetes/test/integration/pods/BUILD
generated
vendored
Normal file
38
vendor/k8s.io/kubernetes/test/integration/pods/BUILD
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
size = "large",
|
||||
srcs = [
|
||||
"main_test.go",
|
||||
"pods_test.go",
|
||||
],
|
||||
tags = ["integration"],
|
||||
deps = [
|
||||
"//test/integration:go_default_library",
|
||||
"//test/integration/framework:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
27
vendor/k8s.io/kubernetes/test/integration/pods/main_test.go
generated
vendored
Normal file
27
vendor/k8s.io/kubernetes/test/integration/pods/main_test.go
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
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 pods
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
framework.EtcdMain(m.Run)
|
||||
}
|
||||
181
vendor/k8s.io/kubernetes/test/integration/pods/pods_test.go
generated
vendored
Normal file
181
vendor/k8s.io/kubernetes/test/integration/pods/pods_test.go
generated
vendored
Normal file
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
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 pods
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/kubernetes/test/integration"
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
)
|
||||
|
||||
func TestPodUpdateActiveDeadlineSeconds(t *testing.T) {
|
||||
_, s, closeFn := framework.RunAMaster(nil)
|
||||
defer closeFn()
|
||||
|
||||
ns := framework.CreateTestingNamespace("pod-activedeadline-update", s, t)
|
||||
defer framework.DeleteTestingNamespace(ns, s, t)
|
||||
|
||||
client := clientset.NewForConfigOrDie(&restclient.Config{Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}}})
|
||||
|
||||
var (
|
||||
iZero = int64(0)
|
||||
i30 = int64(30)
|
||||
i60 = int64(60)
|
||||
iNeg = int64(-1)
|
||||
)
|
||||
|
||||
prototypePod := func() *v1.Pod {
|
||||
return &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "xxx",
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Name: "fake-name",
|
||||
Image: "fakeimage",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
original *int64
|
||||
update *int64
|
||||
valid bool
|
||||
}{
|
||||
{
|
||||
name: "no change, nil",
|
||||
original: nil,
|
||||
update: nil,
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
name: "no change, set",
|
||||
original: &i30,
|
||||
update: &i30,
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
name: "change to positive from nil",
|
||||
original: nil,
|
||||
update: &i60,
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
name: "change to smaller positive",
|
||||
original: &i60,
|
||||
update: &i30,
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
name: "change to larger positive",
|
||||
original: &i30,
|
||||
update: &i60,
|
||||
valid: false,
|
||||
},
|
||||
{
|
||||
name: "change to negative from positive",
|
||||
original: &i30,
|
||||
update: &iNeg,
|
||||
valid: false,
|
||||
},
|
||||
{
|
||||
name: "change to negative from nil",
|
||||
original: nil,
|
||||
update: &iNeg,
|
||||
valid: false,
|
||||
},
|
||||
// zero is not allowed, must be a positive integer
|
||||
{
|
||||
name: "change to zero from positive",
|
||||
original: &i30,
|
||||
update: &iZero,
|
||||
valid: false,
|
||||
},
|
||||
{
|
||||
name: "change to nil from positive",
|
||||
original: &i30,
|
||||
update: nil,
|
||||
valid: false,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range cases {
|
||||
pod := prototypePod()
|
||||
pod.Spec.ActiveDeadlineSeconds = tc.original
|
||||
pod.ObjectMeta.Name = fmt.Sprintf("activedeadlineseconds-test-%v", i)
|
||||
|
||||
if _, err := client.Core().Pods(ns.Name).Create(pod); err != nil {
|
||||
t.Errorf("Failed to create pod: %v", err)
|
||||
}
|
||||
|
||||
pod.Spec.ActiveDeadlineSeconds = tc.update
|
||||
|
||||
_, err := client.Core().Pods(ns.Name).Update(pod)
|
||||
if tc.valid && err != nil {
|
||||
t.Errorf("%v: failed to update pod: %v", tc.name, err)
|
||||
} else if !tc.valid && err == nil {
|
||||
t.Errorf("%v: unexpected allowed update to pod", tc.name)
|
||||
}
|
||||
|
||||
integration.DeletePodOrErrorf(t, client, ns.Name, pod.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPodReadOnlyFilesystem(t *testing.T) {
|
||||
_, s, closeFn := framework.RunAMaster(nil)
|
||||
defer closeFn()
|
||||
|
||||
isReadOnly := true
|
||||
ns := framework.CreateTestingNamespace("pod-readonly-root", s, t)
|
||||
defer framework.DeleteTestingNamespace(ns, s, t)
|
||||
|
||||
client := clientset.NewForConfigOrDie(&restclient.Config{Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}}})
|
||||
|
||||
pod := &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "xxx",
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Name: "fake-name",
|
||||
Image: "fakeimage",
|
||||
SecurityContext: &v1.SecurityContext{
|
||||
ReadOnlyRootFilesystem: &isReadOnly,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if _, err := client.Core().Pods(ns.Name).Create(pod); err != nil {
|
||||
t.Errorf("Failed to create pod: %v", err)
|
||||
}
|
||||
|
||||
integration.DeletePodOrErrorf(t, client, ns.Name, pod.Name)
|
||||
}
|
||||
53
vendor/k8s.io/kubernetes/test/integration/quota/BUILD
generated
vendored
Normal file
53
vendor/k8s.io/kubernetes/test/integration/quota/BUILD
generated
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
size = "large",
|
||||
srcs = [
|
||||
"main_test.go",
|
||||
"quota_test.go",
|
||||
],
|
||||
tags = ["integration"],
|
||||
deps = [
|
||||
"//pkg/client/clientset_generated/internalclientset:go_default_library",
|
||||
"//pkg/client/informers/informers_generated/internalversion:go_default_library",
|
||||
"//pkg/controller:go_default_library",
|
||||
"//pkg/controller/replication:go_default_library",
|
||||
"//pkg/controller/resourcequota:go_default_library",
|
||||
"//pkg/quota/generic:go_default_library",
|
||||
"//pkg/quota/install:go_default_library",
|
||||
"//plugin/pkg/admission/resourcequota:go_default_library",
|
||||
"//plugin/pkg/admission/resourcequota/apis/resourcequota:go_default_library",
|
||||
"//test/integration/framework:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/fields:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/watch:go_default_library",
|
||||
"//vendor/k8s.io/client-go/informers:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/record:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
27
vendor/k8s.io/kubernetes/test/integration/quota/main_test.go
generated
vendored
Normal file
27
vendor/k8s.io/kubernetes/test/integration/quota/main_test.go
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
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 quota
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
framework.EtcdMain(m.Run)
|
||||
}
|
||||
371
vendor/k8s.io/kubernetes/test/integration/quota/quota_test.go
generated
vendored
Normal file
371
vendor/k8s.io/kubernetes/test/integration/quota/quota_test.go
generated
vendored
Normal file
@@ -0,0 +1,371 @@
|
||||
/*
|
||||
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 quota
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
"k8s.io/client-go/informers"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/record"
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||
internalinformers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion"
|
||||
"k8s.io/kubernetes/pkg/controller"
|
||||
replicationcontroller "k8s.io/kubernetes/pkg/controller/replication"
|
||||
resourcequotacontroller "k8s.io/kubernetes/pkg/controller/resourcequota"
|
||||
"k8s.io/kubernetes/pkg/quota/generic"
|
||||
quotainstall "k8s.io/kubernetes/pkg/quota/install"
|
||||
"k8s.io/kubernetes/plugin/pkg/admission/resourcequota"
|
||||
resourcequotaapi "k8s.io/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota"
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
)
|
||||
|
||||
// 1.2 code gets:
|
||||
// quota_test.go:95: Took 4.218619579s to scale up without quota
|
||||
// quota_test.go:199: unexpected error: timed out waiting for the condition, ended with 342 pods (1 minute)
|
||||
// 1.3+ code gets:
|
||||
// quota_test.go:100: Took 4.196205966s to scale up without quota
|
||||
// quota_test.go:115: Took 12.021640372s to scale up with quota
|
||||
func TestQuota(t *testing.T) {
|
||||
// Set up a master
|
||||
h := &framework.MasterHolder{Initialized: make(chan struct{})}
|
||||
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
<-h.Initialized
|
||||
h.M.GenericAPIServer.Handler.ServeHTTP(w, req)
|
||||
}))
|
||||
|
||||
admissionCh := make(chan struct{})
|
||||
clientset := clientset.NewForConfigOrDie(&restclient.Config{QPS: -1, Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}}})
|
||||
internalClientset := internalclientset.NewForConfigOrDie(&restclient.Config{QPS: -1, Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}}})
|
||||
config := &resourcequotaapi.Configuration{}
|
||||
admission, err := resourcequota.NewResourceQuota(config, 5, admissionCh)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
admission.SetInternalKubeClientSet(internalClientset)
|
||||
internalInformers := internalinformers.NewSharedInformerFactory(internalClientset, controller.NoResyncPeriodFunc())
|
||||
admission.SetInternalKubeInformerFactory(internalInformers)
|
||||
qca := quotainstall.NewQuotaConfigurationForAdmission()
|
||||
admission.SetQuotaConfiguration(qca)
|
||||
defer close(admissionCh)
|
||||
|
||||
masterConfig := framework.NewIntegrationTestMasterConfig()
|
||||
masterConfig.GenericConfig.AdmissionControl = admission
|
||||
_, _, closeFn := framework.RunAMasterUsingServer(masterConfig, s, h)
|
||||
defer closeFn()
|
||||
|
||||
ns := framework.CreateTestingNamespace("quotaed", s, t)
|
||||
defer framework.DeleteTestingNamespace(ns, s, t)
|
||||
ns2 := framework.CreateTestingNamespace("non-quotaed", s, t)
|
||||
defer framework.DeleteTestingNamespace(ns2, s, t)
|
||||
|
||||
controllerCh := make(chan struct{})
|
||||
defer close(controllerCh)
|
||||
|
||||
informers := informers.NewSharedInformerFactory(clientset, controller.NoResyncPeriodFunc())
|
||||
rm := replicationcontroller.NewReplicationManager(
|
||||
informers.Core().V1().Pods(),
|
||||
informers.Core().V1().ReplicationControllers(),
|
||||
clientset,
|
||||
replicationcontroller.BurstReplicas,
|
||||
)
|
||||
rm.SetEventRecorder(&record.FakeRecorder{})
|
||||
go rm.Run(3, controllerCh)
|
||||
|
||||
discoveryFunc := clientset.Discovery().ServerPreferredNamespacedResources
|
||||
listerFuncForResource := generic.ListerFuncForResourceFunc(informers.ForResource)
|
||||
qc := quotainstall.NewQuotaConfigurationForControllers(listerFuncForResource)
|
||||
informersStarted := make(chan struct{})
|
||||
resourceQuotaControllerOptions := &resourcequotacontroller.ResourceQuotaControllerOptions{
|
||||
QuotaClient: clientset.Core(),
|
||||
ResourceQuotaInformer: informers.Core().V1().ResourceQuotas(),
|
||||
ResyncPeriod: controller.NoResyncPeriodFunc,
|
||||
InformerFactory: informers,
|
||||
ReplenishmentResyncPeriod: controller.NoResyncPeriodFunc,
|
||||
DiscoveryFunc: discoveryFunc,
|
||||
IgnoredResourcesFunc: qc.IgnoredResources,
|
||||
InformersStarted: informersStarted,
|
||||
Registry: generic.NewRegistry(qc.Evaluators()),
|
||||
}
|
||||
resourceQuotaController, err := resourcequotacontroller.NewResourceQuotaController(resourceQuotaControllerOptions)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected err: %v", err)
|
||||
}
|
||||
go resourceQuotaController.Run(2, controllerCh)
|
||||
|
||||
// Periodically the quota controller to detect new resource types
|
||||
go resourceQuotaController.Sync(discoveryFunc, 30*time.Second, controllerCh)
|
||||
|
||||
internalInformers.Start(controllerCh)
|
||||
informers.Start(controllerCh)
|
||||
close(informersStarted)
|
||||
|
||||
startTime := time.Now()
|
||||
scale(t, ns2.Name, clientset)
|
||||
endTime := time.Now()
|
||||
t.Logf("Took %v to scale up without quota", endTime.Sub(startTime))
|
||||
|
||||
quota := &v1.ResourceQuota{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "quota",
|
||||
Namespace: ns.Name,
|
||||
},
|
||||
Spec: v1.ResourceQuotaSpec{
|
||||
Hard: v1.ResourceList{
|
||||
v1.ResourcePods: resource.MustParse("1000"),
|
||||
},
|
||||
},
|
||||
}
|
||||
waitForQuota(t, quota, clientset)
|
||||
|
||||
startTime = time.Now()
|
||||
scale(t, "quotaed", clientset)
|
||||
endTime = time.Now()
|
||||
t.Logf("Took %v to scale up with quota", endTime.Sub(startTime))
|
||||
}
|
||||
|
||||
func waitForQuota(t *testing.T, quota *v1.ResourceQuota, clientset *clientset.Clientset) {
|
||||
w, err := clientset.Core().ResourceQuotas(quota.Namespace).Watch(metav1.SingleObject(metav1.ObjectMeta{Name: quota.Name}))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if _, err := clientset.Core().ResourceQuotas(quota.Namespace).Create(quota); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
_, err = watch.Until(1*time.Minute, w, func(event watch.Event) (bool, error) {
|
||||
switch event.Type {
|
||||
case watch.Modified:
|
||||
default:
|
||||
return false, nil
|
||||
}
|
||||
switch cast := event.Object.(type) {
|
||||
case *v1.ResourceQuota:
|
||||
if len(cast.Status.Hard) > 0 {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func scale(t *testing.T, namespace string, clientset *clientset.Clientset) {
|
||||
target := int32(100)
|
||||
rc := &v1.ReplicationController{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: v1.ReplicationControllerSpec{
|
||||
Replicas: &target,
|
||||
Selector: map[string]string{"foo": "bar"},
|
||||
Template: &v1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Name: "container",
|
||||
Image: "busybox",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
w, err := clientset.Core().ReplicationControllers(namespace).Watch(metav1.SingleObject(metav1.ObjectMeta{Name: rc.Name}))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if _, err := clientset.Core().ReplicationControllers(namespace).Create(rc); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
_, err = watch.Until(3*time.Minute, w, func(event watch.Event) (bool, error) {
|
||||
switch event.Type {
|
||||
case watch.Modified:
|
||||
default:
|
||||
return false, nil
|
||||
}
|
||||
|
||||
switch cast := event.Object.(type) {
|
||||
case *v1.ReplicationController:
|
||||
fmt.Printf("Found %v of %v replicas\n", int(cast.Status.Replicas), target)
|
||||
if cast.Status.Replicas == target {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
})
|
||||
if err != nil {
|
||||
pods, _ := clientset.Core().Pods(namespace).List(metav1.ListOptions{LabelSelector: labels.Everything().String(), FieldSelector: fields.Everything().String()})
|
||||
t.Fatalf("unexpected error: %v, ended with %v pods", err, len(pods.Items))
|
||||
}
|
||||
}
|
||||
|
||||
func TestQuotaLimitedResourceDenial(t *testing.T) {
|
||||
// Set up a master
|
||||
h := &framework.MasterHolder{Initialized: make(chan struct{})}
|
||||
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
<-h.Initialized
|
||||
h.M.GenericAPIServer.Handler.ServeHTTP(w, req)
|
||||
}))
|
||||
|
||||
admissionCh := make(chan struct{})
|
||||
clientset := clientset.NewForConfigOrDie(&restclient.Config{QPS: -1, Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}}})
|
||||
internalClientset := internalclientset.NewForConfigOrDie(&restclient.Config{QPS: -1, Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}}})
|
||||
|
||||
// stop creation of a pod resource unless there is a quota
|
||||
config := &resourcequotaapi.Configuration{
|
||||
LimitedResources: []resourcequotaapi.LimitedResource{
|
||||
{
|
||||
Resource: "pods",
|
||||
MatchContains: []string{"pods"},
|
||||
},
|
||||
},
|
||||
}
|
||||
qca := quotainstall.NewQuotaConfigurationForAdmission()
|
||||
admission, err := resourcequota.NewResourceQuota(config, 5, admissionCh)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
admission.SetInternalKubeClientSet(internalClientset)
|
||||
internalInformers := internalinformers.NewSharedInformerFactory(internalClientset, controller.NoResyncPeriodFunc())
|
||||
admission.SetInternalKubeInformerFactory(internalInformers)
|
||||
admission.SetQuotaConfiguration(qca)
|
||||
defer close(admissionCh)
|
||||
|
||||
masterConfig := framework.NewIntegrationTestMasterConfig()
|
||||
masterConfig.GenericConfig.AdmissionControl = admission
|
||||
_, _, closeFn := framework.RunAMasterUsingServer(masterConfig, s, h)
|
||||
defer closeFn()
|
||||
|
||||
ns := framework.CreateTestingNamespace("quota", s, t)
|
||||
defer framework.DeleteTestingNamespace(ns, s, t)
|
||||
|
||||
controllerCh := make(chan struct{})
|
||||
defer close(controllerCh)
|
||||
|
||||
informers := informers.NewSharedInformerFactory(clientset, controller.NoResyncPeriodFunc())
|
||||
rm := replicationcontroller.NewReplicationManager(
|
||||
informers.Core().V1().Pods(),
|
||||
informers.Core().V1().ReplicationControllers(),
|
||||
clientset,
|
||||
replicationcontroller.BurstReplicas,
|
||||
)
|
||||
rm.SetEventRecorder(&record.FakeRecorder{})
|
||||
go rm.Run(3, controllerCh)
|
||||
|
||||
discoveryFunc := clientset.Discovery().ServerPreferredNamespacedResources
|
||||
listerFuncForResource := generic.ListerFuncForResourceFunc(informers.ForResource)
|
||||
qc := quotainstall.NewQuotaConfigurationForControllers(listerFuncForResource)
|
||||
informersStarted := make(chan struct{})
|
||||
resourceQuotaControllerOptions := &resourcequotacontroller.ResourceQuotaControllerOptions{
|
||||
QuotaClient: clientset.Core(),
|
||||
ResourceQuotaInformer: informers.Core().V1().ResourceQuotas(),
|
||||
ResyncPeriod: controller.NoResyncPeriodFunc,
|
||||
InformerFactory: informers,
|
||||
ReplenishmentResyncPeriod: controller.NoResyncPeriodFunc,
|
||||
DiscoveryFunc: discoveryFunc,
|
||||
IgnoredResourcesFunc: qc.IgnoredResources,
|
||||
InformersStarted: informersStarted,
|
||||
Registry: generic.NewRegistry(qc.Evaluators()),
|
||||
}
|
||||
resourceQuotaController, err := resourcequotacontroller.NewResourceQuotaController(resourceQuotaControllerOptions)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected err: %v", err)
|
||||
}
|
||||
go resourceQuotaController.Run(2, controllerCh)
|
||||
|
||||
// Periodically the quota controller to detect new resource types
|
||||
go resourceQuotaController.Sync(discoveryFunc, 30*time.Second, controllerCh)
|
||||
|
||||
internalInformers.Start(controllerCh)
|
||||
informers.Start(controllerCh)
|
||||
close(informersStarted)
|
||||
|
||||
// try to create a pod
|
||||
pod := &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: ns.Name,
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Name: "container",
|
||||
Image: "busybox",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
if _, err := clientset.Core().Pods(ns.Name).Create(pod); err == nil {
|
||||
t.Fatalf("expected error for insufficient quota")
|
||||
}
|
||||
|
||||
// now create a covering quota
|
||||
// note: limited resource does a matchContains, so we now have "pods" matching "pods" and "count/pods"
|
||||
quota := &v1.ResourceQuota{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "quota",
|
||||
Namespace: ns.Name,
|
||||
},
|
||||
Spec: v1.ResourceQuotaSpec{
|
||||
Hard: v1.ResourceList{
|
||||
v1.ResourcePods: resource.MustParse("1000"),
|
||||
v1.ResourceName("count/pods"): resource.MustParse("1000"),
|
||||
},
|
||||
},
|
||||
}
|
||||
waitForQuota(t, quota, clientset)
|
||||
|
||||
// attempt to create a new pod once the quota is propagated
|
||||
err = wait.PollImmediate(5*time.Second, time.Minute, func() (bool, error) {
|
||||
// retry until we succeed (to allow time for all changes to propagate)
|
||||
if _, err := clientset.Core().Pods(ns.Name).Create(pod); err == nil {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
50
vendor/k8s.io/kubernetes/test/integration/replicaset/BUILD
generated
vendored
Normal file
50
vendor/k8s.io/kubernetes/test/integration/replicaset/BUILD
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
size = "large",
|
||||
srcs = [
|
||||
"main_test.go",
|
||||
"replicaset_test.go",
|
||||
],
|
||||
tags = ["integration"],
|
||||
deps = [
|
||||
"//pkg/api/v1/pod:go_default_library",
|
||||
"//pkg/controller/replicaset:go_default_library",
|
||||
"//pkg/util/slice:go_default_library",
|
||||
"//test/integration/framework:go_default_library",
|
||||
"//test/utils:go_default_library",
|
||||
"//vendor/k8s.io/api/apps/v1:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/uuid:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||
"//vendor/k8s.io/client-go/informers:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes/typed/apps/v1:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes/typed/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/cache:go_default_library",
|
||||
"//vendor/k8s.io/client-go/util/retry:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
27
vendor/k8s.io/kubernetes/test/integration/replicaset/main_test.go
generated
vendored
Normal file
27
vendor/k8s.io/kubernetes/test/integration/replicaset/main_test.go
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
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 replicaset
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
framework.EtcdMain(m.Run)
|
||||
}
|
||||
1044
vendor/k8s.io/kubernetes/test/integration/replicaset/replicaset_test.go
generated
vendored
Normal file
1044
vendor/k8s.io/kubernetes/test/integration/replicaset/replicaset_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
46
vendor/k8s.io/kubernetes/test/integration/replicationcontroller/BUILD
generated
vendored
Normal file
46
vendor/k8s.io/kubernetes/test/integration/replicationcontroller/BUILD
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
size = "large",
|
||||
srcs = [
|
||||
"main_test.go",
|
||||
"replicationcontroller_test.go",
|
||||
],
|
||||
tags = ["integration"],
|
||||
deps = [
|
||||
"//pkg/api/v1/pod:go_default_library",
|
||||
"//pkg/controller/replication:go_default_library",
|
||||
"//test/integration/framework:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/uuid:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||
"//vendor/k8s.io/client-go/informers:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes/typed/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/cache:go_default_library",
|
||||
"//vendor/k8s.io/client-go/util/retry:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
27
vendor/k8s.io/kubernetes/test/integration/replicationcontroller/main_test.go
generated
vendored
Normal file
27
vendor/k8s.io/kubernetes/test/integration/replicationcontroller/main_test.go
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
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 replicationcontroller
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
framework.EtcdMain(m.Run)
|
||||
}
|
||||
887
vendor/k8s.io/kubernetes/test/integration/replicationcontroller/replicationcontroller_test.go
generated
vendored
Normal file
887
vendor/k8s.io/kubernetes/test/integration/replicationcontroller/replicationcontroller_test.go
generated
vendored
Normal file
@@ -0,0 +1,887 @@
|
||||
/*
|
||||
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 replicationcontroller
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"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/labels"
|
||||
"k8s.io/apimachinery/pkg/util/uuid"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/client-go/informers"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
typedv1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"k8s.io/client-go/util/retry"
|
||||
podutil "k8s.io/kubernetes/pkg/api/v1/pod"
|
||||
"k8s.io/kubernetes/pkg/controller/replication"
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
)
|
||||
|
||||
const (
|
||||
interval = 100 * time.Millisecond
|
||||
timeout = 60 * time.Second
|
||||
)
|
||||
|
||||
func labelMap() map[string]string {
|
||||
return map[string]string{"foo": "bar"}
|
||||
}
|
||||
|
||||
func newRC(name, namespace string, replicas int) *v1.ReplicationController {
|
||||
replicasCopy := int32(replicas)
|
||||
return &v1.ReplicationController{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "ReplicationController",
|
||||
APIVersion: "v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: namespace,
|
||||
Name: name,
|
||||
},
|
||||
Spec: v1.ReplicationControllerSpec{
|
||||
Selector: labelMap(),
|
||||
Replicas: &replicasCopy,
|
||||
Template: &v1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: labelMap(),
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Name: "fake-name",
|
||||
Image: "fakeimage",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newMatchingPod(podName, namespace string) *v1.Pod {
|
||||
return &v1.Pod{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Pod",
|
||||
APIVersion: "v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: podName,
|
||||
Namespace: namespace,
|
||||
Labels: labelMap(),
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Name: "fake-name",
|
||||
Image: "fakeimage",
|
||||
},
|
||||
},
|
||||
},
|
||||
Status: v1.PodStatus{
|
||||
Phase: v1.PodRunning,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// verifyRemainingObjects verifies if the number of the remaining replication
|
||||
// controllers and pods are rcNum and podNum. It returns error if the
|
||||
// communication with the API server fails.
|
||||
func verifyRemainingObjects(t *testing.T, clientSet clientset.Interface, namespace string, rcNum, podNum int) (bool, error) {
|
||||
rcClient := clientSet.CoreV1().ReplicationControllers(namespace)
|
||||
podClient := clientSet.CoreV1().Pods(namespace)
|
||||
pods, err := podClient.List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("Failed to list pods: %v", err)
|
||||
}
|
||||
var ret = true
|
||||
if len(pods.Items) != podNum {
|
||||
ret = false
|
||||
t.Logf("expect %d pods, got %d pods", podNum, len(pods.Items))
|
||||
}
|
||||
rcs, err := rcClient.List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("Failed to list replication controllers: %v", err)
|
||||
}
|
||||
if len(rcs.Items) != rcNum {
|
||||
ret = false
|
||||
t.Logf("expect %d RCs, got %d RCs", rcNum, len(rcs.Items))
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func rmSetup(t *testing.T) (*httptest.Server, framework.CloseFunc, *replication.ReplicationManager, informers.SharedInformerFactory, clientset.Interface) {
|
||||
masterConfig := framework.NewIntegrationTestMasterConfig()
|
||||
_, s, closeFn := framework.RunAMaster(masterConfig)
|
||||
|
||||
config := restclient.Config{Host: s.URL}
|
||||
clientSet, err := clientset.NewForConfig(&config)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in create clientset: %v", err)
|
||||
}
|
||||
resyncPeriod := 12 * time.Hour
|
||||
informers := informers.NewSharedInformerFactory(clientset.NewForConfigOrDie(restclient.AddUserAgent(&config, "rc-informers")), resyncPeriod)
|
||||
|
||||
rm := replication.NewReplicationManager(
|
||||
informers.Core().V1().Pods(),
|
||||
informers.Core().V1().ReplicationControllers(),
|
||||
clientset.NewForConfigOrDie(restclient.AddUserAgent(&config, "replication-controller")),
|
||||
replication.BurstReplicas,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create replication controller")
|
||||
}
|
||||
return s, closeFn, rm, informers, clientSet
|
||||
}
|
||||
|
||||
func rmSimpleSetup(t *testing.T) (*httptest.Server, framework.CloseFunc, clientset.Interface) {
|
||||
masterConfig := framework.NewIntegrationTestMasterConfig()
|
||||
_, s, closeFn := framework.RunAMaster(masterConfig)
|
||||
|
||||
config := restclient.Config{Host: s.URL}
|
||||
clientSet, err := clientset.NewForConfig(&config)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in create clientset: %v", err)
|
||||
}
|
||||
return s, closeFn, clientSet
|
||||
}
|
||||
|
||||
// Run RC controller and informers
|
||||
func runControllerAndInformers(t *testing.T, rm *replication.ReplicationManager, informers informers.SharedInformerFactory, podNum int) chan struct{} {
|
||||
stopCh := make(chan struct{})
|
||||
informers.Start(stopCh)
|
||||
waitToObservePods(t, informers.Core().V1().Pods().Informer(), podNum)
|
||||
go rm.Run(5, stopCh)
|
||||
return stopCh
|
||||
}
|
||||
|
||||
// wait for the podInformer to observe the pods. Call this function before
|
||||
// running the RC controller to prevent the rc manager from creating new pods
|
||||
// rather than adopting the existing ones.
|
||||
func waitToObservePods(t *testing.T, podInformer cache.SharedIndexInformer, podNum int) {
|
||||
if err := wait.PollImmediate(interval, timeout, func() (bool, error) {
|
||||
objects := podInformer.GetIndexer().List()
|
||||
return len(objects) == podNum, nil
|
||||
}); err != nil {
|
||||
t.Fatalf("Error encountered when waiting for podInformer to observe the pods: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func createRCsPods(t *testing.T, clientSet clientset.Interface, rcs []*v1.ReplicationController, pods []*v1.Pod) ([]*v1.ReplicationController, []*v1.Pod) {
|
||||
var createdRCs []*v1.ReplicationController
|
||||
var createdPods []*v1.Pod
|
||||
for _, rc := range rcs {
|
||||
createdRC, err := clientSet.CoreV1().ReplicationControllers(rc.Namespace).Create(rc)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create replication controller %s: %v", rc.Name, err)
|
||||
}
|
||||
createdRCs = append(createdRCs, createdRC)
|
||||
}
|
||||
for _, pod := range pods {
|
||||
createdPod, err := clientSet.CoreV1().Pods(pod.Namespace).Create(pod)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create pod %s: %v", pod.Name, err)
|
||||
}
|
||||
createdPods = append(createdPods, createdPod)
|
||||
}
|
||||
|
||||
return createdRCs, createdPods
|
||||
}
|
||||
|
||||
// Verify .Status.Replicas is equal to .Spec.Replicas
|
||||
func waitRCStable(t *testing.T, clientSet clientset.Interface, rc *v1.ReplicationController) {
|
||||
rcClient := clientSet.CoreV1().ReplicationControllers(rc.Namespace)
|
||||
if err := wait.PollImmediate(interval, timeout, func() (bool, error) {
|
||||
newRC, err := rcClient.Get(rc.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return newRC.Status.Replicas == *rc.Spec.Replicas, nil
|
||||
}); err != nil {
|
||||
t.Fatalf("Failed to verify .Status.Replicas is equal to .Spec.Replicas for rc %s: %v", rc.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Update .Spec.Replicas to replicas and verify .Status.Replicas is changed accordingly
|
||||
func scaleRC(t *testing.T, c clientset.Interface, rc *v1.ReplicationController, replicas int32) {
|
||||
rcClient := c.CoreV1().ReplicationControllers(rc.Namespace)
|
||||
rc = updateRC(t, rcClient, rc.Name, func(rc *v1.ReplicationController) {
|
||||
*rc.Spec.Replicas = replicas
|
||||
})
|
||||
waitRCStable(t, c, rc)
|
||||
}
|
||||
|
||||
func updatePod(t *testing.T, podClient typedv1.PodInterface, podName string, updateFunc func(*v1.Pod)) *v1.Pod {
|
||||
var pod *v1.Pod
|
||||
if err := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
|
||||
newPod, err := podClient.Get(podName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
updateFunc(newPod)
|
||||
pod, err = podClient.Update(newPod)
|
||||
return err
|
||||
}); err != nil {
|
||||
t.Fatalf("Failed to update pod %s: %v", podName, err)
|
||||
}
|
||||
return pod
|
||||
}
|
||||
|
||||
func updatePodStatus(t *testing.T, podClient typedv1.PodInterface, pod *v1.Pod, updateStatusFunc func(*v1.Pod)) {
|
||||
if err := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
|
||||
newPod, err := podClient.Get(pod.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
updateStatusFunc(newPod)
|
||||
_, err = podClient.UpdateStatus(newPod)
|
||||
return err
|
||||
}); err != nil {
|
||||
t.Fatalf("Failed to update status of pod %s: %v", pod.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
func getPods(t *testing.T, podClient typedv1.PodInterface, labelMap map[string]string) *v1.PodList {
|
||||
podSelector := labels.Set(labelMap).AsSelector()
|
||||
options := metav1.ListOptions{LabelSelector: podSelector.String()}
|
||||
pods, err := podClient.List(options)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed obtaining a list of pods that match the pod labels %v: %v", labelMap, err)
|
||||
}
|
||||
return pods
|
||||
}
|
||||
|
||||
func updateRC(t *testing.T, rcClient typedv1.ReplicationControllerInterface, rcName string, updateFunc func(*v1.ReplicationController)) *v1.ReplicationController {
|
||||
var rc *v1.ReplicationController
|
||||
if err := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
|
||||
newRC, err := rcClient.Get(rcName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
updateFunc(newRC)
|
||||
rc, err = rcClient.Update(newRC)
|
||||
return err
|
||||
}); err != nil {
|
||||
t.Fatalf("Failed to update rc %s: %v", rcName, err)
|
||||
}
|
||||
return rc
|
||||
}
|
||||
|
||||
// Verify ControllerRef of a RC pod that has incorrect attributes is automatically patched by the RC
|
||||
func testPodControllerRefPatch(t *testing.T, c clientset.Interface, pod *v1.Pod, ownerReference *metav1.OwnerReference, rc *v1.ReplicationController, expectedOwnerReferenceNum int) {
|
||||
ns := rc.Namespace
|
||||
podClient := c.CoreV1().Pods(ns)
|
||||
updatePod(t, podClient, pod.Name, func(pod *v1.Pod) {
|
||||
pod.OwnerReferences = []metav1.OwnerReference{*ownerReference}
|
||||
})
|
||||
|
||||
if err := wait.PollImmediate(interval, timeout, func() (bool, error) {
|
||||
newPod, err := podClient.Get(pod.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return metav1.GetControllerOf(newPod) != nil, nil
|
||||
}); err != nil {
|
||||
t.Fatalf("Failed to verify ControllerRef for the pod %s is not nil: %v", pod.Name, err)
|
||||
}
|
||||
|
||||
newPod, err := podClient.Get(pod.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to obtain pod %s: %v", pod.Name, err)
|
||||
}
|
||||
controllerRef := metav1.GetControllerOf(newPod)
|
||||
if controllerRef.UID != rc.UID {
|
||||
t.Fatalf("RC owner of the pod %s has a different UID: Expected %v, got %v", newPod.Name, rc.UID, controllerRef.UID)
|
||||
}
|
||||
ownerReferenceNum := len(newPod.GetOwnerReferences())
|
||||
if ownerReferenceNum != expectedOwnerReferenceNum {
|
||||
t.Fatalf("Unexpected number of owner references for pod %s: Expected %d, got %d", newPod.Name, expectedOwnerReferenceNum, ownerReferenceNum)
|
||||
}
|
||||
}
|
||||
|
||||
func setPodsReadyCondition(t *testing.T, clientSet clientset.Interface, pods *v1.PodList, conditionStatus v1.ConditionStatus, lastTransitionTime time.Time) {
|
||||
replicas := int32(len(pods.Items))
|
||||
var readyPods int32
|
||||
err := wait.PollImmediate(interval, timeout, func() (bool, error) {
|
||||
readyPods = 0
|
||||
for i := range pods.Items {
|
||||
pod := &pods.Items[i]
|
||||
if podutil.IsPodReady(pod) {
|
||||
readyPods++
|
||||
continue
|
||||
}
|
||||
pod.Status.Phase = v1.PodRunning
|
||||
_, condition := podutil.GetPodCondition(&pod.Status, v1.PodReady)
|
||||
if condition != nil {
|
||||
condition.Status = conditionStatus
|
||||
condition.LastTransitionTime = metav1.Time{Time: lastTransitionTime}
|
||||
} else {
|
||||
condition = &v1.PodCondition{
|
||||
Type: v1.PodReady,
|
||||
Status: conditionStatus,
|
||||
LastTransitionTime: metav1.Time{Time: lastTransitionTime},
|
||||
}
|
||||
pod.Status.Conditions = append(pod.Status.Conditions, *condition)
|
||||
}
|
||||
_, err := clientSet.CoreV1().Pods(pod.Namespace).UpdateStatus(pod)
|
||||
if err != nil {
|
||||
// When status fails to be updated, we continue to next pod
|
||||
continue
|
||||
}
|
||||
readyPods++
|
||||
}
|
||||
return readyPods >= replicas, nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to mark all ReplicationController pods to ready: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func testScalingUsingScaleSubresource(t *testing.T, c clientset.Interface, rc *v1.ReplicationController, replicas int32) {
|
||||
ns := rc.Namespace
|
||||
rcClient := c.CoreV1().ReplicationControllers(ns)
|
||||
newRC, err := rcClient.Get(rc.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to obtain rc %s: %v", rc.Name, err)
|
||||
}
|
||||
kind := "ReplicationController"
|
||||
scaleClient := c.ExtensionsV1beta1().Scales(ns)
|
||||
scale, err := scaleClient.Get(kind, rc.Name)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to obtain scale subresource for rc %s: %v", rc.Name, err)
|
||||
}
|
||||
if scale.Spec.Replicas != *newRC.Spec.Replicas {
|
||||
t.Fatalf("Scale subresource for rc %s does not match .Spec.Replicas: expected %d, got %d", rc.Name, *newRC.Spec.Replicas, scale.Spec.Replicas)
|
||||
}
|
||||
|
||||
if err := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
|
||||
scale, err := scaleClient.Get(kind, rc.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
scale.Spec.Replicas = replicas
|
||||
_, err = scaleClient.Update(kind, scale)
|
||||
return err
|
||||
}); err != nil {
|
||||
t.Fatalf("Failed to set .Spec.Replicas of scale subresource for rc %s: %v", rc.Name, err)
|
||||
}
|
||||
|
||||
newRC, err = rcClient.Get(rc.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to obtain rc %s: %v", rc.Name, err)
|
||||
}
|
||||
if *newRC.Spec.Replicas != replicas {
|
||||
t.Fatalf(".Spec.Replicas of rc %s does not match its scale subresource: expected %d, got %d", rc.Name, replicas, *newRC.Spec.Replicas)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdoption(t *testing.T) {
|
||||
boolPtr := func(b bool) *bool { return &b }
|
||||
testCases := []struct {
|
||||
name string
|
||||
existingOwnerReferences func(rc *v1.ReplicationController) []metav1.OwnerReference
|
||||
expectedOwnerReferences func(rc *v1.ReplicationController) []metav1.OwnerReference
|
||||
}{
|
||||
{
|
||||
"pod refers rc as an owner, not a controller",
|
||||
func(rc *v1.ReplicationController) []metav1.OwnerReference {
|
||||
return []metav1.OwnerReference{{UID: rc.UID, Name: rc.Name, APIVersion: "v1", Kind: "ReplicationController"}}
|
||||
},
|
||||
func(rc *v1.ReplicationController) []metav1.OwnerReference {
|
||||
return []metav1.OwnerReference{{UID: rc.UID, Name: rc.Name, APIVersion: "v1", Kind: "ReplicationController", Controller: boolPtr(true), BlockOwnerDeletion: boolPtr(true)}}
|
||||
},
|
||||
},
|
||||
{
|
||||
"pod doesn't have owner references",
|
||||
func(rc *v1.ReplicationController) []metav1.OwnerReference {
|
||||
return []metav1.OwnerReference{}
|
||||
},
|
||||
func(rc *v1.ReplicationController) []metav1.OwnerReference {
|
||||
return []metav1.OwnerReference{{UID: rc.UID, Name: rc.Name, APIVersion: "v1", Kind: "ReplicationController", Controller: boolPtr(true), BlockOwnerDeletion: boolPtr(true)}}
|
||||
},
|
||||
},
|
||||
{
|
||||
"pod refers rc as a controller",
|
||||
func(rc *v1.ReplicationController) []metav1.OwnerReference {
|
||||
return []metav1.OwnerReference{{UID: rc.UID, Name: rc.Name, APIVersion: "v1", Kind: "ReplicationController", Controller: boolPtr(true)}}
|
||||
},
|
||||
func(rc *v1.ReplicationController) []metav1.OwnerReference {
|
||||
return []metav1.OwnerReference{{UID: rc.UID, Name: rc.Name, APIVersion: "v1", Kind: "ReplicationController", Controller: boolPtr(true)}}
|
||||
},
|
||||
},
|
||||
{
|
||||
"pod refers other rc as the controller, refers the rc as an owner",
|
||||
func(rc *v1.ReplicationController) []metav1.OwnerReference {
|
||||
return []metav1.OwnerReference{
|
||||
{UID: "1", Name: "anotherRC", APIVersion: "v1", Kind: "ReplicationController", Controller: boolPtr(true)},
|
||||
{UID: rc.UID, Name: rc.Name, APIVersion: "v1", Kind: "ReplicationController"},
|
||||
}
|
||||
},
|
||||
func(rc *v1.ReplicationController) []metav1.OwnerReference {
|
||||
return []metav1.OwnerReference{
|
||||
{UID: "1", Name: "anotherRC", APIVersion: "v1", Kind: "ReplicationController", Controller: boolPtr(true)},
|
||||
{UID: rc.UID, Name: rc.Name, APIVersion: "v1", Kind: "ReplicationController"},
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
for i, tc := range testCases {
|
||||
func() {
|
||||
s, closeFn, rm, informers, clientSet := rmSetup(t)
|
||||
defer closeFn()
|
||||
ns := framework.CreateTestingNamespace(fmt.Sprintf("rc-adoption-%d", i), s, t)
|
||||
defer framework.DeleteTestingNamespace(ns, s, t)
|
||||
|
||||
rcClient := clientSet.CoreV1().ReplicationControllers(ns.Name)
|
||||
podClient := clientSet.CoreV1().Pods(ns.Name)
|
||||
const rcName = "rc"
|
||||
rc, err := rcClient.Create(newRC(rcName, ns.Name, 1))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create replication controllers: %v", err)
|
||||
}
|
||||
podName := fmt.Sprintf("pod%d", i)
|
||||
pod := newMatchingPod(podName, ns.Name)
|
||||
pod.OwnerReferences = tc.existingOwnerReferences(rc)
|
||||
_, err = podClient.Create(pod)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create Pod: %v", err)
|
||||
}
|
||||
|
||||
stopCh := runControllerAndInformers(t, rm, informers, 1)
|
||||
defer close(stopCh)
|
||||
if err := wait.PollImmediate(interval, timeout, func() (bool, error) {
|
||||
updatedPod, err := podClient.Get(pod.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if e, a := tc.expectedOwnerReferences(rc), updatedPod.OwnerReferences; reflect.DeepEqual(e, a) {
|
||||
return true, nil
|
||||
} else {
|
||||
t.Logf("ownerReferences don't match, expect %v, got %v", e, a)
|
||||
return false, nil
|
||||
}
|
||||
}); err != nil {
|
||||
t.Fatalf("test %q failed: %v", tc.name, err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
func TestSpecReplicasChange(t *testing.T) {
|
||||
s, closeFn, rm, informers, c := rmSetup(t)
|
||||
defer closeFn()
|
||||
ns := framework.CreateTestingNamespace("test-spec-replicas-change", s, t)
|
||||
defer framework.DeleteTestingNamespace(ns, s, t)
|
||||
stopCh := runControllerAndInformers(t, rm, informers, 0)
|
||||
defer close(stopCh)
|
||||
|
||||
rc := newRC("rc", ns.Name, 2)
|
||||
rcs, _ := createRCsPods(t, c, []*v1.ReplicationController{rc}, []*v1.Pod{})
|
||||
rc = rcs[0]
|
||||
waitRCStable(t, c, rc)
|
||||
|
||||
// Update .Spec.Replicas and verify .Status.Replicas is changed accordingly
|
||||
scaleRC(t, c, rc, 3)
|
||||
scaleRC(t, c, rc, 0)
|
||||
scaleRC(t, c, rc, 2)
|
||||
|
||||
// Add a template annotation change to test RC's status does update
|
||||
// without .Spec.Replicas change
|
||||
rcClient := c.CoreV1().ReplicationControllers(ns.Name)
|
||||
var oldGeneration int64
|
||||
newRC := updateRC(t, rcClient, rc.Name, func(rc *v1.ReplicationController) {
|
||||
oldGeneration = rc.Generation
|
||||
rc.Spec.Template.Annotations = map[string]string{"test": "annotation"}
|
||||
})
|
||||
savedGeneration := newRC.Generation
|
||||
if savedGeneration == oldGeneration {
|
||||
t.Fatalf("Failed to verify .Generation has incremented for rc %s", rc.Name)
|
||||
}
|
||||
|
||||
if err := wait.PollImmediate(interval, timeout, func() (bool, error) {
|
||||
newRC, err := rcClient.Get(rc.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return newRC.Status.ObservedGeneration >= savedGeneration, nil
|
||||
}); err != nil {
|
||||
t.Fatalf("Failed to verify .Status.ObservedGeneration has incremented for rc %s: %v", rc.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeletingAndFailedPods(t *testing.T) {
|
||||
s, closeFn, rm, informers, c := rmSetup(t)
|
||||
defer closeFn()
|
||||
ns := framework.CreateTestingNamespace("test-deleting-and-failed-pods", s, t)
|
||||
defer framework.DeleteTestingNamespace(ns, s, t)
|
||||
stopCh := runControllerAndInformers(t, rm, informers, 0)
|
||||
defer close(stopCh)
|
||||
|
||||
rc := newRC("rc", ns.Name, 2)
|
||||
rcs, _ := createRCsPods(t, c, []*v1.ReplicationController{rc}, []*v1.Pod{})
|
||||
rc = rcs[0]
|
||||
waitRCStable(t, c, rc)
|
||||
|
||||
// Verify RC creates 2 pods
|
||||
podClient := c.CoreV1().Pods(ns.Name)
|
||||
pods := getPods(t, podClient, labelMap())
|
||||
if len(pods.Items) != 2 {
|
||||
t.Fatalf("len(pods) = %d, want 2", len(pods.Items))
|
||||
}
|
||||
|
||||
// Set first pod as deleting pod
|
||||
// Set finalizers for the pod to simulate pending deletion status
|
||||
deletingPod := &pods.Items[0]
|
||||
updatePod(t, podClient, deletingPod.Name, func(pod *v1.Pod) {
|
||||
pod.Finalizers = []string{"fake.example.com/blockDeletion"}
|
||||
})
|
||||
if err := c.CoreV1().Pods(ns.Name).Delete(deletingPod.Name, &metav1.DeleteOptions{}); err != nil {
|
||||
t.Fatalf("Error deleting pod %s: %v", deletingPod.Name, err)
|
||||
}
|
||||
|
||||
// Set second pod as failed pod
|
||||
failedPod := &pods.Items[1]
|
||||
updatePodStatus(t, podClient, failedPod, func(pod *v1.Pod) {
|
||||
pod.Status.Phase = v1.PodFailed
|
||||
})
|
||||
|
||||
// Pool until 2 new pods have been created to replace deleting and failed pods
|
||||
if err := wait.PollImmediate(interval, timeout, func() (bool, error) {
|
||||
pods = getPods(t, podClient, labelMap())
|
||||
return len(pods.Items) == 4, nil
|
||||
}); err != nil {
|
||||
t.Fatalf("Failed to verify 2 new pods have been created (expected 4 pods): %v", err)
|
||||
}
|
||||
|
||||
// Verify deleting and failed pods are among the four pods
|
||||
foundDeletingPod := false
|
||||
foundFailedPod := false
|
||||
for _, pod := range pods.Items {
|
||||
if pod.UID == deletingPod.UID {
|
||||
foundDeletingPod = true
|
||||
}
|
||||
if pod.UID == failedPod.UID {
|
||||
foundFailedPod = true
|
||||
}
|
||||
}
|
||||
// Verify deleting pod exists
|
||||
if !foundDeletingPod {
|
||||
t.Fatalf("expected deleting pod %s exists, but it is not found", deletingPod.Name)
|
||||
}
|
||||
// Verify failed pod exists
|
||||
if !foundFailedPod {
|
||||
t.Fatalf("expected failed pod %s exists, but it is not found", failedPod.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOverlappingRCs(t *testing.T) {
|
||||
s, closeFn, rm, informers, c := rmSetup(t)
|
||||
defer closeFn()
|
||||
ns := framework.CreateTestingNamespace("test-overlapping-rcs", s, t)
|
||||
defer framework.DeleteTestingNamespace(ns, s, t)
|
||||
stopCh := runControllerAndInformers(t, rm, informers, 0)
|
||||
defer close(stopCh)
|
||||
|
||||
// Create 2 RCs with identical selectors
|
||||
for i := 0; i < 2; i++ {
|
||||
// One RC has 1 replica, and another has 2 replicas
|
||||
rc := newRC(fmt.Sprintf("rc-%d", i+1), ns.Name, i+1)
|
||||
rcs, _ := createRCsPods(t, c, []*v1.ReplicationController{rc}, []*v1.Pod{})
|
||||
waitRCStable(t, c, rcs[0])
|
||||
}
|
||||
|
||||
// Expect 3 total Pods to be created
|
||||
podClient := c.CoreV1().Pods(ns.Name)
|
||||
pods := getPods(t, podClient, labelMap())
|
||||
if len(pods.Items) != 3 {
|
||||
t.Errorf("len(pods) = %d, want 3", len(pods.Items))
|
||||
}
|
||||
|
||||
// Expect both RCs have .status.replicas = .spec.replicas
|
||||
for i := 0; i < 2; i++ {
|
||||
newRC, err := c.CoreV1().ReplicationControllers(ns.Name).Get(fmt.Sprintf("rc-%d", i+1), metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to obtain rc rc-%d: %v", i+1, err)
|
||||
}
|
||||
if newRC.Status.Replicas != *newRC.Spec.Replicas {
|
||||
t.Fatalf(".Status.Replicas %d is not equal to .Spec.Replicas %d", newRC.Status.Replicas, *newRC.Spec.Replicas)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPodOrphaningAndAdoptionWhenLabelsChange(t *testing.T) {
|
||||
s, closeFn, rm, informers, c := rmSetup(t)
|
||||
defer closeFn()
|
||||
ns := framework.CreateTestingNamespace("test-pod-orphaning-and-adoption-when-labels-change", s, t)
|
||||
defer framework.DeleteTestingNamespace(ns, s, t)
|
||||
stopCh := runControllerAndInformers(t, rm, informers, 0)
|
||||
defer close(stopCh)
|
||||
|
||||
rc := newRC("rc", ns.Name, 1)
|
||||
rcs, _ := createRCsPods(t, c, []*v1.ReplicationController{rc}, []*v1.Pod{})
|
||||
rc = rcs[0]
|
||||
waitRCStable(t, c, rc)
|
||||
|
||||
// Orphaning: RC should remove OwnerReference from a pod when the pod's labels change to not match its labels
|
||||
podClient := c.CoreV1().Pods(ns.Name)
|
||||
pods := getPods(t, podClient, labelMap())
|
||||
if len(pods.Items) != 1 {
|
||||
t.Fatalf("len(pods) = %d, want 1", len(pods.Items))
|
||||
}
|
||||
pod := &pods.Items[0]
|
||||
|
||||
// Start by verifying ControllerRef for the pod is not nil
|
||||
if metav1.GetControllerOf(pod) == nil {
|
||||
t.Fatalf("ControllerRef of pod %s is nil", pod.Name)
|
||||
}
|
||||
newLabelMap := map[string]string{"new-foo": "new-bar"}
|
||||
updatePod(t, podClient, pod.Name, func(pod *v1.Pod) {
|
||||
pod.Labels = newLabelMap
|
||||
})
|
||||
if err := wait.PollImmediate(interval, timeout, func() (bool, error) {
|
||||
newPod, err := podClient.Get(pod.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
pod = newPod
|
||||
return metav1.GetControllerOf(newPod) == nil, nil
|
||||
}); err != nil {
|
||||
t.Fatalf("Failed to verify ControllerRef for the pod %s is nil: %v", pod.Name, err)
|
||||
}
|
||||
|
||||
// Adoption: RC should add ControllerRef to a pod when the pod's labels change to match its labels
|
||||
updatePod(t, podClient, pod.Name, func(pod *v1.Pod) {
|
||||
pod.Labels = labelMap()
|
||||
})
|
||||
if err := wait.PollImmediate(interval, timeout, func() (bool, error) {
|
||||
newPod, err := podClient.Get(pod.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
// If the pod is not found, it means the RC picks the pod for deletion (it is extra)
|
||||
// Verify there is only one pod in namespace and it has ControllerRef to the RC
|
||||
if errors.IsNotFound(err) {
|
||||
pods := getPods(t, podClient, labelMap())
|
||||
if len(pods.Items) != 1 {
|
||||
return false, fmt.Errorf("Expected 1 pod in current namespace, got %d", len(pods.Items))
|
||||
}
|
||||
// Set the pod accordingly
|
||||
pod = &pods.Items[0]
|
||||
return true, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
// Always update the pod so that we can save a GET call to API server later
|
||||
pod = newPod
|
||||
// If the pod is found, verify the pod has a ControllerRef
|
||||
return metav1.GetControllerOf(newPod) != nil, nil
|
||||
}); err != nil {
|
||||
t.Fatalf("Failed to verify ControllerRef for pod %s is not nil: %v", pod.Name, err)
|
||||
}
|
||||
// Verify the pod has a ControllerRef to the RC
|
||||
// Do nothing if the pod is nil (i.e., has been picked for deletion)
|
||||
if pod != nil {
|
||||
controllerRef := metav1.GetControllerOf(pod)
|
||||
if controllerRef.UID != rc.UID {
|
||||
t.Fatalf("RC owner of the pod %s has a different UID: Expected %v, got %v", pod.Name, rc.UID, controllerRef.UID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGeneralPodAdoption(t *testing.T) {
|
||||
s, closeFn, rm, informers, c := rmSetup(t)
|
||||
defer closeFn()
|
||||
ns := framework.CreateTestingNamespace("test-general-pod-adoption", s, t)
|
||||
defer framework.DeleteTestingNamespace(ns, s, t)
|
||||
stopCh := runControllerAndInformers(t, rm, informers, 0)
|
||||
defer close(stopCh)
|
||||
|
||||
rc := newRC("rc", ns.Name, 1)
|
||||
rcs, _ := createRCsPods(t, c, []*v1.ReplicationController{rc}, []*v1.Pod{})
|
||||
rc = rcs[0]
|
||||
waitRCStable(t, c, rc)
|
||||
|
||||
podClient := c.CoreV1().Pods(ns.Name)
|
||||
pods := getPods(t, podClient, labelMap())
|
||||
if len(pods.Items) != 1 {
|
||||
t.Fatalf("len(pods) = %d, want 1", len(pods.Items))
|
||||
}
|
||||
pod := &pods.Items[0]
|
||||
var falseVar = false
|
||||
|
||||
// When the only OwnerReference of the pod points to another type of API object such as statefulset
|
||||
// with Controller=false, the RC should add a second OwnerReference (ControllerRef) pointing to itself
|
||||
// with Controller=true
|
||||
ownerReference := metav1.OwnerReference{UID: uuid.NewUUID(), APIVersion: "apps/v1beta1", Kind: "StatefulSet", Name: rc.Name, Controller: &falseVar}
|
||||
testPodControllerRefPatch(t, c, pod, &ownerReference, rc, 2)
|
||||
|
||||
// When the only OwnerReference of the pod points to the RC, but Controller=false
|
||||
ownerReference = metav1.OwnerReference{UID: rc.UID, APIVersion: "v1", Kind: "ReplicationController", Name: rc.Name, Controller: &falseVar}
|
||||
testPodControllerRefPatch(t, c, pod, &ownerReference, rc, 1)
|
||||
}
|
||||
|
||||
func TestReadyAndAvailableReplicas(t *testing.T) {
|
||||
s, closeFn, rm, informers, c := rmSetup(t)
|
||||
defer closeFn()
|
||||
ns := framework.CreateTestingNamespace("test-ready-and-available-replicas", s, t)
|
||||
defer framework.DeleteTestingNamespace(ns, s, t)
|
||||
stopCh := runControllerAndInformers(t, rm, informers, 0)
|
||||
defer close(stopCh)
|
||||
|
||||
rc := newRC("rc", ns.Name, 3)
|
||||
rc.Spec.MinReadySeconds = 3600
|
||||
rcs, _ := createRCsPods(t, c, []*v1.ReplicationController{rc}, []*v1.Pod{})
|
||||
rc = rcs[0]
|
||||
waitRCStable(t, c, rc)
|
||||
|
||||
// First verify no pod is available
|
||||
if rc.Status.AvailableReplicas != 0 {
|
||||
t.Fatalf("Unexpected .Status.AvailableReplicas: Expected 0, saw %d", rc.Status.AvailableReplicas)
|
||||
}
|
||||
|
||||
podClient := c.CoreV1().Pods(ns.Name)
|
||||
pods := getPods(t, podClient, labelMap())
|
||||
if len(pods.Items) != 3 {
|
||||
t.Fatalf("len(pods) = %d, want 3", len(pods.Items))
|
||||
}
|
||||
|
||||
// Separate 3 pods into their own list
|
||||
firstPodList := &v1.PodList{Items: pods.Items[:1]}
|
||||
secondPodList := &v1.PodList{Items: pods.Items[1:2]}
|
||||
thirdPodList := &v1.PodList{Items: pods.Items[2:]}
|
||||
// First pod: Running, but not Ready
|
||||
// by setting the Ready condition to false with LastTransitionTime to be now
|
||||
setPodsReadyCondition(t, c, firstPodList, v1.ConditionFalse, time.Now())
|
||||
// Second pod: Running and Ready, but not Available
|
||||
// by setting LastTransitionTime to now
|
||||
setPodsReadyCondition(t, c, secondPodList, v1.ConditionTrue, time.Now())
|
||||
// Third pod: Running, Ready, and Available
|
||||
// by setting LastTransitionTime to more than 3600 seconds ago
|
||||
setPodsReadyCondition(t, c, thirdPodList, v1.ConditionTrue, time.Now().Add(-120*time.Minute))
|
||||
|
||||
rcClient := c.CoreV1().ReplicationControllers(ns.Name)
|
||||
if err := wait.PollImmediate(interval, timeout, func() (bool, error) {
|
||||
newRC, err := rcClient.Get(rc.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
// Verify 3 pods exist, 2 pods are Ready, and 1 pod is Available
|
||||
return newRC.Status.Replicas == 3 && newRC.Status.ReadyReplicas == 2 && newRC.Status.AvailableReplicas == 1, nil
|
||||
}); err != nil {
|
||||
t.Fatalf("Failed to verify number of Replicas, ReadyReplicas and AvailableReplicas of rc %s to be as expected: %v", rc.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRCScaleSubresource(t *testing.T) {
|
||||
s, closeFn, rm, informers, c := rmSetup(t)
|
||||
defer closeFn()
|
||||
ns := framework.CreateTestingNamespace("test-rc-scale-subresource", s, t)
|
||||
defer framework.DeleteTestingNamespace(ns, s, t)
|
||||
stopCh := runControllerAndInformers(t, rm, informers, 0)
|
||||
defer close(stopCh)
|
||||
|
||||
rc := newRC("rc", ns.Name, 1)
|
||||
rcs, _ := createRCsPods(t, c, []*v1.ReplicationController{rc}, []*v1.Pod{})
|
||||
rc = rcs[0]
|
||||
waitRCStable(t, c, rc)
|
||||
|
||||
// Use scale subresource to scale up .Spec.Replicas to 3
|
||||
testScalingUsingScaleSubresource(t, c, rc, 3)
|
||||
// Use the scale subresource to scale down .Spec.Replicas to 0
|
||||
testScalingUsingScaleSubresource(t, c, rc, 0)
|
||||
}
|
||||
|
||||
func TestExtraPodsAdoptionAndDeletion(t *testing.T) {
|
||||
s, closeFn, rm, informers, c := rmSetup(t)
|
||||
defer closeFn()
|
||||
ns := framework.CreateTestingNamespace("test-extra-pods-adoption-and-deletion", s, t)
|
||||
defer framework.DeleteTestingNamespace(ns, s, t)
|
||||
|
||||
rc := newRC("rc", ns.Name, 2)
|
||||
// Create 3 pods, RC should adopt only 2 of them
|
||||
podList := []*v1.Pod{}
|
||||
for i := 0; i < 3; i++ {
|
||||
pod := newMatchingPod(fmt.Sprintf("pod-%d", i+1), ns.Name)
|
||||
pod.Labels = labelMap()
|
||||
podList = append(podList, pod)
|
||||
}
|
||||
rcs, _ := createRCsPods(t, c, []*v1.ReplicationController{rc}, podList)
|
||||
rc = rcs[0]
|
||||
stopCh := runControllerAndInformers(t, rm, informers, 3)
|
||||
defer close(stopCh)
|
||||
waitRCStable(t, c, rc)
|
||||
|
||||
// Verify the extra pod is deleted eventually by determining whether number of
|
||||
// all pods within namespace matches .spec.replicas of the RC (2 in this case)
|
||||
podClient := c.CoreV1().Pods(ns.Name)
|
||||
if err := wait.PollImmediate(interval, timeout, func() (bool, error) {
|
||||
// All pods have labelMap as their labels
|
||||
pods := getPods(t, podClient, labelMap())
|
||||
return int32(len(pods.Items)) == *rc.Spec.Replicas, nil
|
||||
}); err != nil {
|
||||
t.Fatalf("Failed to verify number of all pods within current namespace matches .spec.replicas of rc %s: %v", rc.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFullyLabeledReplicas(t *testing.T) {
|
||||
s, closeFn, rm, informers, c := rmSetup(t)
|
||||
defer closeFn()
|
||||
ns := framework.CreateTestingNamespace("test-fully-labeled-replicas", s, t)
|
||||
defer framework.DeleteTestingNamespace(ns, s, t)
|
||||
stopCh := runControllerAndInformers(t, rm, informers, 0)
|
||||
defer close(stopCh)
|
||||
|
||||
extraLabelMap := map[string]string{"foo": "bar", "extraKey": "extraValue"}
|
||||
rc := newRC("rc", ns.Name, 2)
|
||||
rcs, _ := createRCsPods(t, c, []*v1.ReplicationController{rc}, []*v1.Pod{})
|
||||
rc = rcs[0]
|
||||
waitRCStable(t, c, rc)
|
||||
|
||||
// Change RC's template labels to have extra labels, but not its selector
|
||||
rcClient := c.CoreV1().ReplicationControllers(ns.Name)
|
||||
updateRC(t, rcClient, rc.Name, func(rc *v1.ReplicationController) {
|
||||
rc.Spec.Template.Labels = extraLabelMap
|
||||
})
|
||||
|
||||
// Set one of the pods to have extra labels
|
||||
podClient := c.CoreV1().Pods(ns.Name)
|
||||
pods := getPods(t, podClient, labelMap())
|
||||
if len(pods.Items) != 2 {
|
||||
t.Fatalf("len(pods) = %d, want 2", len(pods.Items))
|
||||
}
|
||||
fullyLabeledPod := &pods.Items[0]
|
||||
updatePod(t, podClient, fullyLabeledPod.Name, func(pod *v1.Pod) {
|
||||
pod.Labels = extraLabelMap
|
||||
})
|
||||
|
||||
// Verify only one pod is fully labeled
|
||||
if err := wait.PollImmediate(interval, timeout, func() (bool, error) {
|
||||
newRC, err := rcClient.Get(rc.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return (newRC.Status.Replicas == 2 && newRC.Status.FullyLabeledReplicas == 1), nil
|
||||
}); err != nil {
|
||||
t.Fatalf("Failed to verify only one pod is fully labeled: %v", err)
|
||||
}
|
||||
}
|
||||
38
vendor/k8s.io/kubernetes/test/integration/scale/BUILD
generated
vendored
Normal file
38
vendor/k8s.io/kubernetes/test/integration/scale/BUILD
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
size = "large",
|
||||
srcs = ["scale_test.go"],
|
||||
tags = ["integration"],
|
||||
deps = [
|
||||
"//cmd/kube-apiserver/app/testing:go_default_library",
|
||||
"//test/integration/framework:go_default_library",
|
||||
"//vendor/github.com/coreos/etcd/etcdserver/api/v3rpc:go_default_library",
|
||||
"//vendor/github.com/coreos/pkg/capnslog:go_default_library",
|
||||
"//vendor/k8s.io/api/apps/v1beta2:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
242
vendor/k8s.io/kubernetes/test/integration/scale/scale_test.go
generated
vendored
Normal file
242
vendor/k8s.io/kubernetes/test/integration/scale/scale_test.go
generated
vendored
Normal file
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
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 scale
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"path"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
_ "github.com/coreos/etcd/etcdserver/api/v3rpc" // Force package logger init.
|
||||
"github.com/coreos/pkg/capnslog"
|
||||
|
||||
appsv1beta2 "k8s.io/api/apps/v1beta2"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
apitesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing"
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
)
|
||||
|
||||
type subresourceTest struct {
|
||||
resource schema.GroupVersionResource
|
||||
kind schema.GroupVersionKind
|
||||
}
|
||||
|
||||
func makeGVR(group, version, resource string) schema.GroupVersionResource {
|
||||
return schema.GroupVersionResource{Group: group, Version: version, Resource: resource}
|
||||
}
|
||||
func makeGVK(group, version, kind string) schema.GroupVersionKind {
|
||||
return schema.GroupVersionKind{Group: group, Version: version, Kind: kind}
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
framework.EtcdMain(m.Run)
|
||||
}
|
||||
|
||||
func TestScaleSubresources(t *testing.T) {
|
||||
clientSet, tearDown := setup(t)
|
||||
defer tearDown()
|
||||
|
||||
resourceLists, err := clientSet.Discovery().ServerResources()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expectedScaleSubresources := map[schema.GroupVersionResource]schema.GroupVersionKind{
|
||||
makeGVR("", "v1", "replicationcontrollers/scale"): makeGVK("autoscaling", "v1", "Scale"),
|
||||
|
||||
makeGVR("extensions", "v1beta1", "deployments/scale"): makeGVK("extensions", "v1beta1", "Scale"),
|
||||
makeGVR("extensions", "v1beta1", "replicationcontrollers/scale"): makeGVK("extensions", "v1beta1", "Scale"),
|
||||
makeGVR("extensions", "v1beta1", "replicasets/scale"): makeGVK("extensions", "v1beta1", "Scale"),
|
||||
|
||||
makeGVR("apps", "v1beta1", "deployments/scale"): makeGVK("apps", "v1beta1", "Scale"),
|
||||
makeGVR("apps", "v1beta1", "statefulsets/scale"): makeGVK("apps", "v1beta1", "Scale"),
|
||||
|
||||
makeGVR("apps", "v1beta2", "deployments/scale"): makeGVK("apps", "v1beta2", "Scale"),
|
||||
makeGVR("apps", "v1beta2", "replicasets/scale"): makeGVK("apps", "v1beta2", "Scale"),
|
||||
makeGVR("apps", "v1beta2", "statefulsets/scale"): makeGVK("apps", "v1beta2", "Scale"),
|
||||
|
||||
makeGVR("apps", "v1", "deployments/scale"): makeGVK("autoscaling", "v1", "Scale"),
|
||||
makeGVR("apps", "v1", "replicasets/scale"): makeGVK("autoscaling", "v1", "Scale"),
|
||||
makeGVR("apps", "v1", "statefulsets/scale"): makeGVK("autoscaling", "v1", "Scale"),
|
||||
}
|
||||
|
||||
autoscalingGVK := schema.GroupVersionKind{Group: "autoscaling", Version: "v1", Kind: "Scale"}
|
||||
|
||||
discoveredScaleSubresources := map[schema.GroupVersionResource]schema.GroupVersionKind{}
|
||||
for _, resourceList := range resourceLists {
|
||||
containingGV, err := schema.ParseGroupVersion(resourceList.GroupVersion)
|
||||
if err != nil {
|
||||
t.Fatalf("error getting group version for %#v: %v", resourceList, err)
|
||||
}
|
||||
|
||||
for _, resource := range resourceList.APIResources {
|
||||
if !strings.HasSuffix(resource.Name, "/scale") {
|
||||
continue
|
||||
}
|
||||
|
||||
gvr := containingGV.WithResource(resource.Name)
|
||||
if _, exists := discoveredScaleSubresources[gvr]; exists {
|
||||
t.Errorf("scale subresource %#v listed multiple times in discovery", gvr)
|
||||
continue
|
||||
}
|
||||
|
||||
gvk := containingGV.WithKind(resource.Kind)
|
||||
if resource.Group != "" {
|
||||
gvk.Group = resource.Group
|
||||
}
|
||||
if resource.Version != "" {
|
||||
gvk.Version = resource.Version
|
||||
}
|
||||
discoveredScaleSubresources[gvr] = gvk
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure nothing is missing
|
||||
for gvr, gvk := range expectedScaleSubresources {
|
||||
if _, ok := discoveredScaleSubresources[gvr]; !ok {
|
||||
t.Errorf("expected scale subresource %#v of kind %#v was missing from discovery", gvr, gvk)
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure discovery lists expected types
|
||||
for gvr, gvk := range discoveredScaleSubresources {
|
||||
if expectedGVK, expected := expectedScaleSubresources[gvr]; !expected {
|
||||
if gvk == autoscalingGVK {
|
||||
t.Errorf("unexpected scale subresource %#v of kind %#v. new scale subresource should be added to expectedScaleSubresources", gvr, gvk)
|
||||
} else {
|
||||
t.Errorf("unexpected scale subresource %#v of kind %#v. new scale resources are expected to use Scale from the autoscaling/v1 API group", gvr, gvk)
|
||||
}
|
||||
continue
|
||||
} else if expectedGVK != gvk {
|
||||
t.Errorf("scale subresource %#v should be of kind %#v, but %#v was listed in discovery", gvr, expectedGVK, gvk)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Create objects required to exercise scale subresources
|
||||
if _, err := clientSet.CoreV1().ReplicationControllers("default").Create(rcStub); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := clientSet.AppsV1beta2().ReplicaSets("default").Create(rsStub); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := clientSet.AppsV1beta2().Deployments("default").Create(deploymentStub); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := clientSet.AppsV1beta2().StatefulSets("default").Create(ssStub); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Ensure scale subresources return and accept expected kinds
|
||||
for gvr, gvk := range discoveredScaleSubresources {
|
||||
prefix := "/apis"
|
||||
if gvr.Group == corev1.GroupName {
|
||||
prefix = "/api"
|
||||
}
|
||||
|
||||
resourceParts := strings.SplitN(gvr.Resource, "/", 2)
|
||||
|
||||
urlPath := path.Join(prefix, gvr.Group, gvr.Version, "namespaces", "default", resourceParts[0], "test", resourceParts[1])
|
||||
obj := &unstructured.Unstructured{}
|
||||
|
||||
getData, err := clientSet.CoreV1().RESTClient().Get().AbsPath(urlPath).DoRaw()
|
||||
if err != nil {
|
||||
t.Errorf("error fetching %s: %v", urlPath, err)
|
||||
continue
|
||||
}
|
||||
if err := json.Unmarshal(getData, obj); err != nil {
|
||||
t.Errorf("error decoding %s: %v", urlPath, err)
|
||||
t.Log(string(getData))
|
||||
continue
|
||||
}
|
||||
|
||||
if obj.GetObjectKind().GroupVersionKind() != gvk {
|
||||
t.Errorf("expected %#v, got %#v from %s", gvk, obj.GetObjectKind().GroupVersionKind(), urlPath)
|
||||
t.Log(string(getData))
|
||||
continue
|
||||
}
|
||||
|
||||
updateData, err := clientSet.CoreV1().RESTClient().Put().AbsPath(urlPath).Body(getData).DoRaw()
|
||||
if err != nil {
|
||||
t.Errorf("error putting to %s: %v", urlPath, err)
|
||||
t.Log(string(getData))
|
||||
t.Log(string(updateData))
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
replicas = int32(1)
|
||||
|
||||
podStub = corev1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"foo": "bar"}},
|
||||
Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "test", Image: "busybox"}}},
|
||||
}
|
||||
|
||||
rcStub = &corev1.ReplicationController{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test"},
|
||||
Spec: corev1.ReplicationControllerSpec{Selector: podStub.Labels, Replicas: &replicas, Template: &podStub},
|
||||
}
|
||||
|
||||
rsStub = &appsv1beta2.ReplicaSet{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test"},
|
||||
Spec: appsv1beta2.ReplicaSetSpec{Selector: &metav1.LabelSelector{MatchLabels: podStub.Labels}, Replicas: &replicas, Template: podStub},
|
||||
}
|
||||
|
||||
deploymentStub = &appsv1beta2.Deployment{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test"},
|
||||
Spec: appsv1beta2.DeploymentSpec{Selector: &metav1.LabelSelector{MatchLabels: podStub.Labels}, Replicas: &replicas, Template: podStub},
|
||||
}
|
||||
|
||||
ssStub = &appsv1beta2.StatefulSet{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test"},
|
||||
Spec: appsv1beta2.StatefulSetSpec{Selector: &metav1.LabelSelector{MatchLabels: podStub.Labels}, Replicas: &replicas, Template: podStub},
|
||||
}
|
||||
)
|
||||
|
||||
func setup(t *testing.T) (client kubernetes.Interface, tearDown func()) {
|
||||
result := apitesting.StartTestServerOrDie(t, nil, nil, framework.SharedEtcd())
|
||||
|
||||
// TODO: Disable logging here until we resolve teardown issues which result in
|
||||
// massive log spam. Another path forward would be to refactor
|
||||
// StartTestServerOrDie to work with the etcd instance already started by the
|
||||
// integration test scripts.
|
||||
// See https://github.com/kubernetes/kubernetes/issues/49489.
|
||||
repo, err := capnslog.GetRepoLogger("github.com/coreos/etcd")
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't configure logging: %v", err)
|
||||
}
|
||||
repo.SetLogLevel(map[string]capnslog.LogLevel{
|
||||
"etcdserver/api/v3rpc": capnslog.CRITICAL,
|
||||
})
|
||||
|
||||
result.ClientConfig.AcceptContentTypes = ""
|
||||
result.ClientConfig.ContentType = ""
|
||||
result.ClientConfig.NegotiatedSerializer = nil
|
||||
clientSet, err := kubernetes.NewForConfig(result.ClientConfig)
|
||||
if err != nil {
|
||||
t.Fatalf("error creating clientset: %v", err)
|
||||
}
|
||||
|
||||
return clientSet, result.TearDownFn
|
||||
}
|
||||
121
vendor/k8s.io/kubernetes/test/integration/scheduler/BUILD
generated
vendored
Normal file
121
vendor/k8s.io/kubernetes/test/integration/scheduler/BUILD
generated
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
size = "large",
|
||||
srcs = [
|
||||
"extender_test.go",
|
||||
"main_test.go",
|
||||
"predicates_test.go",
|
||||
"preemption_test.go",
|
||||
"priorities_test.go",
|
||||
"scheduler_test.go",
|
||||
"taint_test.go",
|
||||
"volume_binding_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
tags = ["integration"],
|
||||
deps = [
|
||||
"//cmd/kube-scheduler/app:go_default_library",
|
||||
"//cmd/kube-scheduler/app/config:go_default_library",
|
||||
"//pkg/api/legacyscheme:go_default_library",
|
||||
"//pkg/apis/componentconfig:go_default_library",
|
||||
"//pkg/client/clientset_generated/internalclientset:go_default_library",
|
||||
"//pkg/client/informers/informers_generated/internalversion:go_default_library",
|
||||
"//pkg/controller/nodelifecycle:go_default_library",
|
||||
"//pkg/controller/volume/persistentvolume:go_default_library",
|
||||
"//pkg/features:go_default_library",
|
||||
"//pkg/kubeapiserver/admission:go_default_library",
|
||||
"//pkg/scheduler:go_default_library",
|
||||
"//pkg/scheduler/algorithm:go_default_library",
|
||||
"//pkg/scheduler/algorithmprovider:go_default_library",
|
||||
"//pkg/scheduler/api:go_default_library",
|
||||
"//pkg/scheduler/cache:go_default_library",
|
||||
"//pkg/scheduler/factory:go_default_library",
|
||||
"//plugin/pkg/admission/podtolerationrestriction:go_default_library",
|
||||
"//plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction:go_default_library",
|
||||
"//test/integration/framework:go_default_library",
|
||||
"//test/utils:go_default_library",
|
||||
"//test/utils/image:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/api/policy/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/api/storage/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/diff:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/rand:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||
"//vendor/k8s.io/client-go/informers:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes/typed/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/client-go/listers/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/cache:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/record:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["util.go"],
|
||||
importpath = "k8s.io/kubernetes/test/integration/scheduler",
|
||||
deps = [
|
||||
"//pkg/api/legacyscheme:go_default_library",
|
||||
"//pkg/api/v1/pod:go_default_library",
|
||||
"//pkg/controller:go_default_library",
|
||||
"//pkg/controller/disruption:go_default_library",
|
||||
"//pkg/features:go_default_library",
|
||||
"//pkg/scheduler:go_default_library",
|
||||
"//pkg/scheduler/algorithmprovider:go_default_library",
|
||||
"//pkg/scheduler/api:go_default_library",
|
||||
"//pkg/scheduler/factory:go_default_library",
|
||||
"//test/integration/framework:go_default_library",
|
||||
"//test/utils/image:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/api/policy/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/uuid:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/admission:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/util/feature/testing:go_default_library",
|
||||
"//vendor/k8s.io/client-go/informers:go_default_library",
|
||||
"//vendor/k8s.io/client-go/informers/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes/typed/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/client-go/listers/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/record:go_default_library",
|
||||
],
|
||||
)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user