Add generated file

This PR adds generated files under pkg/client and vendor folder.
This commit is contained in:
xing-yang
2018-07-12 10:55:15 -07:00
parent 36b1de0341
commit e213d1890d
17729 changed files with 5090889 additions and 0 deletions

255
vendor/k8s.io/kubernetes/test/integration/master/BUILD generated vendored Normal file
View 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": [],
}),
)

View 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
}

View 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
}

View 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
}

View 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)
}

View 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)
}

View 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
}

View 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()
}

View 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
}