Skip to content

Commit 9595389

Browse files
Prevent route theft by removing the ability to update spec.host
Once created, spec.host is immutable. This preserves the ordering of route creation which makes route ordering safe. This rolls back the "on update" allocation of spec.host, since that complicates allocation. A route with an empty spec.host may be defaulted by the caller (it means they don't care) - clients should be looking at route.status anyway.
1 parent 09a2910 commit 9595389

File tree

4 files changed

+80
-51
lines changed

4 files changed

+80
-51
lines changed

pkg/route/api/validation/validation.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ func ValidateRoute(route *routeapi.Route) field.ErrorList {
6464

6565
func ValidateRouteUpdate(route *routeapi.Route, older *routeapi.Route) field.ErrorList {
6666
allErrs := validation.ValidateObjectMetaUpdate(&route.ObjectMeta, &older.ObjectMeta, field.NewPath("metadata"))
67+
allErrs = append(allErrs, validation.ValidateImmutableField(route.Spec.Host, older.Spec.Host, field.NewPath("spec", "host"))...)
6768
allErrs = append(allErrs, ValidateRoute(route)...)
6869
return allErrs
6970
}

pkg/route/api/validation/validation_test.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,85 @@ func TestValidateTLSInsecureEdgeTerminationPolicy(t *testing.T) {
575575
}
576576
}
577577

578+
// TestValidateRouteBad ensures not specifying a required field results in error and a fully specified
579+
// route passes successfully
580+
func TestValidateRouteUpdate(t *testing.T) {
581+
tests := []struct {
582+
name string
583+
route *api.Route
584+
change func(route *api.Route)
585+
expectedErrors int
586+
}{
587+
{
588+
route: &api.Route{
589+
ObjectMeta: kapi.ObjectMeta{
590+
Name: "bar",
591+
Namespace: "foo",
592+
ResourceVersion: "1",
593+
},
594+
Spec: api.RouteSpec{
595+
Host: "host",
596+
To: kapi.ObjectReference{
597+
Name: "serviceName",
598+
Kind: "Service",
599+
},
600+
},
601+
},
602+
change: func(route *api.Route) { route.Spec.Host = "" },
603+
expectedErrors: 1,
604+
},
605+
{
606+
route: &api.Route{
607+
ObjectMeta: kapi.ObjectMeta{
608+
Name: "bar",
609+
Namespace: "foo",
610+
ResourceVersion: "1",
611+
},
612+
Spec: api.RouteSpec{
613+
Host: "host",
614+
To: kapi.ObjectReference{
615+
Name: "serviceName",
616+
Kind: "Service",
617+
},
618+
},
619+
},
620+
change: func(route *api.Route) { route.Spec.Host = "other" },
621+
expectedErrors: 1,
622+
},
623+
{
624+
route: &api.Route{
625+
ObjectMeta: kapi.ObjectMeta{
626+
Name: "bar",
627+
Namespace: "foo",
628+
ResourceVersion: "1",
629+
},
630+
Spec: api.RouteSpec{
631+
Host: "host",
632+
To: kapi.ObjectReference{
633+
Name: "serviceName",
634+
Kind: "Service",
635+
},
636+
},
637+
},
638+
change: func(route *api.Route) { route.Name = "baz" },
639+
expectedErrors: 1,
640+
},
641+
}
642+
643+
for i, tc := range tests {
644+
copied, err := kapi.Scheme.Copy(tc.route)
645+
if err != nil {
646+
t.Fatal(err)
647+
}
648+
newRoute := copied.(*api.Route)
649+
tc.change(newRoute)
650+
errs := ValidateRouteUpdate(newRoute, tc.route)
651+
if len(errs) != tc.expectedErrors {
652+
t.Errorf("%d: expected %d error(s), got %d. %v", i, tc.expectedErrors, len(errs), errs)
653+
}
654+
}
655+
}
656+
578657
func TestValidateInsecureEdgeTerminationPolicy(t *testing.T) {
579658
tests := []struct {
580659
name string

pkg/route/registry/route/etcd/etcd_test.go

Lines changed: 0 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -116,50 +116,6 @@ func TestUpdate(t *testing.T) {
116116
)
117117
}
118118

119-
func TestUpdateWithAllocation(t *testing.T) {
120-
allocator := &testAllocator{Hostname: "bar"}
121-
storage, server := newStorage(t, allocator)
122-
defer server.Terminate(t)
123-
124-
// create a route with a populated host
125-
originalRoute := validRoute()
126-
originalRoute.Spec.Host = "foo"
127-
created, err := storage.Create(kapi.NewDefaultContext(), originalRoute)
128-
if err != nil {
129-
t.Fatalf("error creating valid route to test allocations: %v", err)
130-
}
131-
132-
createdRoute := created.(*api.Route)
133-
if createdRoute.Spec.Host != "foo" {
134-
t.Fatalf("unexpected host on createdRoute: %#v", createdRoute)
135-
}
136-
if _, ok := createdRoute.Annotations[route.HostGeneratedAnnotationKey]; ok {
137-
t.Fatalf("created route should not have the generated host annotation")
138-
}
139-
140-
// update the route to set the host to empty
141-
createdRoute.Spec.Host = ""
142-
updated, _, err := storage.Update(kapi.NewDefaultContext(), createdRoute)
143-
if err != nil {
144-
t.Fatalf("error updating route to test allocations: %v", err)
145-
}
146-
147-
// route should now have the allocated host of bar and the generated host annotation
148-
updatedRoute := updated.(*api.Route)
149-
if updatedRoute == nil {
150-
t.Fatalf("expected updatedRoute to not be nil")
151-
}
152-
if updatedRoute.Spec.Host != "bar" {
153-
t.Fatalf("unexpected route: %#v", updatedRoute)
154-
}
155-
if v, ok := updatedRoute.Annotations[route.HostGeneratedAnnotationKey]; !ok || v != "true" {
156-
t.Fatalf("unexpected route: %#v", updatedRoute)
157-
}
158-
if !allocator.Allocate || !allocator.Generate {
159-
t.Fatalf("unexpected allocator: %#v", allocator)
160-
}
161-
}
162-
163119
func TestList(t *testing.T) {
164120
storage, server := newStorage(t, nil)
165121
defer server.Terminate(t)

pkg/route/registry/route/strategy.go

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -60,13 +60,6 @@ func (s routeStrategy) PrepareForUpdate(obj, old runtime.Object) {
6060
// Limit to kind/name
6161
// TODO: convert to LocalObjectReference
6262
route.Spec.To = kapi.ObjectReference{Kind: route.Spec.To.Kind, Name: route.Spec.To.Name}
63-
64-
// if the route host has been updated to empty we should allocate the host
65-
err := s.allocateHost(route)
66-
if err != nil {
67-
// TODO: this will be changed when moved to a controller
68-
utilruntime.HandleError(errors.NewInternalError(fmt.Errorf("allocation error: %v for route: %#v", err, obj)))
69-
}
7063
}
7164

7265
// allocateHost allocates a host name ONLY if the host name on the route is empty and an allocator

0 commit comments

Comments
 (0)