Skip to content

Commit c6e84df

Browse files
Merge pull request #14818 from smarterclayton/backcompat_for_routes
Preserve backwards compatibilty for old routes for destination CA
2 parents 940905c + a3c81a1 commit c6e84df

File tree

4 files changed

+108
-1
lines changed

4 files changed

+108
-1
lines changed

pkg/cmd/server/origin/legacy.go

+7
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import (
77

88
buildconfigetcd "github.com/openshift/origin/pkg/build/registry/buildconfig/etcd"
99
deploymentconfigetcd "github.com/openshift/origin/pkg/deploy/registry/deployconfig/etcd"
10+
routeregistry "github.com/openshift/origin/pkg/route/registry/route"
11+
routeetcd "github.com/openshift/origin/pkg/route/registry/route/etcd"
1012
)
1113

1214
var (
@@ -204,6 +206,11 @@ func LegacyStorage(storage map[schema.GroupVersion]map[string]rest.Storage) map[
204206
store := *restStorage.Store
205207
restStorage.DeleteStrategy = orphanByDefault(store.DeleteStrategy)
206208
legacyStorage[resource] = &deploymentconfigetcd.REST{Store: &store}
209+
case "routes":
210+
restStorage := s.(*routeetcd.REST)
211+
store := *restStorage.Store
212+
store.Decorator = routeregistry.DecorateLegacyRouteWithEmptyDestinationCACertificates
213+
legacyStorage[resource] = &routeetcd.REST{Store: &store}
207214

208215
default:
209216
legacyStorage[resource] = s

pkg/route/registry/route/strategy.go

+53-1
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,15 @@ func (routeStrategy) NamespaceScoped() bool {
5252
func (s routeStrategy) PrepareForCreate(ctx apirequest.Context, obj runtime.Object) {
5353
route := obj.(*routeapi.Route)
5454
route.Status = routeapi.RouteStatus{}
55+
stripEmptyDestinationCACertificate(route)
5556
}
5657

5758
func (s routeStrategy) PrepareForUpdate(ctx apirequest.Context, obj, old runtime.Object) {
5859
route := obj.(*routeapi.Route)
5960
oldRoute := old.(*routeapi.Route)
60-
route.Status = oldRoute.Status
6161

62+
route.Status = oldRoute.Status
63+
stripEmptyDestinationCACertificate(route)
6264
// Ignore attempts to clear the spec Host
6365
// Prevents "immutable field" errors when applying the same route definition used to create
6466
if len(route.Spec.Host) == 0 {
@@ -218,6 +220,56 @@ func (routeStatusStrategy) ValidateUpdate(ctx apirequest.Context, obj, old runti
218220
return validation.ValidateRouteStatusUpdate(obj.(*routeapi.Route), old.(*routeapi.Route))
219221
}
220222

223+
const emptyDestinationCertificate = `-----BEGIN COMMENT-----
224+
This is an empty PEM file created to provide backwards compatibility
225+
for reencrypt routes that have no destinationCACertificate. This
226+
content will only appear for routes accessed via /oapi/v1/routes.
227+
-----END COMMENT-----
228+
`
229+
230+
// stripEmptyDestinationCACertificate removes the empty destinationCACertificate if it matches
231+
// the current route destination CA certificate.
232+
func stripEmptyDestinationCACertificate(route *routeapi.Route) {
233+
tls := route.Spec.TLS
234+
if tls == nil || tls.Termination != routeapi.TLSTerminationReencrypt {
235+
return
236+
}
237+
if tls.DestinationCACertificate == emptyDestinationCertificate {
238+
tls.DestinationCACertificate = ""
239+
}
240+
}
241+
242+
// DecorateLegacyRouteWithEmptyDestinationCACertificates is used for /oapi/v1 route endpoints
243+
// to prevent legacy clients from seeing an empty destination CA certificate for reencrypt routes,
244+
// which the 'route.openshift.io/v1' endpoint allows. These values are injected in REST responses
245+
// and stripped in PrepareForCreate and PrepareForUpdate.
246+
func DecorateLegacyRouteWithEmptyDestinationCACertificates(obj runtime.Object) error {
247+
switch t := obj.(type) {
248+
case *routeapi.Route:
249+
tls := t.Spec.TLS
250+
if tls == nil || tls.Termination != routeapi.TLSTerminationReencrypt {
251+
return nil
252+
}
253+
if len(tls.DestinationCACertificate) == 0 {
254+
tls.DestinationCACertificate = emptyDestinationCertificate
255+
}
256+
return nil
257+
case *routeapi.RouteList:
258+
for i := range t.Items {
259+
tls := t.Items[i].Spec.TLS
260+
if tls == nil || tls.Termination != routeapi.TLSTerminationReencrypt {
261+
continue
262+
}
263+
if len(tls.DestinationCACertificate) == 0 {
264+
tls.DestinationCACertificate = emptyDestinationCertificate
265+
}
266+
}
267+
return nil
268+
default:
269+
return fmt.Errorf("unknown type passed to %T", obj)
270+
}
271+
}
272+
221273
// GetAttrs returns labels and fields of a given object for filtering purposes
222274
func GetAttrs(obj runtime.Object) (objLabels labels.Set, objFields fields.Set, err error) {
223275
route, ok := obj.(*routeapi.Route)

pkg/route/registry/route/strategy_test.go

+42
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package route
22

33
import (
4+
"reflect"
45
"testing"
56

67
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -65,6 +66,47 @@ func TestEmptyHostDefaulting(t *testing.T) {
6566
}
6667
}
6768

69+
func TestEmptyDefaultCACertificate(t *testing.T) {
70+
testCases := []struct {
71+
route *routeapi.Route
72+
}{
73+
{
74+
route: &routeapi.Route{
75+
ObjectMeta: metav1.ObjectMeta{
76+
Namespace: "foo",
77+
Name: "myroute",
78+
UID: types.UID("abc"),
79+
ResourceVersion: "1",
80+
},
81+
Spec: routeapi.RouteSpec{
82+
Host: "myhost.com",
83+
},
84+
},
85+
},
86+
}
87+
for i, testCase := range testCases {
88+
copied, _ := kapi.Scheme.Copy(testCase.route)
89+
if err := DecorateLegacyRouteWithEmptyDestinationCACertificates(copied.(*routeapi.Route)); err != nil {
90+
t.Errorf("%d: unexpected error: %v", i, err)
91+
continue
92+
}
93+
routeStrategy{}.PrepareForCreate(nil, copied.(*routeapi.Route))
94+
if !reflect.DeepEqual(testCase.route, copied) {
95+
t.Errorf("%d: unexpected change: %#v", i, copied)
96+
continue
97+
}
98+
if err := DecorateLegacyRouteWithEmptyDestinationCACertificates(copied.(*routeapi.Route)); err != nil {
99+
t.Errorf("%d: unexpected error: %v", i, err)
100+
continue
101+
}
102+
routeStrategy{}.PrepareForUpdate(nil, copied.(*routeapi.Route), &routeapi.Route{})
103+
if !reflect.DeepEqual(testCase.route, copied) {
104+
t.Errorf("%d: unexpected change: %#v", i, copied)
105+
continue
106+
}
107+
}
108+
}
109+
68110
func TestHostWithWildcardPolicies(t *testing.T) {
69111
ctx := apirequest.NewContext()
70112
ctx = apirequest.WithUser(ctx, &user.DefaultInfo{Name: "bob"})

test/cmd/routes.sh

+6
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ os::cmd::expect_success 'oc delete routes foo'
3232
os::cmd::expect_success_and_text 'oc create route edge --service foo --port=8080' 'created'
3333
os::cmd::expect_success_and_text 'oc create route edge --service bar --port=9090' 'created'
3434

35+
# verify that reencrypt routes with no destination CA return the stub PEM block on the old API
36+
project="$(oc project -q)"
37+
os::cmd::expect_success_and_text 'oc create route reencrypt --service baz --port=9090' 'created'
38+
os::cmd::expect_success_and_text 'oc get --raw /oapi/v1/namespaces/${project}/routes/baz' 'This is an empty PEM file'
39+
os::cmd::expect_success_and_not_text 'oc get --raw /apis/route.openshift.io/v1/namespaces/${project}/routes/baz' 'This is an empty PEM file'
40+
3541
os::cmd::expect_success_and_text 'oc set route-backends foo' 'routes/foo'
3642
os::cmd::expect_success_and_text 'oc set route-backends foo' 'Service'
3743
os::cmd::expect_success_and_text 'oc set route-backends foo' '100'

0 commit comments

Comments
 (0)