// Copyright 2020-2023 Project Capsule Authors. // SPDX-License-Identifier: Apache-2.0 package e2e import ( "context" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client" capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2" "github.com/projectcapsule/capsule/pkg/api/meta" "github.com/projectcapsule/capsule/pkg/api/rbac" ) var _ = Describe("Owners", Ordered, Label("config", "tenantowner", "tenant", "permissions", "owners"), func() { originConfig := &capsulev1beta2.CapsuleConfiguration{} tnt1 := &capsulev1beta2.Tenant{ ObjectMeta: metav1.ObjectMeta{ Name: "e2e-owners-1", Labels: map[string]string{ "env": "e2e", }, }, Spec: capsulev1beta2.TenantSpec{ Permissions: capsulev1beta2.Permissions{ MatchOwners: []*metav1.LabelSelector{ { MatchLabels: map[string]string{ "customer": "x", "e2e.suite.capsule/name": "owner_test", }, }, { MatchLabels: map[string]string{ "team": "devops", "e2e.suite.capsule/name": "owner_test", }, }, }, }, Owners: rbac.OwnerListSpec{ { CoreOwnerSpec: rbac.CoreOwnerSpec{ UserSpec: rbac.UserSpec{ Name: "e2e-owners-1", Kind: "User", }, }, }, { CoreOwnerSpec: rbac.CoreOwnerSpec{ UserSpec: rbac.UserSpec{ Name: "e2e-owners-1-group", Kind: "Group", }, }, }, { CoreOwnerSpec: rbac.CoreOwnerSpec{ UserSpec: rbac.UserSpec{ Name: "system:serviceaccount:capsule-system:capsule", Kind: "ServiceAccount", }, }, }, }, }, } tnt2 := &capsulev1beta2.Tenant{ ObjectMeta: metav1.ObjectMeta{ Name: "e2e-owners-2", Labels: map[string]string{ "env": "e2e", }, }, Spec: capsulev1beta2.TenantSpec{ Permissions: capsulev1beta2.Permissions{ MatchOwners: []*metav1.LabelSelector{ { MatchLabels: map[string]string{ "customer": "x", "e2e.suite.capsule/name": "owner_test", }, }, { MatchLabels: map[string]string{ "team": "infrastructure", "e2e.suite.capsule/name": "owner_test", }, }, }, }, Owners: rbac.OwnerListSpec{ { CoreOwnerSpec: rbac.CoreOwnerSpec{ UserSpec: rbac.UserSpec{ Name: "e2e-owners-2", Kind: "User", }, }, }, { CoreOwnerSpec: rbac.CoreOwnerSpec{ UserSpec: rbac.UserSpec{ Name: "e2e-owners-2-group", Kind: "Group", }, }, }, { CoreOwnerSpec: rbac.CoreOwnerSpec{ UserSpec: rbac.UserSpec{ Name: "system:serviceaccount:capsule-system:capsule", Kind: "ServiceAccount", }, }, }, }, }, } ownersInfra := &capsulev1beta2.TenantOwner{ ObjectMeta: metav1.ObjectMeta{ Name: "e2e-owners-infra", Labels: map[string]string{ "team": "infrastructure", "e2e.suite.capsule/name": "owner_test", }, }, Spec: capsulev1beta2.TenantOwnerSpec{ Aggregate: ptr.To(true), CoreOwnerSpec: rbac.CoreOwnerSpec{ UserSpec: rbac.UserSpec{ Kind: rbac.GroupOwner, Name: "oidc:comp:administrators", }, ClusterRoles: []string{ "admin", }, }, }, } ownersDevops := &capsulev1beta2.TenantOwner{ ObjectMeta: metav1.ObjectMeta{ Name: "e2e-owners-devops", Labels: map[string]string{ "team": "devops", "e2e.suite.capsule/name": "owner_test", }, }, Spec: capsulev1beta2.TenantOwnerSpec{ Aggregate: ptr.To(true), CoreOwnerSpec: rbac.CoreOwnerSpec{ UserSpec: rbac.UserSpec{ Kind: rbac.GroupOwner, Name: "oidc:comp:devops", }, ClusterRoles: []string{ "view", }, }, }, } ownersCommon := &capsulev1beta2.TenantOwner{ ObjectMeta: metav1.ObjectMeta{ Name: "e2e-owners-common", Labels: map[string]string{ "team": "infrastructure", "customer": "x", "e2e.suite.capsule/name": "owner_test", }, }, Spec: capsulev1beta2.TenantOwnerSpec{ Aggregate: ptr.To(true), CoreOwnerSpec: rbac.CoreOwnerSpec{ UserSpec: rbac.UserSpec{ Kind: rbac.ServiceAccountOwner, Name: "system:serviceaccount:capsule-system:capsule", }, ClusterRoles: []string{ "edit", }, }, }, } tnt1Owner := &capsulev1beta2.TenantOwner{ ObjectMeta: metav1.ObjectMeta{ Name: "e2e-owners-tnt", Labels: map[string]string{ meta.NewTenantLabel: tnt1.GetName(), }, }, Spec: capsulev1beta2.TenantOwnerSpec{ Aggregate: ptr.To(true), CoreOwnerSpec: rbac.CoreOwnerSpec{ UserSpec: rbac.UserSpec{ Kind: rbac.UserOwner, Name: "tnt-1-user", }, ClusterRoles: []string{ "edit", }, }, }, } userOwnersCommon := &capsulev1beta2.TenantOwner{ ObjectMeta: metav1.ObjectMeta{ Name: "e2e-owners-common-user", Labels: map[string]string{ "team": "infrastructure", "customer": "x", "e2e.suite.capsule/name": "owner_test", }, }, Spec: capsulev1beta2.TenantOwnerSpec{ Aggregate: ptr.To(false), CoreOwnerSpec: rbac.CoreOwnerSpec{ UserSpec: rbac.UserSpec{ Kind: rbac.UserOwner, Name: "some-user", }, ClusterRoles: []string{ "view", }, }, }, } JustBeforeEach(func() { Expect(k8sClient.Get(context.Background(), client.ObjectKey{Name: defaultConfigurationName}, originConfig)).To(Succeed()) for _, tnt := range []*capsulev1beta2.Tenant{tnt1, tnt2} { EventuallyCreation(func() error { tnt.ResourceVersion = "" return k8sClient.Create(context.TODO(), tnt) }).Should(Succeed()) TenantReady(tnt, metav1.ConditionTrue, defaultTimeoutInterval) } for _, tnt := range []*capsulev1beta2.TenantOwner{ownersInfra, ownersDevops, ownersCommon, userOwnersCommon, tnt1Owner} { EventuallyCreation(func() error { tnt.ResourceVersion = "" return k8sClient.Create(context.TODO(), tnt) }).Should(Succeed()) } }) JustAfterEach(func() { for _, tnt := range []*capsulev1beta2.Tenant{tnt1, tnt2} { EventuallyDeletion(tnt) } for _, owners := range []*capsulev1beta2.TenantOwner{ownersInfra, ownersDevops, ownersCommon, userOwnersCommon, tnt1Owner} { EventuallyDeletion(owners) } }) It("Verify owners for", func() { By("checking configuration", func() { Eventually(func(g Gomega) { cfg := &capsulev1beta2.CapsuleConfiguration{} err := k8sClient.Get( context.Background(), client.ObjectKey{Name: defaultConfigurationName}, cfg, ) g.Expect(err).ToNot(HaveOccurred()) expected := rbac.UserListSpec{ {Kind: ownersInfra.Spec.Kind, Name: ownersInfra.Spec.Name}, {Kind: ownersDevops.Spec.Kind, Name: ownersDevops.Spec.Name}, {Kind: ownersCommon.Spec.Kind, Name: ownersCommon.Spec.Name}, {Kind: tnt1Owner.Spec.Kind, Name: tnt1Owner.Spec.Name}, {Kind: rbac.GroupOwner, Name: "projectcapsule.dev"}, } g.Expect(cfg.Status.Users).To(ConsistOf(expected)) g.Expect(cfg.Status.Users).NotTo(ContainElement( rbac.UserSpec{Kind: userOwnersCommon.Spec.Kind, Name: userOwnersCommon.Spec.Name}, )) }, defaultTimeoutInterval, defaultPollInterval).Should(Succeed()) }) By("checking owners (e2e-owners-1)", func() { expectedOwners := rbac.OwnerStatusListSpec{ { UserSpec: rbac.UserSpec{ Kind: rbac.GroupOwner, Name: "e2e-owners-1-group", }, ClusterRoles: []string{"admin", "capsule-namespace-deleter"}, }, { UserSpec: rbac.UserSpec{ Kind: rbac.GroupOwner, Name: "oidc:comp:devops", }, ClusterRoles: []string{"view"}, }, { UserSpec: rbac.UserSpec{ Kind: rbac.ServiceAccountOwner, Name: "system:serviceaccount:capsule-system:capsule", }, ClusterRoles: []string{"admin", "capsule-namespace-deleter", "edit"}, }, { UserSpec: rbac.UserSpec{ Kind: rbac.UserOwner, Name: "e2e-owners-1", }, ClusterRoles: []string{"admin", "capsule-namespace-deleter"}, }, { UserSpec: rbac.UserSpec{ Kind: userOwnersCommon.Spec.Kind, Name: userOwnersCommon.Spec.Name, }, ClusterRoles: []string{"view"}, }, { UserSpec: rbac.UserSpec{ Kind: tnt1Owner.Spec.Kind, Name: tnt1Owner.Spec.Name, }, ClusterRoles: []string{"edit"}, }, } t := &capsulev1beta2.Tenant{} Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: tnt1.GetName()}, t)).Should(Succeed()) t = ExpectTenantOwnersEventually(tnt1.GetName(), expectedOwners) VerifyTenantRoleBindings(t) }) By("creating namespaces (e2e-owners-1)", func() { for _, u := range []rbac.UserSpec{ rbac.UserSpec{ Kind: rbac.GroupOwner, Name: "e2e-owners-1-group", }, rbac.UserSpec{ Kind: rbac.GroupOwner, Name: "oidc:comp:devops", }, rbac.UserSpec{ Kind: rbac.ServiceAccountOwner, Name: "system:serviceaccount:capsule-system:capsule", }, rbac.UserSpec{ Kind: rbac.UserOwner, Name: "e2e-owners-1", }, rbac.UserSpec{ Kind: userOwnersCommon.Spec.Kind, Name: userOwnersCommon.Spec.Name, }, rbac.UserSpec{ Kind: tnt1Owner.Spec.Kind, Name: tnt1Owner.Spec.Name, }, } { ns := NewNamespace("", map[string]string{ meta.TenantLabel: tnt1.GetName(), }) NamespaceCreation(ns, u, defaultTimeoutInterval).Should(Succeed()) NamespaceIsPartOfTenant(tnt1, ns).Should(Succeed()) } }) By("checking owners (e2e-owners-2)", func() { t := &capsulev1beta2.Tenant{} Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: tnt2.GetName()}, t)).Should(Succeed()) expectedOwners := rbac.OwnerStatusListSpec{ { UserSpec: rbac.UserSpec{ Kind: rbac.GroupOwner, Name: "e2e-owners-2-group", }, ClusterRoles: []string{"admin", "capsule-namespace-deleter"}, }, { UserSpec: rbac.UserSpec{ Kind: rbac.GroupOwner, Name: "oidc:comp:administrators", }, ClusterRoles: []string{"admin"}, }, { UserSpec: rbac.UserSpec{ Kind: rbac.ServiceAccountOwner, Name: "system:serviceaccount:capsule-system:capsule", }, ClusterRoles: []string{"admin", "capsule-namespace-deleter", "edit"}, }, { UserSpec: rbac.UserSpec{ Kind: rbac.UserOwner, Name: "e2e-owners-2", }, ClusterRoles: []string{"admin", "capsule-namespace-deleter"}, }, { UserSpec: rbac.UserSpec{ Kind: userOwnersCommon.Spec.Kind, Name: userOwnersCommon.Spec.Name, }, ClusterRoles: []string{"view"}, }, } t = ExpectTenantOwnersEventually(tnt2.GetName(), expectedOwners) VerifyTenantRoleBindings(t) }) By("creating namespaces (e2e-owners-2)", func() { for _, u := range []rbac.UserSpec{ rbac.UserSpec{ Kind: rbac.GroupOwner, Name: "e2e-owners-2-group", }, rbac.UserSpec{ Kind: rbac.GroupOwner, Name: "oidc:comp:administrators", }, rbac.UserSpec{ Kind: rbac.ServiceAccountOwner, Name: "system:serviceaccount:capsule-system:capsule", }, rbac.UserSpec{ Kind: rbac.UserOwner, Name: "e2e-owners-2", }, rbac.UserSpec{ Kind: userOwnersCommon.Spec.Kind, Name: userOwnersCommon.Spec.Name, }, } { ns := NewNamespace("", map[string]string{ meta.TenantLabel: tnt2.GetName(), }) NamespaceCreation(ns, u, defaultTimeoutInterval).Should(Succeed()) NamespaceIsPartOfTenant(tnt2, ns).Should(Succeed()) } }) By("remove common tenant-owners", func() { EventuallyDeletion(ownersCommon) }) By("checking configuration", func() { Eventually(func(g Gomega) { cfg := &capsulev1beta2.CapsuleConfiguration{} err := k8sClient.Get( context.Background(), client.ObjectKey{Name: defaultConfigurationName}, cfg, ) g.Expect(err).ToNot(HaveOccurred()) expected := rbac.UserListSpec{ {Kind: ownersInfra.Spec.Kind, Name: ownersInfra.Spec.Name}, {Kind: ownersDevops.Spec.Kind, Name: ownersDevops.Spec.Name}, {Kind: tnt1Owner.Spec.Kind, Name: tnt1Owner.Spec.Name}, {Kind: rbac.GroupOwner, Name: "projectcapsule.dev"}, } g.Expect(cfg.Status.Users).To(ConsistOf(expected)) }, defaultTimeoutInterval, defaultPollInterval).Should(Succeed()) }) By("checking owners (e2e-owners-1)", func() { t := &capsulev1beta2.Tenant{} Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: tnt1.GetName()}, t)).Should(Succeed()) expectedOwners := rbac.OwnerStatusListSpec{ { UserSpec: rbac.UserSpec{ Kind: rbac.GroupOwner, Name: "e2e-owners-1-group", }, ClusterRoles: []string{"admin", "capsule-namespace-deleter"}, }, { UserSpec: rbac.UserSpec{ Kind: rbac.GroupOwner, Name: "oidc:comp:devops", }, ClusterRoles: []string{"view"}, }, { UserSpec: rbac.UserSpec{ Kind: rbac.ServiceAccountOwner, Name: "system:serviceaccount:capsule-system:capsule", }, ClusterRoles: []string{"admin", "capsule-namespace-deleter"}, }, { UserSpec: rbac.UserSpec{ Kind: rbac.UserOwner, Name: "e2e-owners-1", }, ClusterRoles: []string{"admin", "capsule-namespace-deleter"}, }, { UserSpec: rbac.UserSpec{ Kind: userOwnersCommon.Spec.Kind, Name: userOwnersCommon.Spec.Name, }, ClusterRoles: []string{"view"}, }, { UserSpec: rbac.UserSpec{ Kind: tnt1Owner.Spec.Kind, Name: tnt1Owner.Spec.Name, }, ClusterRoles: []string{"edit"}, }, } t = ExpectTenantOwnersEventually(tnt1.GetName(), expectedOwners) VerifyTenantRoleBindings(t) }) By("checking owners (e2e-owners-2)", func() { t := &capsulev1beta2.Tenant{} Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: tnt2.GetName()}, t)).Should(Succeed()) expectedOwners := rbac.OwnerStatusListSpec{ { UserSpec: rbac.UserSpec{ Kind: rbac.GroupOwner, Name: "e2e-owners-2-group", }, ClusterRoles: []string{"admin", "capsule-namespace-deleter"}, }, { UserSpec: rbac.UserSpec{ Kind: rbac.GroupOwner, Name: "oidc:comp:administrators", }, ClusterRoles: []string{"admin"}, }, { UserSpec: rbac.UserSpec{ Kind: rbac.ServiceAccountOwner, Name: "system:serviceaccount:capsule-system:capsule", }, ClusterRoles: []string{"admin", "capsule-namespace-deleter"}, }, { UserSpec: rbac.UserSpec{ Kind: rbac.UserOwner, Name: "e2e-owners-2", }, ClusterRoles: []string{"admin", "capsule-namespace-deleter"}, }, { UserSpec: rbac.UserSpec{ Kind: userOwnersCommon.Spec.Kind, Name: userOwnersCommon.Spec.Name, }, ClusterRoles: []string{"view"}, }, } t = ExpectTenantOwnersEventually(tnt2.GetName(), expectedOwners) VerifyTenantRoleBindings(t) }) By("remove admin tenant-owners", func() { Expect(k8sClient.Delete(context.TODO(), ownersInfra)).Should(Succeed()) }) By("checking configuration", func() { Eventually(func(g Gomega) { cfg := &capsulev1beta2.CapsuleConfiguration{} err := k8sClient.Get( context.Background(), client.ObjectKey{Name: defaultConfigurationName}, cfg, ) g.Expect(err).ToNot(HaveOccurred()) expected := rbac.UserListSpec{ {Kind: ownersDevops.Spec.Kind, Name: ownersDevops.Spec.Name}, {Kind: tnt1Owner.Spec.Kind, Name: tnt1Owner.Spec.Name}, {Kind: rbac.GroupOwner, Name: "projectcapsule.dev"}, } g.Expect(cfg.Status.Users).To(ConsistOf(expected)) }, defaultTimeoutInterval, defaultPollInterval).Should(Succeed()) }) By("checking owners (e2e-owners-1)", func() { t := &capsulev1beta2.Tenant{} Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: tnt1.GetName()}, t)).Should(Succeed()) expectedOwners := rbac.OwnerStatusListSpec{ { UserSpec: rbac.UserSpec{ Kind: rbac.GroupOwner, Name: "e2e-owners-1-group", }, ClusterRoles: []string{"admin", "capsule-namespace-deleter"}, }, { UserSpec: rbac.UserSpec{ Kind: rbac.GroupOwner, Name: "oidc:comp:devops", }, ClusterRoles: []string{"view"}, }, { UserSpec: rbac.UserSpec{ Kind: rbac.ServiceAccountOwner, Name: "system:serviceaccount:capsule-system:capsule", }, ClusterRoles: []string{"admin", "capsule-namespace-deleter"}, }, { UserSpec: rbac.UserSpec{ Kind: rbac.UserOwner, Name: "e2e-owners-1", }, ClusterRoles: []string{"admin", "capsule-namespace-deleter"}, }, { UserSpec: rbac.UserSpec{ Kind: userOwnersCommon.Spec.Kind, Name: userOwnersCommon.Spec.Name, }, ClusterRoles: []string{"view"}, }, { UserSpec: rbac.UserSpec{ Kind: tnt1Owner.Spec.Kind, Name: tnt1Owner.Spec.Name, }, ClusterRoles: []string{"edit"}, }, } t = ExpectTenantOwnersEventually(tnt1.GetName(), expectedOwners) VerifyTenantRoleBindings(t) }) By("checking owners (e2e-owners-2)", func() { t := &capsulev1beta2.Tenant{} Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: tnt2.GetName()}, t)).Should(Succeed()) expectedOwners := rbac.OwnerStatusListSpec{ { UserSpec: rbac.UserSpec{ Kind: rbac.GroupOwner, Name: "e2e-owners-2-group", }, ClusterRoles: []string{"admin", "capsule-namespace-deleter"}, }, { UserSpec: rbac.UserSpec{ Kind: rbac.ServiceAccountOwner, Name: "system:serviceaccount:capsule-system:capsule", }, ClusterRoles: []string{"admin", "capsule-namespace-deleter"}, }, { UserSpec: rbac.UserSpec{ Kind: rbac.UserOwner, Name: "e2e-owners-2", }, ClusterRoles: []string{"admin", "capsule-namespace-deleter"}, }, { UserSpec: rbac.UserSpec{ Kind: userOwnersCommon.Spec.Kind, Name: userOwnersCommon.Spec.Name, }, ClusterRoles: []string{"view"}, }, } t = ExpectTenantOwnersEventually(tnt2.GetName(), expectedOwners) VerifyTenantRoleBindings(t) }) By("remove admin tenant-owners", func() { EventuallyDeletion(ownersDevops) }) By("checking configuration", func() { Eventually(func(g Gomega) { cfg := &capsulev1beta2.CapsuleConfiguration{} err := k8sClient.Get( context.Background(), client.ObjectKey{Name: defaultConfigurationName}, cfg, ) g.Expect(err).ToNot(HaveOccurred()) expected := rbac.UserListSpec{ {Kind: tnt1Owner.Spec.Kind, Name: tnt1Owner.Spec.Name}, {Kind: rbac.GroupOwner, Name: "projectcapsule.dev"}, } g.Expect(cfg.Status.Users).To(ConsistOf(expected)) }, defaultTimeoutInterval, defaultPollInterval).Should(Succeed()) }) By("checking owners (e2e-owners-1)", func() { t := &capsulev1beta2.Tenant{} Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: tnt1.GetName()}, t)).Should(Succeed()) expectedOwners := rbac.OwnerStatusListSpec{ { UserSpec: rbac.UserSpec{ Kind: rbac.GroupOwner, Name: "e2e-owners-1-group", }, ClusterRoles: []string{"admin", "capsule-namespace-deleter"}, }, { UserSpec: rbac.UserSpec{ Kind: rbac.ServiceAccountOwner, Name: "system:serviceaccount:capsule-system:capsule", }, ClusterRoles: []string{"admin", "capsule-namespace-deleter"}, }, { UserSpec: rbac.UserSpec{ Kind: rbac.UserOwner, Name: "e2e-owners-1", }, ClusterRoles: []string{"admin", "capsule-namespace-deleter"}, }, { UserSpec: rbac.UserSpec{ Kind: userOwnersCommon.Spec.Kind, Name: userOwnersCommon.Spec.Name, }, ClusterRoles: []string{"view"}, }, { UserSpec: rbac.UserSpec{ Kind: tnt1Owner.Spec.Kind, Name: tnt1Owner.Spec.Name, }, ClusterRoles: []string{"edit"}, }, } t = ExpectTenantOwnersEventually(tnt1.GetName(), expectedOwners) VerifyTenantRoleBindings(t) }) By("checking owners (e2e-owners-2)", func() { t := &capsulev1beta2.Tenant{} Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: tnt2.GetName()}, t)).Should(Succeed()) expectedOwners := rbac.OwnerStatusListSpec{ { UserSpec: rbac.UserSpec{ Kind: rbac.GroupOwner, Name: "e2e-owners-2-group", }, ClusterRoles: []string{"admin", "capsule-namespace-deleter"}, }, { UserSpec: rbac.UserSpec{ Kind: rbac.ServiceAccountOwner, Name: "system:serviceaccount:capsule-system:capsule", }, ClusterRoles: []string{"admin", "capsule-namespace-deleter"}, }, { UserSpec: rbac.UserSpec{ Kind: rbac.UserOwner, Name: "e2e-owners-2", }, ClusterRoles: []string{"admin", "capsule-namespace-deleter"}, }, { UserSpec: rbac.UserSpec{ Kind: userOwnersCommon.Spec.Kind, Name: userOwnersCommon.Spec.Name, }, ClusterRoles: []string{"view"}, }, } t = ExpectTenantOwnersEventually(tnt2.GetName(), expectedOwners) VerifyTenantRoleBindings(t) }) }) }) func ExpectTenantOwnersEventually( tenantName string, expected rbac.OwnerStatusListSpec, ) *capsulev1beta2.Tenant { var current *capsulev1beta2.Tenant Eventually(func(g Gomega) { current = &capsulev1beta2.Tenant{} g.Expect(k8sClient.Get( context.TODO(), types.NamespacedName{Name: tenantName}, current, )).To(Succeed()) g.Expect(normalizeOwners(current.Status.Owners)). To(Equal(normalizeOwners(expected))) }, defaultTimeoutInterval, defaultPollInterval).Should(Succeed()) return current }