// launchRouter launches a template router that communicates with the // api via the provided clients. func launchRouter(oc osclient.Interface, kc kclient.Interface, maxDelay int32, name string, reloadInterval int, reloadCounts map[string]int) (templatePlugin *templateplugin.TemplatePlugin) { r := templateplugin.NewFakeTemplateRouter() reloadCounts[name] = 0 r.EnableRateLimiter(reloadInterval, func() error { reloadCounts[name]++ return nil }) templatePlugin = &templateplugin.TemplatePlugin{Router: r} statusPlugin := controller.NewStatusAdmitter(templatePlugin, oc, name) validationPlugin := controller.NewExtendedValidator(statusPlugin, controller.RejectionRecorder(statusPlugin)) uniquePlugin := controller.NewUniqueHost(validationPlugin, controller.HostForRoute, controller.RejectionRecorder(statusPlugin)) var plugin router.Plugin = uniquePlugin if maxDelay > 0 { plugin = NewDelayPlugin(plugin, maxDelay) } factory := controllerfactory.NewDefaultRouterControllerFactory(oc, kc) controller := factory.Create(plugin) controller.Run() return }
// Run launches a template router using the provided options. It never exits. func (o *TemplateRouterOptions) Run() error { pluginCfg := templateplugin.TemplatePluginConfig{ WorkingDir: o.WorkingDir, TemplatePath: o.TemplateFile, ReloadScriptPath: o.ReloadScript, ReloadInterval: o.ReloadInterval, DefaultCertificate: o.DefaultCertificate, DefaultCertificatePath: o.DefaultCertificatePath, DefaultCertificateDir: o.DefaultCertificateDir, StatsPort: o.StatsPort, StatsUsername: o.StatsUsername, StatsPassword: o.StatsPassword, PeerService: o.RouterService, BindPortsAfterSync: o.BindPortsAfterSync, IncludeUDP: o.RouterSelection.IncludeUDP, AllowWildcardRoutes: o.RouterSelection.AllowWildcardRoutes, } oc, kc, err := o.Config.Clients() if err != nil { return err } svcFetcher := templateplugin.NewListWatchServiceLookup(kc.Core(), 10*time.Minute) templatePlugin, err := templateplugin.NewTemplatePlugin(pluginCfg, svcFetcher) if err != nil { return err } statusPlugin := controller.NewStatusAdmitter(templatePlugin, oc, o.RouterName) var nextPlugin router.Plugin = statusPlugin if o.ExtendedValidation { nextPlugin = controller.NewExtendedValidator(nextPlugin, controller.RejectionRecorder(statusPlugin)) } uniqueHostPlugin := controller.NewUniqueHost(nextPlugin, o.RouteSelectionFunc(), controller.RejectionRecorder(statusPlugin)) plugin := controller.NewHostAdmitter(uniqueHostPlugin, o.RouteAdmissionFunc(), o.RestrictSubdomainOwnership, controller.RejectionRecorder(statusPlugin)) factory := o.RouterSelection.NewFactory(oc, kc) controller := factory.Create(plugin, false) controller.Run() proc.StartReaper() select {} }
// Run launches a template router using the provided options. It never exits. func (o *TemplateRouterOptions) Run() error { pluginCfg := templateplugin.TemplatePluginConfig{ WorkingDir: o.WorkingDir, TemplatePath: o.TemplateFile, ReloadScriptPath: o.ReloadScript, ReloadInterval: o.ReloadInterval, DefaultCertificate: o.DefaultCertificate, DefaultCertificatePath: o.DefaultCertificatePath, StatsPort: o.StatsPort, StatsUsername: o.StatsUsername, StatsPassword: o.StatsPassword, PeerService: o.RouterService, IncludeUDP: o.RouterSelection.IncludeUDP, } templatePlugin, err := templateplugin.NewTemplatePlugin(pluginCfg) if err != nil { return err } oc, kc, err := o.Config.Clients() if err != nil { return err } statusPlugin := controller.NewStatusAdmitter(templatePlugin, oc, o.RouterName) var nextPlugin router.Plugin = statusPlugin if o.ExtendedValidation { nextPlugin = controller.NewExtendedValidator(nextPlugin, controller.RejectionRecorder(statusPlugin)) } plugin := controller.NewUniqueHost(nextPlugin, o.RouteSelectionFunc(), controller.RejectionRecorder(statusPlugin)) factory := o.RouterSelection.NewFactory(oc, kc) controller := factory.Create(plugin) controller.Run() proc.StartReaper() select {} }
// TestHandleRouteExtendedValidation test route watch events with extended route configuration validation. func TestHandleRouteExtendedValidation(t *testing.T) { rejections := &fakeRejections{} router := newTestRouter(make(map[string]ServiceUnit)) templatePlugin := newDefaultTemplatePlugin(router, true) // TODO: move tests that rely on unique hosts to pkg/router/controller and remove them from // here extendedValidatorPlugin := controller.NewExtendedValidator(templatePlugin, rejections) plugin := controller.NewUniqueHost(extendedValidatorPlugin, controller.HostForRoute, rejections) original := unversioned.Time{Time: time.Now()} //add route := &routeapi.Route{ ObjectMeta: kapi.ObjectMeta{ CreationTimestamp: original, Namespace: "foo", Name: "test", }, Spec: routeapi.RouteSpec{ Host: "www.example.com", To: kapi.ObjectReference{ Name: "TestService", }, }, } serviceUnitKey := fmt.Sprintf("%s/%s", route.Namespace, route.Spec.To.Name) plugin.HandleRoute(watch.Added, route) if !router.Committed { t.Errorf("Expected router to be committed after HandleRoute call") } actualSU, ok := router.FindServiceUnit(serviceUnitKey) if !ok { t.Errorf("TestHandleRoute was unable to find the service unit %s after HandleRoute was called", route.Spec.To.Name) } else { serviceAliasCfg, ok := actualSU.ServiceAliasConfigs[router.routeKey(route)] if !ok { t.Errorf("TestHandleRoute expected route key %s", router.routeKey(route)) } else { if serviceAliasCfg.Host != route.Spec.Host || serviceAliasCfg.Path != route.Spec.Path { t.Errorf("Expected route did not match service alias config %v : %v", route, serviceAliasCfg) } } } if len(rejections.rejections) > 0 { t.Fatalf("did not expect a recorded rejection: %#v", rejections) } tests := []struct { name string route *routeapi.Route errorExpected bool }{ { name: "No TLS Termination", route: &routeapi.Route{ Spec: routeapi.RouteSpec{ Host: "www.no.tls.test", TLS: &routeapi.TLSConfig{ Termination: "", }, }, }, errorExpected: true, }, { name: "Passthrough termination OK", route: &routeapi.Route{ Spec: routeapi.RouteSpec{ Host: "www.passthrough.test", TLS: &routeapi.TLSConfig{ Termination: routeapi.TLSTerminationPassthrough, }, }, }, errorExpected: false, }, { name: "Reencrypt termination OK with certs", route: &routeapi.Route{ Spec: routeapi.RouteSpec{ Host: "www.example.com", TLS: &routeapi.TLSConfig{ Termination: routeapi.TLSTerminationReencrypt, Certificate: testCertificate, Key: testPrivateKey, CACertificate: testCACertificate, DestinationCACertificate: testDestinationCACertificate, }, }, }, errorExpected: false, }, { name: "Reencrypt termination OK with bad config", route: &routeapi.Route{ Spec: routeapi.RouteSpec{ Host: "www.reencypt.badconfig.test", TLS: &routeapi.TLSConfig{ Termination: routeapi.TLSTerminationReencrypt, Certificate: "def", Key: "ghi", CACertificate: "jkl", DestinationCACertificate: "abc", }, }, }, errorExpected: true, }, { name: "Reencrypt termination OK without certs", route: &routeapi.Route{ Spec: routeapi.RouteSpec{ Host: "www.reencypt.nocerts.test", TLS: &routeapi.TLSConfig{ Termination: routeapi.TLSTerminationReencrypt, DestinationCACertificate: testDestinationCACertificate, }, }, }, errorExpected: false, }, { name: "Reencrypt termination bad config without certs", route: &routeapi.Route{ Spec: routeapi.RouteSpec{ Host: "www.reencypt.badconfignocerts.test", TLS: &routeapi.TLSConfig{ Termination: routeapi.TLSTerminationReencrypt, DestinationCACertificate: "abc", }, }, }, errorExpected: true, }, { name: "Reencrypt termination no dest cert", route: &routeapi.Route{ Spec: routeapi.RouteSpec{ Host: "www.reencypt.nodestcert.test", TLS: &routeapi.TLSConfig{ Termination: routeapi.TLSTerminationReencrypt, Certificate: testCertificate, Key: testPrivateKey, CACertificate: testCACertificate, }, }, }, errorExpected: true, }, { name: "Edge termination OK with certs without host", route: &routeapi.Route{ Spec: routeapi.RouteSpec{ TLS: &routeapi.TLSConfig{ Termination: routeapi.TLSTerminationEdge, Certificate: testCertificate, Key: testPrivateKey, CACertificate: testCACertificate, }, }, }, errorExpected: false, }, { name: "Edge termination OK with certs", route: &routeapi.Route{ Spec: routeapi.RouteSpec{ Host: "www.example.com", TLS: &routeapi.TLSConfig{ Termination: routeapi.TLSTerminationEdge, Certificate: testCertificate, Key: testPrivateKey, CACertificate: testCACertificate, }, }, }, errorExpected: false, }, { name: "Edge termination bad config with certs", route: &routeapi.Route{ Spec: routeapi.RouteSpec{ Host: "www.edge.badconfig.test", TLS: &routeapi.TLSConfig{ Termination: routeapi.TLSTerminationEdge, Certificate: "abc", Key: "abc", CACertificate: "abc", }, }, }, errorExpected: true, }, { name: "Edge termination mismatched key and cert", route: &routeapi.Route{ Spec: routeapi.RouteSpec{ Host: "www.edge.mismatchdkeyandcert.test", TLS: &routeapi.TLSConfig{ Termination: routeapi.TLSTerminationEdge, Certificate: testCertificate, Key: testExpiredCertPrivateKey, CACertificate: testCACertificate, }, }, }, errorExpected: true, }, { name: "Edge termination expired cert", route: &routeapi.Route{ Spec: routeapi.RouteSpec{ Host: "www.edge.expiredcert.test", TLS: &routeapi.TLSConfig{ Termination: routeapi.TLSTerminationEdge, Certificate: testExpiredCAUnknownCertificate, Key: testExpiredCertPrivateKey, CACertificate: testCACertificate, }, }, }, errorExpected: true, }, { name: "Edge termination expired cert key mismatch", route: &routeapi.Route{ Spec: routeapi.RouteSpec{ Host: "www.edge.expiredcertkeymismatch.test", TLS: &routeapi.TLSConfig{ Termination: routeapi.TLSTerminationEdge, Certificate: testExpiredCAUnknownCertificate, Key: testPrivateKey, CACertificate: testCACertificate, }, }, }, errorExpected: true, }, { name: "Edge termination OK without certs", route: &routeapi.Route{ Spec: routeapi.RouteSpec{ Host: "www.edge.nocerts.test", TLS: &routeapi.TLSConfig{ Termination: routeapi.TLSTerminationEdge, }, }, }, errorExpected: false, }, { name: "Edge termination, bad dest cert", route: &routeapi.Route{ Spec: routeapi.RouteSpec{ Host: "www.edge.baddestcert.test", TLS: &routeapi.TLSConfig{ Termination: routeapi.TLSTerminationEdge, DestinationCACertificate: "abc", }, }, }, errorExpected: true, }, { name: "Passthrough termination, bad cert", route: &routeapi.Route{ Spec: routeapi.RouteSpec{ Host: "www.passthrough.badcert.test", TLS: &routeapi.TLSConfig{Termination: routeapi.TLSTerminationPassthrough, Certificate: "test"}, }, }, errorExpected: true, }, { name: "Passthrough termination, bad key", route: &routeapi.Route{ Spec: routeapi.RouteSpec{ Host: "www.passthrough.badkey.test", TLS: &routeapi.TLSConfig{Termination: routeapi.TLSTerminationPassthrough, Key: "test"}, }, }, errorExpected: true, }, { name: "Passthrough termination, bad ca cert", route: &routeapi.Route{ Spec: routeapi.RouteSpec{ Host: "www.passthrough.badcacert.test", TLS: &routeapi.TLSConfig{Termination: routeapi.TLSTerminationPassthrough, CACertificate: "test"}, }, }, errorExpected: true, }, { name: "Passthrough termination, bad dest ca cert", route: &routeapi.Route{ Spec: routeapi.RouteSpec{ Host: "www.passthrough.baddestcacert.test", TLS: &routeapi.TLSConfig{Termination: routeapi.TLSTerminationPassthrough, DestinationCACertificate: "test"}, }, }, errorExpected: true, }, { name: "Invalid termination type", route: &routeapi.Route{ Spec: routeapi.RouteSpec{ TLS: &routeapi.TLSConfig{ Termination: "invalid", }, }, }, errorExpected: false, }, { name: "Double escaped newlines", route: &routeapi.Route{ Spec: routeapi.RouteSpec{ Host: "www.reencrypt.doubleescapednewlines.test", TLS: &routeapi.TLSConfig{ Termination: routeapi.TLSTerminationReencrypt, Certificate: "d\\nef", Key: "g\\nhi", CACertificate: "j\\nkl", DestinationCACertificate: "j\\nkl", }, }, }, errorExpected: true, }, } for _, tc := range tests { err := plugin.HandleRoute(watch.Added, tc.route) if tc.errorExpected { if err == nil { t.Fatalf("test case %s: expected an error, got none", tc.name) } } else { if err != nil { t.Fatalf("test case %s: expected no errors, got %v", tc.name, err) } } } }