Skip to content
This repository was archived by the owner on Jun 26, 2024. It is now read-only.

Commit b54332d

Browse files
committed
initial commit for bridge
1 parent 9a4fa36 commit b54332d

30 files changed

+2478
-0
lines changed

.github/workflows/issues.yml

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
name: 'good first issue'
2+
on: [issues]
3+
4+
jobs:
5+
labels:
6+
runs-on: ubuntu-latest
7+
steps:
8+
- uses: Code-Hex/[email protected]
9+
with:
10+
github-token: ${{ secrets.GITHUB_TOKEN }}
11+
issue-labels: '["good first issue"]'

.github/workflows/test.yml

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
on:
2+
push:
3+
branches:
4+
- "main"
5+
tags:
6+
- "v*.*.*"
7+
pull_request:
8+
9+
jobs:
10+
test:
11+
name: Test
12+
runs-on: ubuntu-latest
13+
steps:
14+
- name: Check out code into the Go module directory
15+
uses: actions/checkout@v2
16+
- uses: actions/setup-go@v2
17+
with:
18+
go-version: '^1.18'
19+
- name: Declare some variables
20+
id: vars
21+
run: |
22+
echo "::set-output name=coverage_txt::${RUNNER_TEMP}/coverage.txt"
23+
- name: Test Coverage (pkg)
24+
run: go test ./... -race -coverprofile=${{ steps.vars.outputs.coverage_txt }}
25+
- name: Upload coverage
26+
uses: codecov/codecov-action@v2
27+
with:
28+
files: ${{ steps.vars.outputs.coverage_txt }}

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.DS_Store
2+
bridge

bridge.go

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package bridge
2+
3+
import (
4+
"net/http"
5+
"time"
6+
7+
"github.com/basemachina/bridge/bridgehttp"
8+
"github.com/basemachina/bridge/internal/auth"
9+
"github.com/basemachina/bridge/internal/ctxtime"
10+
"github.com/basemachina/bridge/internal/proxy"
11+
"github.com/go-logr/logr"
12+
)
13+
14+
const (
15+
OKPath = "/ok"
16+
OKMessage = "bridge is ready"
17+
ProxyPath = "/htproxy"
18+
)
19+
20+
// Env stores configuration settings extract from enviromental variables
21+
// The practice getting from environmental variables comes from https://12factor.net.
22+
type Env struct {
23+
// Port is port to listen HTTP server. Default is 8080.
24+
Port string `envconfig:"PORT" default:"8080" description:"bridge を HTTP としてサーブするために利用します。"`
25+
26+
// LogLevel is INFO or DEBUG. Default is "INFO".
27+
LogLevel string `envconfig:"LOG_LEVEL" default:"INFO"`
28+
29+
// APIURL is an url of basemachina.
30+
APIURL string `envconfig:"BASEMACHINA_API_URL" default:"https://api.basemachina.com"`
31+
32+
// FetchInterval is interval to fetch
33+
FetchInterval time.Duration `envconfig:"FETCH_INTERVAL" default:"1h" description:"認可処理に利用する公開鍵を更新する間隔です。"`
34+
35+
// FetchTimeout is timeout to fetch
36+
FetchTimeout time.Duration `envconfig:"FETCH_TIMEOUT" default:"10s" description:"認可処理に利用する公開鍵を更新するタイムアウトです。"`
37+
38+
// TenantID is ID of tenant
39+
TenantID string `envconfig:"TENANT_ID" default:"" description:"認可処理に利用します。設定されると指定されたテナント ID 以外からのリクエストを拒否します。"`
40+
}
41+
42+
// HTTPHandlerConfig is a config to setup bridge http handler.
43+
type HTTPHandlerConfig struct {
44+
Logger logr.Logger
45+
PublicKeyGetter auth.PublicKeyGetter
46+
TenantID string
47+
Middlewares []bridgehttp.Middleware
48+
}
49+
50+
// NewHTTPHandler is a handler for handling any requests.
51+
func NewHTTPHandler(c *HTTPHandlerConfig) http.Handler {
52+
mux := http.NewServeMux()
53+
mux.HandleFunc(OKPath, func(w http.ResponseWriter, r *http.Request) {
54+
if r.Method != http.MethodGet {
55+
w.WriteHeader(http.StatusMethodNotAllowed)
56+
return
57+
}
58+
w.Write([]byte(OKMessage))
59+
})
60+
middlewares := append(c.Middlewares,
61+
ctxtime.Middleware(),
62+
auth.Middleware(&auth.MiddlewareConfig{
63+
TenantID: c.TenantID,
64+
Logger: c.Logger.WithName("auth"),
65+
PublicKeyGetter: c.PublicKeyGetter,
66+
}),
67+
)
68+
mux.Handle(ProxyPath, bridgehttp.UseMiddlewares(
69+
proxy.NewProxy(c.Logger.WithName("proxy")),
70+
middlewares...,
71+
))
72+
return mux
73+
}

bridge_test.go

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package bridge
2+
3+
import (
4+
"net/http"
5+
"net/http/httptest"
6+
"testing"
7+
8+
"github.com/basemachina/bridge/internal/testlogr"
9+
)
10+
11+
func TestNewHTTPHandler(t *testing.T) {
12+
t.Run("ok path", func(t *testing.T) {
13+
h := NewHTTPHandler(&HTTPHandlerConfig{
14+
Logger: testlogr.Logger,
15+
})
16+
req := httptest.NewRequest("GET", OKPath, nil)
17+
rec := httptest.NewRecorder()
18+
h.ServeHTTP(rec, req)
19+
20+
if rec.Code != http.StatusOK {
21+
t.Fatalf("want status code %d but got %d", http.StatusOK, rec.Code)
22+
}
23+
if got := rec.Body.String(); OKMessage != got {
24+
t.Fatalf("want message %q but got %q", OKMessage, got)
25+
}
26+
})
27+
}

