Replace godep with dep

This commit is contained in:
Manuel de Brito Fontes 2017-10-06 17:26:14 -03:00
parent 1e7489927c
commit bf5616c65b
14883 changed files with 3937406 additions and 361781 deletions

77
vendor/k8s.io/kubernetes/test/e2e/apimachinery/BUILD generated vendored Normal file
View file

@ -0,0 +1,77 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"aggregator.go",
"custom_resource_definition.go",
"etcd_failure.go",
"framework.go",
"garbage_collector.go",
"generated_clientset.go",
"initializers.go",
"namespace.go",
"table_conversion.go",
],
deps = [
"//pkg/api:go_default_library",
"//pkg/api/v1/pod:go_default_library",
"//pkg/apis/rbac:go_default_library",
"//pkg/printers:go_default_library",
"//pkg/util/version:go_default_library",
"//test/e2e/apps:go_default_library",
"//test/e2e/framework:go_default_library",
"//test/e2e/framework/metrics:go_default_library",
"//test/utils:go_default_library",
"//test/utils/image:go_default_library",
"//vendor/github.com/onsi/ginkgo:go_default_library",
"//vendor/github.com/onsi/gomega:go_default_library",
"//vendor/k8s.io/api/admissionregistration/v1alpha1:go_default_library",
"//vendor/k8s.io/api/batch/v1:go_default_library",
"//vendor/k8s.io/api/batch/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/v1beta1: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/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1alpha1: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/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/apimachinery/pkg/watch:go_default_library",
"//vendor/k8s.io/apiserver/pkg/authentication/user:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/names: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/util/cert:go_default_library",
"//vendor/k8s.io/client-go/util/retry:go_default_library",
"//vendor/k8s.io/kube-aggregator/pkg/apis/apiregistration/v1beta1:go_default_library",
"//vendor/k8s.io/sample-apiserver/pkg/apis/wardle/v1alpha1:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View file

@ -0,0 +1,537 @@
/*
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 apimachinery
import (
"crypto/rand"
"crypto/x509"
"encoding/json"
"fmt"
"io/ioutil"
"math/big"
"os"
"strings"
"time"
"k8s.io/api/core/v1"
extensions "k8s.io/api/extensions/v1beta1"
rbacv1beta1 "k8s.io/api/rbac/v1beta1"
apierrs "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
unstructuredv1 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/client-go/discovery"
"k8s.io/client-go/util/cert"
apiregistrationv1beta1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1beta1"
rbacapi "k8s.io/kubernetes/pkg/apis/rbac"
utilversion "k8s.io/kubernetes/pkg/util/version"
"k8s.io/kubernetes/test/e2e/framework"
samplev1alpha1 "k8s.io/sample-apiserver/pkg/apis/wardle/v1alpha1"
. "github.com/onsi/ginkgo"
)
type aggregatorContext struct {
apiserverCert []byte
apiserverKey []byte
apiserverSigningCert []byte
}
var serverAggregatorVersion = utilversion.MustParseSemantic("v1.7.0")
var _ = SIGDescribe("Aggregator", func() {
f := framework.NewDefaultFramework("aggregator")
framework.AddCleanupAction(func() {
cleanTest(f, false)
})
It("Should be able to support the 1.7 Sample API Server using the current Aggregator", func() {
// Make sure the relevant provider supports Agggregator
framework.SkipUnlessServerVersionGTE(serverAggregatorVersion, f.ClientSet.Discovery())
framework.SkipUnlessProviderIs("gce", "gke")
// Testing a 1.7 version of the sample-apiserver
TestSampleAPIServer(f, "gcr.io/kubernetes-e2e-test-images/k8s-aggregator-sample-apiserver-amd64:1.7", "sample-system")
})
})
func cleanTest(f *framework.Framework, block bool) {
// delete the APIService first to avoid causing discovery errors
aggrclient := f.AggregatorClient
_ = aggrclient.ApiregistrationV1beta1().APIServices().Delete("v1alpha1.wardle.k8s.io", nil)
namespace := "sample-system"
client := f.ClientSet
_ = client.ExtensionsV1beta1().Deployments(namespace).Delete("sample-apiserver", nil)
_ = client.CoreV1().Secrets(namespace).Delete("sample-apiserver-secret", nil)
_ = client.CoreV1().Services(namespace).Delete("sample-api", nil)
_ = client.CoreV1().ServiceAccounts(namespace).Delete("sample-apiserver", nil)
_ = client.RbacV1beta1().RoleBindings("kube-system").Delete("wardler-auth-reader", nil)
_ = client.CoreV1().Namespaces().Delete(namespace, nil)
_ = client.RbacV1beta1().ClusterRoles().Delete("wardler", nil)
_ = client.RbacV1beta1().ClusterRoleBindings().Delete("wardler:sample-system:anonymous", nil)
if block {
_ = wait.Poll(100*time.Millisecond, 5*time.Second, func() (bool, error) {
_, err := client.CoreV1().Namespaces().Get("sample-system", metav1.GetOptions{})
if err != nil {
if apierrs.IsNotFound(err) {
return true, nil
}
return false, err
}
return false, nil
})
}
}
func setupSampleAPIServerCert(namespaceName, serviceName string) *aggregatorContext {
aggregatorCertDir, err := ioutil.TempDir("", "test-e2e-aggregator")
if err != nil {
framework.Failf("Failed to create a temp dir for cert generation %v", err)
}
defer os.RemoveAll(aggregatorCertDir)
apiserverSigningKey, err := cert.NewPrivateKey()
if err != nil {
framework.Failf("Failed to create CA private key for apiserver %v", err)
}
apiserverSigningCert, err := cert.NewSelfSignedCACert(cert.Config{CommonName: "e2e-sampleapiserver-ca"}, apiserverSigningKey)
if err != nil {
framework.Failf("Failed to create CA cert for apiserver %v", err)
}
apiserverCACertFile, err := ioutil.TempFile(aggregatorCertDir, "apiserver-ca.crt")
if err != nil {
framework.Failf("Failed to create a temp file for ca cert generation %v", err)
}
if err := ioutil.WriteFile(apiserverCACertFile.Name(), cert.EncodeCertPEM(apiserverSigningCert), 0644); err != nil {
framework.Failf("Failed to write CA cert for apiserver %v", err)
}
apiserverKey, err := cert.NewPrivateKey()
if err != nil {
framework.Failf("Failed to create private key for apiserver %v", err)
}
apiserverCert, err := cert.NewSignedCert(
cert.Config{
CommonName: serviceName + "." + namespaceName + ".svc",
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
},
apiserverKey, apiserverSigningCert, apiserverSigningKey,
)
if err != nil {
framework.Failf("Failed to create cert for apiserver %v", err)
}
apiserverCertFile, err := ioutil.TempFile(aggregatorCertDir, "apiserver.crt")
if err != nil {
framework.Failf("Failed to create a temp file for cert generation %v", err)
}
apiserverKeyFile, err := ioutil.TempFile(aggregatorCertDir, "apiserver.key")
if err != nil {
framework.Failf("Failed to create a temp file for key generation %v", err)
}
if err := ioutil.WriteFile(apiserverCertFile.Name(), cert.EncodeCertPEM(apiserverCert), 0600); err != nil {
framework.Failf("Failed to write cert file for apiserver %v", err)
}
if err := ioutil.WriteFile(apiserverKeyFile.Name(), cert.EncodePrivateKeyPEM(apiserverKey), 0644); err != nil {
framework.Failf("Failed to write key file for apiserver %v", err)
}
return &aggregatorContext{
apiserverCert: cert.EncodeCertPEM(apiserverCert),
apiserverKey: cert.EncodePrivateKeyPEM(apiserverKey),
apiserverSigningCert: cert.EncodeCertPEM(apiserverSigningCert),
}
}
// A basic test if the sample-apiserver code from 1.7 and compiled against 1.7
// will work on the current Aggregator/API-Server.
func TestSampleAPIServer(f *framework.Framework, image, namespaceName string) {
By("Registering the sample API server.")
cleanTest(f, true)
client := f.ClientSet
restClient := client.Discovery().RESTClient()
iclient := f.InternalClientset
aggrclient := f.AggregatorClient
context := setupSampleAPIServerCert(namespaceName, "sample-api")
ns := f.Namespace.Name
if framework.ProviderIs("gke") {
// kubectl create clusterrolebinding user-cluster-admin-binding --clusterrole=cluster-admin --user=user@domain.com
authenticated := rbacv1beta1.Subject{Kind: rbacv1beta1.GroupKind, Name: user.AllAuthenticated}
framework.BindClusterRole(client.RbacV1beta1(), "cluster-admin", ns, authenticated)
}
// kubectl create -f namespace.yaml
var namespace string
err := wait.Poll(100*time.Millisecond, 30*time.Second, func() (bool, error) {
got, err := client.CoreV1().Namespaces().Create(&v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: namespaceName}})
if err != nil {
if strings.HasPrefix(err.Error(), "object is being deleted:") {
return false, nil
}
return false, err
}
namespace = got.Name
return true, nil
})
framework.ExpectNoError(err, "creating namespace %q", namespaceName)
// kubectl create -f secret.yaml
secretName := "sample-apiserver-secret"
secret := &v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: secretName,
},
Type: v1.SecretTypeOpaque,
Data: map[string][]byte{
"tls.crt": context.apiserverCert,
"tls.key": context.apiserverKey,
},
}
_, err = client.CoreV1().Secrets(namespace).Create(secret)
framework.ExpectNoError(err, "creating secret %q in namespace %q", secretName, namespace)
// kubectl create -f deploy.yaml
deploymentName := "sample-apiserver-deployment"
etcdImage := "quay.io/coreos/etcd:v3.0.17"
podLabels := map[string]string{"app": "sample-apiserver", "apiserver": "true"}
replicas := int32(1)
zero := int64(0)
mounts := []v1.VolumeMount{
{
Name: "apiserver-certs",
ReadOnly: true,
MountPath: "/apiserver.local.config/certificates",
},
}
volumes := []v1.Volume{
{
Name: "apiserver-certs",
VolumeSource: v1.VolumeSource{
Secret: &v1.SecretVolumeSource{SecretName: secretName},
},
},
}
containers := []v1.Container{
{
Name: "sample-apiserver",
VolumeMounts: mounts,
Args: []string{
"--etcd-servers=http://localhost:2379",
"--tls-cert-file=/apiserver.local.config/certificates/tls.crt",
"--tls-private-key-file=/apiserver.local.config/certificates/tls.key",
"--audit-log-path=-",
"--audit-log-maxage=0",
"--audit-log-maxbackup=0",
},
Image: image,
},
{
Name: "etcd",
Image: etcdImage,
},
}
d := &extensions.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: deploymentName,
},
Spec: extensions.DeploymentSpec{
Replicas: &replicas,
Strategy: extensions.DeploymentStrategy{
Type: extensions.RollingUpdateDeploymentStrategyType,
},
Template: v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: podLabels,
},
Spec: v1.PodSpec{
TerminationGracePeriodSeconds: &zero,
Containers: containers,
Volumes: volumes,
},
},
},
}
deployment, err := client.ExtensionsV1beta1().Deployments(namespace).Create(d)
framework.ExpectNoError(err, "creating deployment %s in namespace %s", deploymentName, namespace)
err = framework.WaitForDeploymentRevisionAndImage(client, namespace, deploymentName, "1", image)
framework.ExpectNoError(err, "waiting for the deployment of image %s in %s in %s to complete", image, deploymentName, namespace)
err = framework.WaitForDeploymentRevisionAndImage(client, namespace, deploymentName, "1", etcdImage)
framework.ExpectNoError(err, "waiting for the deployment of image %s in %s to complete", etcdImage, deploymentName, namespace)
// kubectl create -f service.yaml
serviceLabels := map[string]string{"apiserver": "true"}
service := &v1.Service{
ObjectMeta: metav1.ObjectMeta{
Namespace: namespace,
Name: "sample-api",
Labels: map[string]string{"test": "aggregator"},
},
Spec: v1.ServiceSpec{
Selector: serviceLabels,
Ports: []v1.ServicePort{
{
Protocol: "TCP",
Port: 443,
TargetPort: intstr.FromInt(443),
},
},
},
}
_, err = client.CoreV1().Services(namespace).Create(service)
framework.ExpectNoError(err, "creating service %s in namespace %s", "sample-apiserver", namespace)
// kubectl create -f serviceAccount.yaml
sa := &v1.ServiceAccount{ObjectMeta: metav1.ObjectMeta{Name: "sample-apiserver"}}
_, err = client.CoreV1().ServiceAccounts(namespace).Create(sa)
framework.ExpectNoError(err, "creating service account %s in namespace %s", "sample-apiserver", namespace)
// kubectl create -f authDelegator.yaml
_, err = client.RbacV1beta1().ClusterRoleBindings().Create(&rbacv1beta1.ClusterRoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: "wardler:" + namespace + ":anonymous",
},
RoleRef: rbacv1beta1.RoleRef{
APIGroup: "rbac.authorization.k8s.io",
Kind: "ClusterRole",
Name: "wardler",
},
Subjects: []rbacv1beta1.Subject{
{
APIGroup: "rbac.authorization.k8s.io",
Kind: "User",
Name: namespace + ":anonymous",
},
},
})
framework.ExpectNoError(err, "creating cluster role binding %s", "wardler:"+namespace+":anonymous")
// kubectl create -f role.yaml
resourceRule, err := rbacapi.NewRule("create", "delete", "deletecollection", "get", "list", "patch", "update", "watch").Groups("wardle.k8s.io").Resources("flunders").Rule()
framework.ExpectNoError(err, "creating cluster resource rule")
urlRule, err := rbacapi.NewRule("get").URLs("*").Rule()
framework.ExpectNoError(err, "creating cluster url rule")
err = wait.Poll(100*time.Millisecond, 30*time.Second, func() (bool, error) {
roleLabels := map[string]string{"kubernetes.io/bootstrapping": "wardle-default"}
role := rbacapi.ClusterRole{
ObjectMeta: metav1.ObjectMeta{
Name: "wardler",
Labels: roleLabels,
},
Rules: []rbacapi.PolicyRule{resourceRule, urlRule},
}
_, err = iclient.Rbac().ClusterRoles().Create(&role)
if err != nil {
return false, nil
}
return true, nil
})
framework.ExpectNoError(err, "creating cluster role wardler - may not have permissions")
// kubectl create -f auth-reader.yaml
_, err = client.RbacV1beta1().RoleBindings("kube-system").Create(&rbacv1beta1.RoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: "wardler-auth-reader",
Annotations: map[string]string{
rbacv1beta1.AutoUpdateAnnotationKey: "true",
},
},
RoleRef: rbacv1beta1.RoleRef{
APIGroup: "",
Kind: "Role",
Name: "extension-apiserver-authentication-reader",
},
Subjects: []rbacv1beta1.Subject{
{
Kind: "ServiceAccount",
Name: "default", // "sample-apiserver",
Namespace: namespace,
},
},
})
framework.ExpectNoError(err, "creating role binding %s:sample-apiserver to access configMap", namespace)
// kubectl create -f apiservice.yaml
_, err = aggrclient.ApiregistrationV1beta1().APIServices().Create(&apiregistrationv1beta1.APIService{
ObjectMeta: metav1.ObjectMeta{Name: "v1alpha1.wardle.k8s.io"},
Spec: apiregistrationv1beta1.APIServiceSpec{
Service: &apiregistrationv1beta1.ServiceReference{
Namespace: namespace,
Name: "sample-api",
},
Group: "wardle.k8s.io",
Version: "v1alpha1",
CABundle: context.apiserverSigningCert,
GroupPriorityMinimum: 2000,
VersionPriority: 200,
},
})
framework.ExpectNoError(err, "creating apiservice %s with namespace %s", "v1alpha1.wardle.k8s.io", namespace)
// Wait for the extension apiserver to be up and healthy
// kubectl get deployments -n sample-system && status == Running
err = framework.WaitForDeploymentStatusValid(client, deployment)
// We seem to need to do additional waiting until the extension api service is actually up.
err = wait.Poll(100*time.Millisecond, 30*time.Second, func() (bool, error) {
request := restClient.Get().AbsPath("/apis/wardle.k8s.io/v1alpha1/namespaces/default/flunders")
request.SetHeader("Accept", "application/json")
_, err := request.DoRaw()
if err != nil {
status, ok := err.(*apierrs.StatusError)
if !ok {
return false, err
}
if status.Status().Code == 404 && strings.HasPrefix(err.Error(), "the server could not find the requested resource") {
return false, nil
}
return false, err
}
return true, nil
})
framework.ExpectNoError(err, "gave up waiting for apiservice wardle to come up successfully")
flunderName := generateFlunderName("rest-flunder")
// kubectl create -f flunders-1.yaml -v 9
// curl -k -v -XPOST https://localhost/apis/wardle.k8s.io/v1alpha1/namespaces/default/flunders
// Request Body: {"apiVersion":"wardle.k8s.io/v1alpha1","kind":"Flunder","metadata":{"labels":{"sample-label":"true"},"name":"test-flunder","namespace":"default"}}
flunder := `{"apiVersion":"wardle.k8s.io/v1alpha1","kind":"Flunder","metadata":{"labels":{"sample-label":"true"},"name":"` + flunderName + `","namespace":"default"}}`
result := restClient.Post().AbsPath("/apis/wardle.k8s.io/v1alpha1/namespaces/default/flunders").Body([]byte(flunder)).Do()
framework.ExpectNoError(result.Error(), "creating a new flunders resource")
var statusCode int
result.StatusCode(&statusCode)
if statusCode != 201 {
framework.Failf("Flunders client creation response was status %d, not 201", statusCode)
}
pods, err := client.CoreV1().Pods(namespace).List(metav1.ListOptions{})
framework.ExpectNoError(result.Error(), "getting pods for flunders service")
// kubectl get flunders -v 9
// curl -k -v -XGET https://localhost/apis/wardle.k8s.io/v1alpha1/namespaces/default/flunders
contents, err := restClient.Get().AbsPath("/apis/wardle.k8s.io/v1alpha1/namespaces/default/flunders").SetHeader("Accept", "application/json").DoRaw()
framework.ExpectNoError(err, "attempting to get a newly created flunders resource")
var flundersList samplev1alpha1.FlunderList
err = json.Unmarshal(contents, &flundersList)
validateErrorWithDebugInfo(f, err, pods, "Error in unmarshalling %T response from server %s", contents, "/apis/wardle.k8s.io/v1alpha1")
if len(flundersList.Items) != 1 {
framework.Failf("failed to get back the correct flunders list %v", flundersList)
}
// kubectl delete flunder test-flunder -v 9
// curl -k -v -XDELETE https://35.193.112.40/apis/wardle.k8s.io/v1alpha1/namespaces/default/flunders/test-flunder
_, err = restClient.Delete().AbsPath("/apis/wardle.k8s.io/v1alpha1/namespaces/default/flunders/" + flunderName).DoRaw()
validateErrorWithDebugInfo(f, err, pods, "attempting to delete a newly created flunders(%v) resource", flundersList.Items)
// kubectl get flunders -v 9
// curl -k -v -XGET https://localhost/apis/wardle.k8s.io/v1alpha1/namespaces/default/flunders
contents, err = restClient.Get().AbsPath("/apis/wardle.k8s.io/v1alpha1/namespaces/default/flunders").SetHeader("Accept", "application/json").DoRaw()
framework.ExpectNoError(err, "confirming delete of a newly created flunders resource")
err = json.Unmarshal(contents, &flundersList)
validateErrorWithDebugInfo(f, err, pods, "Error in unmarshalling %T response from server %s", contents, "/apis/wardle.k8s.io/v1alpha1")
if len(flundersList.Items) != 0 {
framework.Failf("failed to get back the correct deleted flunders list %v", flundersList)
}
flunderName = generateFlunderName("dynamic-flunder")
// Rerun the Create/List/Delete tests using the Dynamic client.
resources, err := client.Discovery().ServerPreferredNamespacedResources()
framework.ExpectNoError(err, "getting server preferred namespaces resources for dynamic client")
groupVersionResources, err := discovery.GroupVersionResources(resources)
framework.ExpectNoError(err, "getting group version resources for dynamic client")
gvr := schema.GroupVersionResource{Group: "wardle.k8s.io", Version: "v1alpha1", Resource: "flunders"}
_, ok := groupVersionResources[gvr]
if !ok {
framework.Failf("could not find group version resource for dynamic client and wardle/flunders.")
}
clientPool := f.ClientPool
dynamicClient, err := clientPool.ClientForGroupVersionResource(gvr)
framework.ExpectNoError(err, "getting group version resources for dynamic client")
apiResource := metav1.APIResource{Name: gvr.Resource, Namespaced: true}
// kubectl create -f flunders-1.yaml
// Request Body: {"apiVersion":"wardle.k8s.io/v1alpha1","kind":"Flunder","metadata":{"labels":{"sample-label":"true"},"name":"test-flunder","namespace":"default"}}
testFlunder := samplev1alpha1.Flunder{
TypeMeta: metav1.TypeMeta{
Kind: "Flunder",
APIVersion: "wardle.k8s.io/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{Name: flunderName},
Spec: samplev1alpha1.FlunderSpec{},
}
jsonFlunder, err := json.Marshal(testFlunder)
framework.ExpectNoError(err, "marshalling test-flunder for create using dynamic client")
unstruct := &unstructuredv1.Unstructured{}
err = unstruct.UnmarshalJSON(jsonFlunder)
framework.ExpectNoError(err, "unmarshalling test-flunder as unstructured for create using dynamic client")
unstruct, err = dynamicClient.Resource(&apiResource, namespace).Create(unstruct)
framework.ExpectNoError(err, "listing flunders using dynamic client")
// kubectl get flunders
obj, err := dynamicClient.Resource(&apiResource, namespace).List(metav1.ListOptions{})
framework.ExpectNoError(err, "listing flunders using dynamic client")
unstructuredList, ok := obj.(*unstructuredv1.UnstructuredList)
validateErrorWithDebugInfo(f, err, pods, "casting flunders list(%T) as unstructuredList using dynamic client", obj)
if len(unstructuredList.Items) != 1 {
framework.Failf("failed to get back the correct flunders list %v from the dynamic client", unstructuredList)
}
// kubectl delete flunder test-flunder
err = dynamicClient.Resource(&apiResource, namespace).Delete(flunderName, &metav1.DeleteOptions{})
validateErrorWithDebugInfo(f, err, pods, "deleting flunders(%v) using dynamic client", unstructuredList.Items)
// kubectl get flunders
obj, err = dynamicClient.Resource(&apiResource, namespace).List(metav1.ListOptions{})
framework.ExpectNoError(err, "listing flunders using dynamic client")
unstructuredList, ok = obj.(*unstructuredv1.UnstructuredList)
validateErrorWithDebugInfo(f, err, pods, "casting flunders list(%T) as unstructuredList using dynamic client", obj)
if len(unstructuredList.Items) != 0 {
framework.Failf("failed to get back the correct deleted flunders list %v from the dynamic client", unstructuredList)
}
cleanTest(f, true)
}
func validateErrorWithDebugInfo(f *framework.Framework, err error, pods *v1.PodList, msg string, fields ...interface{}) {
if err != nil {
namespace := "sample-system"
msg := fmt.Sprintf(msg, fields...)
msg += fmt.Sprintf(" but received unexpected error:\n%v", err)
client := f.ClientSet
ep, err := client.CoreV1().Endpoints(namespace).Get("sample-api", metav1.GetOptions{})
if err == nil {
msg += fmt.Sprintf("\nFound endpoints for sample-api:\n%v", ep)
}
pds, err := client.CoreV1().Pods(namespace).List(metav1.ListOptions{})
if err == nil {
msg += fmt.Sprintf("\nFound pods in sample-system:\n%v", pds)
msg += fmt.Sprintf("\nOriginal pods in sample-system:\n%v", pods)
}
framework.Failf(msg)
}
}
func generateFlunderName(base string) string {
id, err := rand.Int(rand.Reader, big.NewInt(2147483647))
if err != nil {
return base
}
return fmt.Sprintf("%s-%d", base, id)
}

View file

@ -0,0 +1,66 @@
/*
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 apimachinery
import (
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
"k8s.io/apiextensions-apiserver/test/integration/testserver"
utilversion "k8s.io/kubernetes/pkg/util/version"
"k8s.io/kubernetes/test/e2e/framework"
. "github.com/onsi/ginkgo"
)
var crdVersion = utilversion.MustParseSemantic("v1.7.0")
var _ = SIGDescribe("CustomResourceDefinition resources", func() {
f := framework.NewDefaultFramework("custom-resource-definition")
Context("Simple CustomResourceDefinition", func() {
It("creating/deleting custom resource definition objects works [Conformance]", func() {
framework.SkipUnlessServerVersionGTE(crdVersion, f.ClientSet.Discovery())
config, err := framework.LoadConfig()
if err != nil {
framework.Failf("failed to load config: %v", err)
}
apiExtensionClient, err := clientset.NewForConfig(config)
if err != nil {
framework.Failf("failed to initialize apiExtensionClient: %v", err)
}
randomDefinition := testserver.NewRandomNameCustomResourceDefinition(v1beta1.ClusterScoped)
//create CRD and waits for the resource to be recognized and available.
_, err = testserver.CreateNewCustomResourceDefinition(randomDefinition, apiExtensionClient, f.ClientPool)
if err != nil {
framework.Failf("failed to create CustomResourceDefinition: %v", err)
}
defer func() {
err = testserver.DeleteCustomResourceDefinition(randomDefinition, apiExtensionClient)
if err != nil {
framework.Failf("failed to delete CustomResourceDefinition: %v", err)
}
}()
})
})
})

View file

@ -0,0 +1,140 @@
/*
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 apimachinery
import (
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/util/wait"
podutil "k8s.io/kubernetes/pkg/api/v1/pod"
"k8s.io/kubernetes/test/e2e/apps"
"k8s.io/kubernetes/test/e2e/framework"
testutils "k8s.io/kubernetes/test/utils"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = SIGDescribe("Etcd failure [Disruptive]", func() {
f := framework.NewDefaultFramework("etcd-failure")
BeforeEach(func() {
// This test requires:
// - SSH
// - master access
// ... so the provider check should be identical to the intersection of
// providers that provide those capabilities.
framework.SkipUnlessProviderIs("gce")
Expect(framework.RunRC(testutils.RCConfig{
Client: f.ClientSet,
Name: "baz",
Namespace: f.Namespace.Name,
Image: framework.GetPauseImageName(f.ClientSet),
Replicas: 1,
})).NotTo(HaveOccurred())
})
It("should recover from network partition with master", func() {
etcdFailTest(
f,
"sudo iptables -A INPUT -p tcp --destination-port 2379 -j DROP",
"sudo iptables -D INPUT -p tcp --destination-port 2379 -j DROP",
)
})
It("should recover from SIGKILL", func() {
etcdFailTest(
f,
"pgrep etcd | xargs -I {} sudo kill -9 {}",
"echo 'do nothing. monit should restart etcd.'",
)
})
})
func etcdFailTest(f *framework.Framework, failCommand, fixCommand string) {
doEtcdFailure(failCommand, fixCommand)
checkExistingRCRecovers(f)
apps.TestReplicationControllerServeImageOrFail(f, "basic", framework.ServeHostnameImage)
}
// For this duration, etcd will be failed by executing a failCommand on the master.
// If repeat is true, the failCommand will be called at a rate of once per second for
// the failure duration. If repeat is false, failCommand will only be called once at the
// beginning of the failure duration. After this duration, we execute a fixCommand on the
// master and go on to assert that etcd and kubernetes components recover.
const etcdFailureDuration = 20 * time.Second
func doEtcdFailure(failCommand, fixCommand string) {
By("failing etcd")
masterExec(failCommand)
time.Sleep(etcdFailureDuration)
masterExec(fixCommand)
}
func masterExec(cmd string) {
result, err := framework.SSH(cmd, framework.GetMasterHost()+":22", framework.TestContext.Provider)
Expect(err).NotTo(HaveOccurred())
if result.Code != 0 {
framework.LogSSHResult(result)
framework.Failf("master exec command returned non-zero")
}
}
func checkExistingRCRecovers(f *framework.Framework) {
By("assert that the pre-existing replication controller recovers")
podClient := f.ClientSet.Core().Pods(f.Namespace.Name)
rcSelector := labels.Set{"name": "baz"}.AsSelector()
By("deleting pods from existing replication controller")
framework.ExpectNoError(wait.Poll(time.Millisecond*500, time.Second*60, func() (bool, error) {
options := metav1.ListOptions{LabelSelector: rcSelector.String()}
pods, err := podClient.List(options)
if err != nil {
framework.Logf("apiserver returned error, as expected before recovery: %v", err)
return false, nil
}
if len(pods.Items) == 0 {
return false, nil
}
for _, pod := range pods.Items {
err = podClient.Delete(pod.Name, metav1.NewDeleteOptions(0))
Expect(err).NotTo(HaveOccurred())
}
framework.Logf("apiserver has recovered")
return true, nil
}))
By("waiting for replication controller to recover")
framework.ExpectNoError(wait.Poll(time.Millisecond*500, time.Second*60, func() (bool, error) {
options := metav1.ListOptions{LabelSelector: rcSelector.String()}
pods, err := podClient.List(options)
Expect(err).NotTo(HaveOccurred())
for _, pod := range pods.Items {
if pod.DeletionTimestamp == nil && podutil.IsPodReady(&pod) {
return true, nil
}
}
return false, nil
}))
}

View file

@ -0,0 +1,23 @@
/*
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 apimachinery
import "github.com/onsi/ginkgo"
func SIGDescribe(text string, body func()) bool {
return ginkgo.Describe("[sig-api-machinery] "+text, body)
}

View file

@ -0,0 +1,973 @@
/*
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 apimachinery
import (
"fmt"
"time"
batchv1 "k8s.io/api/batch/v1"
batchv1beta1 "k8s.io/api/batch/v1beta1"
"k8s.io/api/core/v1"
"k8s.io/api/extensions/v1beta1"
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
apiextensionsclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
apiextensionstestserver "k8s.io/apiextensions-apiserver/test/integration/testserver"
"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/types"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apiserver/pkg/storage/names"
clientset "k8s.io/client-go/kubernetes"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/test/e2e/framework"
"k8s.io/kubernetes/test/e2e/framework/metrics"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
imageutils "k8s.io/kubernetes/test/utils/image"
)
func getForegroundOptions() *metav1.DeleteOptions {
policy := metav1.DeletePropagationForeground
return &metav1.DeleteOptions{PropagationPolicy: &policy}
}
func getBackgroundOptions() *metav1.DeleteOptions {
policy := metav1.DeletePropagationBackground
return &metav1.DeleteOptions{PropagationPolicy: &policy}
}
func getOrphanOptions() *metav1.DeleteOptions {
var trueVar = true
return &metav1.DeleteOptions{OrphanDependents: &trueVar}
}
func getNonOrphanOptions() *metav1.DeleteOptions {
var falseVar = false
return &metav1.DeleteOptions{OrphanDependents: &falseVar}
}
var (
zero = int64(0)
CronJobGroupVersionResource = schema.GroupVersionResource{Group: batchv1beta1.GroupName, Version: "v1beta1", Resource: "cronjobs"}
)
func getPodTemplateSpec(labels map[string]string) v1.PodTemplateSpec {
return v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: labels,
},
Spec: v1.PodSpec{
TerminationGracePeriodSeconds: &zero,
Containers: []v1.Container{
{
Name: "nginx",
Image: imageutils.GetE2EImage(imageutils.NginxSlim),
},
},
},
}
}
func newOwnerDeployment(f *framework.Framework, deploymentName string, labels map[string]string) *v1beta1.Deployment {
replicas := int32(2)
return &v1beta1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: deploymentName,
},
Spec: v1beta1.DeploymentSpec{
Replicas: &replicas,
Selector: &metav1.LabelSelector{MatchLabels: labels},
Strategy: v1beta1.DeploymentStrategy{
Type: v1beta1.RollingUpdateDeploymentStrategyType,
},
Template: getPodTemplateSpec(labels),
},
}
}
func getSelector() map[string]string {
return map[string]string{"app": "gc-test"}
}
func newOwnerRC(f *framework.Framework, name string, replicas int32, labels map[string]string) *v1.ReplicationController {
template := getPodTemplateSpec(labels)
return &v1.ReplicationController{
TypeMeta: metav1.TypeMeta{
Kind: "ReplicationController",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: f.Namespace.Name,
Name: name,
},
Spec: v1.ReplicationControllerSpec{
Replicas: &replicas,
Selector: labels,
Template: &template,
},
}
}
// verifyRemainingDeploymentsReplicaSetsPods verifies if the number
// of the remaining deployments, replica set and pods are deploymentNum,
// rsNum and podNum. It returns error if the communication with the API
// server fails.
func verifyRemainingDeploymentsReplicaSetsPods(
f *framework.Framework,
clientSet clientset.Interface,
deployment *v1beta1.Deployment,
deploymentNum, rsNum, podNum int,
) (bool, error) {
var ret = true
rs, err := clientSet.Extensions().ReplicaSets(f.Namespace.Name).List(metav1.ListOptions{})
if err != nil {
return false, fmt.Errorf("Failed to list rs: %v", err)
}
if len(rs.Items) != rsNum {
ret = false
By(fmt.Sprintf("expected %d rs, got %d rs", rsNum, len(rs.Items)))
}
deployments, err := clientSet.Extensions().Deployments(f.Namespace.Name).List(metav1.ListOptions{})
if err != nil {
return false, fmt.Errorf("Failed to list deployments: %v", err)
}
if len(deployments.Items) != deploymentNum {
ret = false
By(fmt.Sprintf("expected %d Deploymentss, got %d Deployments", deploymentNum, len(deployments.Items)))
}
pods, err := clientSet.CoreV1().Pods(f.Namespace.Name).List(metav1.ListOptions{})
if err != nil {
return false, fmt.Errorf("Failed to list pods: %v", err)
}
if len(pods.Items) != podNum {
ret = false
By(fmt.Sprintf("expected %v Pods, got %d Pods", podNum, len(pods.Items)))
}
return ret, nil
}
func newGCPod(name string) *v1.Pod {
return &v1.Pod{
TypeMeta: metav1.TypeMeta{
Kind: "Pod",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
Spec: v1.PodSpec{
TerminationGracePeriodSeconds: new(int64),
Containers: []v1.Container{
{
Name: "nginx",
Image: imageutils.GetE2EImage(imageutils.NginxSlim),
},
},
},
}
}
// verifyRemainingReplicationControllersPods 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 verifyRemainingReplicationControllersPods(f *framework.Framework, clientSet clientset.Interface, rcNum, podNum int) (bool, error) {
rcClient := clientSet.Core().ReplicationControllers(f.Namespace.Name)
pods, err := clientSet.Core().Pods(f.Namespace.Name).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
By(fmt.Sprintf("expected %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
By(fmt.Sprintf("expected %d RCs, got %d RCs", rcNum, len(rcs.Items)))
}
return ret, nil
}
// verifyRemainingCronJobsJobsPods verifies if the number of remaining cronjobs,
// jobs and pods. It returns error if the communication with the API server fails.
func verifyRemainingCronJobsJobsPods(f *framework.Framework, clientSet clientset.Interface,
cjNum, jobNum, podNum int) (bool, error) {
var ret = true
cronJobs, err := f.ClientSet.BatchV1beta1().CronJobs(f.Namespace.Name).List(metav1.ListOptions{})
if err != nil {
return false, fmt.Errorf("Failed to list cronjobs: %v", err)
}
if len(cronJobs.Items) != cjNum {
ret = false
By(fmt.Sprintf("expected %d cronjobs, got %d cronjobs", cjNum, len(cronJobs.Items)))
}
jobs, err := f.ClientSet.Batch().Jobs(f.Namespace.Name).List(metav1.ListOptions{})
if err != nil {
return false, fmt.Errorf("Failed to list jobs: %v", err)
}
if len(jobs.Items) != jobNum {
ret = false
By(fmt.Sprintf("expected %d jobs, got %d jobs", jobNum, len(jobs.Items)))
}
pods, err := f.ClientSet.Core().Pods(f.Namespace.Name).List(metav1.ListOptions{})
if err != nil {
return false, fmt.Errorf("Failed to list pods: %v", err)
}
if len(pods.Items) != podNum {
ret = false
By(fmt.Sprintf("expected %d pods, got %d pods", podNum, len(pods.Items)))
}
return ret, nil
}
func gatherMetrics(f *framework.Framework) {
By("Gathering metrics")
var summary framework.TestDataSummary
grabber, err := metrics.NewMetricsGrabber(f.ClientSet, f.KubemarkExternalClusterClientSet, false, false, true, false, false)
if err != nil {
framework.Logf("Failed to create MetricsGrabber. Skipping metrics gathering.")
} else {
received, err := grabber.Grab()
if err != nil {
framework.Logf("MetricsGrabber failed grab metrics. Skipping metrics gathering.")
} else {
summary = (*framework.MetricsForE2E)(&received)
framework.Logf(summary.PrintHumanReadable())
}
}
}
func newCronJob(name, schedule string) *batchv1beta1.CronJob {
parallelism := int32(1)
completions := int32(1)
return &batchv1beta1.CronJob{
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
TypeMeta: metav1.TypeMeta{
Kind: "CronJob",
},
Spec: batchv1beta1.CronJobSpec{
Schedule: schedule,
JobTemplate: batchv1beta1.JobTemplateSpec{
Spec: batchv1.JobSpec{
Parallelism: &parallelism,
Completions: &completions,
Template: v1.PodTemplateSpec{
Spec: v1.PodSpec{
RestartPolicy: v1.RestartPolicyOnFailure,
TerminationGracePeriodSeconds: &zero,
Containers: []v1.Container{
{
Name: "c",
Image: imageutils.GetBusyBoxImage(),
Command: []string{"sleep", "300"},
},
},
},
},
},
},
},
}
}
var _ = SIGDescribe("Garbage collector", func() {
f := framework.NewDefaultFramework("gc")
It("should delete pods created by rc when not orphaning", func() {
clientSet := f.ClientSet
rcClient := clientSet.Core().ReplicationControllers(f.Namespace.Name)
podClient := clientSet.Core().Pods(f.Namespace.Name)
rcName := "simpletest.rc"
// TODO: find better way to keep this label unique in the test
uniqLabels := map[string]string{"gctest": "delete_pods"}
rc := newOwnerRC(f, rcName, 2, uniqLabels)
By("create the rc")
rc, err := rcClient.Create(rc)
if err != nil {
framework.Failf("Failed to create replication controller: %v", err)
}
// wait for rc to create some pods
if err := wait.Poll(5*time.Second, 30*time.Second, func() (bool, error) {
pods, err := podClient.List(metav1.ListOptions{})
if err != nil {
return false, fmt.Errorf("Failed to list pods: %v", err)
}
// We intentionally don't wait the number of pods to reach
// rc.Spec.Replicas. We want to see if the garbage collector and the
// rc manager work properly if the rc is deleted before it reaches
// stasis.
if len(pods.Items) > 0 {
return true, nil
} else {
return false, nil
}
}); err != nil {
framework.Failf("failed to wait for the rc to create some pods: %v", err)
}
By("delete the rc")
deleteOptions := getNonOrphanOptions()
deleteOptions.Preconditions = metav1.NewUIDPreconditions(string(rc.UID))
if err := rcClient.Delete(rc.ObjectMeta.Name, deleteOptions); err != nil {
framework.Failf("failed to delete the rc: %v", err)
}
By("wait for all pods to be garbage collected")
// wait for the RCs and Pods to reach the expected numbers.
if err := wait.Poll(5*time.Second, 60*time.Second, func() (bool, error) {
return verifyRemainingReplicationControllersPods(f, clientSet, 0, 0)
}); err != nil {
framework.Failf("failed to wait for all pods to be deleted: %v", err)
remainingPods, err := podClient.List(metav1.ListOptions{})
if err != nil {
framework.Failf("failed to list pods post mortem: %v", err)
} else {
framework.Failf("remaining pods are: %#v", remainingPods)
}
}
gatherMetrics(f)
})
It("should orphan pods created by rc if delete options say so", func() {
clientSet := f.ClientSet
rcClient := clientSet.Core().ReplicationControllers(f.Namespace.Name)
podClient := clientSet.Core().Pods(f.Namespace.Name)
rcName := "simpletest.rc"
// TODO: find better way to keep this label unique in the test
uniqLabels := map[string]string{"gctest": "orphan_pods"}
rc := newOwnerRC(f, rcName, 100, uniqLabels)
By("create the rc")
rc, err := rcClient.Create(rc)
if err != nil {
framework.Failf("Failed to create replication controller: %v", err)
}
// wait for rc to create pods
if err := wait.Poll(5*time.Second, 30*time.Second, func() (bool, error) {
rc, err := rcClient.Get(rc.Name, metav1.GetOptions{})
if err != nil {
return false, fmt.Errorf("Failed to get rc: %v", err)
}
if rc.Status.Replicas == *rc.Spec.Replicas {
return true, nil
} else {
return false, nil
}
}); err != nil {
framework.Failf("failed to wait for the rc.Status.Replicas to reach rc.Spec.Replicas: %v", err)
}
By("delete the rc")
deleteOptions := getOrphanOptions()
deleteOptions.Preconditions = metav1.NewUIDPreconditions(string(rc.UID))
if err := rcClient.Delete(rc.ObjectMeta.Name, deleteOptions); err != nil {
framework.Failf("failed to delete the rc: %v", err)
}
By("wait for the rc to be deleted")
// Orphaning the 100 pods takes 100 PATCH operations. The default qps of
// a client is 5. If the qps is saturated, it will take 20s to orphan
// the pods. However, apiserver takes hundreds of ms to finish one
// PATCH, and the gc sends the patching in a single thread, so the
// actual qps is less than 5. Also, the e2e tests are running in
// parallel, the GC controller might get distracted by other tests.
// According to the test logs, 120s is enough time.
if err := wait.Poll(5*time.Second, 120*time.Second, func() (bool, error) {
rcs, err := rcClient.List(metav1.ListOptions{})
if err != nil {
return false, fmt.Errorf("Failed to list rcs: %v", err)
}
if len(rcs.Items) != 0 {
return false, nil
}
return true, nil
}); err != nil {
framework.Failf("%v", err)
}
By("wait for 30 seconds to see if the garbage collector mistakenly deletes the pods")
if err := wait.Poll(5*time.Second, 30*time.Second, func() (bool, error) {
pods, err := podClient.List(metav1.ListOptions{})
if err != nil {
return false, fmt.Errorf("Failed to list pods: %v", err)
}
if e, a := int(*(rc.Spec.Replicas)), len(pods.Items); e != a {
return false, fmt.Errorf("expect %d pods, got %d pods", e, a)
}
return false, nil
}); err != nil && err != wait.ErrWaitTimeout {
framework.Failf("%v", err)
}
gatherMetrics(f)
})
It("should orphan pods created by rc if deleteOptions.OrphanDependents is nil", func() {
clientSet := f.ClientSet
rcClient := clientSet.Core().ReplicationControllers(f.Namespace.Name)
podClient := clientSet.Core().Pods(f.Namespace.Name)
rcName := "simpletest.rc"
// TODO: find better way to keep this label unique in the test
uniqLabels := map[string]string{"gctest": "orphan_pods_nil_option"}
rc := newOwnerRC(f, rcName, 2, uniqLabels)
By("create the rc")
rc, err := rcClient.Create(rc)
if err != nil {
framework.Failf("Failed to create replication controller: %v", err)
}
// wait for rc to create some pods
if err := wait.Poll(5*time.Second, 30*time.Second, func() (bool, error) {
rc, err := rcClient.Get(rc.Name, metav1.GetOptions{})
if err != nil {
return false, fmt.Errorf("Failed to get rc: %v", err)
}
if rc.Status.Replicas == *rc.Spec.Replicas {
return true, nil
} else {
return false, nil
}
}); err != nil {
framework.Failf("failed to wait for the rc.Status.Replicas to reach rc.Spec.Replicas: %v", err)
}
By("delete the rc")
deleteOptions := &metav1.DeleteOptions{}
deleteOptions.Preconditions = metav1.NewUIDPreconditions(string(rc.UID))
if err := rcClient.Delete(rc.ObjectMeta.Name, deleteOptions); err != nil {
framework.Failf("failed to delete the rc: %v", err)
}
By("wait for 30 seconds to see if the garbage collector mistakenly deletes the pods")
if err := wait.Poll(5*time.Second, 30*time.Second, func() (bool, error) {
pods, err := podClient.List(metav1.ListOptions{})
if err != nil {
return false, fmt.Errorf("Failed to list pods: %v", err)
}
if e, a := int(*(rc.Spec.Replicas)), len(pods.Items); e != a {
return false, fmt.Errorf("expect %d pods, got %d pods", e, a)
}
return false, nil
}); err != nil && err != wait.ErrWaitTimeout {
framework.Failf("%v", err)
}
gatherMetrics(f)
})
It("should delete RS created by deployment when not orphaning", func() {
clientSet := f.ClientSet
deployClient := clientSet.Extensions().Deployments(f.Namespace.Name)
rsClient := clientSet.Extensions().ReplicaSets(f.Namespace.Name)
deploymentName := "simpletest.deployment"
// TODO: find better way to keep this label unique in the test
uniqLabels := map[string]string{"gctest": "delete_rs"}
deployment := newOwnerDeployment(f, deploymentName, uniqLabels)
By("create the deployment")
createdDeployment, err := deployClient.Create(deployment)
if err != nil {
framework.Failf("Failed to create deployment: %v", err)
}
// wait for deployment to create some rs
By("Wait for the Deployment to create new ReplicaSet")
err = wait.PollImmediate(500*time.Millisecond, 1*time.Minute, func() (bool, error) {
rsList, err := rsClient.List(metav1.ListOptions{})
if err != nil {
return false, fmt.Errorf("Failed to list rs: %v", err)
}
return len(rsList.Items) > 0, nil
})
if err != nil {
framework.Failf("Failed to wait for the Deployment to create some ReplicaSet: %v", err)
}
By("delete the deployment")
deleteOptions := getNonOrphanOptions()
deleteOptions.Preconditions = metav1.NewUIDPreconditions(string(createdDeployment.UID))
if err := deployClient.Delete(deployment.ObjectMeta.Name, deleteOptions); err != nil {
framework.Failf("failed to delete the deployment: %v", err)
}
By("wait for all rs to be garbage collected")
err = wait.PollImmediate(500*time.Millisecond, 1*time.Minute, func() (bool, error) {
return verifyRemainingDeploymentsReplicaSetsPods(f, clientSet, deployment, 0, 0, 0)
})
if err == wait.ErrWaitTimeout {
err = fmt.Errorf("Failed to wait for all rs to be garbage collected: %v", err)
remainingRSs, err := rsClient.List(metav1.ListOptions{})
if err != nil {
framework.Failf("failed to list RSs post mortem: %v", err)
} else {
framework.Failf("remaining rs are: %#v", remainingRSs)
}
}
gatherMetrics(f)
})
It("should orphan RS created by deployment when deleteOptions.OrphanDependents is true", func() {
clientSet := f.ClientSet
deployClient := clientSet.Extensions().Deployments(f.Namespace.Name)
rsClient := clientSet.Extensions().ReplicaSets(f.Namespace.Name)
deploymentName := "simpletest.deployment"
// TODO: find better way to keep this label unique in the test
uniqLabels := map[string]string{"gctest": "orphan_rs"}
deployment := newOwnerDeployment(f, deploymentName, uniqLabels)
By("create the deployment")
createdDeployment, err := deployClient.Create(deployment)
if err != nil {
framework.Failf("Failed to create deployment: %v", err)
}
// wait for deployment to create some rs
By("Wait for the Deployment to create new ReplicaSet")
err = wait.PollImmediate(500*time.Millisecond, 1*time.Minute, func() (bool, error) {
rsList, err := rsClient.List(metav1.ListOptions{})
if err != nil {
return false, fmt.Errorf("Failed to list rs: %v", err)
}
return len(rsList.Items) > 0, nil
})
if err != nil {
framework.Failf("Failed to wait for the Deployment to create some ReplicaSet: %v", err)
}
By("delete the deployment")
deleteOptions := getOrphanOptions()
deleteOptions.Preconditions = metav1.NewUIDPreconditions(string(createdDeployment.UID))
if err := deployClient.Delete(deployment.ObjectMeta.Name, deleteOptions); err != nil {
framework.Failf("failed to delete the deployment: %v", err)
}
By("wait for 2 Minute to see if the garbage collector mistakenly deletes the rs")
err = wait.PollImmediate(5*time.Second, 2*time.Minute, func() (bool, error) {
return verifyRemainingDeploymentsReplicaSetsPods(f, clientSet, deployment, 0, 1, 2)
})
if err != nil {
err = fmt.Errorf("Failed to wait to see if the garbage collecter mistakenly deletes the rs: %v", err)
remainingRSs, err := rsClient.List(metav1.ListOptions{})
if err != nil {
framework.Failf("failed to list RSs post mortem: %v", err)
} else {
framework.Failf("remaining rs post mortem: %#v", remainingRSs)
}
remainingDSs, err := deployClient.List(metav1.ListOptions{})
if err != nil {
framework.Failf("failed to list Deployments post mortem: %v", err)
} else {
framework.Failf("remaining deployment's post mortem: %#v", remainingDSs)
}
}
rs, err := clientSet.Extensions().ReplicaSets(f.Namespace.Name).List(metav1.ListOptions{})
if err != nil {
framework.Failf("Failed to list ReplicaSet %v", err)
}
for _, replicaSet := range rs.Items {
if metav1.GetControllerOf(&replicaSet.ObjectMeta) != nil {
framework.Failf("Found ReplicaSet with non nil ownerRef %v", replicaSet)
}
}
gatherMetrics(f)
})
It("should keep the rc around until all its pods are deleted if the deleteOptions says so", func() {
clientSet := f.ClientSet
rcClient := clientSet.Core().ReplicationControllers(f.Namespace.Name)
podClient := clientSet.Core().Pods(f.Namespace.Name)
rcName := "simpletest.rc"
// TODO: find better way to keep this label unique in the test
uniqLabels := map[string]string{"gctest": "delete_pods_foreground"}
rc := newOwnerRC(f, rcName, 100, uniqLabels)
By("create the rc")
rc, err := rcClient.Create(rc)
if err != nil {
framework.Failf("Failed to create replication controller: %v", err)
}
// wait for rc to create pods
if err := wait.Poll(5*time.Second, 30*time.Second, func() (bool, error) {
rc, err := rcClient.Get(rc.Name, metav1.GetOptions{})
if err != nil {
return false, fmt.Errorf("Failed to get rc: %v", err)
}
if rc.Status.Replicas == *rc.Spec.Replicas {
return true, nil
} else {
return false, nil
}
}); err != nil {
framework.Failf("failed to wait for the rc.Status.Replicas to reach rc.Spec.Replicas: %v", err)
}
By("delete the rc")
deleteOptions := getForegroundOptions()
deleteOptions.Preconditions = metav1.NewUIDPreconditions(string(rc.UID))
if err := rcClient.Delete(rc.ObjectMeta.Name, deleteOptions); err != nil {
framework.Failf("failed to delete the rc: %v", err)
}
By("wait for the rc to be deleted")
// default client QPS is 20, deleting each pod requires 2 requests, so 30s should be enough
// TODO: 30s is enough assuming immediate processing of dependents following
// owner deletion, but in practice there can be a long delay between owner
// deletion and dependent deletion processing. For now, increase the timeout
// and investigate the processing delay.
if err := wait.Poll(1*time.Second, 60*time.Second, func() (bool, error) {
_, err := rcClient.Get(rc.Name, metav1.GetOptions{})
if err == nil {
pods, _ := podClient.List(metav1.ListOptions{})
framework.Logf("%d pods remaining", len(pods.Items))
count := 0
for _, pod := range pods.Items {
if pod.ObjectMeta.DeletionTimestamp == nil {
count++
}
}
framework.Logf("%d pods has nil DeletionTimestamp", count)
framework.Logf("")
return false, nil
} else {
if errors.IsNotFound(err) {
return true, nil
} else {
return false, err
}
}
}); err != nil {
pods, err2 := podClient.List(metav1.ListOptions{})
if err2 != nil {
framework.Failf("%v", err2)
}
framework.Logf("%d remaining pods are:", len(pods.Items))
framework.Logf("The ObjectMeta of the remaining pods are:")
for _, pod := range pods.Items {
framework.Logf("%#v", pod.ObjectMeta)
}
framework.Failf("failed to delete the rc: %v", err)
}
// There shouldn't be any pods
pods, err := podClient.List(metav1.ListOptions{})
if err != nil {
framework.Failf("%v", err)
}
if len(pods.Items) != 0 {
framework.Failf("expected no pods, got %#v", pods)
}
gatherMetrics(f)
})
// TODO: this should be an integration test
It("should not delete dependents that have both valid owner and owner that's waiting for dependents to be deleted", func() {
clientSet := f.ClientSet
rcClient := clientSet.Core().ReplicationControllers(f.Namespace.Name)
podClient := clientSet.Core().Pods(f.Namespace.Name)
rc1Name := "simpletest-rc-to-be-deleted"
replicas := int32(100)
halfReplicas := int(replicas / 2)
// TODO: find better way to keep this label unique in the test
uniqLabels := map[string]string{"gctest": "valid_and_pending_owners"}
rc1 := newOwnerRC(f, rc1Name, replicas, uniqLabels)
By("create the rc1")
rc1, err := rcClient.Create(rc1)
if err != nil {
framework.Failf("Failed to create replication controller: %v", err)
}
rc2Name := "simpletest-rc-to-stay"
// TODO: find better way to keep this label unique in the test
uniqLabels = map[string]string{"another.key": "another.value"}
rc2 := newOwnerRC(f, rc2Name, 0, uniqLabels)
By("create the rc2")
rc2, err = rcClient.Create(rc2)
if err != nil {
framework.Failf("Failed to create replication controller: %v", err)
}
// wait for rc1 to be stable
if err := wait.Poll(5*time.Second, 30*time.Second, func() (bool, error) {
rc1, err := rcClient.Get(rc1.Name, metav1.GetOptions{})
if err != nil {
return false, fmt.Errorf("Failed to get rc: %v", err)
}
if rc1.Status.Replicas == *rc1.Spec.Replicas {
return true, nil
} else {
return false, nil
}
}); err != nil {
framework.Failf("failed to wait for the rc.Status.Replicas to reach rc.Spec.Replicas: %v", err)
}
By(fmt.Sprintf("set half of pods created by rc %s to have rc %s as owner as well", rc1Name, rc2Name))
pods, err := podClient.List(metav1.ListOptions{})
Expect(err).NotTo(HaveOccurred())
patch := fmt.Sprintf(`{"metadata":{"ownerReferences":[{"apiVersion":"v1","kind":"ReplicationController","name":"%s","uid":"%s"}]}}`, rc2.ObjectMeta.Name, rc2.ObjectMeta.UID)
for i := 0; i < halfReplicas; i++ {
pod := pods.Items[i]
_, err := podClient.Patch(pod.Name, types.StrategicMergePatchType, []byte(patch))
Expect(err).NotTo(HaveOccurred())
}
By(fmt.Sprintf("delete the rc %s", rc1Name))
deleteOptions := getForegroundOptions()
deleteOptions.Preconditions = metav1.NewUIDPreconditions(string(rc1.UID))
if err := rcClient.Delete(rc1.ObjectMeta.Name, deleteOptions); err != nil {
framework.Failf("failed to delete the rc: %v", err)
}
By("wait for the rc to be deleted")
// TODO: shorten the timeout when we make GC's periodic API rediscovery more efficient.
// Tracked at https://github.com/kubernetes/kubernetes/issues/50046.
if err := wait.Poll(5*time.Second, 90*time.Second, func() (bool, error) {
_, err := rcClient.Get(rc1.Name, metav1.GetOptions{})
if err == nil {
pods, _ := podClient.List(metav1.ListOptions{})
framework.Logf("%d pods remaining", len(pods.Items))
count := 0
for _, pod := range pods.Items {
if pod.ObjectMeta.DeletionTimestamp == nil {
count++
}
}
framework.Logf("%d pods has nil DeletionTimestamp", count)
framework.Logf("")
return false, nil
} else {
if errors.IsNotFound(err) {
return true, nil
} else {
return false, err
}
}
}); err != nil {
pods, err2 := podClient.List(metav1.ListOptions{})
if err2 != nil {
framework.Failf("%v", err2)
}
framework.Logf("%d remaining pods are:", len(pods.Items))
framework.Logf("ObjectMeta of remaining pods are:")
for _, pod := range pods.Items {
framework.Logf("%#v", pod.ObjectMeta)
}
framework.Failf("failed to delete rc %s, err: %v", rc1Name, err)
}
// half of the pods should still exist,
pods, err = podClient.List(metav1.ListOptions{})
if err != nil {
framework.Failf("%v", err)
}
if len(pods.Items) != halfReplicas {
framework.Failf("expected %d pods, got %d", halfReplicas, len(pods.Items))
}
for _, pod := range pods.Items {
if pod.ObjectMeta.DeletionTimestamp != nil {
framework.Failf("expected pod DeletionTimestamp to be nil, got %#v", pod.ObjectMeta)
}
// they should only have 1 ownerReference left
if len(pod.ObjectMeta.OwnerReferences) != 1 {
framework.Failf("expected pod to only have 1 owner, got %#v", pod.ObjectMeta.OwnerReferences)
}
}
gatherMetrics(f)
})
// TODO: should be an integration test
It("should not be blocked by dependency circle", func() {
clientSet := f.ClientSet
podClient := clientSet.Core().Pods(f.Namespace.Name)
pod1 := newGCPod("pod1")
pod1, err := podClient.Create(pod1)
Expect(err).NotTo(HaveOccurred())
pod2 := newGCPod("pod2")
pod2, err = podClient.Create(pod2)
Expect(err).NotTo(HaveOccurred())
pod3 := newGCPod("pod3")
pod3, err = podClient.Create(pod3)
Expect(err).NotTo(HaveOccurred())
// create circular dependency
addRefPatch := func(name string, uid types.UID) []byte {
return []byte(fmt.Sprintf(`{"metadata":{"ownerReferences":[{"apiVersion":"v1","kind":"Pod","name":"%s","uid":"%s","controller":true,"blockOwnerDeletion":true}]}}`, name, uid))
}
pod1, err = podClient.Patch(pod1.Name, types.StrategicMergePatchType, addRefPatch(pod3.Name, pod3.UID))
Expect(err).NotTo(HaveOccurred())
framework.Logf("pod1.ObjectMeta.OwnerReferences=%#v", pod1.ObjectMeta.OwnerReferences)
pod2, err = podClient.Patch(pod2.Name, types.StrategicMergePatchType, addRefPatch(pod1.Name, pod1.UID))
Expect(err).NotTo(HaveOccurred())
framework.Logf("pod2.ObjectMeta.OwnerReferences=%#v", pod2.ObjectMeta.OwnerReferences)
pod3, err = podClient.Patch(pod3.Name, types.StrategicMergePatchType, addRefPatch(pod2.Name, pod2.UID))
Expect(err).NotTo(HaveOccurred())
framework.Logf("pod3.ObjectMeta.OwnerReferences=%#v", pod3.ObjectMeta.OwnerReferences)
// delete one pod, should result in the deletion of all pods
deleteOptions := getForegroundOptions()
deleteOptions.Preconditions = metav1.NewUIDPreconditions(string(pod1.UID))
err = podClient.Delete(pod1.ObjectMeta.Name, deleteOptions)
Expect(err).NotTo(HaveOccurred())
var pods *v1.PodList
var err2 error
// TODO: shorten the timeout when we make GC's periodic API rediscovery more efficient.
// Tracked at https://github.com/kubernetes/kubernetes/issues/50046.
if err := wait.Poll(5*time.Second, 90*time.Second, func() (bool, error) {
pods, err2 = podClient.List(metav1.ListOptions{})
if err2 != nil {
return false, fmt.Errorf("Failed to list pods: %v", err)
}
if len(pods.Items) == 0 {
return true, nil
} else {
return false, nil
}
}); err != nil {
framework.Logf("pods are %#v", pods.Items)
framework.Failf("failed to wait for all pods to be deleted: %v", err)
}
})
It("should support cascading deletion of custom resources", func() {
config, err := framework.LoadConfig()
if err != nil {
framework.Failf("failed to load config: %v", err)
}
apiExtensionClient, err := apiextensionsclientset.NewForConfig(config)
if err != nil {
framework.Failf("failed to initialize apiExtensionClient: %v", err)
}
// Create a random custom resource definition and ensure it's available for
// use.
definition := apiextensionstestserver.NewRandomNameCustomResourceDefinition(apiextensionsv1beta1.ClusterScoped)
defer func() {
err = apiextensionstestserver.DeleteCustomResourceDefinition(definition, apiExtensionClient)
if err != nil && !errors.IsNotFound(err) {
framework.Failf("failed to delete CustomResourceDefinition: %v", err)
}
}()
client, err := apiextensionstestserver.CreateNewCustomResourceDefinition(definition, apiExtensionClient, f.ClientPool)
if err != nil {
framework.Failf("failed to create CustomResourceDefinition: %v", err)
}
// Get a client for the custom resource.
resourceClient := client.Resource(&metav1.APIResource{
Name: definition.Spec.Names.Plural,
Namespaced: false,
}, api.NamespaceNone)
apiVersion := definition.Spec.Group + "/" + definition.Spec.Version
// Create a custom owner resource.
ownerName := names.SimpleNameGenerator.GenerateName("owner")
owner := &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": apiVersion,
"kind": definition.Spec.Names.Kind,
"metadata": map[string]interface{}{
"name": ownerName,
},
},
}
persistedOwner, err := resourceClient.Create(owner)
if err != nil {
framework.Failf("failed to create owner resource %q: %v", ownerName, err)
}
framework.Logf("created owner resource %q", ownerName)
// Create a custom dependent resource.
dependentName := names.SimpleNameGenerator.GenerateName("dependent")
dependent := &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": apiVersion,
"kind": definition.Spec.Names.Kind,
"metadata": map[string]interface{}{
"name": dependentName,
"ownerReferences": []map[string]string{
{
"uid": string(persistedOwner.GetUID()),
"apiVersion": apiVersion,
"kind": definition.Spec.Names.Kind,
"name": ownerName,
},
},
},
},
}
persistedDependent, err := resourceClient.Create(dependent)
if err != nil {
framework.Failf("failed to create dependent resource %q: %v", dependentName, err)
}
framework.Logf("created dependent resource %q", dependentName)
// Delete the owner.
background := metav1.DeletePropagationBackground
err = resourceClient.Delete(ownerName, &metav1.DeleteOptions{PropagationPolicy: &background})
if err != nil {
framework.Failf("failed to delete owner resource %q: %v", ownerName, err)
}
// Ensure the dependent is deleted.
if err := wait.Poll(5*time.Second, 60*time.Second, func() (bool, error) {
_, err := resourceClient.Get(dependentName, metav1.GetOptions{})
return errors.IsNotFound(err), nil
}); err != nil {
framework.Logf("owner: %#v", persistedOwner)
framework.Logf("dependent: %#v", persistedDependent)
framework.Failf("failed waiting for dependent resource %q to be deleted", dependentName)
}
// Ensure the owner is deleted.
_, err = resourceClient.Get(ownerName, metav1.GetOptions{})
if err == nil {
framework.Failf("expected owner resource %q to be deleted", ownerName)
} else {
if !errors.IsNotFound(err) {
framework.Failf("unexpected error getting owner resource %q: %v", ownerName, err)
}
}
})
It("should delete jobs and pods created by cronjob", func() {
framework.SkipIfMissingResource(f.ClientPool, CronJobGroupVersionResource, f.Namespace.Name)
By("Create the cronjob")
cronJob := newCronJob("simple", "*/1 * * * ?")
cronJob, err := f.ClientSet.BatchV1beta1().CronJobs(f.Namespace.Name).Create(cronJob)
Expect(err).NotTo(HaveOccurred())
By("Wait for the CronJob to create new Job")
err = wait.PollImmediate(500*time.Millisecond, 2*time.Minute, func() (bool, error) {
jobs, err := f.ClientSet.Batch().Jobs(f.Namespace.Name).List(metav1.ListOptions{})
if err != nil {
return false, fmt.Errorf("Failed to list jobs: %v", err)
}
return len(jobs.Items) > 0, nil
})
if err != nil {
framework.Failf("Failed to wait for the CronJob to create some Jobs: %v", err)
}
By("Delete the cronjob")
if err := f.ClientSet.BatchV1beta1().CronJobs(f.Namespace.Name).Delete(cronJob.Name, getBackgroundOptions()); err != nil {
framework.Failf("Failed to delete the CronJob: %v", err)
}
By("Verify if cronjob does not leave jobs nor pods behind")
err = wait.PollImmediate(500*time.Millisecond, 1*time.Minute, func() (bool, error) {
return verifyRemainingCronJobsJobsPods(f, f.ClientSet, 0, 0, 0)
})
if err != nil {
framework.Failf("Failed to wait for all jobs and pods to be deleted: %v", err)
}
gatherMetrics(f)
})
})

