xmidt-org/themis

View on GitHub
routes.go

Summary

Maintainability
A
40 mins
Test Coverage
package main

import (
    "errors"

    "github.com/xmidt-org/themis/key"
    "github.com/xmidt-org/themis/token"
    "github.com/xmidt-org/themis/xhealth"
    "github.com/xmidt-org/themis/xhttp/xhttpserver"
    "github.com/xmidt-org/themis/xhttp/xhttpserver/pprof"
    "github.com/xmidt-org/themis/xmetrics"
    "github.com/xmidt-org/themis/xmetrics/xmetricshttp"

    "github.com/gorilla/mux"
    "github.com/justinas/alice"
    "github.com/prometheus/client_golang/prometheus"
    "go.uber.org/fx"
)

type ServerChainIn struct {
    fx.In

    RequestCount     *prometheus.CounterVec   `name:"server_request_count"`
    RequestDuration  *prometheus.HistogramVec `name:"server_request_duration_ms"`
    RequestsInFlight *prometheus.GaugeVec     `name:"server_requests_in_flight"`
}

func provideServerChainFactory(in ServerChainIn) xhttpserver.ChainFactory {
    return xhttpserver.ChainFactoryFunc(func(name string, o xhttpserver.Options) (alice.Chain, error) {
        var (
            curryLabel = prometheus.Labels{
                ServerLabel: name,
            }

            serverLabellers = xmetricshttp.NewServerLabellers(
                xmetricshttp.CodeLabeller{},
                xmetricshttp.MethodLabeller{},
            )
        )

        requestCount, err := in.RequestCount.CurryWith(curryLabel)
        if err != nil {
            return alice.Chain{}, err
        }

        requestDuration, err := in.RequestDuration.CurryWith(curryLabel)
        if err != nil {
            return alice.Chain{}, err
        }

        requestsInFlight, err := in.RequestsInFlight.CurryWith(curryLabel)
        if err != nil {
            return alice.Chain{}, err
        }

        return alice.New(
            xmetricshttp.HandlerCounter{
                Metric:   xmetrics.LabelledCounterVec{CounterVec: requestCount},
                Labeller: serverLabellers,
            }.Then,
            xmetricshttp.HandlerDuration{
                Metric:   xmetrics.LabelledObserverVec{ObserverVec: requestDuration},
                Labeller: serverLabellers,
            }.Then,
            xmetricshttp.HandlerInFlight{
                Metric: xmetrics.LabelledGaugeVec{GaugeVec: requestsInFlight},
            }.Then,
        ), nil
    })
}

type KeyRoutesIn struct {
    fx.In
    Router     *mux.Router `name:"servers.key"`
    Handler    key.Handler
    HandlerJWK key.HandlerJWK
}

func BuildKeyRoutes(in KeyRoutesIn) {
    if in.Router != nil {
        keys := in.Router.PathPrefix("/keys/{kid}").Methods("GET").Subrouter()

        keys.Headers("Accept", key.ContentTypePEM).Handler(in.Handler)
        keys.Headers("Accept", key.ContentTypeJWK).Handler(in.HandlerJWK)
        keys.Path("").Handler(in.Handler) // default
        keys.Path("/key.pem").Handler(in.Handler)
        keys.Path("/key.json").Handler(in.HandlerJWK)
    }
}

type IssuerRoutesIn struct {
    fx.In
    Router  *mux.Router `name:"servers.issuer"`
    Handler token.IssueHandler
}

func BuildIssuerRoutes(in IssuerRoutesIn) {
    if in.Router != nil && in.Handler != nil {
        in.Router.Handle("/issue", in.Handler).Methods("GET")
    }
}

type ClaimsRoutesIn struct {
    fx.In
    Router  *mux.Router `name:"servers.claims"`
    Handler token.ClaimsHandler
}

func BuildClaimsRoutes(in ClaimsRoutesIn) {
    if in.Router != nil && in.Handler != nil {
        in.Router.Handle("/claims", in.Handler).Methods("GET")
    }
}

// CheckServerRequirements is an fx.Invoke function that does post-configuration verification
// that we have required servers.  The valid server configurations are:
//
//    Both keys and issuer present.  Claims is optional in this case
//    Neither keys or issuer present.  Claims is required in this case
//
// Any other arrangements results in an error.
func CheckServerRequirements(k KeyRoutesIn, i IssuerRoutesIn, c ClaimsRoutesIn) error {
    if k.Router != nil && i.Router != nil {
        // all good ... no need to check anything else
        return nil
    }

    if k.Router == nil && i.Router == nil {
        if c.Router == nil {
            return errors.New("A claims server is required if no keys or issuer server is configured")
        }

        // Only a claims server is allowed
        return nil
    }

    if k.Router != nil {
        return errors.New("If a keys server is configured, an issuer server must be configured")
    }

    if i.Router != nil {
        return errors.New("If an issuer server is configured, a keys server must be configured")
    }

    return nil
}

type MetricsRoutesIn struct {
    fx.In
    Router  *mux.Router `name:"servers.metrics"`
    Handler xmetricshttp.Handler
}

func BuildMetricsRoutes(in MetricsRoutesIn) {
    if in.Router != nil && in.Handler != nil {
        in.Router.Handle("/metrics", in.Handler).Methods("GET")
    }
}

type HealthRoutesIn struct {
    fx.In
    Router  *mux.Router `name:"servers.health"`
    Handler xhealth.Handler
}

func BuildHealthRoutes(in HealthRoutesIn) {
    if in.Router != nil && in.Handler != nil {
        in.Router.Handle("/health", in.Handler).Methods("GET")
    }
}

type PprofRoutesIn struct {
    fx.In
    Router *mux.Router `name:"servers.pprof"`
}

func BuildPprofRoutes(in PprofRoutesIn) {
    if in.Router != nil {
        pprof.BuildRoutes(in.Router)
    }
}