Skip to content

Commit

Permalink
adds support for tls and bindata for web app assets
Browse files Browse the repository at this point in the history
  • Loading branch information
danmux committed May 8, 2018
1 parent 6cd6675 commit d1142b9
Show file tree
Hide file tree
Showing 35 changed files with 3,358 additions and 86 deletions.
36 changes: 30 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,17 @@ A workflow engine, well suited to long running business process execution, for e
* Customer onboarding.


Quickstart
----------
start two host processes:
Quick Start
-----------
Download or build from scratch `floe` executable.

1. floe -tags=linux,go,couch -admin=123456 -host=h1 -bind=127.0.0.1:8080
Start two host processes:

2. floe -tags=linux,go,couch -admin=123456 -host=h2 -bind=127.0.0.1:8090
1. floe -tags=linux,go,couch -admin=123456 -host_name=h1 -pub_bind=127.0.0.1:8080

2. floe -tags=linux,go,couch -admin=123456 -host_name=h2 -pub_bind=127.0.0.1:8090

These commands default to reading in a `default.yml`

web
---
Expand Down Expand Up @@ -104,4 +107,25 @@ The most common type of node - executes a command, e.g. runs a mke command or a
`ignore-fail` - Only ever send the good event, even if the node failed. Can't be used in conjunction with UseStatus.
`cmd` - Use this if you are running an executable that only depends on the binary
`shell` - Use this if you are running something that requires the shell, e.g. bash scripts.
`args` - An array of command line arguments - for simple arguments these can be included space delimited in the `cmd` or `shell` lines, if there are quote enclosed arguments then use this args array.
`args` - An array of command line arguments - for simple arguments these can be included space delimited in the `cmd` or `shell` lines, if there are quote enclosed arguments then use this args array.


Development
-----------
The web assets are shipped in the binary as bindata so if you change the web stuff then run `go generate ./server` to regen the `bindata.go`

During dev you can use the

TLS Testing
-----------

Generate a self signed cert and key and add them on the command line