View file

@ -0,0 +1,327 @@
/*
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 apimachinery
import (
"strconv"
"time"
batchv1 "k8s.io/api/batch/v1"
batchv1beta1 "k8s.io/api/batch/v1beta1"
"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/apimachinery/pkg/util/uuid"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/kubernetes/test/e2e/framework"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
imageutils "k8s.io/kubernetes/test/utils/image"
)
func stagingClientPod(name, value string) v1.Pod {
return v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Labels: map[string]string{
"name": "foo",
"time": value,
},
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "nginx",
Image: imageutils.GetE2EImage(imageutils.NginxSlim),
Ports: []v1.ContainerPort{{ContainerPort: 80}},
},
},
},
}
}
func testingPod(name, value string) v1.Pod {
return v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Labels: map[string]string{
"name": "foo",
"time": value,
},
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "nginx",
Image: imageutils.GetE2EImage(imageutils.NginxSlim),
Ports: []v1.ContainerPort{{ContainerPort: 80}},
LivenessProbe: &v1.Probe{
Handler: v1.Handler{
HTTPGet: &v1.HTTPGetAction{
Path: "/index.html",
Port: intstr.FromInt(8080),
},
},
InitialDelaySeconds: 30,
},
},
},
},
}
}
func observeCreation(w watch.Interface) {
select {
case event, _ := <-w.ResultChan():
if event.Type != watch.Added {
framework.Failf("Failed to observe the creation: %v", event)
}
case <-time.After(30 * time.Second):
framework.Failf("Timeout while waiting for observing the creation")
}
}
func observeObjectDeletion(w watch.Interface) (obj runtime.Object) {
// output to give us a duration to failure. Maybe we aren't getting the
// full timeout for some reason. My guess would be watch failure
framework.Logf("Starting to observe pod deletion")
deleted := false
timeout := false
timer := time.After(framework.DefaultPodDeletionTimeout)
for !deleted && !timeout {
select {
case event, normal := <-w.ResultChan():
if !normal {
framework.Failf("The channel was closed unexpectedly")
return
}
if event.Type == watch.Deleted {
obj = event.Object
deleted = true
}
case <-timer:
timeout = true
}
}
if !deleted {
framework.Failf("Failed to observe pod deletion")
}
return
}
func observerUpdate(w watch.Interface, expectedUpdate func(runtime.Object) bool) {
timer := time.After(30 * time.Second)
updated := false
timeout := false
for !updated && !timeout {
select {
case event, _ := <-w.ResultChan():
if event.Type == watch.Modified {
if expectedUpdate(event.Object) {
updated = true
}
}
case <-timer:
timeout = true
}
}
if !updated {
framework.Failf("Failed to observe pod update")
}
return
}
var _ = SIGDescribe("Generated clientset", func() {
f := framework.NewDefaultFramework("clientset")
It("should create pods, set the deletionTimestamp and deletionGracePeriodSeconds of the pod", func() {
podClient := f.ClientSet.Core().Pods(f.Namespace.Name)
By("constructing the pod")
name := "pod" + string(uuid.NewUUID())
value := strconv.Itoa(time.Now().Nanosecond())
podCopy := testingPod(name, value)
pod := &podCopy
By("setting up watch")
selector := labels.SelectorFromSet(labels.Set(map[string]string{"time": value})).String()
options := metav1.ListOptions{LabelSelector: selector}
pods, err := podClient.List(options)
if err != nil {
framework.Failf("Failed to query for pods: %v", err)
}
Expect(len(pods.Items)).To(Equal(0))
options = metav1.ListOptions{
LabelSelector: selector,
ResourceVersion: pods.ListMeta.ResourceVersion,
}
w, err := podClient.Watch(options)
if err != nil {
framework.Failf("Failed to set up watch: %v", err)
}
By("creating the pod")
pod, err = podClient.Create(pod)
if err != nil {
framework.Failf("Failed to create pod: %v", err)
}
By("verifying the pod is in kubernetes")
options = metav1.ListOptions{
LabelSelector: selector,
ResourceVersion: pod.ResourceVersion,
}
pods, err = podClient.List(options)
if err != nil {
framework.Failf("Failed to query for pods: %v", err)
}
Expect(len(pods.Items)).To(Equal(1))
By("verifying pod creation was observed")
observeCreation(w)
// We need to wait for the pod to be scheduled, otherwise the deletion
// will be carried out immediately rather than gracefully.
framework.ExpectNoError(f.WaitForPodRunning(pod.Name))
By("deleting the pod gracefully")
gracePeriod := int64(31)
if err := podClient.Delete(pod.Name, metav1.NewDeleteOptions(gracePeriod)); err != nil {
framework.Failf("Failed to delete pod: %v", err)
}
By("verifying the deletionTimestamp and deletionGracePeriodSeconds of the pod is set")
observerUpdate(w, func(obj runtime.Object) bool {
pod := obj.(*v1.Pod)
return pod.ObjectMeta.DeletionTimestamp != nil && *pod.ObjectMeta.DeletionGracePeriodSeconds == gracePeriod
})
})
})
func newTestingCronJob(name string, value string) *batchv1beta1.CronJob {
parallelism := int32(1)
completions := int32(1)
return &batchv1beta1.CronJob{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Labels: map[string]string{
"time": value,
},
},
Spec: batchv1beta1.CronJobSpec{
Schedule: "*/1 * * * ?",
ConcurrencyPolicy: batchv1beta1.AllowConcurrent,
JobTemplate: batchv1beta1.JobTemplateSpec{
Spec: batchv1.JobSpec{
Parallelism: &parallelism,
Completions: &completions,
Template: v1.PodTemplateSpec{
Spec: v1.PodSpec{
RestartPolicy: v1.RestartPolicyOnFailure,
Volumes: []v1.Volume{
{
Name: "data",
VolumeSource: v1.VolumeSource{
EmptyDir: &v1.EmptyDirVolumeSource{},
},
},
},
Containers: []v1.Container{
{
Name: "c",
Image: imageutils.GetBusyBoxImage(),
VolumeMounts: []v1.VolumeMount{
{
MountPath: "/data",
Name: "data",
},
},
},
},
},
},
},
},
},
}
}
var _ = SIGDescribe("Generated clientset", func() {
f := framework.NewDefaultFramework("clientset")
BeforeEach(func() {
framework.SkipIfMissingResource(f.ClientPool, CronJobGroupVersionResource, f.Namespace.Name)
})
It("should create v1beta1 cronJobs, delete cronJobs, watch cronJobs", func() {
cronJobClient := f.ClientSet.BatchV1beta1().CronJobs(f.Namespace.Name)
By("constructing the cronJob")
name := "cronjob" + string(uuid.NewUUID())
value := strconv.Itoa(time.Now().Nanosecond())
cronJob := newTestingCronJob(name, value)
By("setting up watch")
selector := labels.SelectorFromSet(labels.Set(map[string]string{"time": value})).String()
options := metav1.ListOptions{LabelSelector: selector}
cronJobs, err := cronJobClient.List(options)
if err != nil {
framework.Failf("Failed to query for cronJobs: %v", err)
}
Expect(len(cronJobs.Items)).To(Equal(0))
options = metav1.ListOptions{
LabelSelector: selector,
ResourceVersion: cronJobs.ListMeta.ResourceVersion,
}
w, err := cronJobClient.Watch(options)
if err != nil {
framework.Failf("Failed to set up watch: %v", err)
}
By("creating the cronJob")
cronJob, err = cronJobClient.Create(cronJob)
if err != nil {
framework.Failf("Failed to create cronJob: %v", err)
}
By("verifying the cronJob is in kubernetes")
options = metav1.ListOptions{
LabelSelector: selector,
ResourceVersion: cronJob.ResourceVersion,
}
cronJobs, err = cronJobClient.List(options)
if err != nil {
framework.Failf("Failed to query for cronJobs: %v", err)
}
Expect(len(cronJobs.Items)).To(Equal(1))
By("verifying cronJob creation was observed")
observeCreation(w)
By("deleting the cronJob")
// Use DeletePropagationBackground so the CronJob is really gone when the call returns.
propagationPolicy := metav1.DeletePropagationBackground
if err := cronJobClient.Delete(cronJob.Name, &metav1.DeleteOptions{PropagationPolicy: &propagationPolicy}); err != nil {
framework.Failf("Failed to delete cronJob: %v", err)
}
options = metav1.ListOptions{LabelSelector: selector}
cronJobs, err = cronJobClient.List(options)
if err != nil {
framework.Failf("Failed to list cronJobs to verify deletion: %v", err)
}
Expect(len(cronJobs.Items)).To(Equal(0))
})
})

