Skip to content
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
14 changes: 14 additions & 0 deletions pkg/route/OWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
approvers:
- frobware
- knobunc
- Miciah
- sgreene570
- smarterclayton
reviewers:
- danehans
- frobware
- knobunc
- Miciah
- sgreene570
- smarterclayton
component: Routing
44 changes: 44 additions & 0 deletions pkg/route/routeapihelpers/routeapihelpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Package routeapihelpers contains utilities for handling OpenShift route objects.
package routeapihelpers

import (
"fmt"
"net/url"

routev1 "github.com/openshift/api/route/v1"
corev1 "k8s.io/api/core/v1"
)

// IngressURI calculates an admitted ingress URI.
// If 'host' is nonempty, only the ingress for that host is considered.
// If 'host' is empty, the first admitted ingress is used.
func IngressURI(route *routev1.Route, host string) (*url.URL, *routev1.RouteIngress, error) {
scheme := "http"
if route.Spec.TLS != nil {
scheme = "https"
}

for _, ingress := range route.Status.Ingress {
if host == "" || host == ingress.Host {
uri := &url.URL{
Scheme: scheme,
Host: ingress.Host,
}

for _, condition := range ingress.Conditions {
if condition.Type == routev1.RouteAdmitted && condition.Status == corev1.ConditionTrue {
return uri, &ingress, nil
}
}

if host == ingress.Host {
return uri, &ingress, fmt.Errorf("ingress for host %s in route %s in namespace %s is not admitted", host, route.ObjectMeta.Name, route.ObjectMeta.Namespace)
}
}
}

if host == "" {
return nil, nil, fmt.Errorf("no admitted ingress for route %s in namespace %s", route.ObjectMeta.Name, route.ObjectMeta.Namespace)
}
return nil, nil, fmt.Errorf("no ingress for host %s in route %s in namespace %s", host, route.ObjectMeta.Name, route.ObjectMeta.Namespace)
}
301 changes: 301 additions & 0 deletions pkg/route/routeapihelpers/routeapihelpers_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,301 @@
package routeapihelpers

