Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for ingress-configured router #12416

Merged
merged 2 commits into from
Jan 20, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions contrib/completions/bash/openshift
Original file line number Diff line number Diff line change
Expand Up @@ -20811,6 +20811,8 @@ _openshift_infra_f5-router()
local_nonpersistent_flags+=("--denied-domains=")
flags+=("--disable-namespace-ownership-check")
local_nonpersistent_flags+=("--disable-namespace-ownership-check")
flags+=("--enable-ingress")
local_nonpersistent_flags+=("--enable-ingress")
flags+=("--f5-host=")
local_nonpersistent_flags+=("--f5-host=")
flags+=("--f5-http-vserver=")
Expand Down Expand Up @@ -20996,6 +20998,8 @@ _openshift_infra_router()
local_nonpersistent_flags+=("--denied-domains=")
flags+=("--disable-namespace-ownership-check")
local_nonpersistent_flags+=("--disable-namespace-ownership-check")
flags+=("--enable-ingress")
local_nonpersistent_flags+=("--enable-ingress")
flags+=("--extended-validation")
local_nonpersistent_flags+=("--extended-validation")
flags+=("--fields=")
Expand Down
4 changes: 4 additions & 0 deletions contrib/completions/zsh/openshift
Original file line number Diff line number Diff line change
Expand Up @@ -20959,6 +20959,8 @@ _openshift_infra_f5-router()
local_nonpersistent_flags+=("--denied-domains=")
flags+=("--disable-namespace-ownership-check")
local_nonpersistent_flags+=("--disable-namespace-ownership-check")
flags+=("--enable-ingress")
local_nonpersistent_flags+=("--enable-ingress")
flags+=("--f5-host=")
local_nonpersistent_flags+=("--f5-host=")
flags+=("--f5-http-vserver=")
Expand Down Expand Up @@ -21144,6 +21146,8 @@ _openshift_infra_router()
local_nonpersistent_flags+=("--denied-domains=")
flags+=("--disable-namespace-ownership-check")
local_nonpersistent_flags+=("--disable-namespace-ownership-check")
flags+=("--enable-ingress")
local_nonpersistent_flags+=("--enable-ingress")
flags+=("--extended-validation")
local_nonpersistent_flags+=("--extended-validation")
flags+=("--fields=")
Expand Down
4 changes: 4 additions & 0 deletions docs/man/man1/openshift-infra-f5-router.1
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ You may restrict the set of routes exposed to a single project (with \-\-namespa
\fB\-\-disable\-namespace\-ownership\-check\fP=false
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.

.PP
\fB\-\-enable\-ingress\fP=false
Enable configuration via ingress resources

.PP
\fB\-\-f5\-host\fP=""
The host of F5 BIG\-IP's management interface
Expand Down
4 changes: 4 additions & 0 deletions docs/man/man1/openshift-infra-router.1
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ You may restrict the set of routes exposed to a single project (with \-\-namespa
\fB\-\-disable\-namespace\-ownership\-check\fP=false
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.

.PP
\fB\-\-enable\-ingress\fP=false
Enable configuration via ingress resources

.PP
\fB\-\-extended\-validation\fP=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.
Expand Down
8 changes: 8 additions & 0 deletions pkg/api/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package api
import (
"fmt"

kapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/validation"
"k8s.io/kubernetes/pkg/api/validation/path"
)
Expand Down Expand Up @@ -33,3 +34,10 @@ func GetFieldLabelConversionFunc(supportedLabels map[string]string, overrideLabe
return "", "", fmt.Errorf("field label not supported: %s", label)
}
}

// GetResourceKey returns a string of the form [namespace]/[name] for
// the given resource. This is a common way of ensuring a key for a
// resource that is unique across the cluster.
func GetResourceKey(obj kapi.ObjectMeta) string {
return fmt.Sprintf("%s/%s", obj.Namespace, obj.Name)
}
2 changes: 1 addition & 1 deletion pkg/cmd/infra/router/f5.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ func (o *F5RouterOptions) Run() error {

factory := o.RouterSelection.NewFactory(oc, kc)
watchNodes := (len(o.InternalAddress) != 0 && len(o.VxlanGateway) != 0)
controller := factory.Create(plugin, watchNodes)
controller := factory.Create(plugin, watchNodes, o.EnableIngress)
controller.Run()

select {}
Expand Down
9 changes: 8 additions & 1 deletion pkg/cmd/infra/router/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ type RouterSelection struct {
AllowWildcardRoutes bool

DisableNamespaceOwnershipCheck bool

EnableIngress bool
}

// Bind sets the appropriate labels
Expand All @@ -70,6 +72,7 @@ func (o *RouterSelection) Bind(flag *pflag.FlagSet) {
flag.StringSliceVar(&o.AllowedDomains, "allowed-domains", envVarAsStrings("ROUTER_ALLOWED_DOMAINS", "", ","), "List of comma separated domains to allow in routes. If specified, only the domains in this list will be allowed routes. Note that domains in the denied list take precedence over the ones in the allowed list")
flag.BoolVar(&o.AllowWildcardRoutes, "allow-wildcard-routes", cmdutil.Env("ROUTER_ALLOW_WILDCARD_ROUTES", "") == "true", "Allow wildcard host names for routes")
flag.BoolVar(&o.DisableNamespaceOwnershipCheck, "disable-namespace-ownership-check", cmdutil.Env("ROUTER_DISABLE_NAMESPACE_OWNERSHIP_CHECK", "") == "true", "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.")
flag.BoolVar(&o.EnableIngress, "enable-ingress", cmdutil.Env("ROUTER_ENABLE_INGRESS", "") == "true", "Enable configuration via ingress resources")
}

// RouteSelectionFunc returns a func that identifies the host for a route.
Expand All @@ -81,10 +84,14 @@ func (o *RouterSelection) RouteSelectionFunc() controller.RouteHostFunc {
if !o.OverrideHostname && len(route.Spec.Host) > 0 {
return route.Spec.Host
}
// GetNameForHost returns the ingress name for a generated route, and the route route
// name otherwise. When a route and ingress in the same namespace share a name, the
// route and the ingress' rules should receive the same generated host.
nameForHost := controller.GetNameForHost(route.Name)
s, err := variable.ExpandStrict(o.HostnameTemplate, func(key string) (string, bool) {
switch key {
case "name":
return route.Name, true
return nameForHost, true
case "namespace":
return route.Namespace, true
default:
Expand Down
2 changes: 1 addition & 1 deletion pkg/cmd/infra/router/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ func (o *TemplateRouterOptions) Run() error {
plugin := controller.NewHostAdmitter(uniqueHostPlugin, o.RouteAdmissionFunc(), o.AllowWildcardRoutes, o.RouterSelection.DisableNamespaceOwnershipCheck, controller.RejectionRecorder(statusPlugin))

factory := o.RouterSelection.NewFactory(oc, kc)
controller := factory.Create(plugin, false)
controller := factory.Create(plugin, false, o.EnableIngress)
controller.Run()

proc.StartReaper()
Expand Down
4 changes: 4 additions & 0 deletions pkg/route/api/v1/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ package v1

import "k8s.io/kubernetes/pkg/runtime"

// If adding or changing route defaults, updates may be required to
// pkg/router/controller/controller.go to ensure the routes generated from
// ingress resources will match routes created via the api.

func SetDefaults_RouteSpec(obj *RouteSpec) {
if len(obj.WildcardPolicy) == 0 {
obj.WildcardPolicy = WildcardPolicyNone
Expand Down
130 changes: 116 additions & 14 deletions pkg/router/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/golang/glog"
kapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/extensions"
utilruntime "k8s.io/kubernetes/pkg/util/runtime"
"k8s.io/kubernetes/pkg/util/sets"
utilwait "k8s.io/kubernetes/pkg/util/wait"
Expand All @@ -30,25 +31,38 @@ type RouterController struct {
NextRoute func() (watch.EventType, *routeapi.Route, error)
NextNode func() (watch.EventType, *kapi.Node, error)
NextEndpoints func() (watch.EventType, *kapi.Endpoints, error)
NextIngress func() (watch.EventType, *extensions.Ingress, error)
NextSecret func() (watch.EventType, *kapi.Secret, error)

RoutesListConsumed func() bool
EndpointsListConsumed func() bool
IngressesListConsumed func() bool
SecretsListConsumed func() bool
routesListConsumed bool
endpointsListConsumed bool
ingressesListConsumed bool
secretsListConsumed bool
filteredByNamespace bool
syncing bool

RoutesListSuccessfulAtLeastOnce func() bool
EndpointsListSuccessfulAtLeastOnce func() bool
IngressesListSuccessfulAtLeastOnce func() bool
SecretsListSuccessfulAtLeastOnce func() bool
RoutesListCount func() int
EndpointsListCount func() int
IngressesListCount func() int
SecretsListCount func() int

WatchNodes bool

Namespaces NamespaceLister
NamespaceSyncInterval time.Duration
NamespaceWaitInterval time.Duration
NamespaceRetries int

EnableIngress bool
IngressTranslator *IngressTranslator
}

// Run begins watching and syncing.
Expand All @@ -63,6 +77,10 @@ func (c *RouterController) Run() {
if c.WatchNodes {
go utilwait.Forever(c.HandleNode, 0)
}
if c.EnableIngress {
go utilwait.Forever(c.HandleIngress, 0)
go utilwait.Forever(c.HandleSecret, 0)
}
go c.watchForFirstSync()
}

Expand All @@ -74,22 +92,31 @@ func (c *RouterController) handleFirstSync() bool {

synced := c.RoutesListSuccessfulAtLeastOnce() &&
c.EndpointsListSuccessfulAtLeastOnce() &&
(c.Namespaces == nil || c.filteredByNamespace)
(c.Namespaces == nil || c.filteredByNamespace) &&
(!c.EnableIngress ||
(c.IngressesListSuccessfulAtLeastOnce() && c.SecretsListSuccessfulAtLeastOnce()))
if !synced {
return false
}

// If either of the event queues were empty after the initial
// List, the tracking listConsumed variable's default value of
// 'false' may prevent the router from committing the readiness
// status. Set the value to 'true' to ensure that state will be
// committed if necessary.
// If any of the event queues were empty after the initial List,
// the tracking listConsumed variable's default value of 'false'
// may prevent the router from committing. Set the value to
// 'true' to ensure that state can be committed if necessary.
if c.RoutesListCount() == 0 {
c.routesListConsumed = true
}
if c.EndpointsListCount() == 0 {
c.endpointsListConsumed = true
}
if c.EnableIngress {
if c.IngressesListCount() == 0 {
c.ingressesListConsumed = true
}
if c.SecretsListCount() == 0 {
c.secretsListConsumed = true
}
}
c.commit()

return true
Expand All @@ -109,6 +136,14 @@ func (c *RouterController) HandleNamespaces() {
for i := 0; i < c.NamespaceRetries; i++ {
namespaces, err := c.Namespaces.NamespaceNames()
if err == nil {

// The ingress translator synchronizes access to its cache with a
// lock, so calls to it are made outside of the controller lock to
// avoid unintended interaction.
if c.EnableIngress {
c.IngressTranslator.UpdateNamespaces(namespaces)
}

c.lock.Lock()
defer c.lock.Unlock()

Expand Down Expand Up @@ -161,13 +196,7 @@ func (c *RouterController) HandleRoute() {
c.lock.Lock()
defer c.lock.Unlock()

glog.V(4).Infof("Processing Route: %s -> %s", route.Name, route.Spec.To.Name)
glog.V(4).Infof(" Alias: %s", route.Spec.Host)
glog.V(4).Infof(" Event: %s", eventType)

if err := c.Plugin.HandleRoute(eventType, route); err != nil {
utilruntime.HandleError(err)
}
c.processRoute(eventType, route)

// Change the local sync state within the lock to ensure that all
// event handlers have the same view of sync state.
Expand Down Expand Up @@ -196,10 +225,61 @@ func (c *RouterController) HandleEndpoints() {
c.commit()
}

// HandleIngress handles a single Ingress event and synchronizes the router backend.
func (c *RouterController) HandleIngress() {
eventType, ingress, err := c.NextIngress()
if err != nil {
utilruntime.HandleError(fmt.Errorf("unable to read ingress: %v", err))
return
}

// The ingress translator synchronizes access to its cache with a
// lock, so calls to it are made outside of the controller lock to
// avoid unintended interaction.
events := c.IngressTranslator.TranslateIngressEvent(eventType, ingress)

c.lock.Lock()
defer c.lock.Unlock()

c.processIngressEvents(events)

// Change the local sync state within the lock to ensure that all
// event handlers have the same view of sync state.
c.ingressesListConsumed = c.IngressesListConsumed()
c.commit()
}

// HandleSecret handles a single Secret event and synchronizes the router backend.
func (c *RouterController) HandleSecret() {
eventType, secret, err := c.NextSecret()
if err != nil {
utilruntime.HandleError(fmt.Errorf("unable to read secret: %v", err))
return

}

// The ingress translator synchronizes access to its cache with a
// lock, so calls to it are made outside of the controller lock to
// avoid unintended interaction.
events := c.IngressTranslator.TranslateSecretEvent(eventType, secret)

c.lock.Lock()
defer c.lock.Unlock()

c.processIngressEvents(events)

// Change the local sync state within the lock to ensure that all
// event handlers have the same view of sync state.
c.secretsListConsumed = c.SecretsListConsumed()
c.commit()
}

// commit notifies the plugin that it is safe to commit state.
func (c *RouterController) commit() {
syncing := !(c.endpointsListConsumed && c.routesListConsumed &&
(c.Namespaces == nil || c.filteredByNamespace))
(c.Namespaces == nil || c.filteredByNamespace) &&
(!c.EnableIngress ||
(c.ingressesListConsumed && c.secretsListConsumed)))
c.logSyncState(syncing)
if syncing {
return
Expand All @@ -219,3 +299,25 @@ func (c *RouterController) logSyncState(syncing bool) {
}
}
}

// processRoute logs and propagates a route event to the plugin
func (c *RouterController) processRoute(eventType watch.EventType, route *routeapi.Route) {
glog.V(4).Infof("Processing Route: %s/%s -> %s", route.Namespace, route.Name, route.Spec.To.Name)
glog.V(4).Infof(" Alias: %s", route.Spec.Host)
glog.V(4).Infof(" Path: %s", route.Spec.Path)
glog.V(4).Infof(" Event: %s", eventType)

if err := c.Plugin.HandleRoute(eventType, route); err != nil {
utilruntime.HandleError(err)
}
}

// processIngressEvents logs and propagates the route events resulting from an ingress or secret event
func (c *RouterController) processIngressEvents(events []ingressRouteEvents) {
for _, ingressEvent := range events {
glog.V(4).Infof("Processing Ingress %s", ingressEvent.ingressKey)
for _, routeEvent := range ingressEvent.routeEvents {
c.processRoute(routeEvent.eventType, routeEvent.route)
}
}
}
Loading