forked from openshift/origin
-
Notifications
You must be signed in to change notification settings - Fork 1
/
activation.go
130 lines (118 loc) · 5.18 KB
/
activation.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package hostindex
import (
"sort"
routeapi "github.com/openshift/origin/pkg/route/apis/route"
)
// Changed allows a route activation function to record which routes moved from inactive
// to active or vice versa.
type Changed interface {
// Activated should be invoked if the route was previously inactive and is now active.
Activated(route *routeapi.Route)
// Displaced should be invoked if the route was previously active and is now inactive.
Displaced(route *routeapi.Route)
}
// RouteActivationFunc attempts to add routes from inactive into the active set. Any routes that are not
// valid must be returned in the displaced list. All routes that are added into active should be passed to
// Changed.Activated() and any route in active that ends up in the displaced list should be passed to
// Changed.Displaced(). It is the caller's responsiblity to invoke Activated or Displaced for inactive routes.
// Routes must be provided to the function in lowest to highest order and the ordering should be preserved
// in activated.
type RouteActivationFunc func(changed Changed, active []*routeapi.Route, inactive ...*routeapi.Route) (activated, displaced []*routeapi.Route)
// OldestFirst identifies all unique host+port combinations in active and inactive, ordered by oldest
// first. Duplicates are returned in displaced in order. It assumes all provided route have the same
// spec.host value.
func OldestFirst(changed Changed, active []*routeapi.Route, inactive ...*routeapi.Route) (updated, displaced []*routeapi.Route) {
if len(inactive) == 0 {
return active, nil
}
// Routes must be unique in the updated list (no duplicate spec.host / spec.path combination).
return zipperMerge(active, inactive, changed, func(route *routeapi.Route) bool { return true })
}
// SameNamespace identifies all unique host+port combinations in active and inactive that are from
// the same namespace as the oldest route (creation timestamp and then uid ordering). Duplicates and
// non matching routes are returned in displaced and are unordered. It assumes all provided routes
// have the same spec.host value.
func SameNamespace(changed Changed, active []*routeapi.Route, inactive ...*routeapi.Route) (updated, displaced []*routeapi.Route) {
if len(inactive) == 0 {
return active, nil
}
ns := inactive[0].Namespace
// if:
// * there was no previous claimant
// * we're newer than the oldest claimant
// * or we're in the same namespace as the oldest claimant
// add the new routes to the existing active set
if len(active) == 0 || routeapi.RouteLessThan(active[0], inactive[0]) {
if len(active) > 0 {
ns = active[0].Namespace
}
updated = active
for _, route := range inactive {
updated, displaced = appendRoute(changed, updated, displaced, route, ns == route.Namespace, false)
}
sort.Slice(updated, func(i, j int) bool { return routeapi.RouteLessThan(updated[i], updated[j]) })
return updated, displaced
}
// We're claiming the host and in a different namespace than the previous holder so we must recalculate
// everything. We do that with a zipper merge of the two sorted lists, appending routes as we go.
// Routes must be unique in the updated list (no duplicate spec.host / spec.path combination).
ns = ""
return zipperMerge(active, inactive, changed, func(route *routeapi.Route) bool {
if len(ns) == 0 {
ns = route.Namespace
return true
}
return ns == route.Namespace
})
}
// zipperMerge assumes both active and inactive are in order and takes the oldest route from either
// list until all items are processed. If fn returns false the item will be skipped.
func zipperMerge(active, inactive []*routeapi.Route, changed Changed, fn func(*routeapi.Route) bool) (updated, displaced []*routeapi.Route) {
i, j := 0, 0
for {
switch {
case j >= len(active):
for ; i < len(inactive); i++ {
updated, displaced = appendRoute(changed, updated, displaced, inactive[i], fn(inactive[i]), false)
}
return updated, displaced
case i >= len(inactive):
for ; j < len(active); j++ {
updated, displaced = appendRoute(changed, updated, displaced, active[j], fn(active[j]), true)
}
return updated, displaced
default:
a, b := inactive[i], active[j]
if routeapi.RouteLessThan(a, b) {
updated, displaced = appendRoute(changed, updated, displaced, a, fn(a), false)
i++
} else {
updated, displaced = appendRoute(changed, updated, displaced, b, fn(b), true)
j++
}
}
}
}
// appendRoute adds the route to the end of the appropriate list if matches is true and no route already exists in the list
// with the same path.
func appendRoute(changed Changed, updated, displaced []*routeapi.Route, route *routeapi.Route, matches bool, isActive bool) ([]*routeapi.Route, []*routeapi.Route) {
if matches && !hasExistingMatch(updated, route) {
if !isActive {
changed.Activated(route)
}
return append(updated, route), displaced
}
if isActive {
changed.Displaced(route)
}
return updated, append(displaced, route)
}
// hasExistingMatch returns true if a route is in exists with the same path.
func hasExistingMatch(exists []*routeapi.Route, route *routeapi.Route) bool {
for _, existing := range exists {
if existing.Spec.Path == route.Spec.Path {
return true
}
}
return false
}