View file

@ -0,0 +1,417 @@
/*
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 apimachinery
import (
"fmt"
"strings"
"time"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"k8s.io/api/admissionregistration/v1alpha1"
"k8s.io/api/core/v1"
"k8s.io/api/extensions/v1beta1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
clientset "k8s.io/client-go/kubernetes"
clientretry "k8s.io/client-go/util/retry"
"k8s.io/kubernetes/test/e2e/framework"
imageutils "k8s.io/kubernetes/test/utils/image"
)
var _ = SIGDescribe("Initializers [Feature:Initializers]", func() {
f := framework.NewDefaultFramework("initializers")
// TODO: Add failure traps once we have JustAfterEach
// See https://github.com/onsi/ginkgo/issues/303
It("should be invisible to controllers by default", func() {
ns := f.Namespace.Name
c := f.ClientSet
podName := "uninitialized-pod"
framework.Logf("Creating pod %s", podName)
ch := make(chan struct{})
go func() {
_, err := c.Core().Pods(ns).Create(newUninitializedPod(podName))
Expect(err).NotTo(HaveOccurred())
close(ch)
}()
// wait to ensure the scheduler does not act on an uninitialized pod
err := wait.PollImmediate(2*time.Second, 15*time.Second, func() (bool, error) {
p, err := c.Core().Pods(ns).Get(podName, metav1.GetOptions{})
if err != nil {
if errors.IsNotFound(err) {
return false, nil
}
return false, err
}
return len(p.Spec.NodeName) > 0, nil
})
Expect(err).To(Equal(wait.ErrWaitTimeout))
// verify that we can update an initializing pod
pod, err := c.Core().Pods(ns).Get(podName, metav1.GetOptions{})
Expect(err).NotTo(HaveOccurred())
pod.Annotations = map[string]string{"update-1": "test"}
pod, err = c.Core().Pods(ns).Update(pod)
Expect(err).NotTo(HaveOccurred())
// verify the list call filters out uninitialized pods
pods, err := c.Core().Pods(ns).List(metav1.ListOptions{IncludeUninitialized: true})
Expect(err).NotTo(HaveOccurred())
Expect(pods.Items).To(HaveLen(1))
pods, err = c.Core().Pods(ns).List(metav1.ListOptions{})
Expect(err).NotTo(HaveOccurred())
Expect(pods.Items).To(HaveLen(0))
// clear initializers
pod.Initializers = nil
pod, err = c.Core().Pods(ns).Update(pod)
Expect(err).NotTo(HaveOccurred())
// pod should now start running
err = framework.WaitForPodRunningInNamespace(c, pod)
Expect(err).NotTo(HaveOccurred())
// ensure create call returns
<-ch
// verify that we cannot start the pod initializing again
pod, err = c.Core().Pods(ns).Get(podName, metav1.GetOptions{})
Expect(err).NotTo(HaveOccurred())
pod.Initializers = &metav1.Initializers{
Pending: []metav1.Initializer{{Name: "Other"}},
}
_, err = c.Core().Pods(ns).Update(pod)
if !errors.IsInvalid(err) || !strings.Contains(err.Error(), "immutable") {
Fail(fmt.Sprintf("expected invalid error: %v", err))
}
})
It("should dynamically register and apply initializers to pods [Serial]", func() {
ns := f.Namespace.Name
c := f.ClientSet
podName := "uninitialized-pod"
framework.Logf("Creating pod %s", podName)
// create and register an initializer
initializerName := "pod.test.e2e.kubernetes.io"
initializerConfigName := "e2e-test-initializer"
_, err := c.AdmissionregistrationV1alpha1().InitializerConfigurations().Create(&v1alpha1.InitializerConfiguration{
ObjectMeta: metav1.ObjectMeta{Name: initializerConfigName},
Initializers: []v1alpha1.Initializer{
{
Name: initializerName,
Rules: []v1alpha1.Rule{
{APIGroups: []string{""}, APIVersions: []string{"*"}, Resources: []string{"pods"}},
},
},
},
})
if errors.IsNotFound(err) {
framework.Skipf("dynamic configuration of initializers requires the alpha admissionregistration.k8s.io group to be enabled")
}
Expect(err).NotTo(HaveOccurred())
// we must remove the initializer when the test is complete and ensure no pods are pending for that initializer
defer cleanupInitializer(c, initializerConfigName, initializerName)
// poller configuration is 1 second, wait at least that long
time.Sleep(3 * time.Second)
// run create that blocks
ch := make(chan struct{})
go func() {
defer close(ch)
_, err := c.Core().Pods(ns).Create(newInitPod(podName))
Expect(err).NotTo(HaveOccurred())
}()
// wait until the pod shows up uninitialized
By("Waiting until the pod is visible to a client")
var pod *v1.Pod
err = wait.PollImmediate(2*time.Second, 15*time.Second, func() (bool, error) {
pod, err = c.Core().Pods(ns).Get(podName, metav1.GetOptions{IncludeUninitialized: true})
if errors.IsNotFound(err) {
return false, nil
}
if err != nil {
return false, err
}
return true, nil
})
Expect(err).NotTo(HaveOccurred())
Expect(pod.Initializers).NotTo(BeNil())
Expect(pod.Initializers.Pending).To(HaveLen(1))
Expect(pod.Initializers.Pending[0].Name).To(Equal(initializerName))
// pretend we are an initializer
By("Completing initialization")
pod.Initializers = nil
pod, err = c.Core().Pods(ns).Update(pod)
Expect(err).NotTo(HaveOccurred())
// ensure create call returns
<-ch
// pod should now start running
err = framework.WaitForPodRunningInNamespace(c, pod)
Expect(err).NotTo(HaveOccurred())
// bypass initialization by explicitly passing an empty pending list
By("Setting an empty initializer as an admin to bypass initialization")
podName = "preinitialized-pod"
pod = newUninitializedPod(podName)
pod.Initializers.Pending = nil
pod, err = c.Core().Pods(ns).Create(pod)
Expect(err).NotTo(HaveOccurred())
Expect(pod.Initializers).To(BeNil())
// bypass initialization for mirror pods
By("Creating a mirror pod that bypasses initialization")
podName = "mirror-pod"
pod = newInitPod(podName)
pod.Annotations = map[string]string{
v1.MirrorPodAnnotationKey: "true",
}
pod.Spec.NodeName = "node-does-not-yet-exist"
pod, err = c.Core().Pods(ns).Create(pod)
Expect(err).NotTo(HaveOccurred())
Expect(pod.Initializers).To(BeNil())
Expect(pod.Annotations[v1.MirrorPodAnnotationKey]).To(Equal("true"))
})
It("don't cause replicaset controller creating extra pods if the initializer is not handled [Serial]", func() {
ns := f.Namespace.Name
c := f.ClientSet
podName := "uninitialized-pod"
framework.Logf("Creating pod %s", podName)
// create and register an initializer, without setting up a controller to handle it.
initializerName := "pod.test.e2e.kubernetes.io"
initializerConfigName := "e2e-test-initializer"
_, err := c.AdmissionregistrationV1alpha1().InitializerConfigurations().Create(&v1alpha1.InitializerConfiguration{
ObjectMeta: metav1.ObjectMeta{Name: initializerConfigName},
Initializers: []v1alpha1.Initializer{
{
Name: initializerName,
Rules: []v1alpha1.Rule{
{APIGroups: []string{""}, APIVersions: []string{"*"}, Resources: []string{"pods"}},
},
},
},
})
if errors.IsNotFound(err) {
framework.Skipf("dynamic configuration of initializers requires the alpha admissionregistration.k8s.io group to be enabled")
}
Expect(err).NotTo(HaveOccurred())
// we must remove the initializer when the test is complete and ensure no pods are pending for that initializer
defer cleanupInitializer(c, initializerConfigName, initializerName)
// poller configuration is 1 second, wait at least that long
time.Sleep(3 * time.Second)
// create a replicaset
persistedRS, err := c.ExtensionsV1beta1().ReplicaSets(ns).Create(newReplicaset())
Expect(err).NotTo(HaveOccurred())
// wait for replicaset controller to confirm that it has handled the creation
err = waitForRSObservedGeneration(c, persistedRS.Namespace, persistedRS.Name, persistedRS.Generation)
Expect(err).NotTo(HaveOccurred())
// update the replicaset spec to trigger a resync
patch := []byte(`{"spec":{"minReadySeconds":5}}`)
persistedRS, err = c.ExtensionsV1beta1().ReplicaSets(ns).Patch(persistedRS.Name, types.StrategicMergePatchType, patch)
Expect(err).NotTo(HaveOccurred())
// wait for replicaset controller to confirm that it has handle the spec update
err = waitForRSObservedGeneration(c, persistedRS.Namespace, persistedRS.Name, persistedRS.Generation)
Expect(err).NotTo(HaveOccurred())
// verify that the replicaset controller doesn't create extra pod
selector, err := metav1.LabelSelectorAsSelector(persistedRS.Spec.Selector)
Expect(err).NotTo(HaveOccurred())
listOptions := metav1.ListOptions{
LabelSelector: selector.String(),
IncludeUninitialized: true,
}
pods, err := c.Core().Pods(ns).List(listOptions)
Expect(err).NotTo(HaveOccurred())
Expect(len(pods.Items)).Should(Equal(1))
})
It("will be set to nil if a patch removes the last pending initializer", func() {
ns := f.Namespace.Name
c := f.ClientSet
podName := "to-be-patch-initialized-pod"
framework.Logf("Creating pod %s", podName)
// TODO: lower the timeout so that the server responds faster.
_, err := c.CoreV1().Pods(ns).Create(newUninitializedPod(podName))
if err != nil && !errors.IsTimeout(err) {
framework.Failf("expect err to be timeout error, got %v", err)
}
uninitializedPod, err := c.CoreV1().Pods(ns).Get(podName, metav1.GetOptions{})
Expect(err).NotTo(HaveOccurred())
Expect(uninitializedPod.Initializers).NotTo(BeNil())
Expect(len(uninitializedPod.Initializers.Pending)).Should(Equal(1))
patch := fmt.Sprintf(`{"metadata":{"initializers":{"pending":[{"$patch":"delete","name":"%s"}]}}}`, uninitializedPod.Initializers.Pending[0].Name)
patchedPod, err := c.CoreV1().Pods(ns).Patch(uninitializedPod.Name, types.StrategicMergePatchType, []byte(patch))
Expect(err).NotTo(HaveOccurred())
Expect(patchedPod.Initializers).To(BeNil())
})
})
func newUninitializedPod(podName string) *v1.Pod {
pod := newInitPod(podName)
pod.Initializers = &metav1.Initializers{
Pending: []metav1.Initializer{{Name: "test.k8s.io"}},
}
return pod
}
func newReplicaset() *v1beta1.ReplicaSet {
name := "initializer-test-replicaset"
replicas := int32(1)
labels := map[string]string{"initializer-test": "single-replicaset"}
return &v1beta1.ReplicaSet{
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
Spec: v1beta1.ReplicaSetSpec{
Replicas: &replicas,
Template: v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: labels,
},
Spec: v1.PodSpec{
TerminationGracePeriodSeconds: &zero,
Containers: []v1.Container{
{
Name: name + "-container",
Image: "gcr.io/google_containers/porter:4524579c0eb935c056c8e75563b4e1eda31587e0",
},
},
},
},
},
}
}
func newInitPod(podName string) *v1.Pod {
containerName := fmt.Sprintf("%s-container", podName)
port := 8080
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: podName,
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: containerName,
Image: imageutils.GetE2EImage(imageutils.Porter),
Env: []v1.EnvVar{{Name: fmt.Sprintf("SERVE_PORT_%d", port), Value: "foo"}},
Ports: []v1.ContainerPort{{ContainerPort: int32(port)}},
},
},
RestartPolicy: v1.RestartPolicyNever,
},
}
return pod
}
// removeInitializersFromAllPods walks all pods and ensures they don't have the provided initializer,
// to guarantee completing the test doesn't block the entire cluster.
func removeInitializersFromAllPods(c clientset.Interface, initializerName string) {
pods, err := c.Core().Pods("").List(metav1.ListOptions{IncludeUninitialized: true})
if err != nil {
return
}
for _, p := range pods.Items {
if p.Initializers == nil {
continue
}
err := clientretry.RetryOnConflict(clientretry.DefaultRetry, func() error {
pod, err := c.Core().Pods(p.Namespace).Get(p.Name, metav1.GetOptions{IncludeUninitialized: true})
if err != nil {
if errors.IsNotFound(err) {
return nil
}
return err
}
if pod.Initializers == nil {
return nil
}
var updated []metav1.Initializer
for _, pending := range pod.Initializers.Pending {
if pending.Name != initializerName {
updated = append(updated, pending)
}
}
if len(updated) == len(pod.Initializers.Pending) {
return nil
}
pod.Initializers.Pending = updated
if len(updated) == 0 {
pod.Initializers = nil
}
framework.Logf("Found initializer on pod %s in ns %s", pod.Name, pod.Namespace)
_, err = c.Core().Pods(p.Namespace).Update(pod)
return err
})
if err != nil {
framework.Logf("Unable to remove initializer from pod %s in ns %s: %v", p.Name, p.Namespace, err)
}
}
}
// remove the initializerConfig, and remove the initializer from all pods
func cleanupInitializer(c clientset.Interface, initializerConfigName, initializerName string) {
if err := c.AdmissionregistrationV1alpha1().InitializerConfigurations().Delete(initializerConfigName, nil); err != nil && !errors.IsNotFound(err) {
framework.Logf("got error on deleting %s", initializerConfigName)
}
// poller configuration is 1 second, wait at least that long
time.Sleep(3 * time.Second)
// clear our initializer from anyone who got it
removeInitializersFromAllPods(c, initializerName)
}
// waits till the RS status.observedGeneration matches metadata.generation.
func waitForRSObservedGeneration(c clientset.Interface, ns, name string, generation int64) error {
return wait.PollImmediate(1*time.Second, 1*time.Minute, func() (bool, error) {
rs, err := c.Extensions().ReplicaSets(ns).Get(name, metav1.GetOptions{})
if err != nil {
return false, err
}
if generation > rs.Status.ObservedGeneration {
return false, nil
}
return true, nil
})
}

View file

@ -0,0 +1,277 @@
/*
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 apimachinery
import (
"fmt"
"strings"
"sync"
"time"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/apimachinery/pkg/util/wait"
clientset "k8s.io/client-go/kubernetes"
"k8s.io/kubernetes/test/e2e/framework"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
func extinguish(f *framework.Framework, totalNS int, maxAllowedAfterDel int, maxSeconds int) {
var err error
By("Creating testing namespaces")
wg := &sync.WaitGroup{}
wg.Add(totalNS)
for n := 0; n < totalNS; n += 1 {
go func(n int) {
defer wg.Done()
defer GinkgoRecover()
_, err = f.CreateNamespace(fmt.Sprintf("nslifetest-%v", n), nil)
Expect(err).NotTo(HaveOccurred())
}(n)
}
wg.Wait()
//Wait 10 seconds, then SEND delete requests for all the namespaces.
By("Waiting 10 seconds")
time.Sleep(time.Duration(10 * time.Second))
deleted, err := framework.DeleteNamespaces(f.ClientSet, []string{"nslifetest"}, nil /* skipFilter */)
Expect(err).NotTo(HaveOccurred())
Expect(len(deleted)).To(Equal(totalNS))
By("Waiting for namespaces to vanish")
//Now POLL until all namespaces have been eradicated.
framework.ExpectNoError(wait.Poll(2*time.Second, time.Duration(maxSeconds)*time.Second,
func() (bool, error) {
var cnt = 0
nsList, err := f.ClientSet.Core().Namespaces().List(metav1.ListOptions{})
if err != nil {
return false, err
}
for _, item := range nsList.Items {
if strings.Contains(item.Name, "nslifetest") {
cnt++
}
}
if cnt > maxAllowedAfterDel {
framework.Logf("Remaining namespaces : %v", cnt)
return false, nil
}
return true, nil
}))
}
func waitForPodInNamespace(c clientset.Interface, ns, podName string) *v1.Pod {
var pod *v1.Pod
var err error
err = wait.PollImmediate(2*time.Second, 15*time.Second, func() (bool, error) {
pod, err = c.Core().Pods(ns).Get(podName, metav1.GetOptions{IncludeUninitialized: true})
if errors.IsNotFound(err) {
return false, nil
}
if err != nil {
return false, err
}
return true, nil
})
Expect(err).NotTo(HaveOccurred())
return pod
}
func ensurePodsAreRemovedWhenNamespaceIsDeleted(f *framework.Framework) {
By("Creating a test namespace")
namespace, err := f.CreateNamespace("nsdeletetest", nil)
Expect(err).NotTo(HaveOccurred())
By("Waiting for a default service account to be provisioned in namespace")
err = framework.WaitForDefaultServiceAccountInNamespace(f.ClientSet, namespace.Name)
Expect(err).NotTo(HaveOccurred())
By("Creating a pod in the namespace")
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "nginx",
Image: framework.GetPauseImageName(f.ClientSet),
},
},
},
}
pod, err = f.ClientSet.Core().Pods(namespace.Name).Create(pod)
Expect(err).NotTo(HaveOccurred())
By("Waiting for the pod to have running status")
framework.ExpectNoError(framework.WaitForPodRunningInNamespace(f.ClientSet, pod))
By("Creating an uninitialized pod in the namespace")
podB := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod-uninitialized",
Initializers: &metav1.Initializers{Pending: []metav1.Initializer{{Name: "test.initializer.k8s.io"}}},
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "nginx",
Image: framework.GetPauseImageName(f.ClientSet),
},
},
},
}
go func() {
_, err = f.ClientSet.Core().Pods(namespace.Name).Create(podB)
// This error is ok, because we will delete the pod before it completes initialization
framework.Logf("error from create uninitialized namespace: %v", err)
}()
podB = waitForPodInNamespace(f.ClientSet, namespace.Name, podB.Name)
By("Deleting the namespace")
err = f.ClientSet.Core().Namespaces().Delete(namespace.Name, nil)
Expect(err).NotTo(HaveOccurred())
By("Waiting for the namespace to be removed.")
maxWaitSeconds := int64(60) + *pod.Spec.TerminationGracePeriodSeconds
framework.ExpectNoError(wait.Poll(1*time.Second, time.Duration(maxWaitSeconds)*time.Second,
func() (bool, error) {
_, err = f.ClientSet.Core().Namespaces().Get(namespace.Name, metav1.GetOptions{})
if err != nil && errors.IsNotFound(err) {
return true, nil
}
return false, nil
}))
By("Recreating the namespace")
namespace, err = f.CreateNamespace("nsdeletetest", nil)
Expect(err).NotTo(HaveOccurred())
By("Verifying there are no pods in the namespace")
_, err = f.ClientSet.Core().Pods(namespace.Name).Get(pod.Name, metav1.GetOptions{})
Expect(err).To(HaveOccurred())
_, err = f.ClientSet.Core().Pods(namespace.Name).Get(podB.Name, metav1.GetOptions{IncludeUninitialized: true})
Expect(err).To(HaveOccurred())
}
func ensureServicesAreRemovedWhenNamespaceIsDeleted(f *framework.Framework) {
var err error
By("Creating a test namespace")
namespace, err := f.CreateNamespace("nsdeletetest", nil)
Expect(err).NotTo(HaveOccurred())
By("Waiting for a default service account to be provisioned in namespace")
err = framework.WaitForDefaultServiceAccountInNamespace(f.ClientSet, namespace.Name)
Expect(err).NotTo(HaveOccurred())
By("Creating a service in the namespace")
serviceName := "test-service"
labels := map[string]string{
"foo": "bar",
"baz": "blah",
}
service := &v1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: serviceName,
},
Spec: v1.ServiceSpec{
Selector: labels,
Ports: []v1.ServicePort{{
Port: 80,
TargetPort: intstr.FromInt(80),
}},
},
}
service, err = f.ClientSet.Core().Services(namespace.Name).Create(service)
Expect(err).NotTo(HaveOccurred())
By("Deleting the namespace")
err = f.ClientSet.Core().Namespaces().Delete(namespace.Name, nil)
Expect(err).NotTo(HaveOccurred())
By("Waiting for the namespace to be removed.")
maxWaitSeconds := int64(60)
framework.ExpectNoError(wait.Poll(1*time.Second, time.Duration(maxWaitSeconds)*time.Second,
func() (bool, error) {
_, err = f.ClientSet.Core().Namespaces().Get(namespace.Name, metav1.GetOptions{})
if err != nil && errors.IsNotFound(err) {
return true, nil
}
return false, nil
}))
By("Recreating the namespace")
namespace, err = f.CreateNamespace("nsdeletetest", nil)
Expect(err).NotTo(HaveOccurred())
By("Verifying there is no service in the namespace")
_, err = f.ClientSet.Core().Services(namespace.Name).Get(service.Name, metav1.GetOptions{})
Expect(err).To(HaveOccurred())
}
// This test must run [Serial] due to the impact of running other parallel
// tests can have on its performance. Each test that follows the common
// test framework follows this pattern:
// 1. Create a Namespace
// 2. Do work that generates content in that namespace
// 3. Delete a Namespace
// Creation of a Namespace is non-trivial since it requires waiting for a
// ServiceAccount to be generated.
// Deletion of a Namespace is non-trivial and performance intensive since
// its an orchestrated process. The controller that handles deletion must
// query the namespace for all existing content, and then delete each piece
// of content in turn. As the API surface grows to add more KIND objects
// that could exist in a Namespace, the number of calls that the namespace
// controller must orchestrate grows since it must LIST, DELETE (1x1) each
// KIND.
// There is work underway to improve this, but it's
// most likely not going to get significantly better until etcd v3.
// Going back to this test, this test generates 100 Namespace objects, and then
// rapidly deletes all of them. This causes the NamespaceController to observe
// and attempt to process a large number of deletes concurrently. In effect,
// it's like running 100 traditional e2e tests in parallel. If the namespace
// controller orchestrating deletes is slowed down deleting another test's
// content then this test may fail. Since the goal of this test is to soak
// Namespace creation, and soak Namespace deletion, its not appropriate to
// further soak the cluster with other parallel Namespace deletion activities
// that each have a variable amount of content in the associated Namespace.
// When run in [Serial] this test appears to delete Namespace objects at a
// rate of approximately 1 per second.
var _ = SIGDescribe("Namespaces [Serial]", func() {
f := framework.NewDefaultFramework("namespaces")
It("should ensure that all pods are removed when a namespace is deleted.",
func() { ensurePodsAreRemovedWhenNamespaceIsDeleted(f) })
It("should ensure that all services are removed when a namespace is deleted.",
func() { ensureServicesAreRemovedWhenNamespaceIsDeleted(f) })
It("should delete fast enough (90 percent of 100 namespaces in 150 seconds)",
func() { extinguish(f, 100, 10, 150) })
// On hold until etcd3; see #7372
It("should always delete fast (ALL of 100 namespaces in 150 seconds) [Feature:ComprehensiveNamespaceDraining]",
func() { extinguish(f, 100, 0, 150) })
})

