vendor updates

This commit is contained in:
Serguei Bezverkhi
2018-03-06 17:33:18 -05:00
parent 4b3ebc171b
commit e9033989a0
5854 changed files with 248382 additions and 119809 deletions

View File

@ -38,6 +38,7 @@ go_library(
"//vendor/github.com/stretchr/testify/assert:go_default_library",
"//vendor/k8s.io/api/admissionregistration/v1alpha1:go_default_library",
"//vendor/k8s.io/api/admissionregistration/v1beta1:go_default_library",
"//vendor/k8s.io/api/authorization/v1: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",
@ -49,7 +50,7 @@ go_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/apis/meta/v1beta1: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",

View File

@ -41,6 +41,7 @@ import (
rbacapi "k8s.io/kubernetes/pkg/apis/rbac"
utilversion "k8s.io/kubernetes/pkg/util/version"
"k8s.io/kubernetes/test/e2e/framework"
imageutils "k8s.io/kubernetes/test/utils/image"
samplev1alpha1 "k8s.io/sample-apiserver/pkg/apis/wardle/v1alpha1"
. "github.com/onsi/ginkgo"
@ -52,14 +53,20 @@ var _ = SIGDescribe("Aggregator", func() {
var ns string
var c clientset.Interface
var aggrclient *aggregatorclient.Clientset
f := framework.NewDefaultFramework("aggregator")
framework.AddCleanupAction(func() {
// Cleanup actions will be called even when the tests are skipped and leaves namespace unset.
if len(ns) > 0 {
cleanTest(c, aggrclient, ns)
}
// BeforeEachs run in LIFO order, AfterEachs run in FIFO order.
// We want cleanTest to happen before the namespace cleanup AfterEach
// inserted by NewDefaultFramework, so we put this AfterEach in front
// of NewDefaultFramework.
AfterEach(func() {
cleanTest(c, aggrclient, ns)
})
f := framework.NewDefaultFramework("aggregator")
// We want namespace initialization BeforeEach inserted by
// NewDefaultFramework to happen before this, so we put this BeforeEach
// after NewDefaultFramework.
BeforeEach(func() {
c = f.ClientSet
ns = f.Namespace.Name
@ -72,7 +79,7 @@ var _ = SIGDescribe("Aggregator", func() {
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.7v2")
TestSampleAPIServer(f, imageutils.GetE2EImage(imageutils.APIServer))
})
})
@ -126,7 +133,7 @@ func TestSampleAPIServer(f *framework.Framework, image string) {
// kubectl create -f deploy.yaml
deploymentName := "sample-apiserver-deployment"
etcdImage := "quay.io/coreos/etcd:v3.1.10"
etcdImage := "quay.io/coreos/etcd:v3.2.14"
podLabels := map[string]string{"app": "sample-apiserver", "apiserver": "true"}
replicas := int32(1)
zero := int64(0)
@ -284,6 +291,13 @@ func TestSampleAPIServer(f *framework.Framework, image string) {
})
framework.ExpectNoError(err, "creating role binding %s:sample-apiserver to access configMap", namespace)
// Wait for the extension apiserver to be up and healthy
// kubectl get deployments -n <aggregated-api-namespace> && status == Running
// NOTE: aggregated apis should generally be set up in there own namespace (<aggregated-api-namespace>). As the test framework
// is setting up a new namespace, we are just using that.
err = framework.WaitForDeploymentComplete(client, deployment)
framework.ExpectNoError(err, "deploying extension apiserver in namespace %s", namespace)
// kubectl create -f apiservice.yaml
_, err = aggrclient.ApiregistrationV1beta1().APIServices().Create(&apiregistrationv1beta1.APIService{
ObjectMeta: metav1.ObjectMeta{Name: "v1alpha1.wardle.k8s.io"},
@ -301,13 +315,6 @@ func TestSampleAPIServer(f *framework.Framework, image string) {
})
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 <aggregated-api-namespace> && status == Running
// NOTE: aggregated apis should generally be set up in there own namespace (<aggregated-api-namespace>). As the test framework
// is setting up a new namespace, we are just using that.
err = framework.WaitForDeploymentComplete(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")
@ -317,6 +324,9 @@ func TestSampleAPIServer(f *framework.Framework, image string) {
if !ok {
return false, err
}
if status.Status().Code == 503 {
return false, nil
}
if status.Status().Code == 404 && strings.HasPrefix(err.Error(), "the server could not find the requested resource") {
return false, nil
}

View File

@ -83,13 +83,8 @@ func getBackgroundOptions() *metav1.DeleteOptions {
}
func getOrphanOptions() *metav1.DeleteOptions {
var trueVar = true
return &metav1.DeleteOptions{OrphanDependents: &trueVar}
}
func getNonOrphanOptions() *metav1.DeleteOptions {
var falseVar = false
return &metav1.DeleteOptions{OrphanDependents: &falseVar}
policy := metav1.DeletePropagationOrphan
return &metav1.DeleteOptions{PropagationPolicy: &policy}
}
var (
@ -255,7 +250,7 @@ func verifyRemainingCronJobsJobsPods(f *framework.Framework, clientSet clientset
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{})
jobs, err := f.ClientSet.BatchV1().Jobs(f.Namespace.Name).List(metav1.ListOptions{})
if err != nil {
return false, fmt.Errorf("Failed to list jobs: %v", err)
}
@ -330,7 +325,14 @@ func newCronJob(name, schedule string) *batchv1beta1.CronJob {
var _ = SIGDescribe("Garbage collector", func() {
f := framework.NewDefaultFramework("gc")
It("should delete pods created by rc when not orphaning", func() {
/*
Testname: garbage-collector-delete-rc--propagation-background
Description: Ensure that if deleteOptions.PropagationPolicy is set to Background,
then deleting a ReplicationController should cause pods created
by that RC to also be deleted.
*/
framework.ConformanceIt("should delete pods created by rc when not orphaning", func() {
clientSet := f.ClientSet
rcClient := clientSet.CoreV1().ReplicationControllers(f.Namespace.Name)
podClient := clientSet.CoreV1().Pods(f.Namespace.Name)
@ -362,7 +364,7 @@ var _ = SIGDescribe("Garbage collector", func() {
framework.Failf("failed to wait for the rc to create some pods: %v", err)
}
By("delete the rc")
deleteOptions := getNonOrphanOptions()
deleteOptions := getBackgroundOptions()
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)
@ -383,7 +385,13 @@ var _ = SIGDescribe("Garbage collector", func() {
gatherMetrics(f)
})
It("should orphan pods created by rc if delete options say so", func() {
/*
Testname: garbage-collector-delete-rc--propagation-orphan
Description: Ensure that if deleteOptions.PropagationPolicy is set to Orphan,
then deleting a ReplicationController should cause pods created
by that RC to be orphaned.
*/
framework.ConformanceIt("should orphan pods created by rc if delete options say so", func() {
clientSet := f.ClientSet
rcClient := clientSet.CoreV1().ReplicationControllers(f.Namespace.Name)
podClient := clientSet.CoreV1().Pods(f.Namespace.Name)
@ -501,7 +509,13 @@ var _ = SIGDescribe("Garbage collector", func() {
gatherMetrics(f)
})
It("should delete RS created by deployment when not orphaning", func() {
/*
Testname: garbage-collector-delete-deployment-propagation-background
Description: Ensure that if deleteOptions.PropagationPolicy is set to Background,
then deleting a Deployment should cause ReplicaSets created
by that Deployment to also be deleted.
*/
framework.ConformanceIt("should delete RS created by deployment when not orphaning", func() {
clientSet := f.ClientSet
deployClient := clientSet.ExtensionsV1beta1().Deployments(f.Namespace.Name)
rsClient := clientSet.ExtensionsV1beta1().ReplicaSets(f.Namespace.Name)
@ -529,7 +543,7 @@ var _ = SIGDescribe("Garbage collector", func() {
}
By("delete the deployment")
deleteOptions := getNonOrphanOptions()
deleteOptions := getBackgroundOptions()
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)
@ -552,7 +566,13 @@ var _ = SIGDescribe("Garbage collector", func() {
gatherMetrics(f)
})
It("should orphan RS created by deployment when deleteOptions.OrphanDependents is true", func() {
/*
Testname: garbage-collector-delete-deployment-propagation-true
Description: Ensure that if deleteOptions.PropagationPolicy is set to Orphan,
then deleting a Deployment should cause ReplicaSets created
by that Deployment to be orphaned.
*/
framework.ConformanceIt("should orphan RS created by deployment when deleteOptions.PropagationPolicy is Orphan", func() {
clientSet := f.ClientSet
deployClient := clientSet.ExtensionsV1beta1().Deployments(f.Namespace.Name)
rsClient := clientSet.ExtensionsV1beta1().ReplicaSets(f.Namespace.Name)
@ -617,7 +637,12 @@ var _ = SIGDescribe("Garbage collector", func() {
gatherMetrics(f)
})
It("should keep the rc around until all its pods are deleted if the deleteOptions says so", func() {
/*
Testname: garbage-collector-delete-rc-after-owned-pods
Description: Ensure that if deleteOptions.PropagationPolicy is set to Foreground,
then a ReplicationController should not be deleted until all its dependent pods are deleted.
*/
framework.ConformanceIt("should keep the rc around until all its pods are deleted if the deleteOptions says so", func() {
clientSet := f.ClientSet
rcClient := clientSet.CoreV1().ReplicationControllers(f.Namespace.Name)
podClient := clientSet.CoreV1().Pods(f.Namespace.Name)
@ -701,7 +726,12 @@ var _ = SIGDescribe("Garbage collector", func() {
})
// 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() {
/*
Testname: garbage-collector-multiple-owners
Description: Ensure that if a Pod has multiple valid owners, it will not be deleted
when one of of those owners gets deleted.
*/
framework.ConformanceIt("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.CoreV1().ReplicationControllers(f.Namespace.Name)
podClient := clientSet.CoreV1().Pods(f.Namespace.Name)
@ -812,7 +842,12 @@ var _ = SIGDescribe("Garbage collector", func() {
})
// TODO: should be an integration test
It("should not be blocked by dependency circle", func() {
/*
Testname: garbage-collector-dependency-cycle
Description: Ensure that a dependency cycle will
not block the garbage collector.
*/
framework.ConformanceIt("should not be blocked by dependency circle", func() {
clientSet := f.ClientSet
podClient := clientSet.CoreV1().Pods(f.Namespace.Name)
pod1 := newGCPod("pod1")
@ -974,7 +1009,7 @@ var _ = SIGDescribe("Garbage collector", func() {
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{})
jobs, err := f.ClientSet.BatchV1().Jobs(f.Namespace.Name).List(metav1.ListOptions{})
if err != nil {
return false, fmt.Errorf("Failed to list jobs: %v", err)
}

View File

@ -315,7 +315,7 @@ func newReplicaset() *v1beta1.ReplicaSet {
Containers: []v1.Container{
{
Name: name + "-container",
Image: "gcr.io/google_containers/porter:4524579c0eb935c056c8e75563b4e1eda31587e0",
Image: imageutils.GetE2EImage(imageutils.Porter),
},
},
},
@ -405,7 +405,7 @@ func cleanupInitializer(c clientset.Interface, initializerConfigName, initialize
// 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{})
rs, err := c.ExtensionsV1beta1().ReplicaSets(ns).Get(name, metav1.GetOptions{})
if err != nil {
return false, err
}

View File

@ -24,11 +24,13 @@ import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
authorizationv1 "k8s.io/api/authorization/v1"
"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"
metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
"k8s.io/client-go/util/workqueue"
"k8s.io/kubernetes/pkg/printers"
"k8s.io/kubernetes/test/e2e/framework"
imageutils "k8s.io/kubernetes/test/utils/image"
@ -47,8 +49,8 @@ var _ = SIGDescribe("Servers with support for Table transformation", func() {
_, err := c.CoreV1().Pods(ns).Create(newTablePod(podName))
Expect(err).NotTo(HaveOccurred())
table := &metav1alpha1.Table{}
err = c.CoreV1().RESTClient().Get().Resource("pods").Namespace(ns).Name(podName).SetHeader("Accept", "application/json;as=Table;v=v1alpha1;g=meta.k8s.io").Do().Into(table)
table := &metav1beta1.Table{}
err = c.CoreV1().RESTClient().Get().Resource("pods").Namespace(ns).Name(podName).SetHeader("Accept", "application/json;as=Table;v=v1beta1;g=meta.k8s.io").Do().Into(table)
Expect(err).NotTo(HaveOccurred())
framework.Logf("Table: %#v", table)
@ -92,10 +94,10 @@ var _ = SIGDescribe("Servers with support for Table transformation", func() {
Fail("Unable to create template %d, exiting", i)
})
pagedTable := &metav1alpha1.Table{}
pagedTable := &metav1beta1.Table{}
err := c.CoreV1().RESTClient().Get().Namespace(ns).Resource("podtemplates").
VersionedParams(&metav1.ListOptions{Limit: 2}, metav1.ParameterCodec).
SetHeader("Accept", "application/json;as=Table;v=v1alpha1;g=meta.k8s.io").
SetHeader("Accept", "application/json;as=Table;v=v1beta1;g=meta.k8s.io").
Do().Into(pagedTable)
Expect(err).NotTo(HaveOccurred())
// TODO: kops PR job is still using etcd2, which prevents this feature from working. Remove this check when kops is upgraded to etcd3
@ -111,7 +113,7 @@ var _ = SIGDescribe("Servers with support for Table transformation", func() {
err = c.CoreV1().RESTClient().Get().Namespace(ns).Resource("podtemplates").
VersionedParams(&metav1.ListOptions{Continue: pagedTable.Continue}, metav1.ParameterCodec).
SetHeader("Accept", "application/json;as=Table;v=v1alpha1;g=meta.k8s.io").
SetHeader("Accept", "application/json;as=Table;v=v1beta1;g=meta.k8s.io").
Do().Into(pagedTable)
Expect(err).NotTo(HaveOccurred())
Expect(len(pagedTable.Rows)).To(BeNumerically(">", 0))
@ -121,8 +123,8 @@ var _ = SIGDescribe("Servers with support for Table transformation", func() {
It("should return generic metadata details across all namespaces for nodes", func() {
c := f.ClientSet
table := &metav1alpha1.Table{}
err := c.CoreV1().RESTClient().Get().Resource("nodes").SetHeader("Accept", "application/json;as=Table;v=v1alpha1;g=meta.k8s.io").Do().Into(table)
table := &metav1beta1.Table{}
err := c.CoreV1().RESTClient().Get().Resource("nodes").SetHeader("Accept", "application/json;as=Table;v=v1beta1;g=meta.k8s.io").Do().Into(table)
Expect(err).NotTo(HaveOccurred())
framework.Logf("Table: %#v", table)
@ -141,14 +143,22 @@ var _ = SIGDescribe("Servers with support for Table transformation", func() {
It("should return a 406 for a backend which does not implement metadata", func() {
c := f.ClientSet
table := &metav1alpha1.Table{}
err := c.CoreV1().RESTClient().Get().Resource("services").SetHeader("Accept", "application/json;as=Table;v=v1alpha1;g=meta.k8s.io").Do().Into(table)
table := &metav1beta1.Table{}
sar := &authorizationv1.SelfSubjectAccessReview{
Spec: authorizationv1.SelfSubjectAccessReviewSpec{
NonResourceAttributes: &authorizationv1.NonResourceAttributes{
Path: "/",
Verb: "get",
},
},
}
err := c.AuthorizationV1().RESTClient().Post().Resource("selfsubjectaccessreviews").SetHeader("Accept", "application/json;as=Table;v=v1beta1;g=meta.k8s.io").Body(sar).Do().Into(table)
Expect(err).To(HaveOccurred())
Expect(err.(errors.APIStatus).Status().Code).To(Equal(int32(406)))
})
})
func printTable(table *metav1alpha1.Table) string {
func printTable(table *metav1beta1.Table) string {
buf := &bytes.Buffer{}
tw := tabwriter.NewWriter(buf, 5, 8, 1, ' ', 0)
err := printers.PrintTable(table, tw, printers.PrintOptions{})

View File

@ -27,8 +27,6 @@ import (
extensions "k8s.io/api/extensions/v1beta1"
rbacv1beta1 "k8s.io/api/rbac/v1beta1"
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
crdclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
"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"
@ -39,6 +37,7 @@ import (
clientset "k8s.io/client-go/kubernetes"
utilversion "k8s.io/kubernetes/pkg/util/version"
"k8s.io/kubernetes/test/e2e/framework"
imageutils "k8s.io/kubernetes/test/utils/image"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
@ -46,28 +45,29 @@ import (
)
const (
secretName = "sample-webhook-secret"
deploymentName = "sample-webhook-deployment"
serviceName = "e2e-test-webhook"
roleBindingName = "webhook-auth-reader"
secretName = "sample-webhook-secret"
deploymentName = "sample-webhook-deployment"
serviceName = "e2e-test-webhook"
roleBindingName = "webhook-auth-reader"
// The webhook configuration names should not be reused between test instances.
crdWebhookConfigName = "e2e-test-webhook-config-crd"
webhookConfigName = "e2e-test-webhook-config"
mutatingWebhookConfigName = "e2e-test-mutating-webhook-config"
skipNamespaceLabelKey = "skip-webhook-admission"
skipNamespaceLabelValue = "yes"
skippedNamespaceName = "exempted-namesapce"
disallowedPodName = "disallowed-pod"
disallowedConfigMapName = "disallowed-configmap"
allowedConfigMapName = "allowed-configmap"
crdName = "e2e-test-webhook-crd"
crdKind = "E2e-test-webhook-crd"
crdWebhookConfigName = "e2e-test-webhook-config-crd"
podMutatingWebhookConfigName = "e2e-test-mutating-webhook-pod"
crdMutatingWebhookConfigName = "e2e-test-mutating-webhook-config-crd"
crdAPIGroup = "webhook-crd-test.k8s.io"
crdAPIVersion = "v1"
webhookFailClosedConfigName = "e2e-test-webhook-fail-closed"
failNamespaceLabelKey = "fail-closed-webhook"
failNamespaceLabelValue = "yes"
failNamespaceName = "fail-closed-namesapce"
skipNamespaceLabelKey = "skip-webhook-admission"
skipNamespaceLabelValue = "yes"
skippedNamespaceName = "exempted-namesapce"
disallowedPodName = "disallowed-pod"
hangingPodName = "hanging-pod"
disallowedConfigMapName = "disallowed-configmap"
allowedConfigMapName = "allowed-configmap"
failNamespaceLabelKey = "fail-closed-webhook"
failNamespaceLabelValue = "yes"
failNamespaceName = "fail-closed-namesapce"
)
var serverWebhookVersion = utilversion.MustParseSemantic("v1.8.0")
@ -99,44 +99,57 @@ var _ = SIGDescribe("AdmissionWebhook", func() {
// Note that in 1.9 we will have backwards incompatible change to
// admission webhooks, so the image will be updated to 1.9 sometime in
// the development 1.9 cycle.
deployWebhookAndService(f, "gcr.io/kubernetes-e2e-test-images/k8s-sample-admission-webhook-amd64:1.8v6", context)
deployWebhookAndService(f, imageutils.GetE2EImage(imageutils.AdmissionWebhook), context)
})
AfterEach(func() {
cleanWebhookTest(client, namespaceName)
})
It("Should be able to deny pod and configmap creation", func() {
registerWebhook(f, context)
defer client.AdmissionregistrationV1beta1().ValidatingWebhookConfigurations().Delete(webhookConfigName, nil)
webhookCleanup := registerWebhook(f, context)
defer webhookCleanup()
testWebhook(f)
})
It("Should be able to deny custom resource creation", func() {
crdCleanup, dynamicClient := createCRD(f)
defer crdCleanup()
registerWebhookForCRD(f, context)
defer client.AdmissionregistrationV1beta1().ValidatingWebhookConfigurations().Delete(crdWebhookConfigName, nil)
testCRDWebhook(f, dynamicClient)
testcrd, err := framework.CreateTestCRD(f)
if err != nil {
return
}
defer testcrd.CleanUp()
webhookCleanup := registerWebhookForCRD(f, context, testcrd)
defer webhookCleanup()
testCRDWebhook(f, testcrd.Crd, testcrd.DynamicClient)
})
It("Should unconditionally reject operations on fail closed webhook", func() {
registerFailClosedWebhook(f, context)
defer f.ClientSet.AdmissionregistrationV1beta1().ValidatingWebhookConfigurations().Delete(webhookFailClosedConfigName, nil)
webhookCleanup := registerFailClosedWebhook(f, context)
defer webhookCleanup()
testFailClosedWebhook(f)
})
It("Should mutate configmap", func() {
registerMutatingWebhookForConfigMap(f, context)
defer client.AdmissionregistrationV1beta1().MutatingWebhookConfigurations().Delete(mutatingWebhookConfigName, nil)
webhookCleanup := registerMutatingWebhookForConfigMap(f, context)
defer webhookCleanup()
testMutatingConfigMapWebhook(f)
})
It("Should mutate pod and apply defaults after mutation", func() {
webhookCleanup := registerMutatingWebhookForPod(f, context)
defer webhookCleanup()
testMutatingPodWebhook(f)
})
It("Should mutate crd", func() {
crdCleanup, dynamicClient := createCRD(f)
defer crdCleanup()
registerMutatingWebhookForCRD(f, context)
defer client.AdmissionregistrationV1beta1().MutatingWebhookConfigurations().Delete(crdMutatingWebhookConfigName, nil)
testMutatingCRDWebhook(f, dynamicClient)
testcrd, err := framework.CreateTestCRD(f)
if err != nil {
return
}
defer testcrd.CleanUp()
webhookCleanup := registerMutatingWebhookForCRD(f, context, testcrd)
defer webhookCleanup()
testMutatingCRDWebhook(f, testcrd.Crd, testcrd.DynamicClient)
})
// TODO: add more e2e tests for mutating webhooks
@ -291,11 +304,12 @@ func deployWebhookAndService(f *framework.Framework, image string, context *cert
func strPtr(s string) *string { return &s }
func registerWebhook(f *framework.Framework, context *certContext) {
func registerWebhook(f *framework.Framework, context *certContext) func() {
client := f.ClientSet
By("Registering the webhook via the AdmissionRegistration API")
namespace := f.Namespace.Name
configName := webhookConfigName
// A webhook that cannot talk to server, with fail-open policy
failOpenHook := failingWebhook(namespace, "fail-open.k8s.io")
policyIgnore := v1beta1.Ignore
@ -303,7 +317,7 @@ func registerWebhook(f *framework.Framework, context *certContext) {
_, err := client.AdmissionregistrationV1beta1().ValidatingWebhookConfigurations().Create(&v1beta1.ValidatingWebhookConfiguration{
ObjectMeta: metav1.ObjectMeta{
Name: webhookConfigName,
Name: configName,
},
Webhooks: []v1beta1.Webhook{
{
@ -359,21 +373,26 @@ func registerWebhook(f *framework.Framework, context *certContext) {
failOpenHook,
},
})
framework.ExpectNoError(err, "registering webhook config %s with namespace %s", webhookConfigName, namespace)
framework.ExpectNoError(err, "registering webhook config %s with namespace %s", configName, namespace)
// The webhook configuration is honored in 1s.
time.Sleep(10 * time.Second)
return func() {
client.AdmissionregistrationV1beta1().ValidatingWebhookConfigurations().Delete(configName, nil)
}
}
func registerMutatingWebhookForConfigMap(f *framework.Framework, context *certContext) {
func registerMutatingWebhookForConfigMap(f *framework.Framework, context *certContext) func() {
client := f.ClientSet
By("Registering the mutating configmap webhook via the AdmissionRegistration API")
namespace := f.Namespace.Name
configName := mutatingWebhookConfigName
_, err := client.AdmissionregistrationV1beta1().MutatingWebhookConfigurations().Create(&v1beta1.MutatingWebhookConfiguration{
ObjectMeta: metav1.ObjectMeta{
Name: mutatingWebhookConfigName,
Name: configName,
},
Webhooks: []v1beta1.Webhook{
{
@ -416,11 +435,13 @@ func registerMutatingWebhookForConfigMap(f *framework.Framework, context *certCo
},
},
})
framework.ExpectNoError(err, "registering mutating webhook config %s with namespace %s", mutatingWebhookConfigName, namespace)
framework.ExpectNoError(err, "registering mutating webhook config %s with namespace %s", configName, namespace)
// The webhook configuration is honored in 1s.
time.Sleep(10 * time.Second)
return func() { client.AdmissionregistrationV1beta1().MutatingWebhookConfigurations().Delete(configName, nil) }
}
func testMutatingConfigMapWebhook(f *framework.Framework) {
By("create a configmap that should be updated by the webhook")
client := f.ClientSet
@ -437,6 +458,80 @@ func testMutatingConfigMapWebhook(f *framework.Framework) {
}
}
func registerMutatingWebhookForPod(f *framework.Framework, context *certContext) func() {
client := f.ClientSet
By("Registering the mutating pod webhook via the AdmissionRegistration API")
namespace := f.Namespace.Name
configName := podMutatingWebhookConfigName
_, err := client.AdmissionregistrationV1beta1().MutatingWebhookConfigurations().Create(&v1beta1.MutatingWebhookConfiguration{
ObjectMeta: metav1.ObjectMeta{
Name: configName,
},
Webhooks: []v1beta1.Webhook{
{
Name: "adding-init-container.k8s.io",
Rules: []v1beta1.RuleWithOperations{{
Operations: []v1beta1.OperationType{v1beta1.Create},
Rule: v1beta1.Rule{
APIGroups: []string{""},
APIVersions: []string{"v1"},
Resources: []string{"pods"},
},
}},
ClientConfig: v1beta1.WebhookClientConfig{
Service: &v1beta1.ServiceReference{
Namespace: namespace,
Name: serviceName,
Path: strPtr("/mutating-pods"),
},
CABundle: context.signingCert,
},
},
},
})
framework.ExpectNoError(err, "registering mutating webhook config %s with namespace %s", configName, namespace)
// The webhook configuration is honored in 1s.
time.Sleep(10 * time.Second)
return func() { client.AdmissionregistrationV1beta1().MutatingWebhookConfigurations().Delete(configName, nil) }
}
func testMutatingPodWebhook(f *framework.Framework) {
By("create a pod that should be updated by the webhook")
client := f.ClientSet
configMap := toBeMutatedPod(f)
mutatedPod, err := client.CoreV1().Pods(f.Namespace.Name).Create(configMap)
Expect(err).To(BeNil())
if len(mutatedPod.Spec.InitContainers) != 1 {
framework.Failf("expect pod to have 1 init container, got %#v", mutatedPod.Spec.InitContainers)
}
if got, expected := mutatedPod.Spec.InitContainers[0].Name, "webhook-added-init-container"; got != expected {
framework.Failf("expect the init container name to be %q, got %q", expected, got)
}
if got, expected := mutatedPod.Spec.InitContainers[0].TerminationMessagePolicy, v1.TerminationMessageReadFile; got != expected {
framework.Failf("expect the init terminationMessagePolicy to be default to %q, got %q", expected, got)
}
}
func toBeMutatedPod(f *framework.Framework) *v1.Pod {
return &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "webhook-to-be-mutated",
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "example",
Image: framework.GetPauseImageName(f.ClientSet),
},
},
},
}
}
func testWebhook(f *framework.Framework) {
By("create a pod that should be denied by the webhook")
client := f.ClientSet
@ -453,6 +548,17 @@ func testWebhook(f *framework.Framework) {
framework.Failf("expect error contains %q, got %q", expectedErrMsg2, err.Error())
}
By("create a pod that causes the webhook to hang")
client = f.ClientSet
// Creating the pod, the request should be rejected
pod = hangingPod(f)
_, err = client.CoreV1().Pods(f.Namespace.Name).Create(pod)
Expect(err).NotTo(BeNil())
expectedTimeoutErr := "request did not complete within allowed duration"
if !strings.Contains(err.Error(), expectedTimeoutErr) {
framework.Failf("expect timeout error %q, got %q", expectedTimeoutErr, err.Error())
}
By("create a configmap that should be denied by the webhook")
// Creating the configmap, the request should be rejected
configmap := nonCompliantConfigMap(f)
@ -539,11 +645,12 @@ func failingWebhook(namespace, name string) v1beta1.Webhook {
}
}
func registerFailClosedWebhook(f *framework.Framework, context *certContext) {
func registerFailClosedWebhook(f *framework.Framework, context *certContext) func() {
client := f.ClientSet
By("Registering a webhook that server cannot talk to, with fail closed policy, via the AdmissionRegistration API")
namespace := f.Namespace.Name
configName := webhookFailClosedConfigName
// A webhook that cannot talk to server, with fail-closed policy
policyFail := v1beta1.Fail
hook := failingWebhook(namespace, "fail-closed.k8s.io")
@ -560,7 +667,7 @@ func registerFailClosedWebhook(f *framework.Framework, context *certContext) {
_, err := client.AdmissionregistrationV1beta1().ValidatingWebhookConfigurations().Create(&v1beta1.ValidatingWebhookConfiguration{
ObjectMeta: metav1.ObjectMeta{
Name: webhookFailClosedConfigName,
Name: configName,
},
Webhooks: []v1beta1.Webhook{
// Server cannot talk to this webhook, so it always fails.
@ -568,10 +675,13 @@ func registerFailClosedWebhook(f *framework.Framework, context *certContext) {
hook,
},
})
framework.ExpectNoError(err, "registering webhook config %s with namespace %s", webhookFailClosedConfigName, namespace)
framework.ExpectNoError(err, "registering webhook config %s with namespace %s", configName, namespace)
// The webhook configuration is honored in 10s.
time.Sleep(10 * time.Second)
return func() {
f.ClientSet.AdmissionregistrationV1beta1().ValidatingWebhookConfigurations().Delete(configName, nil)
}
}
func testFailClosedWebhook(f *framework.Framework) {
@ -631,6 +741,25 @@ func nonCompliantPod(f *framework.Framework) *v1.Pod {
}
}
func hangingPod(f *framework.Framework) *v1.Pod {
return &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: hangingPodName,
Labels: map[string]string{
"webhook-e2e-test": "wait-forever",
},
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "wait-forever",
Image: framework.GetPauseImageName(f.ClientSet),
},
},
},
}
}
func nonCompliantConfigMap(f *framework.Framework) *v1.ConfigMap {
return &v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
@ -686,64 +815,15 @@ func cleanWebhookTest(client clientset.Interface, namespaceName string) {
_ = client.RbacV1beta1().RoleBindings("kube-system").Delete(roleBindingName, nil)
}
// newCRDForAdmissionWebhookTest generates a CRD
func newCRDForAdmissionWebhookTest() *apiextensionsv1beta1.CustomResourceDefinition {
return &apiextensionsv1beta1.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{Name: crdName + "s." + crdAPIGroup},
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
Group: crdAPIGroup,
Version: crdAPIVersion,
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
Plural: crdName + "s",
Singular: crdName,
Kind: crdKind,
ListKind: crdName + "List",
},
Scope: apiextensionsv1beta1.NamespaceScoped,
},
}
}
func createCRD(f *framework.Framework) (func(), dynamic.ResourceInterface) {
config, err := framework.LoadConfig()
if err != nil {
framework.Failf("failed to load config: %v", err)
}
apiExtensionClient, err := crdclientset.NewForConfig(config)
if err != nil {
framework.Failf("failed to initialize apiExtensionClient: %v", err)
}
crd := newCRDForAdmissionWebhookTest()
//create CRD and waits for the resource to be recognized and available.
dynamicClient, err := testserver.CreateNewCustomResourceDefinitionWatchUnsafe(crd, apiExtensionClient, f.ClientPool)
if err != nil {
framework.Failf("failed to create CustomResourceDefinition: %v", err)
}
resourceClient := dynamicClient.Resource(&metav1.APIResource{
Name: crd.Spec.Names.Plural,
Namespaced: true,
}, f.Namespace.Name)
return func() {
err = testserver.DeleteCustomResourceDefinition(crd, apiExtensionClient)
if err != nil {
framework.Failf("failed to delete CustomResourceDefinition: %v", err)
}
}, resourceClient
}
func registerWebhookForCRD(f *framework.Framework, context *certContext) {
func registerWebhookForCRD(f *framework.Framework, context *certContext, testcrd *framework.TestCrd) func() {
client := f.ClientSet
By("Registering the crd webhook via the AdmissionRegistration API")
namespace := f.Namespace.Name
configName := crdWebhookConfigName
_, err := client.AdmissionregistrationV1beta1().ValidatingWebhookConfigurations().Create(&v1beta1.ValidatingWebhookConfiguration{
ObjectMeta: metav1.ObjectMeta{
Name: crdWebhookConfigName,
Name: configName,
},
Webhooks: []v1beta1.Webhook{
{
@ -751,9 +831,9 @@ func registerWebhookForCRD(f *framework.Framework, context *certContext) {
Rules: []v1beta1.RuleWithOperations{{
Operations: []v1beta1.OperationType{v1beta1.Create},
Rule: v1beta1.Rule{
APIGroups: []string{crdAPIGroup},
APIVersions: []string{crdAPIVersion},
Resources: []string{crdName + "s"},
APIGroups: []string{testcrd.ApiGroup},
APIVersions: []string{testcrd.ApiVersion},
Resources: []string{testcrd.GetPluralName()},
},
}},
ClientConfig: v1beta1.WebhookClientConfig{
@ -767,20 +847,24 @@ func registerWebhookForCRD(f *framework.Framework, context *certContext) {
},
},
})
framework.ExpectNoError(err, "registering crd webhook config %s with namespace %s", webhookConfigName, namespace)
framework.ExpectNoError(err, "registering crd webhook config %s with namespace %s", configName, namespace)
// The webhook configuration is honored in 1s.
time.Sleep(10 * time.Second)
return func() {
client.AdmissionregistrationV1beta1().ValidatingWebhookConfigurations().Delete(configName, nil)
}
}
func registerMutatingWebhookForCRD(f *framework.Framework, context *certContext) {
func registerMutatingWebhookForCRD(f *framework.Framework, context *certContext, testcrd *framework.TestCrd) func() {
client := f.ClientSet
By("Registering the mutating webhook for crd via the AdmissionRegistration API")
namespace := f.Namespace.Name
configName := crdMutatingWebhookConfigName
_, err := client.AdmissionregistrationV1beta1().MutatingWebhookConfigurations().Create(&v1beta1.MutatingWebhookConfiguration{
ObjectMeta: metav1.ObjectMeta{
Name: crdMutatingWebhookConfigName,
Name: configName,
},
Webhooks: []v1beta1.Webhook{
{
@ -788,9 +872,9 @@ func registerMutatingWebhookForCRD(f *framework.Framework, context *certContext)
Rules: []v1beta1.RuleWithOperations{{
Operations: []v1beta1.OperationType{v1beta1.Create},
Rule: v1beta1.Rule{
APIGroups: []string{crdAPIGroup},
APIVersions: []string{crdAPIVersion},
Resources: []string{crdName + "s"},
APIGroups: []string{testcrd.ApiGroup},
APIVersions: []string{testcrd.ApiVersion},
Resources: []string{testcrd.GetPluralName()},
},
}},
ClientConfig: v1beta1.WebhookClientConfig{
@ -807,9 +891,9 @@ func registerMutatingWebhookForCRD(f *framework.Framework, context *certContext)
Rules: []v1beta1.RuleWithOperations{{
Operations: []v1beta1.OperationType{v1beta1.Create},
Rule: v1beta1.Rule{
APIGroups: []string{crdAPIGroup},
APIVersions: []string{crdAPIVersion},
Resources: []string{crdName + "s"},
APIGroups: []string{testcrd.ApiGroup},
APIVersions: []string{testcrd.ApiVersion},
Resources: []string{testcrd.GetPluralName()},
},
}},
ClientConfig: v1beta1.WebhookClientConfig{
@ -823,15 +907,16 @@ func registerMutatingWebhookForCRD(f *framework.Framework, context *certContext)
},
},
})
framework.ExpectNoError(err, "registering crd webhook config %s with namespace %s", crdMutatingWebhookConfigName, namespace)
framework.ExpectNoError(err, "registering crd webhook config %s with namespace %s", configName, namespace)
// The webhook configuration is honored in 1s.
time.Sleep(10 * time.Second)
return func() { client.AdmissionregistrationV1beta1().MutatingWebhookConfigurations().Delete(configName, nil) }
}
func testCRDWebhook(f *framework.Framework, crdClient dynamic.ResourceInterface) {
func testCRDWebhook(f *framework.Framework, crd *apiextensionsv1beta1.CustomResourceDefinition, crdClient dynamic.ResourceInterface) {
By("Creating a custom resource that should be denied by the webhook")
crd := newCRDForAdmissionWebhookTest()
crInstance := &unstructured.Unstructured{
Object: map[string]interface{}{
"kind": crd.Spec.Names.Kind,
@ -853,9 +938,8 @@ func testCRDWebhook(f *framework.Framework, crdClient dynamic.ResourceInterface)
}
}
func testMutatingCRDWebhook(f *framework.Framework, crdClient dynamic.ResourceInterface) {
func testMutatingCRDWebhook(f *framework.Framework, crd *apiextensionsv1beta1.CustomResourceDefinition, crdClient dynamic.ResourceInterface) {
By("Creating a custom resource that should be mutated by the webhook")
crd := newCRDForAdmissionWebhookTest()
cr := &unstructured.Unstructured{
Object: map[string]interface{}{
"kind": crd.Spec.Names.Kind,