mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-12-08 02:15:27 +00:00
Remove alpha InitializerConfiguration types, Initializers admission plugin
This commit is contained in:
@@ -1,423 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package apimachinery
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"k8s.io/api/admissionregistration/v1alpha1"
|
||||
apps "k8s.io/api/apps/v1"
|
||||
"k8s.io/api/core/v1"
|
||||
"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() {
|
||||
pod := newUninitializedPod(podName)
|
||||
_, err := c.CoreV1().Pods(ns).Create(pod)
|
||||
Expect(err).NotTo(HaveOccurred(), "failed to create pod %s in namespace: %s", podName, ns)
|
||||
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.CoreV1().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.CoreV1().Pods(ns).Get(podName, metav1.GetOptions{})
|
||||
Expect(err).NotTo(HaveOccurred(), "failed to get pod %s in namespace: %s", podName, ns)
|
||||
pod.Annotations = map[string]string{"update-1": "test"}
|
||||
pod, err = c.CoreV1().Pods(ns).Update(pod)
|
||||
Expect(err).NotTo(HaveOccurred(), "failed to update pod %s in namespace %s to: %+v", pod.Name, ns, pod)
|
||||
|
||||
// verify the list call filters out uninitialized pods
|
||||
listOptions := metav1.ListOptions{IncludeUninitialized: true}
|
||||
pods, err := c.CoreV1().Pods(ns).List(listOptions)
|
||||
Expect(err).NotTo(HaveOccurred(), "failed to list pods in namespace: %s, given list options: %+v", ns, listOptions)
|
||||
Expect(pods.Items).To(HaveLen(1))
|
||||
pods, err = c.CoreV1().Pods(ns).List(metav1.ListOptions{})
|
||||
Expect(err).NotTo(HaveOccurred(), "failed to list pods in namespace: %s", ns)
|
||||
Expect(pods.Items).To(HaveLen(0))
|
||||
|
||||
// clear initializers
|
||||
pod.Initializers = nil
|
||||
pod, err = c.CoreV1().Pods(ns).Update(pod)
|
||||
Expect(err).NotTo(HaveOccurred(), "failed to update pod %s in namespace %s to: %+v", pod.Name, ns, pod)
|
||||
|
||||
// pod should now start running
|
||||
err = framework.WaitForPodRunningInNamespace(c, pod)
|
||||
Expect(err).NotTo(HaveOccurred(), "error while waiting for pod %s to go to Running phase in namespace: %s", pod.Name, pod.Namespace)
|
||||
|
||||
// ensure create call returns
|
||||
<-ch
|
||||
|
||||
// verify that we cannot start the pod initializing again
|
||||
pod, err = c.CoreV1().Pods(ns).Get(podName, metav1.GetOptions{})
|
||||
Expect(err).NotTo(HaveOccurred(), "failed to get pod %s in namespace: %s", podName, ns)
|
||||
pod.Initializers = &metav1.Initializers{
|
||||
Pending: []metav1.Initializer{{Name: "Other"}},
|
||||
}
|
||||
_, err = c.CoreV1().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"
|
||||
initializerConfig := &v1alpha1.InitializerConfiguration{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: initializerConfigName},
|
||||
Initializers: []v1alpha1.Initializer{
|
||||
{
|
||||
Name: initializerName,
|
||||
Rules: []v1alpha1.Rule{
|
||||
{APIGroups: []string{""}, APIVersions: []string{"*"}, Resources: []string{"pods"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
_, err := c.AdmissionregistrationV1alpha1().InitializerConfigurations().Create(initializerConfig)
|
||||
if errors.IsNotFound(err) {
|
||||
framework.Skipf("dynamic configuration of initializers requires the alpha admissionregistration.k8s.io group to be enabled")
|
||||
}
|
||||
Expect(err).NotTo(HaveOccurred(), "failed to create and register initializer with config: %+v", initializerConfig)
|
||||
|
||||
// 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)
|
||||
pod := newInitPod(podName)
|
||||
_, err := c.CoreV1().Pods(ns).Create(pod)
|
||||
Expect(err).NotTo(HaveOccurred(), "failed to create pod %s in namespace: %s", podName, ns)
|
||||
}()
|
||||
|
||||
// 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.CoreV1().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(), "failed to get pod %s from namespace: %s", podName, ns)
|
||||
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.CoreV1().Pods(ns).Update(pod)
|
||||
Expect(err).NotTo(HaveOccurred(), "failed to update pod %s in namespace %s to: %+v", pod.Name, ns, pod)
|
||||
|
||||
// ensure create call returns
|
||||
<-ch
|
||||
|
||||
// pod should now start running
|
||||
err = framework.WaitForPodRunningInNamespace(c, pod)
|
||||
Expect(err).NotTo(HaveOccurred(), "error while waiting for pod %s to go to Running phase in namespace: %s", pod.Name, pod.Namespace)
|
||||
|
||||
// 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.CoreV1().Pods(ns).Create(pod)
|
||||
Expect(err).NotTo(HaveOccurred(), "failed to create pod %s in namespace: %s", podName, ns)
|
||||
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.CoreV1().Pods(ns).Create(pod)
|
||||
Expect(err).NotTo(HaveOccurred(), "failed to create pod %s in namespace: %s", podName, ns)
|
||||
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"
|
||||
initializerConfig := &v1alpha1.InitializerConfiguration{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: initializerConfigName},
|
||||
Initializers: []v1alpha1.Initializer{
|
||||
{
|
||||
Name: initializerName,
|
||||
Rules: []v1alpha1.Rule{
|
||||
{APIGroups: []string{""}, APIVersions: []string{"*"}, Resources: []string{"pods"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
_, err := c.AdmissionregistrationV1alpha1().InitializerConfigurations().Create(initializerConfig)
|
||||
if errors.IsNotFound(err) {
|
||||
framework.Skipf("dynamic configuration of initializers requires the alpha admissionregistration.k8s.io group to be enabled")
|
||||
}
|
||||
Expect(err).NotTo(HaveOccurred(), "failed to create and register initializer with config: %+v", initializerConfig)
|
||||
|
||||
// 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
|
||||
rs := newReplicaset()
|
||||
persistedRS, err := c.AppsV1().ReplicaSets(ns).Create(rs)
|
||||
Expect(err).NotTo(HaveOccurred(), "failed to create replicaset %s in namespace: %s", persistedRS.Name, ns)
|
||||
// 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(), "replicaset %s failed to observe generation: %d", persistedRS.Name, persistedRS.Generation)
|
||||
|
||||
// update the replicaset spec to trigger a resync
|
||||
patch := []byte(`{"spec":{"minReadySeconds":5}}`)
|
||||
persistedRS, err = c.AppsV1().ReplicaSets(ns).Patch(persistedRS.Name, types.StrategicMergePatchType, patch)
|
||||
Expect(err).NotTo(HaveOccurred(), "failed to apply to replicaset %s in namespace %s a strategic merge patch: %s", persistedRS.Name, ns, patch)
|
||||
|
||||
// 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(), "replicaset %s failed to observe generation: %d", persistedRS.Name, persistedRS.Generation)
|
||||
|
||||
// verify that the replicaset controller doesn't create extra pod
|
||||
selector, err := metav1.LabelSelectorAsSelector(persistedRS.Spec.Selector)
|
||||
Expect(err).NotTo(HaveOccurred(), "failed to convert label selector %+v of LabelSelector api type into a struct that implements labels.Selector", persistedRS.Spec.Selector)
|
||||
|
||||
listOptions := metav1.ListOptions{
|
||||
LabelSelector: selector.String(),
|
||||
IncludeUninitialized: true,
|
||||
}
|
||||
pods, err := c.CoreV1().Pods(ns).List(listOptions)
|
||||
Expect(err).NotTo(HaveOccurred(), "failed to list pods in namespace: %s, given list options: %+v", ns, listOptions)
|
||||
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(), "failed to get pod %s in namespace: %s", podName, ns)
|
||||
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(), "failed to apply to pod %s in namespace %s a strategic merge patch: %s", uninitializedPod.Name, ns, patch)
|
||||
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() *apps.ReplicaSet {
|
||||
name := "initializer-test-replicaset"
|
||||
replicas := int32(1)
|
||||
labels := map[string]string{"initializer-test": "single-replicaset"}
|
||||
return &apps.ReplicaSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
Spec: apps.ReplicaSetSpec{
|
||||
Replicas: &replicas,
|
||||
Template: v1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: labels,
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
TerminationGracePeriodSeconds: &zero,
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Name: name + "-container",
|
||||
Image: imageutils.GetE2EImage(imageutils.Porter),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
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.CoreV1().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.CoreV1().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.CoreV1().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.AppsV1().ReplicaSets(ns).Get(name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if generation > rs.Status.ObservedGeneration {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
})
|
||||
}
|
||||
@@ -74,10 +74,6 @@ var kindWhiteList = sets.NewString(
|
||||
"AdmissionReview",
|
||||
// --
|
||||
|
||||
// k8s.io/api/admissionregistration
|
||||
"InitializerConfiguration",
|
||||
// --
|
||||
|
||||
// k8s.io/api/authentication
|
||||
"TokenRequest",
|
||||
"TokenReview",
|
||||
|
||||
@@ -394,11 +394,6 @@ func GetEtcdStorageData() map[schema.GroupVersionResource]StorageData {
|
||||
},
|
||||
// --
|
||||
|
||||
// k8s.io/kubernetes/pkg/apis/admissionregistration/v1alpha1
|
||||
gvr("admissionregistration.k8s.io", "v1alpha1", "initializerconfigurations"): {
|
||||
Stub: `{"metadata":{"name":"ic1"},"initializers":[{"name":"initializer.k8s.io","rules":[{"apiGroups":["group"],"apiVersions":["version"],"resources":["resource"]}],"failurePolicy":"Ignore"}]}`,
|
||||
ExpectedEtcdPath: "/registry/initializerconfigurations/ic1",
|
||||
},
|
||||
// k8s.io/kubernetes/pkg/apis/admissionregistration/v1beta1
|
||||
gvr("admissionregistration.k8s.io", "v1beta1", "validatingwebhookconfigurations"): {
|
||||
Stub: `{"metadata":{"name":"hook1","creationTimestamp":null},"webhooks":[{"name":"externaladmissionhook.k8s.io","clientConfig":{"service":{"namespace":"ns","name":"n"},"caBundle":null},"rules":[{"operations":["CREATE"],"apiGroups":["group"],"apiVersions":["version"],"resources":["resource"]}],"failurePolicy":"Ignore"}]}`,
|
||||
|
||||
@@ -21,15 +21,12 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
admissionregistrationv1alpha1 "k8s.io/api/admissionregistration/v1alpha1"
|
||||
networkingv1 "k8s.io/api/networking/v1"
|
||||
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||
apiextensionsclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/apiserver/pkg/features"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing"
|
||||
@@ -104,11 +101,6 @@ func TestCRD(t *testing.T) {
|
||||
result := kubeapiservertesting.StartTestServerOrDie(t, nil, []string{"--admission-control", "Initializers"}, framework.SharedEtcd())
|
||||
defer result.TearDownFn()
|
||||
|
||||
kubeclient, err := kubernetes.NewForConfig(result.ClientConfig)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
apiextensionsclient, err := apiextensionsclientset.NewForConfig(result.ClientConfig)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
@@ -141,135 +133,6 @@ func TestCRD(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Errorf("Failed to list foos.cr.bar.com instances: %v", err)
|
||||
}
|
||||
|
||||
t.Logf("Creating InitializerConfiguration")
|
||||
_, err = kubeclient.AdmissionregistrationV1alpha1().InitializerConfigurations().Create(&admissionregistrationv1alpha1.InitializerConfiguration{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foos.cr.bar.com",
|
||||
},
|
||||
Initializers: []admissionregistrationv1alpha1.Initializer{
|
||||
{
|
||||
Name: "cr.bar.com",
|
||||
Rules: []admissionregistrationv1alpha1.Rule{
|
||||
{
|
||||
APIGroups: []string{"cr.bar.com"},
|
||||
APIVersions: []string{"*"},
|
||||
Resources: []string{"*"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create InitializerConfiguration: %v", err)
|
||||
}
|
||||
|
||||
// TODO DO NOT MERGE THIS
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
t.Logf("Creating Foo instance")
|
||||
foo := &Foo{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "cr.bar.com/v1",
|
||||
Kind: "Foo",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "foo"},
|
||||
}
|
||||
unstructuredFoo, err := unstructuredFoo(foo)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to create Foo: %v", err)
|
||||
}
|
||||
createErr := make(chan error, 1)
|
||||
go func() {
|
||||
_, err := dynamicClient.Resource(fooResource).Namespace("default").Create(unstructuredFoo, metav1.CreateOptions{})
|
||||
t.Logf("Foo instance create returned: %v", err)
|
||||
if err != nil {
|
||||
createErr <- err
|
||||
}
|
||||
}()
|
||||
|
||||
err = wait.PollImmediate(100*time.Millisecond, wait.ForeverTestTimeout, func() (bool, error) {
|
||||
select {
|
||||
case createErr := <-createErr:
|
||||
return true, createErr
|
||||
default:
|
||||
}
|
||||
|
||||
t.Logf("Checking that Foo instance is visible with IncludeUninitialized=true")
|
||||
_, err := dynamicClient.Resource(fooResource).Namespace("default").Get(foo.ObjectMeta.Name, metav1.GetOptions{
|
||||
IncludeUninitialized: true,
|
||||
})
|
||||
switch {
|
||||
case err == nil:
|
||||
return true, nil
|
||||
case errors.IsNotFound(err):
|
||||
return false, nil
|
||||
default:
|
||||
return false, err
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
t.Logf("Removing initializer from Foo instance")
|
||||
success := false
|
||||
for i := 0; i < 10; i++ {
|
||||
// would love to replace the following with a patch, but removing strings from the intitializer array
|
||||
// is not what JSON (Merge) patch authors had in mind.
|
||||
fooUnstructured, err := dynamicClient.Resource(fooResource).Namespace("default").Get(foo.ObjectMeta.Name, metav1.GetOptions{
|
||||
IncludeUninitialized: true,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error getting Foo instance: %v", err)
|
||||
}
|
||||
bs, _ := fooUnstructured.MarshalJSON()
|
||||
t.Logf("Got Foo instance: %v", string(bs))
|
||||
foo := Foo{}
|
||||
if err := json.Unmarshal(bs, &foo); err != nil {
|
||||
t.Fatalf("Error parsing Foo instance: %v", err)
|
||||
}
|
||||
|
||||
// remove initialize
|
||||
if foo.ObjectMeta.Initializers == nil {
|
||||
t.Fatalf("Expected initializers to be set in Foo instance")
|
||||
}
|
||||
found := false
|
||||
for i := range foo.ObjectMeta.Initializers.Pending {
|
||||
if foo.ObjectMeta.Initializers.Pending[i].Name == "cr.bar.com" {
|
||||
foo.ObjectMeta.Initializers.Pending = append(foo.ObjectMeta.Initializers.Pending[:i], foo.ObjectMeta.Initializers.Pending[i+1:]...)
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatalf("Expected cr.bar.com as initializer on Foo instance")
|
||||
}
|
||||
if len(foo.ObjectMeta.Initializers.Pending) == 0 && foo.ObjectMeta.Initializers.Result == nil {
|
||||
foo.ObjectMeta.Initializers = nil
|
||||
}
|
||||
bs, err = json.Marshal(&foo)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
fooUnstructured.UnmarshalJSON(bs)
|
||||
|
||||
_, err = dynamicClient.Resource(fooResource).Namespace("default").Update(fooUnstructured, metav1.UpdateOptions{})
|
||||
if err != nil && !errors.IsConflict(err) {
|
||||
t.Fatalf("Failed to update Foo instance: %v", err)
|
||||
} else if err == nil {
|
||||
success = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !success {
|
||||
t.Fatalf("Failed to remove initializer from Foo object")
|
||||
}
|
||||
|
||||
t.Logf("Checking that Foo instance is visible after removing the initializer")
|
||||
if _, err := dynamicClient.Resource(fooResource).Namespace("default").Get(foo.ObjectMeta.Name, metav1.GetOptions{}); err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
type Foo struct {
|
||||
|
||||
Reference in New Issue
Block a user