View file

@ -0,0 +1,123 @@
/*
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 apimachinery
import (
"bytes"
"fmt"
"text/tabwriter"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
metav1alpha1 "k8s.io/apimachinery/pkg/apis/meta/v1alpha1"
"k8s.io/kubernetes/pkg/printers"
"k8s.io/kubernetes/test/e2e/framework"
imageutils "k8s.io/kubernetes/test/utils/image"
)
var _ = SIGDescribe("Servers with support for Table transformation", func() {
f := framework.NewDefaultFramework("tables")
It("should return pod details", func() {
ns := f.Namespace.Name
c := f.ClientSet
podName := "pod-1"
framework.Logf("Creating pod %s", podName)
_, err := c.Core().Pods(ns).Create(newTablePod(podName))
Expect(err).NotTo(HaveOccurred())
table := &metav1alpha1.Table{}
err = c.Core().RESTClient().Get().Resource("pods").Namespace(ns).Name(podName).SetHeader("Accept", "application/json;as=Table;v=v1alpha1;g=meta.k8s.io").Do().Into(table)
Expect(err).NotTo(HaveOccurred())
framework.Logf("Table: %#v", table)
Expect(len(table.ColumnDefinitions)).To(BeNumerically(">", 2))
Expect(len(table.Rows)).To(Equal(1))
Expect(len(table.Rows[0].Cells)).To(Equal(len(table.ColumnDefinitions)))
Expect(table.ColumnDefinitions[0].Name).To(Equal("Name"))
Expect(table.Rows[0].Cells[0]).To(Equal(podName))
out := printTable(table)
Expect(out).To(MatchRegexp("^NAME\\s"))
Expect(out).To(MatchRegexp("\npod-1\\s"))
framework.Logf("Table:\n%s", out)
})
It("should return generic metadata details across all namespaces for nodes", func() {
c := f.ClientSet
table := &metav1alpha1.Table{}
err := c.Core().RESTClient().Get().Resource("nodes").SetHeader("Accept", "application/json;as=Table;v=v1alpha1;g=meta.k8s.io").Do().Into(table)
Expect(err).NotTo(HaveOccurred())
framework.Logf("Table: %#v", table)
Expect(len(table.ColumnDefinitions)).To(BeNumerically(">=", 2))
Expect(len(table.Rows)).To(BeNumerically(">=", 1))
Expect(len(table.Rows[0].Cells)).To(Equal(len(table.ColumnDefinitions)))
Expect(table.ColumnDefinitions[0].Name).To(Equal("Name"))
out := printTable(table)
Expect(out).To(MatchRegexp("^NAME\\s"))
framework.Logf("Table:\n%s", out)
})
It("should return a 406 for a backend which does not implement metadata", func() {
c := f.ClientSet
table := &metav1alpha1.Table{}
err := c.Core().RESTClient().Get().Resource("services").SetHeader("Accept", "application/json;as=Table;v=v1alpha1;g=meta.k8s.io").Do().Into(table)
Expect(err).To(HaveOccurred())
Expect(err.(errors.APIStatus).Status().Code).To(Equal(int32(406)))
})
})
func printTable(table *metav1alpha1.Table) string {
buf := &bytes.Buffer{}
tw := tabwriter.NewWriter(buf, 5, 8, 1, ' ', 0)
err := printers.PrintTable(table, tw, printers.PrintOptions{})
Expect(err).NotTo(HaveOccurred())
tw.Flush()
return buf.String()
}
func newTablePod(podName string) *v1.Pod {
containerName := fmt.Sprintf("%s-container", podName)
port := 8080
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: podName,
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: containerName,
Image: imageutils.GetE2EImage(imageutils.Porter),
Env: []v1.EnvVar{{Name: fmt.Sprintf("SERVE_PORT_%d", port), Value: "foo"}},
Ports: []v1.ContainerPort{{ContainerPort: int32(port)}},
},
},
RestartPolicy: v1.RestartPolicyNever,
},
}
return pod
}