Example #1
0
// Run launches an F5 route sync process using the provided options. It never exits.
func (o *F5RouterOptions) Run() error {
	cfg := f5plugin.F5PluginConfig{
		Host:          o.Host,
		Username:      o.Username,
		Password:      o.Password,
		HttpVserver:   o.HttpVserver,
		HttpsVserver:  o.HttpsVserver,
		PrivateKey:    o.PrivateKey,
		Insecure:      o.Insecure,
		PartitionPath: o.PartitionPath,
	}
	f5Plugin, err := f5plugin.NewF5Plugin(cfg)
	if err != nil {
		return err
	}

	oc, kc, err := o.Config.Clients()
	if err != nil {
		return err
	}

	statusPlugin := controller.NewStatusAdmitter(f5Plugin, oc, o.RouterName)
	plugin := controller.NewUniqueHost(statusPlugin, o.RouteSelectionFunc(), statusPlugin)

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

	select {}
}
Example #2
0
// 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,
		DefaultCertificate: o.DefaultCertificate,
		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
	}

	plugin := controller.NewUniqueHost(templatePlugin, controller.HostForRoute)

	oc, kc, err := o.Config.Clients()
	if err != nil {
		return err
	}

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

	proc.StartReaper()

	select {}
}
Example #3
0
func TestNamespaceScopingFromEmpty(t *testing.T) {
	router := newTestRouter(make(map[string]ServiceAliasConfig))
	templatePlugin := newDefaultTemplatePlugin(router, true)
	// TODO: move tests that rely on unique hosts to pkg/router/controller and remove them from
	// here
	plugin := controller.NewUniqueHost(templatePlugin, controller.HostForRoute, controller.LogRejections)

	// no namespaces allowed
	plugin.HandleNamespaces(sets.String{})

	//add
	route := &routeapi.Route{
		ObjectMeta: kapi.ObjectMeta{Namespace: "foo", Name: "test"},
		Spec: routeapi.RouteSpec{
			Host: "www.example.com",
			To: routeapi.RouteTargetReference{
				Name:   "TestService",
				Weight: new(int32),
			},
		},
	}

	// ignores all events for namespace that doesn't match
	for _, s := range []watch.EventType{watch.Added, watch.Modified, watch.Deleted} {
		plugin.HandleRoute(s, route)
		if _, ok := router.FindServiceUnit("foo/TestService"); ok || plugin.HostLen() != 0 {
			t.Errorf("unexpected router state %#v", router)
		}
	}

	// allow non matching
	plugin.HandleNamespaces(sets.NewString("bar"))
	for _, s := range []watch.EventType{watch.Added, watch.Modified, watch.Deleted} {
		plugin.HandleRoute(s, route)
		if _, ok := router.FindServiceUnit("foo/TestService"); ok || plugin.HostLen() != 0 {
			t.Errorf("unexpected router state %#v", router)
		}
	}

	// allow foo
	plugin.HandleNamespaces(sets.NewString("foo", "bar"))
	plugin.HandleRoute(watch.Added, route)
	if _, ok := router.FindServiceUnit("foo/TestService"); !ok || plugin.HostLen() != 1 {
		t.Errorf("unexpected router state %#v", router)
	}

	// forbid foo, and make sure it's cleared
	plugin.HandleNamespaces(sets.NewString("bar"))
	if _, ok := router.FindServiceUnit("foo/TestService"); ok || plugin.HostLen() != 0 {
		t.Errorf("unexpected router state %#v", router)
	}
	plugin.HandleRoute(watch.Modified, route)
	if _, ok := router.FindServiceUnit("foo/TestService"); ok || plugin.HostLen() != 0 {
		t.Errorf("unexpected router state %#v", router)
	}
	plugin.HandleRoute(watch.Added, route)
	if _, ok := router.FindServiceUnit("foo/TestService"); ok || plugin.HostLen() != 0 {
		t.Errorf("unexpected router state %#v", router)
	}
}
Example #4
0
// Run launches an F5 route sync process using the provided options. It never exits.
func (o *F5RouterOptions) Run() error {
	cfg := f5plugin.F5PluginConfig{
		Host:         o.Host,
		Username:     o.Username,
		Password:     o.Password,
		HttpVserver:  o.HttpVserver,
		HttpsVserver: o.HttpsVserver,
		PrivateKey:   o.PrivateKey,
		Insecure:     o.Insecure,
	}
	f5Plugin, err := f5plugin.NewF5Plugin(cfg)
	if err != nil {
		return err
	}

	plugin := controller.NewUniqueHost(f5Plugin, controller.HostForRoute)

	oc, kc, err := o.Config.Clients()
	if err != nil {
		return err
	}

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

	select {}
}
Example #5
0
// Run launches an F5 route sync process using the provided options. It never exits.
func (o *F5RouterOptions) Run() error {
	cfg := f5plugin.F5PluginConfig{
		Host:            o.Host,
		Username:        o.Username,
		Password:        o.Password,
		HttpVserver:     o.HttpVserver,
		HttpsVserver:    o.HttpsVserver,
		PrivateKey:      o.PrivateKey,
		Insecure:        o.Insecure,
		PartitionPath:   o.PartitionPath,
		InternalAddress: o.InternalAddress,
		VxlanGateway:    o.VxlanGateway,
	}
	f5Plugin, err := f5plugin.NewF5Plugin(cfg)
	if err != nil {
		return err
	}

	oc, _, kc, err := o.Config.Clients()
	if err != nil {
		return err
	}

	statusPlugin := controller.NewStatusAdmitter(f5Plugin, oc, o.RouterName)
	uniqueHostPlugin := controller.NewUniqueHost(statusPlugin, o.RouteSelectionFunc(), statusPlugin)
	plugin := controller.NewHostAdmitter(uniqueHostPlugin, o.F5RouteAdmitterFunc(), false, statusPlugin)

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

	select {}
}
Example #6
0
// 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
}
Example #7
0
// 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 {}
}
Example #8
0
// 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 {}
}
Example #9
0
// 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)
			}
		}
	}
}
Example #10
0
// TestHandleRoute test route watch events
func TestHandleRoute(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
	plugin := controller.NewUniqueHost(templatePlugin, 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)
	}

	// attempt to add a second route with a newer time, verify it is ignored
	duplicateRoute := &routeapi.Route{
		ObjectMeta: kapi.ObjectMeta{
			CreationTimestamp: unversioned.Time{Time: original.Add(time.Hour)},
			Namespace:         "foo",
			Name:              "dupe",
		},
		Spec: routeapi.RouteSpec{
			Host: "www.example.com",
			To: kapi.ObjectReference{
				Name: "TestService2",
			},
		},
	}
	if err := plugin.HandleRoute(watch.Added, duplicateRoute); err == nil {
		t.Fatal("unexpected non-error")
	}
	if _, ok := router.FindServiceUnit("foo/TestService2"); ok {
		t.Fatalf("unexpected second unit: %#v", router)
	}
	if r, ok := plugin.RoutesForHost("www.example.com"); !ok || r[0].Name != "test" {
		t.Fatalf("unexpected claimed routes: %#v", r)
	}
	if len(rejections.rejections) != 1 ||
		rejections.rejections[0].route.Name != "dupe" ||
		rejections.rejections[0].reason != "HostAlreadyClaimed" ||
		rejections.rejections[0].message != "route test already exposes www.example.com and is older" {
		t.Fatalf("did not record rejection: %#v", rejections)
	}
	rejections.rejections = nil

	// attempt to remove the second route that is not being used, verify it is ignored
	if err := plugin.HandleRoute(watch.Deleted, duplicateRoute); err == nil {
		t.Fatal("unexpected non-error")
	}
	if _, ok := router.FindServiceUnit("foo/TestService2"); ok {
		t.Fatalf("unexpected second unit: %#v", router)
	}
	if _, ok := router.FindServiceUnit("foo/TestService"); !ok {
		t.Fatalf("unexpected first unit: %#v", router)
	}
	if r, ok := plugin.RoutesForHost("www.example.com"); !ok || r[0].Name != "test" {
		t.Fatalf("unexpected claimed routes: %#v", r)
	}
	if len(rejections.rejections) != 1 ||
		rejections.rejections[0].route.Name != "dupe" ||
		rejections.rejections[0].reason != "HostAlreadyClaimed" ||
		rejections.rejections[0].message != "route test already exposes www.example.com and is older" {
		t.Fatalf("did not record rejection: %#v", rejections)
	}
	rejections.rejections = nil

	// add a second route with an older time, verify it takes effect
	duplicateRoute.CreationTimestamp = unversioned.Time{Time: original.Add(-time.Hour)}
	if err := plugin.HandleRoute(watch.Added, duplicateRoute); err != nil {
		t.Fatal("unexpected error")
	}
	otherSU, ok := router.FindServiceUnit("foo/TestService2")
	if !ok {
		t.Fatalf("missing second unit: %#v", router)
	}
	if len(actualSU.ServiceAliasConfigs) != 0 || len(otherSU.ServiceAliasConfigs) != 1 {
		t.Errorf("incorrect router state: %#v", router)
	}
	if _, ok := actualSU.ServiceAliasConfigs[router.routeKey(route)]; ok {
		t.Errorf("unexpected service alias config %s", router.routeKey(route))
	}
	if len(rejections.rejections) != 1 ||
		rejections.rejections[0].route.Name != "test" ||
		rejections.rejections[0].reason != "HostAlreadyClaimed" ||
		rejections.rejections[0].message != "replaced by older route dupe" {
		t.Fatalf("did not record rejection: %#v", rejections)
	}
	rejections.rejections = nil

	//mod
	route.Spec.Host = "www.example2.com"
	if err := plugin.HandleRoute(watch.Modified, route); err != nil {
		t.Fatal("unexpected error")
	}
	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 plugin.HostLen() != 1 {
		t.Fatalf("did not clear claimed route: %#v", plugin)
	}
	if len(rejections.rejections) != 0 {
		t.Fatalf("unexpected rejection: %#v", rejections)
	}

	//delete
	if err := plugin.HandleRoute(watch.Deleted, route); err != nil {
		t.Fatal("unexpected error")
	}
	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 {
		_, ok := actualSU.ServiceAliasConfigs[router.routeKey(route)]

		if ok {
			t.Errorf("TestHandleRoute did not expect route key %s", router.routeKey(route))
		}
	}
	if plugin.HostLen() != 0 {
		t.Errorf("did not clear claimed route: %#v", plugin)
	}
	if len(rejections.rejections) != 0 {
		t.Fatalf("unexpected rejection: %#v", rejections)
	}
}
Example #11
0
// TestHandleCPEndpoints test endpoint watch events with UDP excluded
func TestHandleTCPEndpoints(t *testing.T) {
	testCases := []struct {
		name                string          //human readable name for test case
		eventType           watch.EventType //type to be passed to the HandleEndpoints method
		endpoints           *kapi.Endpoints //endpoints to be passed to the HandleEndpoints method
		expectedServiceUnit *ServiceUnit    //service unit that will be compared against.
	}{
		{
			name:      "Endpoint add",
			eventType: watch.Added,
			endpoints: &kapi.Endpoints{
				ObjectMeta: kapi.ObjectMeta{
					Namespace: "foo",
					Name:      "test", //kapi.endpoints inherits the name of the service
				},
				Subsets: []kapi.EndpointSubset{{
					Addresses: []kapi.EndpointAddress{{IP: "1.1.1.1"}},
					Ports: []kapi.EndpointPort{
						{Port: 345},
						{Port: 346, Protocol: kapi.ProtocolUDP},
					},
				}}, //not specifying a port to force the port 80 assumption
			},
			expectedServiceUnit: &ServiceUnit{
				Name: "foo/test", //service name from kapi.endpoints object
				EndpointTable: []Endpoint{
					{
						ID:   "1.1.1.1:345",
						IP:   "1.1.1.1",
						Port: "345",
					},
				},
			},
		},
		{
			name:      "Endpoint mod",
			eventType: watch.Modified,
			endpoints: &kapi.Endpoints{
				ObjectMeta: kapi.ObjectMeta{
					Namespace: "foo",
					Name:      "test",
				},
				Subsets: []kapi.EndpointSubset{{
					Addresses: []kapi.EndpointAddress{{IP: "2.2.2.2"}},
					Ports: []kapi.EndpointPort{
						{Port: 8080},
						{Port: 8081, Protocol: kapi.ProtocolUDP},
					},
				}},
			},
			expectedServiceUnit: &ServiceUnit{
				Name: "foo/test",
				EndpointTable: []Endpoint{
					{
						ID:   "2.2.2.2:8080",
						IP:   "2.2.2.2",
						Port: "8080",
					},
				},
			},
		},
		{
			name:      "Endpoint delete",
			eventType: watch.Deleted,
			endpoints: &kapi.Endpoints{
				ObjectMeta: kapi.ObjectMeta{
					Namespace: "foo",
					Name:      "test",
				},
				Subsets: []kapi.EndpointSubset{{
					Addresses: []kapi.EndpointAddress{{IP: "3.3.3.3"}},
					Ports:     []kapi.EndpointPort{{Port: 0}},
				}},
			},
			expectedServiceUnit: &ServiceUnit{
				Name:          "foo/test",
				EndpointTable: []Endpoint{},
			},
		},
	}

	router := newTestRouter(make(map[string]ServiceUnit))
	templatePlugin := newDefaultTemplatePlugin(router, false)
	// TODO: move tests that rely on unique hosts to pkg/router/controller and remove them from
	// here
	plugin := controller.NewUniqueHost(templatePlugin, controller.HostForRoute, controller.LogRejections)

	for _, tc := range testCases {
		plugin.HandleEndpoints(tc.eventType, tc.endpoints)

		if !router.Committed {
			t.Errorf("Expected router to be committed after HandleEndpoints call")
		}

		su, ok := router.FindServiceUnit(tc.expectedServiceUnit.Name)

		if !ok {
			t.Errorf("TestHandleEndpoints test case %s failed.  Couldn't find expected service unit with name %s", tc.name, tc.expectedServiceUnit.Name)
		} else {
			for expectedKey, expectedEp := range tc.expectedServiceUnit.EndpointTable {
				actualEp := su.EndpointTable[expectedKey]

				if expectedEp.ID != actualEp.ID || expectedEp.IP != actualEp.IP || expectedEp.Port != actualEp.Port {
					t.Errorf("TestHandleEndpoints test case %s failed.  Expected endpoint didn't match actual endpoint %v : %v", tc.name, expectedEp, actualEp)
				}
			}
		}
	}
}