bridgehttp/http.go

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package bridgehttp
2+
3+
import "net/http"
4+
5+
// Middleware is middleware for http.Handler
6+
type Middleware func(http.Handler) http.Handler
7+
8+
// UseMiddlewares uses some middlewares when handled specified h (http.Handler)
9+
func UseMiddlewares(h http.Handler, middlewares ...Middleware) http.Handler {
10+
for i := len(middlewares) - 1; i >= 0; i-- {
11+
h = middlewares[i](h)
12+
}
13+
return h
14+
}

bridgehttp/http_test.go

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package bridgehttp
2+
3+
import (
4+
"bytes"
5+
"net/http"
6+
"net/http/httptest"
7+
"testing"
8+
)
9+
10+
func TestUseMiddlewares(t *testing.T) {
11+
var buf bytes.Buffer
12+
baseHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
13+
buf.WriteString("handler")
14+
})
15+
alpha := []string{"A", "B", "C"}
16+
middlewares := make([]Middleware, 0, len(alpha))
17+
for _, v := range alpha {
18+
v := v
19+
middlewares = append(middlewares, func(next http.Handler) http.Handler {
20+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
21+
buf.WriteString(v)
22+
next.ServeHTTP(w, r)
23+
buf.WriteString(v)
24+
})
25+
})
26+
}
27+
28+
h := UseMiddlewares(baseHandler, middlewares...)
29+
req := httptest.NewRequest("GET", "/", nil)
30+
rec := httptest.NewRecorder()
31+
h.ServeHTTP(rec, req)
32+
33+
want := "ABChandlerCBA"
34+
got := buf.String()
35+
if want != got {
36+
t.Errorf("want %q, but got %q", want, got)
37+
}
38+
}

cmd/bridge/container.go

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package main
2+
3+
import (
4+
"net/http"
5+
6+
"github.com/basemachina/bridge"
7+
"github.com/go-logr/logr"
8+
)
9+
10+
type Container struct {
11+
HTTPServer *http.Server
12+
FetchWorker *FetchWorker
13+
Logger logr.Logger
14+
}
15+
16+
func BridgeContainerProvider() (*Container, func(), error) {
17+
env, err := ReadFromEnv()
18+
if err != nil {
19+
return nil, nil, err
20+
}
21+
logger, cleanup, err := NewLogger(env)
22+
if err != nil {
23+
return nil, nil, err
24+
}
25+
fetchWorker, cleanup2, err := NewFetchWorker(env, logger)
26+
if err != nil {
27+
return nil, nil, err
28+
}
29+
httpHandlerConfig := &bridge.HTTPHandlerConfig{
30+
Logger: logger,
31+
PublicKeyGetter: fetchWorker,
32+
TenantID: env.TenantID,
33+
}
34+
handler := bridge.NewHTTPHandler(httpHandlerConfig)
35+
server, cleanup3, err := NewHTTPServer(env, handler)
36+
if err != nil {
37+
cleanup2()
38+
cleanup()
39+
return nil, nil, err
40+
}
41+
container := &Container{
42+
HTTPServer: server,
43+
FetchWorker: fetchWorker,
44+
Logger: logger,
45+
}
46+
return container, func() {
47+
cleanup3()
48+
cleanup2()
49+
cleanup()
50+
}, nil
51+
}

cmd/bridge/env.go

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/basemachina/bridge"
7+
"github.com/kelseyhightower/envconfig"
8+
)
9+
10+
// ReadFromEnv reads configuration from environmental variables
11+
// defined by Env struct.
12+
func ReadFromEnv() (*bridge.Env, error) {
13+
var env bridge.Env
14+
if err := envconfig.Process("", &env); err != nil {
15+
return nil, fmt.Errorf("failed to process envconfig: %w", err)
16+
}
17+
return &env, nil
18+
}

cmd/bridge/env_test.go

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package main
2+
3+
import (
4+
"os"
5+
"testing"
6+
)
7+
8+
func TestReadFromEnv(t *testing.T) {
9+
reset := setenvs(t, map[string]string{
10+
"PORT": "10000",
11+
})
12+
t.Cleanup(reset)
13+
14+
env, err := ReadFromEnv()
15+
if err != nil {
16+
t.Fatal(err)
17+
}
18+
19+
const wantPort = "10000"
20+
if got := env.Port; got != wantPort {
21+
t.Fatalf("got %v, want %v", got, wantPort)
22+
}
23+
}
24+
25+
func setenv(t *testing.T, k, v string) func() {
26+
t.Helper()
27+
28+
prev := os.Getenv(k)
29+
if err := os.Setenv(k, v); err != nil {
30+
t.Fatal(err)
31+
}
32+
return func() {
33+
if prev == "" {
34+
os.Unsetenv(k)
35+
} else {
36+
if err := os.Setenv(k, prev); err != nil {
37+
t.Fatal(err)
38+
}
39+
}
40+
}
41+
}
42+
43+
func setenvs(t *testing.T, kv map[string]string) func() {
44+
t.Helper()
45+
46+
resetFuncs := make([]func(), 0, len(kv))
47+
for k, v := range kv {
48+
resetFunc := setenv(t, k, v)
49+
resetFuncs = append(resetFuncs, resetFunc)
50+
}
51+
return func() {
52+
for _, resetFunc := range resetFuncs {
53+
resetFunc()
54+
}
55+
}
56+
}

0 commit comments

Comments
 (0)