Skip to content

Commit 6e85fcc

Browse files
Merge pull request #17420 from smarterclayton/router_consistency
Automatic merge from submit-queue. Make updating status on the router optional User can specify --update-status=false on the router to have a router that doesn't write back to routes. The default is true for backwards compatibilty. Allows testing routers when you don't have route status/update. Also unify a number of host setup paths. Makes F5 and template more consistent. Question for reviewer - on the F5 plugin we weren't performing a number of "standard" operations like setting the canonical hostname. Was there a concrete reason for that? @openshift/sig-networking
2 parents 9ac06b1 + 8d3e864 commit 6e85fcc

File tree

12 files changed

+194
-70
lines changed

12 files changed

+194
-70
lines changed

pkg/cmd/infra/router/f5.go

+13-6
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
projectinternalclientset "github.com/openshift/origin/pkg/project/generated/internalclientset"
1919
routeapi "github.com/openshift/origin/pkg/route/apis/route"
2020
routeinternalclientset "github.com/openshift/origin/pkg/route/generated/internalclientset"
21+
"github.com/openshift/origin/pkg/router"
2122
"github.com/openshift/origin/pkg/router/controller"
2223
f5plugin "github.com/openshift/origin/pkg/router/f5"
2324
"github.com/openshift/origin/pkg/version"
@@ -46,8 +47,6 @@ type F5RouterOptions struct {
4647

4748
// F5Router is the config necessary to start an F5 router plugin.
4849
type F5Router struct {
49-
RouterName string
50-
5150
// Host specifies the hostname or IP address of the F5 BIG-IP host.
5251
Host string
5352

@@ -95,7 +94,6 @@ type F5Router struct {
9594

9695
// Bind binds F5Router arguments to flags
9796
func (o *F5Router) Bind(flag *pflag.FlagSet) {
98-
flag.StringVar(&o.RouterName, "name", util.Env("ROUTER_SERVICE_NAME", "public"), "The name the router will identify itself with in the route status")
9997
flag.StringVar(&o.Host, "f5-host", util.Env("ROUTER_EXTERNAL_HOST_HOSTNAME", ""), "The host of F5 BIG-IP's management interface")
10098
flag.StringVar(&o.Username, "f5-username", util.Env("ROUTER_EXTERNAL_HOST_USERNAME", ""), "The username for F5 BIG-IP's management utility")
10199
flag.StringVar(&o.Password, "f5-password", util.Env("ROUTER_EXTERNAL_HOST_PASSWORD", ""), "The password for F5 BIG-IP's management utility")
@@ -230,9 +228,18 @@ func (o *F5RouterOptions) Run() error {
230228
return err
231229
}
232230

233-
statusPlugin := controller.NewStatusAdmitter(f5Plugin, routeclient.Route(), o.RouterName, "")
234-
uniqueHostPlugin := controller.NewUniqueHost(statusPlugin, o.RouteSelectionFunc(), o.RouterSelection.DisableNamespaceOwnershipCheck, statusPlugin)
235-
plugin := controller.NewHostAdmitter(uniqueHostPlugin, o.F5RouteAdmitterFunc(), false, o.RouterSelection.DisableNamespaceOwnershipCheck, statusPlugin)
231+
var recorder controller.RejectionRecorder = controller.LogRejections
232+
var plugin router.Plugin = f5Plugin
233+
if o.UpdateStatus {
234+
status := controller.NewStatusAdmitter(plugin, routeclient.Route(), o.RouterName, o.RouterCanonicalHostname)
235+
recorder = status
236+
plugin = status
237+
}
238+
if o.ExtendedValidation {
239+
plugin = controller.NewExtendedValidator(plugin, recorder)
240+
}
241+
plugin = controller.NewUniqueHost(plugin, o.RouteSelectionFunc(), o.RouterSelection.DisableNamespaceOwnershipCheck, recorder)
242+
plugin = controller.NewHostAdmitter(plugin, o.F5RouteAdmitterFunc(), o.AllowWildcardRoutes, o.RouterSelection.DisableNamespaceOwnershipCheck, recorder)
236243

237244
factory := o.RouterSelection.NewFactory(routeclient, projectclient.Project().Projects(), kc)
238245
watchNodes := (len(o.InternalAddress) != 0 && len(o.VxlanGateway) != 0)

pkg/cmd/infra/router/router.go

+21
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"k8s.io/apimachinery/pkg/fields"
1212
"k8s.io/apimachinery/pkg/labels"
1313
"k8s.io/apimachinery/pkg/util/sets"
14+
"k8s.io/apimachinery/pkg/util/validation"
1415
kclientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
1516

1617
cmdutil "github.com/openshift/origin/pkg/cmd/util"
@@ -25,8 +26,13 @@ import (
2526
// RouterSelection controls what routes and resources on the server are considered
2627
// part of this router.
2728
type RouterSelection struct {
29+
RouterName string
30+
RouterCanonicalHostname string
31+
2832
ResyncInterval time.Duration
2933

34+
UpdateStatus bool
35+
3036
HostnameTemplate string
3137
OverrideHostname bool
3238

@@ -52,13 +58,18 @@ type RouterSelection struct {
5258

5359
DisableNamespaceOwnershipCheck bool
5460

61+
ExtendedValidation bool
62+
5563
EnableIngress bool
5664

5765
ListenAddr string
5866
}
5967

6068
// Bind sets the appropriate labels
6169
func (o *RouterSelection) Bind(flag *pflag.FlagSet) {
70+
flag.StringVar(&o.RouterName, "name", cmdutil.Env("ROUTER_SERVICE_NAME", "public"), "The name the router will identify itself with in the route status")
71+
flag.StringVar(&o.RouterCanonicalHostname, "router-canonical-hostname", cmdutil.Env("ROUTER_CANONICAL_HOSTNAME", ""), "CanonicalHostname is the external host name for the router that can be used as a CNAME for the host requested for this route. This value is optional and may not be set in all cases.")
72+
flag.BoolVar(&o.UpdateStatus, "update-status", isTrue(cmdutil.Env("ROUTER_UPDATE_STATUS", "true")), "If true, the router will update admitted route status.")
6273
flag.DurationVar(&o.ResyncInterval, "resync-interval", controllerfactory.DefaultResyncInterval, "The interval at which the route list should be fully refreshed")
6374
flag.StringVar(&o.HostnameTemplate, "hostname-template", cmdutil.Env("ROUTER_SUBDOMAIN", ""), "If specified, a template that should be used to generate the hostname for a route without spec.host (e.g. '${name}-${namespace}.myapps.mycompany.com')")
6475
flag.BoolVar(&o.OverrideHostname, "override-hostname", isTrue(cmdutil.Env("ROUTER_OVERRIDE_HOSTNAME", "")), "Override the spec.host value for a route with --hostname-template")
@@ -72,6 +83,7 @@ func (o *RouterSelection) Bind(flag *pflag.FlagSet) {
7283
flag.BoolVar(&o.AllowWildcardRoutes, "allow-wildcard-routes", isTrue(cmdutil.Env("ROUTER_ALLOW_WILDCARD_ROUTES", "")), "Allow wildcard host names for routes")
7384
flag.BoolVar(&o.DisableNamespaceOwnershipCheck, "disable-namespace-ownership-check", isTrue(cmdutil.Env("ROUTER_DISABLE_NAMESPACE_OWNERSHIP_CHECK", "")), "Disables the namespace ownership checks for a route host with different paths or for overlapping host names in the case of wildcard routes. Please be aware that if namespace ownership checks are disabled, routes in a different namespace can use this mechanism to 'steal' sub-paths for existing domains. This is only safe if route creation privileges are restricted, or if all the users can be trusted.")
7485
flag.BoolVar(&o.EnableIngress, "enable-ingress", isTrue(cmdutil.Env("ROUTER_ENABLE_INGRESS", "")), "Enable configuration via ingress resources")
86+
flag.BoolVar(&o.ExtendedValidation, "extended-validation", isTrue(cmdutil.Env("EXTENDED_VALIDATION", "true")), "If set, then an additional extended validation step is performed on all routes admitted in by this router. Defaults to true and enables the extended validation checks.")
7587
flag.StringVar(&o.ListenAddr, "listen-addr", cmdutil.Env("ROUTER_LISTEN_ADDR", ""), "The name of an interface to listen on to expose metrics and health checking. If not specified, will not listen. Overrides stats port.")
7688
}
7789

@@ -205,6 +217,15 @@ func (o *RouterSelection) Complete() error {
205217
o.BlacklistedDomains = sets.NewString(o.DeniedDomains...)
206218
o.WhitelistedDomains = sets.NewString(o.AllowedDomains...)
207219

220+
if routerCanonicalHostname := o.RouterCanonicalHostname; len(routerCanonicalHostname) > 0 {
221+
if errs := validation.IsDNS1123Subdomain(routerCanonicalHostname); len(errs) != 0 {
222+
return fmt.Errorf("invalid canonical hostname: %s", routerCanonicalHostname)
223+
}
224+
if errs := validation.IsValidIP(routerCanonicalHostname); len(errs) == 0 {
225+
return fmt.Errorf("canonical hostname must not be an IP address: %s", routerCanonicalHostname)
226+
}
227+
}
228+
208229
return nil
209230
}
210231

pkg/cmd/infra/router/template.go

+12-24
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import (
1717

1818
ktypes "k8s.io/apimachinery/pkg/types"
1919
"k8s.io/apimachinery/pkg/util/sets"
20-
"k8s.io/apimachinery/pkg/util/validation"
2120
"k8s.io/apiserver/pkg/authentication/authenticatorfactory"
2221
"k8s.io/apiserver/pkg/authorization/authorizer"
2322
"k8s.io/apiserver/pkg/authorization/authorizerfactory"
@@ -70,8 +69,6 @@ type TemplateRouterOptions struct {
7069
}
7170

7271
type TemplateRouter struct {
73-
RouterName string
74-
RouterCanonicalHostname string
7572
WorkingDir string
7673
TemplateFile string
7774
ReloadScript string
@@ -80,7 +77,6 @@ type TemplateRouter struct {
8077
DefaultCertificatePath string
8178
DefaultCertificateDir string
8279
DefaultDestinationCAPath string
83-
ExtendedValidation bool
8480
RouterService *ktypes.NamespacedName
8581
BindPortsAfterSync bool
8682
MaxConnections string
@@ -108,8 +104,6 @@ func reloadInterval() time.Duration {
108104
}
109105

110106
func (o *TemplateRouter) Bind(flag *pflag.FlagSet) {
111-
flag.StringVar(&o.RouterName, "name", util.Env("ROUTER_SERVICE_NAME", "public"), "The name the router will identify itself with in the route status")
112-
flag.StringVar(&o.RouterCanonicalHostname, "router-canonical-hostname", util.Env("ROUTER_CANONICAL_HOSTNAME", ""), "CanonicalHostname is the external host name for the router that can be used as a CNAME for the host requested for this route. This value is optional and may not be set in all cases.")
113107
flag.StringVar(&o.WorkingDir, "working-dir", "/var/lib/haproxy/router", "The working directory for the router plugin")
114108
flag.StringVar(&o.DefaultCertificate, "default-certificate", util.Env("DEFAULT_CERTIFICATE", ""), "The contents of a default certificate to use for routes that don't expose a TLS server cert; in PEM format")
115109
flag.StringVar(&o.DefaultCertificatePath, "default-certificate-path", util.Env("DEFAULT_CERTIFICATE_PATH", ""), "A path to default certificate to use for routes that don't expose a TLS server cert; in PEM format")
@@ -118,8 +112,7 @@ func (o *TemplateRouter) Bind(flag *pflag.FlagSet) {
118112
flag.StringVar(&o.TemplateFile, "template", util.Env("TEMPLATE_FILE", ""), "The path to the template file to use")
119113
flag.StringVar(&o.ReloadScript, "reload", util.Env("RELOAD_SCRIPT", ""), "The path to the reload script to use")
120114
flag.DurationVar(&o.ReloadInterval, "interval", reloadInterval(), "Controls how often router reloads are invoked. Mutiple router reload requests are coalesced for the duration of this interval since the last reload time.")
121-
flag.BoolVar(&o.ExtendedValidation, "extended-validation", isTrue(util.Env("EXTENDED_VALIDATION", "true")), "If set, then an additional extended validation step is performed on all routes admitted in by this router. Defaults to true and enables the extended validation checks.")
122-
flag.BoolVar(&o.BindPortsAfterSync, "bind-ports-after-sync", isTrue(util.Env("ROUTER_BIND_PORTS_AFTER_SYNC", "")), "Bind ports only after route state has been synchronized")
115+
flag.BoolVar(&o.BindPortsAfterSync, "bind-ports-after-sync", util.Env("ROUTER_BIND_PORTS_AFTER_SYNC", "") == "true", "Bind ports only after route state has been synchronized")
123116
flag.StringVar(&o.MaxConnections, "max-connections", util.Env("ROUTER_MAX_CONNECTIONS", ""), "Specifies the maximum number of concurrent connections.")
124117
flag.StringVar(&o.Ciphers, "ciphers", util.Env("ROUTER_CIPHERS", ""), "Specifies the cipher suites to use. You can choose a predefined cipher set ('modern', 'intermediate', or 'old') or specify exact cipher suites by passing a : separated list.")
125118
flag.BoolVar(&o.StrictSNI, "strict-sni", isTrue(util.Env("ROUTER_STRICT_SNI", "")), "Use strict-sni bind processing (do not use default cert).")
@@ -180,7 +173,6 @@ func NewCommandTemplateRouter(name string) *cobra.Command {
180173
func (o *TemplateRouterOptions) Complete() error {
181174
routerSvcName := util.Env("ROUTER_SERVICE_NAME", "")
182175
routerSvcNamespace := util.Env("ROUTER_SERVICE_NAMESPACE", "")
183-
routerCanonicalHostname := util.Env("ROUTER_CANONICAL_HOSTNAME", "")
184176
if len(routerSvcName) > 0 {
185177
if len(routerSvcNamespace) == 0 {
186178
return fmt.Errorf("ROUTER_SERVICE_NAMESPACE is required when ROUTER_SERVICE_NAME is specified")
@@ -219,15 +211,6 @@ func (o *TemplateRouterOptions) Complete() error {
219211
return fmt.Errorf("invalid reload interval: %v - must be a positive duration", nsecs)
220212
}
221213

222-
if len(routerCanonicalHostname) > 0 {
223-
if errs := validation.IsDNS1123Subdomain(routerCanonicalHostname); len(errs) != 0 {
224-
return fmt.Errorf("invalid canonical hostname: %s", routerCanonicalHostname)
225-
}
226-
if errs := validation.IsValidIP(routerCanonicalHostname); len(errs) == 0 {
227-
return fmt.Errorf("canonical hostname must not be an IP address: %s", routerCanonicalHostname)
228-
}
229-
}
230-
231214
return o.RouterSelection.Complete()
232215
}
233216

@@ -238,7 +221,7 @@ func (o *TemplateRouterOptions) Validate() error {
238221
if len(o.MetricsType) > 0 && !supportedMetricsTypes.Has(o.MetricsType) {
239222
return fmt.Errorf("supported metrics types are: %s", strings.Join(supportedMetricsTypes.List(), ", "))
240223
}
241-
if len(o.RouterName) == 0 {
224+
if len(o.RouterName) == 0 && o.UpdateStatus {
242225
return errors.New("router must have a name to identify itself in route status")
243226
}
244227
if len(o.TemplateFile) == 0 {
@@ -421,13 +404,18 @@ func (o *TemplateRouterOptions) Run() error {
421404
return err
422405
}
423406

424-
statusPlugin := controller.NewStatusAdmitter(templatePlugin, routeclient.Route(), o.RouterName, o.RouterCanonicalHostname)
425-
var nextPlugin router.Plugin = statusPlugin
407+
var recorder controller.RejectionRecorder = controller.LogRejections
408+
var plugin router.Plugin = templatePlugin
409+
if o.UpdateStatus {
410+
status := controller.NewStatusAdmitter(plugin, routeclient.Route(), o.RouterName, o.RouterCanonicalHostname)
411+
recorder = status
412+
plugin = status
413+
}
426414
if o.ExtendedValidation {
427-
nextPlugin = controller.NewExtendedValidator(nextPlugin, controller.RejectionRecorder(statusPlugin))
415+
plugin = controller.NewExtendedValidator(plugin, recorder)
428416
}
429-
uniqueHostPlugin := controller.NewUniqueHost(nextPlugin, o.RouteSelectionFunc(), o.RouterSelection.DisableNamespaceOwnershipCheck, controller.RejectionRecorder(statusPlugin))
430-
plugin := controller.NewHostAdmitter(uniqueHostPlugin, o.RouteAdmissionFunc(), o.AllowWildcardRoutes, o.RouterSelection.DisableNamespaceOwnershipCheck, controller.RejectionRecorder(statusPlugin))
417+
plugin = controller.NewUniqueHost(plugin, o.RouteSelectionFunc(), o.RouterSelection.DisableNamespaceOwnershipCheck, recorder)
418+
plugin = controller.NewHostAdmitter(plugin, o.RouteAdmissionFunc(), o.AllowWildcardRoutes, o.RouterSelection.DisableNamespaceOwnershipCheck, recorder)
431419

432420
factory := o.RouterSelection.NewFactory(routeclient, projectclient.Project().Projects(), kc)
433421
controller := factory.Create(plugin, false, o.EnableIngress)

pkg/router/controller/status.go

+9
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,15 @@ type RejectionRecorder interface {
2424
RecordRouteRejection(route *routeapi.Route, reason, message string)
2525
}
2626

27+
// LogRejections writes rejection messages to the log.
28+
var LogRejections = logRecorder{}
29+
30+
type logRecorder struct{}
31+
32+
func (logRecorder) RecordRouteRejection(route *routeapi.Route, reason, message string) {
33+
glog.V(3).Infof("Rejected route %s in namespace %s: %s: %s", route.Name, route.Namespace, reason, message)
34+
}
35+
2736
// StatusAdmitter ensures routes added to the plugin have status set.
2837
type StatusAdmitter struct {
2938
plugin router.Plugin

pkg/router/controller/unique_host.go

-8
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,6 @@ func HostForRoute(route *routeapi.Route) string {
2525
type HostToRouteMap map[string][]*routeapi.Route
2626
type RouteToHostMap map[string]string
2727

28-
var LogRejections = logRecorder{}
29-
30-
type logRecorder struct{}
31-
32-
func (logRecorder) RecordRouteRejection(route *routeapi.Route, reason, message string) {
33-
glog.V(4).Infof("Rejected route %s: %s: %s", route.Name, reason, message)
34-
}
35-
3628
// UniqueHost implements the router.Plugin interface to provide
3729
// a template based, backend-agnostic router.
3830
type UniqueHost struct {

test/extended/router/headers.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import (
1818
exutil "github.com/openshift/origin/test/extended/util"
1919
)
2020

21-
var _ = g.Describe("[Conformance][Area:Networking][Feature:Router] router headers", func() {
21+
var _ = g.Describe("[Conformance][Area:Networking][Feature:Router]", func() {
2222
defer g.GinkgoRecover()
2323
var (
2424
configPath = exutil.FixturePath("testdata", "router-http-echo-server.yaml")

test/extended/router/metrics.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import (
2323
exutil "github.com/openshift/origin/test/extended/util"
2424
)
2525

26-
var _ = g.Describe("[Conformance][Area:Networking][Feature:Router] openshift router metrics", func() {
26+
var _ = g.Describe("[Conformance][Area:Networking][Feature:Router]", func() {
2727
defer g.GinkgoRecover()
2828
var (
2929
oc = exutil.NewCLI("router-metrics", exutil.KubeConfigPath())

test/extended/router/scoped.go

+29-12
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,18 @@ import (
1111
g "github.com/onsi/ginkgo"
1212
o "github.com/onsi/gomega"
1313

14+
routeapi "github.com/openshift/origin/pkg/route/apis/route"
1415
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1516
"k8s.io/apimachinery/pkg/util/wait"
17+
kapi "k8s.io/kubernetes/pkg/apis/core"
1618
e2e "k8s.io/kubernetes/test/e2e/framework"
1719

1820
exutil "github.com/openshift/origin/test/extended/util"
1921
)
2022

2123
const changeTimeoutSeconds = 3 * 60
2224

23-
var _ = g.Describe("[Conformance][Area:Networking][Feature:Router] openshift routers", func() {
25+
var _ = g.Describe("[Conformance][Area:Networking][Feature:Router]", func() {
2426
defer g.GinkgoRecover()
2527
var (
2628
configPath = exutil.FixturePath("testdata", "scoped-router.yaml")
@@ -32,19 +34,16 @@ var _ = g.Describe("[Conformance][Area:Networking][Feature:Router] openshift rou
3234
if len(imagePrefix) == 0 {
3335
imagePrefix = "openshift/origin"
3436
}
35-
err := oc.AsAdmin().Run("adm").Args("policy", "add-cluster-role-to-user", "system:router", oc.Username()).Execute()
36-
o.Expect(err).NotTo(o.HaveOccurred())
37-
err = oc.Run("new-app").Args("-f", configPath, "-p", "IMAGE="+imagePrefix+"-haproxy-router").Execute()
37+
err := oc.AsAdmin().Run("new-app").Args("-f", configPath, "-p", "IMAGE="+imagePrefix+"-haproxy-router").Execute()
3838
o.Expect(err).NotTo(o.HaveOccurred())
3939
})
4040

4141
g.Describe("The HAProxy router", func() {
4242
g.It("should serve the correct routes when scoped to a single namespace and label set", func() {
4343
defer func() {
44-
// This should be done if the test fails but
45-
// for now always dump the logs.
46-
// if g.CurrentGinkgoTestDescription().Failed
47-
dumpScopedRouterLogs(oc, g.CurrentGinkgoTestDescription().FullTestText)
44+
if g.CurrentGinkgoTestDescription().Failed {
45+
dumpScopedRouterLogs(oc, g.CurrentGinkgoTestDescription().FullTestText)
46+
}
4847
}()
4948
oc.SetOutputDir(exutil.TestContext.OutputDir)
5049
ns := oc.KubeFramework().Namespace.Name
@@ -91,10 +90,9 @@ var _ = g.Describe("[Conformance][Area:Networking][Feature:Router] openshift rou
9190

9291
g.It("should override the route host with a custom value", func() {
9392
defer func() {
94-
// This should be done if the test fails but
95-
// for now always dump the logs.
96-
// if g.CurrentGinkgoTestDescription().Failed
97-
dumpScopedRouterLogs(oc, g.CurrentGinkgoTestDescription().FullTestText)
93+
if g.CurrentGinkgoTestDescription().Failed {
94+
dumpScopedRouterLogs(oc, g.CurrentGinkgoTestDescription().FullTestText)
95+
}
9896
}()
9997
oc.SetOutputDir(exutil.TestContext.OutputDir)
10098
ns := oc.KubeFramework().Namespace.Name
@@ -142,6 +140,16 @@ var _ = g.Describe("[Conformance][Area:Networking][Feature:Router] openshift rou
142140
err = expectRouteStatusCodeExec(ns, execPodName, routerURL+"/Letter", host, http.StatusOK)
143141
o.Expect(err).NotTo(o.HaveOccurred())
144142
}
143+
144+
g.By("checking that the router reported the correct ingress and override")
145+
r, err := oc.RouteClient().Route().Routes(ns).Get("route-1", metav1.GetOptions{})
146+
o.Expect(err).NotTo(o.HaveOccurred())
147+
ingress := ingressForName(r, "test-override")
148+
o.Expect(ingress).NotTo(o.BeNil())
149+
o.Expect(ingress.Host).To(o.Equal(fmt.Sprintf(pattern, "route-1", ns)))
150+
status, condition := routeapi.IngressConditionStatus(ingress, routeapi.RouteAdmitted)
151+
o.Expect(status).To(o.Equal(kapi.ConditionTrue))
152+
o.Expect(condition.LastTransitionTime).NotTo(o.BeNil())
145153
})
146154
})
147155
})
@@ -223,3 +231,12 @@ func dumpScopedRouterLogs(oc *exutil.CLI, name string) {
223231
log, _ := e2e.GetPodLogs(oc.AdminKubeClient(), oc.KubeFramework().Namespace.Name, "scoped-router", "router")
224232
e2e.Logf("Scoped Router test %s logs:\n %s", name, log)
225233
}
234+
235+
func ingressForName(r *routeapi.Route, name string) *routeapi.RouteIngress {
236+
for i, ingress := range r.Status.Ingress {
237+
if ingress.RouterName == name {
238+
return &r.Status.Ingress[i]
239+
}
240+
}
241+
return nil
242+
}

0 commit comments

Comments
 (0)