import (
"net/url"
"reflect"
"regexp"
"testing"

routev1 "github.com/openshift/api/route/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func TestIngressURI(t *testing.T) {
for _, testCase := range []struct {
name string
route *routev1.Route
host string
uri *url.URL
ingress *routev1.RouteIngress
error string
}{
{
name: "no ingress",
route: &routev1.Route{
ObjectMeta: metav1.ObjectMeta{
Namespace: "example-namespace",
Name: "example-name",
},
},
error: "^no admitted ingress for route example-name in namespace example-namespace$",
},
{
name: "no admitted ingress, host-agnostic",
route: &routev1.Route{
ObjectMeta: metav1.ObjectMeta{
Namespace: "example-namespace",
Name: "example-name",
},
Status: routev1.RouteStatus{
Ingress: []routev1.RouteIngress{
{
Host: "example.com",
RouterName: "example-router",
},
},
},
},
error: "^no admitted ingress for route example-name in namespace example-namespace$",
},
{
name: "explicitly non-admitted ingress, host-agnostic",
route: &routev1.Route{
ObjectMeta: metav1.ObjectMeta{
Namespace: "example-namespace",
Name: "example-name",
},
Status: routev1.RouteStatus{
Ingress: []routev1.RouteIngress{
{
Host: "example.com",
RouterName: "example-router",
Conditions: []routev1.RouteIngressCondition{
{
Type: routev1.RouteAdmitted,
Status: corev1.ConditionFalse,
Reason: "ExampleReason",
Message: "Example message",
},
},
},
},
},
},
error: "^no admitted ingress for route example-name in namespace example-namespace$",
},
{
name: "no admitted ingress, unrecognized host",
route: &routev1.Route{
ObjectMeta: metav1.ObjectMeta{
Namespace: "example-namespace",
Name: "example-name",
},
Status: routev1.RouteStatus{
Ingress: []routev1.RouteIngress{
{
Host: "example.com",
RouterName: "example-router",
},
},
},
},
host: "a.example.com",
error: "^no ingress for host a.example.com in route example-name in namespace example-namespace$",
},
{
name: "no admitted ingress, host not admitted",
route: &routev1.Route{
ObjectMeta: metav1.ObjectMeta{
Namespace: "example-namespace",
Name: "example-name",
},
Status: routev1.RouteStatus{
Ingress: []routev1.RouteIngress{
{
Host: "example.com",
RouterName: "example-router",
},
},
},
},
host: "example.com",
uri: &url.URL{
Scheme: "http",
Host: "example.com",
},
ingress: &routev1.RouteIngress{
Host: "example.com",
RouterName: "example-router",
},
error: "^ingress for host example.com in route example-name in namespace example-namespace is not admitted$",
},
{
name: "admitted ingress, host-agnostic",
route: &routev1.Route{
ObjectMeta: metav1.ObjectMeta{
Namespace: "example-namespace",
Name: "example-name",
},
Status: routev1.RouteStatus{
Ingress: []routev1.RouteIngress{
{
Host: "a.example.com",
RouterName: "example-router",
},
{
Host: "b.example.com",
RouterName: "example-router",
Conditions: []routev1.RouteIngressCondition{
{
Type: routev1.RouteAdmitted,
Status: corev1.ConditionTrue,
Reason: "ExampleReason",
Message: "Example message",
},
},
},
{
Host: "c.example.com",
RouterName: "example-router",
Conditions: []routev1.RouteIngressCondition{
{
Type: routev1.RouteAdmitted,
Status: corev1.ConditionTrue,
Reason: "ExampleReason",
Message: "Example message",
},
},
},
},
},
},
uri: &url.URL{
Scheme: "http",
Host: "b.example.com",
},
ingress: &routev1.RouteIngress{
Host: "b.example.com",
RouterName: "example-router",
Conditions: []routev1.RouteIngressCondition{
{
Type: routev1.RouteAdmitted,
Status: corev1.ConditionTrue,
Reason: "ExampleReason",
Message: "Example message",
},
},
},
},
{
name: "admitted ingress, host-specific",
route: &routev1.Route{
ObjectMeta: metav1.ObjectMeta{
Namespace: "example-namespace",
Name: "example-name",
},
Status: routev1.RouteStatus{
Ingress: []routev1.RouteIngress{
{
Host: "a.example.com",
RouterName: "example-router",
},
{
Host: "b.example.com",
RouterName: "example-router",
Conditions: []routev1.RouteIngressCondition{
{
Type: routev1.RouteAdmitted,
Status: corev1.ConditionTrue,
Reason: "ExampleReason",
Message: "Example message",
},
},
},
{
Host: "c.example.com",
RouterName: "example-router",
Conditions: []routev1.RouteIngressCondition{
{
Type: routev1.RouteAdmitted,
Status: corev1.ConditionTrue,
Reason: "ExampleReason",
Message: "Example message",
},
},
},
},
},
},
host: "c.example.com",
uri: &url.URL{
Scheme: "http",
Host: "c.example.com",
},
ingress: &routev1.RouteIngress{
Host: "c.example.com",
RouterName: "example-router",
Conditions: []routev1.RouteIngressCondition{
{
Type: routev1.RouteAdmitted,
Status: corev1.ConditionTrue,
Reason: "ExampleReason",
Message: "Example message",
},
},
},
},
{
name: "admitted ingress, TLS",
route: &routev1.Route{
ObjectMeta: metav1.ObjectMeta{
Namespace: "example-namespace",
Name: "example-name",
},
Spec: routev1.RouteSpec{
TLS: &routev1.TLSConfig{},
},
Status: routev1.RouteStatus{
Ingress: []routev1.RouteIngress{
{
Host: "example.com",
RouterName: "example-router",
Conditions: []routev1.RouteIngressCondition{
{
Type: routev1.RouteAdmitted,
Status: corev1.ConditionTrue,
Reason: "ExampleReason",
Message: "Example message",
},
},
},
},
},
},
uri: &url.URL{
Scheme: "https",
Host: "example.com",
},
ingress: &routev1.RouteIngress{
Host: "example.com",
RouterName: "example-router",
Conditions: []routev1.RouteIngressCondition{
{
Type: routev1.RouteAdmitted,
Status: corev1.ConditionTrue,
Reason: "ExampleReason",
Message: "Example message",
},
},
},
},
} {
t.Run(testCase.name, func(t *testing.T) {
uri, ingress, err := IngressURI(testCase.route, testCase.host)
if testCase.error != "" && err == nil {
t.Fatalf("returned no error, expected %s", testCase.error)
} else if testCase.error == "" && err != nil {
t.Fatalf("expected no error, returned %v", err)
} else if err != nil && !regexp.MustCompile(testCase.error).MatchString(err.Error()) {
t.Fatal(err)
}

if !reflect.DeepEqual(uri, testCase.uri) {
t.Fatal(uri)
}
if !reflect.DeepEqual(ingress, testCase.ingress) {
t.Fatal(ingress)
}
})
}
}