Created
April 15, 2020 16:18
-
-
Save aryszka/16f51b59736fee27c823d49d9d5ced65 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package routeswithfallback | |
import ( | |
"errors" | |
log "github.com/sirupsen/logrus" | |
"github.com/zalando/skipper/eskip" | |
"github.com/zalando/skipper/routing" | |
) | |
type client struct { | |
source, fallback routing.DataClient | |
injectHealthCheck, initialized, fallbackLoaded bool | |
routes []*eskip.Route | |
} | |
var ( | |
errLoadFailure = errors.New("route load failure") | |
errTriggerFullLoad = errors.New("trigger full load from data client") | |
) | |
func newClient(source, fallback routing.DataClient, injectHealthCheck bool) routing.DataClient { | |
return &client{ | |
source: source, | |
fallback: fallback, | |
injectHealthCheck: injectHealthCheck, | |
} | |
} | |
func New(source, fallback routing.DataClient) routing.DataClient { | |
return newClient(source, fallback, false) | |
} | |
func WithHealthCheck(source, fallback routing.DataClient) routing.DataClient { | |
return newClient(source, fallback, true) | |
} | |
func injectHealthCheck(r []*eskip.Route) []*eskip.Route { | |
return append(r, &eskip.Route{ | |
Id: "__healthcheck", | |
Predicates: []*eskip.Predicate{{ | |
Name: "Path", | |
Args: []interface{}{"/health"}, | |
}}, | |
Filters: []*eskip.Filter{{ | |
Name: "status", | |
Args: []interface{}{"200"}, | |
}}, | |
BackendType: eskip.ShuntBackend, | |
}) | |
} | |
func mapRoutes(r []*eskip.Route) map[string]*eskip.Route { | |
m := make(map[string]*eskip.Route) | |
for _, ri := range r { | |
m[ri.Id] = ri | |
} | |
return m | |
} | |
func flattenRoutes(m map[string]*eskip.Route) []*eskip.Route { | |
var r []*eskip.Route | |
for _, ri := range m { | |
r = append(r, ri) | |
} | |
return r | |
} | |
func upsertRoutes(r, u []*eskip.Route) []*eskip.Route { | |
mr := mapRoutes(r) | |
for _, ui := range u { | |
mr[ui.Id] = ui | |
} | |
return flattenRoutes(mr) | |
} | |
func deleteRoutes(r []*eskip.Route, deleteIDs []string) []*eskip.Route { | |
mr := mapRoutes(r) | |
for _, id := range deleteIDs { | |
delete(mr, id) | |
} | |
return flattenRoutes(mr) | |
} | |
func (c *client) LoadAll() ([]*eskip.Route, error) { | |
r, err := c.source.LoadAll() | |
if err == nil { | |
if c.injectHealthCheck { | |
r = injectHealthCheck(r) | |
} | |
c.routes = r | |
c.initialized = true | |
return c.routes, nil | |
} | |
log.Errorf("Failed to load routes: %v.", err) | |
if c.initialized || c.fallbackLoaded { | |
return c.routes, nil | |
} | |
r, err = c.fallback.LoadAll() | |
if err != nil { | |
log.Errorf("Failed to load fallback routes: %v.", err) | |
return nil, errLoadFailure | |
} | |
if c.injectHealthCheck { | |
r = injectHealthCheck(r) | |
} | |
c.routes = r | |
c.fallbackLoaded = true | |
return c.routes, nil | |
} | |
func (c *client) LoadUpdate() ([]*eskip.Route, []string, error) { | |
if !c.initialized { | |
return nil, nil, errTriggerFullLoad | |
} | |
r, d, err := c.source.LoadUpdate() | |
if err != nil { | |
log.Errorf("Failed to update routes: %v.", err) | |
return nil, nil, errTriggerFullLoad | |
} | |
c.routes = upsertRoutes(c.routes, r) | |
c.routes = deleteRoutes(c.routes, d) | |
return r, d, nil | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment