Skip to content

Commit 080fe59

Browse files
Merge pull request #14880 from tnozicka/add-tests-for-rc-to-dc-controller-ref
Add extended tests for DC ControllerRef
2 parents 6d94ba8 + 952ee3a commit 080fe59

File tree

2 files changed

+234
-0
lines changed

2 files changed

+234
-0
lines changed

test/extended/deployments/deployments.go

+108
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@ import (
1212
"k8s.io/apimachinery/pkg/api/errors"
1313
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1414
"k8s.io/apimachinery/pkg/labels"
15+
"k8s.io/apimachinery/pkg/types"
1516
"k8s.io/apimachinery/pkg/util/wait"
1617
kapiv1 "k8s.io/kubernetes/pkg/api/v1"
18+
kcontroller "k8s.io/kubernetes/pkg/controller"
1719
e2e "k8s.io/kubernetes/test/e2e/framework"
1820

1921
"github.com/openshift/origin/pkg/client"
@@ -24,6 +26,7 @@ import (
2426
)
2527

2628
const deploymentRunTimeout = 5 * time.Minute
29+
const deploymentChangeTimeout = 30 * time.Second
2730

2831
var _ = g.Describe("deploymentconfigs", func() {
2932
defer g.GinkgoRecover()
@@ -968,4 +971,109 @@ var _ = g.Describe("deploymentconfigs", func() {
968971
o.Expect(err).NotTo(o.HaveOccurred())
969972
})
970973
})
974+
975+
g.Describe("", func() {
976+
dcName := "deployment-simple"
977+
g.AfterEach(func() {
978+
failureTrap(oc, dcName, g.CurrentGinkgoTestDescription().Failed)
979+
failureTrapForDetachedRCs(oc, dcName, g.CurrentGinkgoTestDescription().Failed)
980+
})
981+
982+
g.It("should adhere to Three Laws of Controllers [Conformance]", func() {
983+
namespace := oc.Namespace()
984+
rcName := func(i int) string { return fmt.Sprintf("%s-%d", dcName, i) }
985+
986+
var dc *deployapi.DeploymentConfig
987+
var rc1 *kapiv1.ReplicationController
988+
var err error
989+
990+
g.By("should create ControllerRef in RCs it creates", func() {
991+
dc, err = readDCFixture(simpleDeploymentFixture)
992+
o.Expect(err).NotTo(o.HaveOccurred())
993+
dc, err = oc.Client().DeploymentConfigs(namespace).Create(dc)
994+
o.Expect(err).NotTo(o.HaveOccurred())
995+
996+
err = waitForLatestCondition(oc, dcName, deploymentRunTimeout, deploymentRunning)
997+
o.Expect(err).NotTo(o.HaveOccurred())
998+
999+
rc1, err = oc.KubeClient().CoreV1().ReplicationControllers(namespace).Get(rcName(1), metav1.GetOptions{})
1000+
o.Expect(err).NotTo(o.HaveOccurred())
1001+
validRef := HasValidDCControllerRef(dc, rc1)
1002+
o.Expect(validRef).To(o.BeTrue())
1003+
})
1004+
1005+
err = waitForLatestCondition(oc, dcName, deploymentRunTimeout, deploymentReachedCompletion)
1006+
o.Expect(err).NotTo(o.HaveOccurred())
1007+
1008+
g.By("releasing RCs that no longer match its selector", func() {
1009+
dc, err = oc.Client().DeploymentConfigs(namespace).Get(dcName, metav1.GetOptions{})
1010+
o.Expect(err).NotTo(o.HaveOccurred())
1011+
1012+
patch := []byte(fmt.Sprintf(`{"metadata": {"labels":{"openshift.io/deployment-config.name": "%s-detached"}}}`, dcName))
1013+
rc1, err = oc.KubeClient().CoreV1().ReplicationControllers(namespace).Patch(rcName(1), types.StrategicMergePatchType, patch)
1014+
o.Expect(err).NotTo(o.HaveOccurred())
1015+
1016+
rc1, err = waitForRCModification(oc, namespace, rcName(1), deploymentChangeTimeout,
1017+
rc1.GetResourceVersion(), rCConditionFromMeta(controllerRefChangeCondition(kcontroller.GetControllerOf(rc1))))
1018+
o.Expect(err).NotTo(o.HaveOccurred())
1019+
controllerRef := kcontroller.GetControllerOf(rc1)
1020+
o.Expect(controllerRef).To(o.BeNil())
1021+
1022+
dc, err = waitForDCModification(oc, namespace, dcName, deploymentChangeTimeout,
1023+
dc.GetResourceVersion(), func(config *deployapi.DeploymentConfig) (bool, error) {
1024+
return config.Status.AvailableReplicas != dc.Status.AvailableReplicas, nil
1025+
})
1026+
o.Expect(err).NotTo(o.HaveOccurred())
1027+
o.Expect(dc.Status.AvailableReplicas).To(o.BeZero())
1028+
o.Expect(dc.Status.UnavailableReplicas).To(o.BeZero())
1029+
})
1030+
1031+
g.By("adopting RCs that match its selector and have no ControllerRef", func() {
1032+
patch := []byte(fmt.Sprintf(`{"metadata": {"labels":{"openshift.io/deployment-config.name": "%s"}}}`, dcName))
1033+
rc1, err = oc.KubeClient().CoreV1().ReplicationControllers(namespace).Patch(rcName(1), types.StrategicMergePatchType, patch)
1034+
o.Expect(err).NotTo(o.HaveOccurred())
1035+
1036+
rc1, err = waitForRCModification(oc, namespace, rcName(1), deploymentChangeTimeout,
1037+
rc1.GetResourceVersion(), rCConditionFromMeta(controllerRefChangeCondition(kcontroller.GetControllerOf(rc1))))
1038+
o.Expect(err).NotTo(o.HaveOccurred())
1039+
validRef := HasValidDCControllerRef(dc, rc1)
1040+
o.Expect(validRef).To(o.BeTrue())
1041+
1042+
dc, err = waitForDCModification(oc, namespace, dcName, deploymentChangeTimeout,
1043+
dc.GetResourceVersion(), func(config *deployapi.DeploymentConfig) (bool, error) {
1044+
return config.Status.AvailableReplicas != dc.Status.AvailableReplicas, nil
1045+
})
1046+
o.Expect(err).NotTo(o.HaveOccurred())
1047+
o.Expect(dc.Status.AvailableReplicas).To(o.Equal(dc.Spec.Replicas))
1048+
o.Expect(dc.Status.UnavailableReplicas).To(o.BeZero())
1049+
})
1050+
1051+
g.By("deleting owned RCs when deleted", func() {
1052+
// FIXME: Add delete option when we have new client available.
1053+
// This is working fine now because of finalizers on RCs but when GC gets fixed
1054+
// and we remove them this will probably break and will require setting deleteOptions
1055+
// to achieve cascade delete
1056+
err = oc.Client().DeploymentConfigs(namespace).Delete(dcName)
1057+
o.Expect(err).NotTo(o.HaveOccurred())
1058+
1059+
err = wait.PollImmediate(200*time.Millisecond, 5*time.Minute, func() (bool, error) {
1060+
pods, err := oc.KubeClient().CoreV1().Pods(namespace).List(metav1.ListOptions{})
1061+
if err != nil {
1062+
return false, err
1063+
}
1064+
return len(pods.Items) == 0, nil
1065+
})
1066+
o.Expect(err).NotTo(o.HaveOccurred())
1067+
1068+
err = wait.PollImmediate(200*time.Millisecond, 30*time.Second, func() (bool, error) {
1069+
rcs, err := oc.KubeClient().CoreV1().ReplicationControllers(namespace).List(metav1.ListOptions{})
1070+
if err != nil {
1071+
return false, err
1072+
}
1073+
return len(rcs.Items) == 0, nil
1074+
})
1075+
o.Expect(err).NotTo(o.HaveOccurred())
1076+
})
1077+
})
1078+
})
9711079
})