openssl req \
-x509 \
-nodes \
-newkey rsa:2048 \
-keyout server.key \
-out server.crt \
-days 3650 \
-subj "/C=GB/ST=London/L=London/O=Global Security/OU=IT Department/CN=*"
11 changes: 10 additions & 1 deletion cmd/floe/integ_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,16 @@ flows:
failed := make(chan error)
addrChan := make(chan string, 1)
go func() {
err := start("hi1", "master", "%tmp/floe", addr, adminToken, in, addrChan)
c := srvConf{
Root: "%tmp/floe",
HostName: "hi1",
AdminToken: adminToken,
Tags: "master",
WebDev: true,
}
c.PubBind = addr

err := start(c, in, addrChan)
if err != nil {
failed <- err
}
Expand Down
48 changes: 35 additions & 13 deletions cmd/floe/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,47 @@ import (
)

func main() {
root := flag.String("root", "~/.flow", "the root folder for configs and workspaces")
in := flag.String("in", "config.yml", "the host config yaml")
host := flag.String("host", "h1", "a short host name to use in id creation and routing")
bind := flag.String("bind", ":8080", "what to bind the server to")
adminToken := flag.String("admin", "", "admin token")
tags := flag.String("tags", "master", "host tags")
c := srvConf{}
flag.StringVar(&c.Root, "root", "~/.flow", "the root folder for configs and workspaces")
flag.StringVar(&c.ConfFile, "conf", "config.yml", "the host config yaml")
flag.StringVar(&c.HostName, "host_name", "h1", "a short host name to use in id creation and routing")
flag.StringVar(&c.AdminToken, "admin", "", "admin token to share in a cluster to confirm it's a p2p call")
flag.StringVar(&c.Tags, "tags", "master", "host tags")

flag.StringVar(&c.PubBind, "pub_bind", ":443", "what to bind the public server to")
flag.StringVar(&c.PubCert, "pub_cert", "", "public certificate path")
flag.StringVar(&c.PubKey, "pub_key", "", "key path for the public endpoint")

flag.StringVar(&c.PrvBind, "prv_bind", "", "what to bind the private server to")
flag.StringVar(&c.PrvCert, "prv_cert", "", "private certificate path")
flag.StringVar(&c.PrvKey, "prv_key", "", "key path for the private endpoint")

flag.BoolVar(&c.WebDev, "dev", false, "set to true to use local webapp folder during development")

flag.Parse()

cfg, err := ioutil.ReadFile(*in)
cfg, err := ioutil.ReadFile(c.ConfFile)
if err != nil {
log.Error(err)
os.Exit(1)
}

log.Debug(start(*host, *tags, *root, *bind, *adminToken, cfg, nil))
log.Debug(start(c, cfg, nil))
}

type srvConf struct {
server.Conf

Root string
ConfFile string
HostName string
AdminToken string
Tags string

WebDev bool
}

func start(host, tags, root, bind, adminToken string, conf []byte, addr chan string) error {
func start(sc srvConf, conf []byte, addr chan string) error {

c, err := config.ParseYAML(conf)
if err != nil {
Expand All @@ -46,7 +68,7 @@ func start(host, tags, root, bind, adminToken string, conf []byte, addr chan str
case "", "memory":
s = store.NewMemStore()
case "local":
s, err = store.NewLocalStore(filepath.Join(root, "store"))
s, err = store.NewLocalStore(filepath.Join(sc.Root, "store"))
if err != nil {
return err
}
Expand All @@ -56,9 +78,9 @@ func start(host, tags, root, bind, adminToken string, conf []byte, addr chan str
// TODO - implement other stores e.g. s3

q := &event.Queue{}
hub := hub.New(host, tags, filepath.Join(root, "spaces"), adminToken, c, s, q)
server.AdminToken = adminToken
hub := hub.New(sc.HostName, sc.Tags, filepath.Join(sc.Root, "spaces"), sc.AdminToken, c, s, q)
server.AdminToken = sc.AdminToken

server.LaunchWeb(bind, c.Common.BaseURL, hub, q, addr)
server.LaunchWeb(sc.Conf, c.Common.BaseURL, hub, q, addr, sc.WebDev)
return nil
}
1 change: 1 addition & 0 deletions dev/docker/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*_rsa*
30 changes: 30 additions & 0 deletions dev/docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
FROM ubuntu:trusty

# gcc for cgo
RUN apt-get update && apt-get install -y --no-install-recommends \
g++ \
gcc \
libc6-dev \
make \
pkg-config \
curl \
git \
openssh-client \
openssh-server \
&& rm -rf /var/lib/apt/lists/*

ENV GOLANG_VERSION 1.10.2

# Install Go
RUN set -eux; \
mkdir -p /usr/local/go; \
curl --insecure https://dl.google.com/go/go${GOLANG_VERSION}.linux-amd64.tar.gz | tar xvzf - -C /usr/local/go --strip-components=1

# add the deployment private key
COPY deploy_rsa /root/.ssh/id_rsa
RUN echo " IdentityFile ~/.ssh/id_rsa" >> /etc/ssh/ssh_config

# Set environment variables.
ENV PATH /usr/local/go/bin:$PATH

WORKDIR /root
7 changes: 7 additions & 0 deletions dev/docker/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from the docker folder - generate your key pair

```
ssh-keygen -N "" -t rsa -b 4096 -C "build@floe.it" -f deploy_rsa
```

add the public key deploy_rsa.pub to the github repo deploy key
7 changes: 7 additions & 0 deletions dev/env.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
set -ex

result=${PWD}
export GOPATH=$result
export PATH=$PATH:$result/bin

export
814 changes: 814 additions & 0 deletions server/bindata.go

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions server/generate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package server

//go:generate go-bindata-assetfs -pkg=server -prefix=../ ../webapp/...
123 changes: 82 additions & 41 deletions server/middleware.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package server

import (
"compress/gzip"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"runtime/debug"
Expand Down Expand Up @@ -81,78 +83,78 @@ type handler struct {
}

// the boolean return is set true if the caller should produce its own response
func authRequest(rw http.ResponseWriter, r *http.Request) *session{
func authRequest(rw http.ResponseWriter, r *http.Request) *session {
var code int
var sesh *session

tok := r.Header.Get("X-Floe-Auth")
if tok == "" {
log.Debug("checking cookie")
c, err := r.Cookie(cookieName)
if err != nil {
log.Warning("cookie problem", err)
} else {
tok = c.Value
}
}

if tok == "" {
code = rUnauth
jsonResp(rw, code, wrapper{Message: "missing session"})
return nil
tok := r.Header.Get("X-Floe-Auth")
if tok == "" {
log.Debug("checking cookie")
c, err := r.Cookie(cookieName)
if err != nil {
log.Warning("cookie problem", err)
} else {
tok = c.Value
}
}

log.Debug("checking token", tok, AdminToken)
if tok == "" {
code = rUnauth
jsonResp(rw, code, wrapper{Message: "missing session"})
return nil
}

// default to this agent for testing admin token
if tok == AdminToken {
log.Debug("found admin token", tok)
sesh = &session{
token: tok,
lastActive: time.Now(),
user: "Admin",
}
log.Debug("checking token", tok, AdminToken)

// default to this agent for testing admin token
if tok == AdminToken {
log.Debug("found admin token", tok)
sesh = &session{
token: tok,
lastActive: time.Now(),
user: "Admin",
}
}

if sesh == nil {
sesh = goodToken(tok)
if sesh == nil {
sesh = goodToken(tok)
if sesh == nil {
code = rUnauth
jsonResp(rw, code, wrapper{Message: "invalid session"})
return nil
}
code = rUnauth
jsonResp(rw, code, wrapper{Message: "invalid session"})
return nil
}
}

// refresh cookie
setCookie(rw, tok)

// refresh cookie
setCookie(rw, tok)

return sesh
}

func (h handler) mw(f contextFunc, auth bool) func(rw http.ResponseWriter, r *http.Request, ps httprouter.Params) {
return func(rw http.ResponseWriter, r *http.Request, ps httprouter.Params) {
fn := func(rw http.ResponseWriter, r *http.Request, ps httprouter.Params) {

var code int
start := time.Now()
log.Debugf("req: %s %s", r.Method, r.URL.String())
defer func() {
log.Debugf("rsp: %v %s %d %s", time.Since(start), r.Method, code, r.URL.String())
}()

cors(rw, r)

// handler nil is the options catch all so the cors response above is all we need
if f == nil {
code = rOK
jsonResp(rw, code, "ok")
return
}

// authenticate session is needed
// authenticate session is needed
var sesh *session
if auth {
sesh = authRequest(rw, r)
if sesh == nil{
sesh = authRequest(rw, r)
if sesh == nil {
return
}
}
Expand Down Expand Up @@ -180,6 +182,45 @@ func (h handler) mw(f contextFunc, auth bool) func(rw http.ResponseWriter, r *ht

jsonResp(rw, code, reply)
}

return zipper(fn)
}

type gzipResponseWriter struct {
io.Writer
http.ResponseWriter
}

func (w gzipResponseWriter) Write(b []byte) (int, error) {
return w.Writer.Write(b)
}

func zipper(fn func(rw http.ResponseWriter, r *http.Request, ps httprouter.Params)) func(rw http.ResponseWriter, r *http.Request, ps httprouter.Params) {
return func(rw http.ResponseWriter, r *http.Request, ps httprouter.Params) {
if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
fn(rw, r, ps)
return
}
rw.Header().Set("Vary", "Accept-Encoding")
rw.Header().Set("Content-Encoding", "gzip")
gz := gzip.NewWriter(rw)
defer gz.Close()
gzr := gzipResponseWriter{Writer: gz, ResponseWriter: rw}
fn(gzr, r, ps)
}
}

func serveFiles(r *httprouter.Router, path string, root http.FileSystem) {
if len(path) < 10 || path[len(path)-10:] != "/*filepath" {
panic("path must end with /*filepath in path '" + path + "'")
}

fileServer := http.FileServer(root)

r.GET(path, zipper(func(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
req.URL.Path = ps.ByName("filepath")
fileServer.ServeHTTP(w, req)
}))
}

// setupTriggers goes through all the known trigger types to set up the associated routes
Expand Down
Loading

0 comments on commit d1142b9

Please sign in to comment.