Skip to content

Instantly share code, notes, and snippets.

@aryszka
Created April 15, 2020 16:18
Show Gist options
  • Save aryszka/16f51b59736fee27c823d49d9d5ced65 to your computer and use it in GitHub Desktop.
Save aryszka/16f51b59736fee27c823d49d9d5ced65 to your computer and use it in GitHub Desktop.
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