test/extended/deployments/util.go

+126
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,30 @@ package deployments
22

33
import (
44
"fmt"
5+
"io/ioutil"
6+
"reflect"
57
"sort"
68
"strings"
79
"time"
810

11+
"github.com/ghodss/yaml"
12+
913
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1014
"k8s.io/apimachinery/pkg/fields"
1115
"k8s.io/apimachinery/pkg/util/sets"
1216
"k8s.io/apimachinery/pkg/util/wait"
1317
"k8s.io/apimachinery/pkg/watch"
1418
kapi "k8s.io/kubernetes/pkg/api"
1519
kapiv1 "k8s.io/kubernetes/pkg/api/v1"
20+
kcontroller "k8s.io/kubernetes/pkg/controller"
1621
e2e "k8s.io/kubernetes/test/e2e/framework"
1722

1823
deployapi "github.com/openshift/origin/pkg/deploy/apis/apps"
24+
deployapiv1 "github.com/openshift/origin/pkg/deploy/apis/apps/v1"
1925
deployutil "github.com/openshift/origin/pkg/deploy/util"
2026
exutil "github.com/openshift/origin/test/extended/util"
27+
"k8s.io/apimachinery/pkg/labels"
28+
"k8s.io/apimachinery/pkg/selection"
2129
)
2230

2331
func deploymentStatuses(rcs []kapi.ReplicationController) []string {
@@ -385,6 +393,67 @@ func waitForDeployerToComplete(oc *exutil.CLI, name string, timeout time.Duratio
385393
return output, nil
386394
}
387395

396+
func isControllerRefChange(controllee metav1.Object, old *metav1.OwnerReference) (bool, error) {
397+
if old != nil && old.Controller != nil && *old.Controller == false {
398+
return false, fmt.Errorf("old ownerReference is not a controllerRef")
399+
}
400+
return !reflect.DeepEqual(old, kcontroller.GetControllerOf(controllee)), nil
401+
}
402+
403+
func controllerRefChangeCondition(old *metav1.OwnerReference) func(controllee metav1.Object) (bool, error) {
404+
return func(controllee metav1.Object) (bool, error) {
405+
return isControllerRefChange(controllee, old)
406+
}
407+
}
408+
409+
func rCConditionFromMeta(condition func(metav1.Object) (bool, error)) func(rc *kapiv1.ReplicationController) (bool, error) {
410+
return func(rc *kapiv1.ReplicationController) (bool, error) {
411+
return condition(rc)
412+
}
413+
}
414+
415+
func waitForRCModification(oc *exutil.CLI, namespace string, name string, timeout time.Duration, resourceVersion string, condition func(rc *kapiv1.ReplicationController) (bool, error)) (*kapiv1.ReplicationController, error) {
416+
watcher, err := oc.KubeClient().CoreV1().ReplicationControllers(namespace).Watch(metav1.SingleObject(metav1.ObjectMeta{Name: name, ResourceVersion: resourceVersion}))
417+
if err != nil {
418+
return nil, err
419+
}
420+
421+
event, err := watch.Until(timeout, watcher, func(event watch.Event) (bool, error) {
422+
if event.Type != watch.Modified {
423+
return false, fmt.Errorf("different kind of event appeared while waiting for modification: event: %#v", event)
424+
}
425+
return condition(event.Object.(*kapiv1.ReplicationController))
426+
})
427+
if err != nil {
428+
return nil, err
429+
}
430+
if event.Type != watch.Modified {
431+
return nil, fmt.Errorf("waiting for RC modification failed: event: %v", event)
432+
}
433+
return event.Object.(*kapiv1.ReplicationController), nil
434+
}
435+
436+
func waitForDCModification(oc *exutil.CLI, namespace string, name string, timeout time.Duration, resourceVersion string, condition func(rc *deployapi.DeploymentConfig) (bool, error)) (*deployapi.DeploymentConfig, error) {
437+
watcher, err := oc.Client().DeploymentConfigs(namespace).Watch(metav1.SingleObject(metav1.ObjectMeta{Name: name, ResourceVersion: resourceVersion}))
438+
if err != nil {
439+
return nil, err
440+
}
441+
442+
event, err := watch.Until(timeout, watcher, func(event watch.Event) (bool, error) {
443+
if event.Type != watch.Modified {
444+
return false, fmt.Errorf("different kind of event appeared while waiting for modification: event: %#v", event)
445+
}
446+
return condition(event.Object.(*deployapi.DeploymentConfig))
447+
})
448+
if err != nil {
449+
return nil, err
450+
}
451+
if event.Type != watch.Modified {
452+
return nil, fmt.Errorf("waiting for DC modification failed: event: %v", event)
453+
}
454+
return event.Object.(*deployapi.DeploymentConfig), nil
455+
}
456+
388457
// createFixture will create the provided fixture and return the resource and the
389458
// name separately.
390459
// TODO: Probably move to a more general location like test/extended/util/cli.go
@@ -437,3 +506,60 @@ func failureTrap(oc *exutil.CLI, name string, failed bool) {
437506
}
438507
}
439508
}
509+
510+
func failureTrapForDetachedRCs(oc *exutil.CLI, dcName string, failed bool) {
511+
if !failed {
512+
return
513+
}
514+
kclient := oc.KubeClient()
515+
requirement, err := labels.NewRequirement(deployapi.DeploymentConfigAnnotation, selection.NotEquals, []string{dcName})
516+
if err != nil {
517+
e2e.Logf("failed to create requirement for DC %q", dcName)
518+
return
519+
}
520+
dc, err := kclient.CoreV1().ReplicationControllers(oc.Namespace()).List(metav1.ListOptions{
521+
LabelSelector: labels.NewSelector().Add(*requirement).String(),
522+
})
523+
if err != nil {
524+
e2e.Logf("Error getting detached RCs; DC %q: %v", dcName, err)
525+
return
526+
}
527+
if len(dc.Items) == 0 {
528+
e2e.Logf("No detached RCs found.")
529+
} else {
530+
out, err := oc.Run("get").Args("rc", "-o", "yaml", "-l", fmt.Sprintf("%s!=%s", deployapi.DeploymentConfigAnnotation, dcName)).Output()
531+
if err != nil {
532+
e2e.Logf("Failed to list detached RCs!")
533+
return
534+
}
535+
e2e.Logf("There are detached RCs: \n%s", out)
536+
}
537+
}
538+
539+
// Checks controllerRef from controllee to DC.
540+
// Return true is the controllerRef is valid, false otherwise
541+
func HasValidDCControllerRef(dc metav1.Object, controllee metav1.Object) bool {
542+
ref := kcontroller.GetControllerOf(controllee)
543+
return ref != nil &&
544+
ref.UID == dc.GetUID() &&
545+
ref.APIVersion == deployutil.DeploymentConfigControllerRefKind.GroupVersion().String() &&
546+
ref.Kind == deployutil.DeploymentConfigControllerRefKind.Kind &&
547+
ref.Name == dc.GetName()
548+
}
549+
550+
func readDCFixture(path string) (*deployapi.DeploymentConfig, error) {
551+
data, err := ioutil.ReadFile(path)
552+
if err != nil {
553+
return nil, err
554+
}
555+
556+
dcv1 := new(deployapiv1.DeploymentConfig)
557+
err = yaml.Unmarshal(data, dcv1)
558+
if err != nil {
559+
return nil, err
560+
}
561+
562+
dc := new(deployapi.DeploymentConfig)
563+
err = deployapiv1.Convert_v1_DeploymentConfig_To_apps_DeploymentConfig(dcv1, dc, nil)
564+
return dc, err
565+
}

0 commit comments

Comments
 (0)