Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement a new static mode #76

Merged
merged 1 commit into from
Aug 13, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ See also: [Classic Watchdog](https://github.com/openfaas/faas/tree/master/watchd

History/context: the original watchdog supported mode the Serializing fork mode only and Afterburn was available for testing via a pull request.

When the of-watchdog is complete this version will support four modes as listed below. We may consolidate or remove some of these modes before going to 1.0 so please consider modes 2-4 experimental.
When the of-watchdog is complete this version will support five modes as listed below. We may consolidate or remove some of these modes before going to 1.0 so please consider modes 2-4 experimental.

### 1. HTTP (mode=http)

Expand Down Expand Up @@ -143,6 +143,10 @@ Vastly accelerated processing speed but requires a client library for each langu

* Exec timeout: not supported.

### 5. Static (mode=static)

This mode starts an HTTP file server for serving static content found at the directory specified by `static_path`.

## Configuration

Environmental variables:
Expand All @@ -152,6 +156,7 @@ Environmental variables:
| Option | Implemented | Usage |
|------------------------|--------------|-------------------------------|
| `function_process` | Yes | Process to execute a server in `http` mode or to be executed for each request in the other modes. For non `http` mode the process must accept input via STDIN and print output via STDOUT. Alias: `fprocess` |
| `static_path` | Yes | Absolute or relative path to the directory that will be served if `mode="static"` |
| `read_timeout` | Yes | HTTP timeout for reading the payload from the client caller (in seconds) |
| `write_timeout` | Yes | HTTP timeout for writing a response body from your function (in seconds) |
| `exec_timeout` | Yes | Exec timeout for process exec'd for each incoming request (in seconds). Disabled if set to 0. |
Expand Down
11 changes: 9 additions & 2 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type WatchdogConfig struct {
OperationalMode int
SuppressLock bool
UpstreamURL string
StaticPath string

// BufferHTTPBody buffers the HTTP body in memory
// to prevent transfer type of chunked encoding
Expand Down Expand Up @@ -48,7 +49,7 @@ func (w WatchdogConfig) Process() (string, []string) {
}

// New create config based upon environmental variables.
func New(env []string) (WatchdogConfig, error) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We were not using the error return value so I thought about removing it and cleanup the code a bit.

func New(env []string) WatchdogConfig {

envMap := mapEnv(env)

Expand All @@ -74,11 +75,17 @@ func New(env []string) (WatchdogConfig, error) {
contentType = val
}

staticPath := "/home/app/public"
if val, exists := envMap["static_path"]; exists {
staticPath = val
}

config := WatchdogConfig{
TCPPort: getInt(envMap, "port", 8080),
HTTPReadTimeout: getDuration(envMap, "read_timeout", time.Second*10),
HTTPWriteTimeout: getDuration(envMap, "write_timeout", time.Second*10),
FunctionProcess: functionProcess,
StaticPath: staticPath,
InjectCGIHeaders: true,
ExecTimeout: getDuration(envMap, "exec_timeout", time.Second*10),
OperationalMode: ModeStreaming,
Expand All @@ -94,7 +101,7 @@ func New(env []string) (WatchdogConfig, error) {
config.OperationalMode = WatchdogModeConst(val)
}

return config, nil
return config
}

func mapEnv(env []string) map[string]string {
Expand Down
9 changes: 8 additions & 1 deletion config/config_modes.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@ const (
// ModeAfterBurn for performance tuning
ModeAfterBurn = 3

//ModeHTTP for routing requests over HTTP
// ModeHTTP for routing requests over HTTP
ModeHTTP = 4

// ModeStatic for serving static content
ModeStatic = 5
)

// WatchdogModeConst as a const int
Expand All @@ -25,6 +28,8 @@ func WatchdogModeConst(mode string) int {
return ModeSerializing
case "http":
return ModeHTTP
case "static":
return ModeStatic
default:
return 0
}
Expand All @@ -41,6 +46,8 @@ func WatchdogMode(mode int) string {
return "serializing"
case ModeHTTP:
return "http"
case ModeStatic:
return "static"
default:
return "unknown"
}
Expand Down
72 changes: 24 additions & 48 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,22 @@ import (
)

func TestNew(t *testing.T) {
defaults, err := New([]string{})
if err != nil {
t.Errorf("Expected no errors")
}
defaults := New([]string{})
if defaults.TCPPort != 8080 {
t.Errorf("Want TCPPort: 8080, got: %d", defaults.TCPPort)
}
}

func Test_OperationalMode_Default(t *testing.T) {
defaults, err := New([]string{})
if err != nil {
t.Errorf("Expected no errors")
}
defaults := New([]string{})
if defaults.OperationalMode != ModeStreaming {
t.Errorf("Want %s. got: %s", WatchdogMode(ModeStreaming), WatchdogMode(defaults.OperationalMode))
}
}
func Test_BufferHttpModeDefaultsToFalse(t *testing.T) {
env := []string{}

actual, err := New(env)
if err != nil {
t.Errorf("Expected no errors")
}
actual := New(env)
want := false
if actual.BufferHTTPBody != want {
t.Errorf("Want %v. got: %v", want, actual.BufferHTTPBody)
Expand All @@ -42,10 +33,7 @@ func Test_BufferHttpMode_CanBeSetToTrue(t *testing.T) {
"buffer_http=true",
}

actual, err := New(env)
if err != nil {
t.Errorf("Expected no errors")
}
actual := New(env)
want := true
if actual.BufferHTTPBody != want {
t.Errorf("Want %v. got: %v", want, actual.BufferHTTPBody)
Expand All @@ -57,23 +45,29 @@ func Test_OperationalMode_AfterBurn(t *testing.T) {
"mode=afterburn",
}

actual, err := New(env)
if err != nil {
t.Errorf("Expected no errors")
}
actual := New(env)

if actual.OperationalMode != ModeAfterBurn {
t.Errorf("Want %s. got: %s", WatchdogMode(ModeAfterBurn), WatchdogMode(actual.OperationalMode))
}
}

func Test_OperationalMode_Static(t *testing.T) {
env := []string{
"mode=static",
}

actual := New(env)

if actual.OperationalMode != ModeStatic {
t.Errorf("Want %s. got: %s", WatchdogMode(ModeStatic), WatchdogMode(actual.OperationalMode))
}
}

func Test_ContentType_Default(t *testing.T) {
env := []string{}

actual, err := New(env)
if err != nil {
t.Errorf("Expected no errors")
}
actual := New(env)

if actual.ContentType != "application/octet-stream" {
t.Errorf("Default (ContentType) Want %s. got: %s", actual.ContentType, "octet-stream")
Expand All @@ -85,10 +79,7 @@ func Test_ContentType_Override(t *testing.T) {
"content_type=application/json",
}

actual, err := New(env)
if err != nil {
t.Errorf("Expected no errors")
}
actual := New(env)

if actual.ContentType != "application/json" {
t.Errorf("(ContentType) Want %s. got: %s", actual.ContentType, "application/json")
Expand All @@ -100,10 +91,7 @@ func Test_FunctionProcessLegacyName(t *testing.T) {
"fprocess=env",
}

actual, err := New(env)
if err != nil {
t.Errorf("Expected no errors")
}
actual := New(env)

if actual.FunctionProcess != "env" {
t.Errorf("Want %s. got: %s", "env", actual.FunctionProcess)
Expand All @@ -115,10 +103,7 @@ func Test_FunctionProcessAlternativeName(t *testing.T) {
"function_process=env",
}

actual, err := New(env)
if err != nil {
t.Errorf("Expected no errors")
}
actual := New(env)

if actual.FunctionProcess != "env" {
t.Errorf("Want %s. got: %s", "env", actual.FunctionProcess)
Expand Down Expand Up @@ -161,10 +146,7 @@ func Test_FunctionProcess_Arguments(t *testing.T) {

for _, testCase := range cases {
t.Run(testCase.scenario, func(t *testing.T) {
actual, err := New([]string{testCase.env})
if err != nil {
t.Errorf("Expected no errors")
}
actual := New([]string{testCase.env})

process, args := actual.Process()
if process != testCase.wantProcess {
Expand Down Expand Up @@ -192,10 +174,7 @@ func Test_PortOverride(t *testing.T) {
"port=8081",
}

actual, err := New(env)
if err != nil {
t.Errorf("Expected no errors")
}
actual := New(env)

if actual.TCPPort != 8081 {
t.Errorf("Want %d. got: %d", 8081, actual.TCPPort)
Expand Down Expand Up @@ -241,10 +220,7 @@ func Test_Timeouts(t *testing.T) {
}

for _, testCase := range cases {
actual, err := New(testCase.env)
if err != nil {
t.Errorf("(%s) Expected no errors", testCase.name)
}
actual := New(testCase.env)
if testCase.readTimeout != actual.HTTPReadTimeout {
t.Errorf("(%s) HTTPReadTimeout want: %s, got: %s", testCase.name, actual.HTTPReadTimeout, testCase.readTimeout)
}
Expand Down
19 changes: 13 additions & 6 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,9 @@ var (
func main() {
atomic.StoreInt32(&acceptingConnections, 0)

watchdogConfig, configErr := config.New(os.Environ())
if configErr != nil {
fmt.Fprintf(os.Stderr, configErr.Error())
os.Exit(-1)
}
watchdogConfig := config.New(os.Environ())

if len(watchdogConfig.FunctionProcess) == 0 {
if len(watchdogConfig.FunctionProcess) == 0 && watchdogConfig.OperationalMode != config.ModeStatic {
fmt.Fprintf(os.Stderr, "Provide a \"function_process\" or \"fprocess\" environmental variable for your function.\n")
os.Exit(-1)
}
Expand Down Expand Up @@ -150,6 +146,8 @@ func buildRequestHandler(watchdogConfig config.WatchdogConfig) http.Handler {
case config.ModeHTTP:
requestHandler = makeHTTPRequestHandler(watchdogConfig)
break
case config.ModeStatic:
requestHandler = makeStaticRequestHandler(watchdogConfig)
default:
log.Panicf("unknown watchdog mode: %d", watchdogConfig.OperationalMode)
break
Expand Down Expand Up @@ -338,6 +336,15 @@ func makeHTTPRequestHandler(watchdogConfig config.WatchdogConfig) func(http.Resp
}
}

func makeStaticRequestHandler(watchdogConfig config.WatchdogConfig) http.HandlerFunc {
if watchdogConfig.StaticPath == "" {
log.Fatal(`For mode=static you must specify the "static_path" to serve`)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it make sense to have a default? I don't know.

}

log.Printf("Serving files at %s", watchdogConfig.StaticPath)
return http.FileServer(http.Dir(watchdogConfig.StaticPath)).ServeHTTP
}

func lockFilePresent() bool {
path := filepath.Join(os.TempDir(), ".lock")
if _, err := os.Stat(path); os.IsNotExist(err) {
Expand Down