Skip to content

Commit

Permalink
[master] Add core functionalities
Browse files Browse the repository at this point in the history
  • Loading branch information
4thel00z committed Oct 29, 2020
1 parent 58faa37 commit 48b1dc8
Show file tree
Hide file tree
Showing 16 changed files with 1,174 additions and 0 deletions.
54 changes: 54 additions & 0 deletions pkg/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# `/pkg`

Library code that's ok to use by external applications (e.g., `/pkg/mypubliclib`). Other projects will import these libraries expecting them to work, so think twice before you put something here :-) Note that the `internal` directory is a better way to ensure your private packages are not importable because it's enforced by Go. The `/pkg` directory is still a good way to explicitly communicate that the code in that directory is safe for use by others. The [`I'll take pkg over internal`](https://travisjeffery.com/b/2019/11/i-ll-take-pkg-over-internal/) blog post by Travis Jeffery provides a good overview of the `pkg` and `internal` directories and when it might make sense to use them.

It's also a way to group Go code in one place when your root directory contains lots of non-Go components and directories making it easier to run various Go tools (as mentioned in these talks: [`Best Practices for Industrial Programming`](https://www.youtube.com/watch?v=PTE4VJIdHPg) from GopherCon EU 2018, [GopherCon 2018: Kat Zien - How Do You Structure Your Go Apps](https://www.youtube.com/watch?v=oL6JBUk6tj0) and [GoLab 2018 - Massimiliano Pippi - Project layout patterns in Go](https://www.youtube.com/watch?v=3gQa1LWwuzk)).

Note that this is not a universally accepted pattern and for every popular repo that uses it you can find 10 that don't. It's up to you to decide if you want to use this pattern or not. Regardless of whether or not it's a good pattern more people will know what you mean than not. It is a bit confusing for new Go devs, but it's a pretty simple confusion to resolve and that's one of the goals for this project layout repo.

Ok not to use it if your app project is really small and where an extra level of nesting doesn't add much value (unless you really want to). Think about it when it's getting big enough and your root directory gets pretty busy (especially if you have a lot of non-Go app components).

Examples:

* https://github.com/prometheus/prometheus/tree/master/pkg
* https://github.com/jaegertracing/jaeger/tree/master/pkg
* https://github.com/istio/istio/tree/master/pkg
* https://github.com/GoogleContainerTools/kaniko
* https://github.com/google/gvisor/tree/master/pkg
* https://github.com/google/syzkaller/tree/master/pkg
* https://github.com/perkeep/perkeep/tree/master/pkg
* https://github.com/minio/minio/tree/master/pkg
* https://github.com/heptio/ark/tree/master/pkg
* https://github.com/argoproj/argo/tree/master/pkg
* https://github.com/heptio/sonobuoy/tree/master/pkg
* https://github.com/helm/helm/tree/master/pkg
* https://github.com/kubernetes/kubernetes/tree/master/pkg
* https://github.com/kubernetes/kops/tree/master/pkg
* https://github.com/moby/moby/tree/master/pkg
* https://github.com/grafana/grafana/tree/master/pkg
* https://github.com/influxdata/influxdb/tree/master/pkg
* https://github.com/cockroachdb/cockroach/tree/master/pkg
* https://github.com/derekparker/delve/tree/master/pkg
* https://github.com/etcd-io/etcd/tree/master/pkg
* https://github.com/oklog/oklog/tree/master/pkg
* https://github.com/flynn/flynn/tree/master/pkg
* https://github.com/jesseduffield/lazygit/tree/master/pkg
* https://github.com/gopasspw/gopass/tree/master/pkg
* https://github.com/sosedoff/pgweb/tree/master/pkg
* https://github.com/GoogleContainerTools/skaffold/tree/master/pkg
* https://github.com/knative/serving/tree/master/pkg
* https://github.com/grafana/loki/tree/master/pkg
* https://github.com/bloomberg/goldpinger/tree/master/pkg
* https://github.com/crossplaneio/crossplane/tree/master/pkg
* https://github.com/Ne0nd0g/merlin/tree/master/pkg
* https://github.com/jenkins-x/jx/tree/master/pkg
* https://github.com/DataDog/datadog-agent/tree/master/pkg
* https://github.com/dapr/dapr/tree/master/pkg
* https://github.com/cortexproject/cortex/tree/master/pkg
* https://github.com/dexidp/dex/tree/master/pkg
* https://github.com/pusher/oauth2_proxy/tree/master/pkg
* https://github.com/pdfcpu/pdfcpu/tree/master/pkg
* https://github.com/weaveworks/kured/pkg
* https://github.com/weaveworks/footloose/pkg
* https://github.com/weaveworks/ignite/pkg
* https://github.com/tmrts/boilr/tree/master/pkg
99 changes: 99 additions & 0 deletions pkg/libwebhook/app.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package libwebhook

import (
"fmt"
"github.com/davecgh/go-spew/spew"
"github.com/monzo/typhon"
"log"
"strings"
)

var (
routes = map[string]Route{}
)

type App struct {
Addr string `json:"addr"`
Config Config `json:"config"`
Modules map[string]Module `json:"modules"`
Router *typhon.Router
Debug bool
Verbose bool
}

func NewApp(addr string, config Config, verbose, debug bool, modules ...Module) App {

m := map[string]Module{}
for _, module := range modules {
m[module.Namespace()] = module
}

app := App{
Addr: addr,
Config: config,
Modules: m,
Debug: debug,
Verbose: verbose,
}

app.Router = &typhon.Router{}

for _, module := range modules {
for _, route := range module.Routes() {
app.Router.Register(strings.ToUpper(route.Method), module.LongPath(route), route.Service(&app))
}
}

return app
}

func (app App) Routes() map[string]Route {
if len(routes) > 0 {
return routes
}

addr := app.Addr

for _, module := range app.Modules {
version := module.Version()
namespace := module.Namespace()

for _, route := range module.Routes() {
route.CurlExample = strings.ReplaceAll(route.CurlExample, "<addr>", addr)
route.CurlExample = strings.ReplaceAll(route.CurlExample, "<version>", version)
route.CurlExample = strings.ReplaceAll(route.CurlExample, "<namespace>", namespace)
route.CurlExample = strings.ReplaceAll(route.CurlExample, "<path>", route.Path)
if app.Debug {
// Add module wise injections of f.e. the <auth> tag
}

routes[module.LongPath(route)] = route
}
}
return routes
}
func (app App) PrintRoutes(addr string) {
routes := app.Routes()
if len(routes) > 0 {
log.Println("👠\tThe routes 🛣️ are:")
}
for path, route := range routes {
log.Printf("\thttp://%v%s with method: %s", addr, path, route.Method)
log.Printf("\tQuery this endpoint like this:\n\t\t%s", route.CurlExample)

}
}

func (app App) Register(module Module) {
for path, route := range module.Routes() {
fmt.Println("HANDLER", path, route.Service)
app.Router.Register(strings.ToUpper(route.Method), path, route.Service(&app))
}

}

func (app App) PrintConfig() {
fmt.Print("======================\t✈️\tConfig incoming\t✈️\t======================\n")
spew.Dump(app.Config)
fmt.Print("======================\t✈️ Config landed! ✈️\t======================\n")
}
30 changes: 30 additions & 0 deletions pkg/libwebhook/filters/auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package filters

import (
"github.com/monzo/typhon"
"webhookd/pkg/libwebhook"
)

func Auth(app libwebhook.App) typhon.Filter {
return func(req typhon.Request, svc typhon.Service) typhon.Response {
pattern := app.Router.Pattern(req)
routes := app.Routes()
route, ok := routes[pattern]
if !ok {
return svc(req)
}

if route.TokenValidator == nil {
return svc(req)
}

validator := route.TokenValidator

if validator == nil {
return svc(req)
}

return validator(req, svc)

}
}
41 changes: 41 additions & 0 deletions pkg/libwebhook/filters/validation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package filters

import (
"context"
"fmt"
"github.com/monzo/typhon"
"webhookd/pkg/libwebhook"
)

const (
ValidationResult = "validation_result"
)

func Validation(app libwebhook.App) typhon.Filter {
return func(req typhon.Request, svc typhon.Service) typhon.Response {
pattern := app.Router.Pattern(req)
routes := app.Routes()
route, ok := routes[pattern]
if !ok {
return svc(req)
}

if route.Validator == nil {
return svc(req)
}

val, err := (*route.Validator)(req)

if err != nil {
msg := err.Error()
return req.Response(libwebhook.GenericResponse{
Message: fmt.Sprintf("[%s] %s validation error", pattern, route.Method),
Error: &msg,
})
}

req.Context = context.WithValue(req.Context, ValidationResult, val)
return svc(req)

}
}
29 changes: 29 additions & 0 deletions pkg/libwebhook/jwt/models.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package jwt

import (
"github.com/dgrijalva/jwt-go"
"github.com/monzo/typhon"
)

type Jwks struct {
Keys []JSONWebKeys `json:"keys"`
}

type JSONWebKeys struct {
Kty string `json:"kty"`
Kid string `json:"kid"`
Use string `json:"use"`
N string `json:"n"`
E string `json:"e"`
X5c []string `json:"x5c"`
}

type CustomClaims struct {
Scope string `json:"scope"`
jwt.StandardClaims
}

type TokenExtractor func(r typhon.Request) (string, error)
type ErrorHandler func(r typhon.Request, errMsg string) typhon.Response
type EmptyTokenHandler typhon.Service
type ScopeChecker func(tokenString string) bool
Loading

0 comments on commit 48b1dc8

Please sign in to comment.