Compare commits

..

2 Commits

Author SHA1 Message Date
409fa5e642 Alteration for version passing
Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2019-12-31 12:27:21 +00:00
03fe48db99 Pass version from main
Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2019-12-31 12:22:21 +00:00
13 changed files with 122 additions and 443 deletions

1
.github/CODEOWNERS vendored
View File

@ -1 +0,0 @@
@alexellis

View File

@ -1,41 +0,0 @@
<!--- Provide a general summary of the issue in the Title above -->
## Expected Behaviour
<!--- If you're describing a bug, tell us what should happen -->
<!--- If you're suggesting a change/improvement, tell us how it should work -->
## Current Behaviour
<!--- If describing a bug, tell us what happens instead of the expected behavior -->
<!--- If suggesting a change/improvement, explain the difference from current behavior -->
## Possible Solution
<!--- Not obligatory, but suggest a fix/reason for the bug, -->
<!--- or ideas how to implement the addition or change -->
## Steps to Reproduce (for bugs)
<!--- Provide a link to a live example, or an unambiguous set of steps to -->
<!--- reproduce this bug. Include code to reproduce, if relevant -->
1.
2.
3.
4.
## Context
<!--- How has this issue affected you? What are you trying to accomplish? -->
<!--- Providing context helps us come up with a solution that is most useful in the real world -->
## Your Environment
* OS and architecture:
* Versions:
```sh
go version
containerd -version
uname -a
cat /etc/os-release
```

View File

@ -1,40 +0,0 @@
<!--- Provide a general summary of your changes in the Title above -->
## Description
<!--- Describe your changes in detail -->
## Motivation and Context
<!--- Why is this change required? What problem does it solve? -->
<!--- If it fixes an open issue, please link to the issue here. -->
- [ ] I have raised an issue to propose this change **this is required**
## How Has This Been Tested?
<!--- Please describe in detail how you tested your changes. -->
<!--- Include details of your testing environment, and the tests you ran to -->
<!--- see how your change affects other areas of the code, etc. -->
## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
## Checklist:
Commits:
- [ ] I've read the [CONTRIBUTION](https://github.com/openfaas/faas/blob/master/CONTRIBUTING.md) guide
- [ ] My commit message has a body and describe how this was tested and why it is required.
- [ ] I have signed-off my commits with `git commit -s` for the Developer Certificate of Origin (DCO)
Code:
- [ ] My code follows the code style of this project.
- [ ] I have added tests to cover my changes.
Docs:
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.

View File

@ -1,19 +1,9 @@
sudo: required sudo: required
language: go language: go
go: go:
- '1.12' - '1.12'
addons:
apt:
packages:
- runc
script: script:
- make dist - make dist
- make prepare-test
- make test-e2e
deploy: deploy:
provider: releases provider: releases
api_key: api_key:

View File

@ -1,8 +1,6 @@
Version := $(shell git describe --tags --dirty) Version := $(shell git describe --tags --dirty)
GitCommit := $(shell git rev-parse HEAD) GitCommit := $(shell git rev-parse HEAD)
LDFLAGS := "-s -w -X main.Version=$(Version) -X main.GitCommit=$(GitCommit)" LDFLAGS := "-s -w -X main.Version=$(Version) -X main.GitCommit=$(GitCommit)"
CONTAINERD_VER := 1.3.2
FAASC_VER := 0.4.0
.PHONY: all .PHONY: all
all: local all: local
@ -15,34 +13,3 @@ dist:
CGO_ENABLED=0 GOOS=linux go build -ldflags $(LDFLAGS) -a -installsuffix cgo -o bin/faasd CGO_ENABLED=0 GOOS=linux go build -ldflags $(LDFLAGS) -a -installsuffix cgo -o bin/faasd
CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=7 go build -ldflags $(LDFLAGS) -a -installsuffix cgo -o bin/faasd-armhf CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=7 go build -ldflags $(LDFLAGS) -a -installsuffix cgo -o bin/faasd-armhf
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags $(LDFLAGS) -a -installsuffix cgo -o bin/faasd-arm64 CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags $(LDFLAGS) -a -installsuffix cgo -o bin/faasd-arm64
.PHONY: prepare-test
prepare-test:
curl -sLSf https://github.com/containerd/containerd/releases/download/v$(CONTAINERD_VER)/containerd-$(CONTAINERD_VER).linux-amd64.tar.gz > /tmp/containerd.tar.gz && sudo tar -xvf /tmp/containerd.tar.gz -C /usr/local/bin/ --strip-components=1
curl -SLfs https://raw.githubusercontent.com/containerd/containerd/v1.3.2/containerd.service | sudo tee /etc/systemd/system/containerd.service
sudo systemctl daemon-reload && sudo systemctl start containerd
sudo curl -fSLs "https://github.com/genuinetools/netns/releases/download/v0.5.3/netns-linux-amd64" --output "/usr/local/bin/netns" && sudo chmod a+x "/usr/local/bin/netns"
sudo /sbin/sysctl -w net.ipv4.conf.all.forwarding=1
sudo curl -sSLf "https://github.com/alexellis/faas-containerd/releases/download/$(FAASC_VER)/faas-containerd" --output "/usr/local/bin/faas-containerd" && sudo chmod a+x "/usr/local/bin/faas-containerd" || :
sudo cp $(GOPATH)/src/github.com/alexellis/faasd/bin/faasd /usr/local/bin/
cd $(GOPATH)/src/github.com/alexellis/faasd/ && sudo /usr/local/bin/faasd install
sudo systemctl status -l containerd --no-pager
sudo journalctl -u faas-containerd --no-pager
sudo systemctl status -l faas-containerd --no-pager
sudo systemctl status -l faasd --no-pager
curl -sSLf https://cli.openfaas.com | sudo sh
sleep 120 && sudo journalctl -u faasd --no-pager
.PHONY: test-e2e
test-e2e:
sudo cat /run/faasd/secrets/basic-auth-password | /usr/local/bin/faas-cli login --password-stdin
/usr/local/bin/faas-cli store deploy figlet --env write_timeout=1s --env read_timeout=1s
sleep 2
/usr/local/bin/faas-cli list -v
uname | /usr/local/bin/faas-cli invoke figlet
uname | /usr/local/bin/faas-cli invoke figlet --async
sleep 10
/usr/local/bin/faas-cli list -v
/usr/local/bin/faas-cli remove figlet
sleep 3
/usr/local/bin/faas-cli list

View File

@ -30,20 +30,10 @@ Other operations are pending development in the provider.
### Pre-reqs ### Pre-reqs
* Linux * Linux - ideally Ubuntu, which is used for testing
PC / Cloud - any Linux that containerd works on should be fair game, but faasd is tested with Ubuntu 18.04
For Raspberry Pi Raspbian Stretch or newer also works fine
For MacOS users try [multipass.run](https://multipass.run) or [Vagrant](https://www.vagrantup.com/)
For Windows users, install [Git Bash](https://git-scm.com/downloads) along with multipass or vagrant. You can also use WSL1 or WSL2 which provides a Linux environment.
* Installation steps as per [faas-containerd](https://github.com/alexellis/faas-containerd) for building and for development * Installation steps as per [faas-containerd](https://github.com/alexellis/faas-containerd) for building and for development
* [netns](https://github.com/genuinetools/netns/releases) binary in `$PATH` * [netns](https://github.com/genuinetools/netns/releases) binary in `$PATH`
* [containerd v1.3.2](https://github.com/containerd/containerd) * [containerd v1.3.2](https://github.com/containerd/containerd)
* [faas-cli](https://github.com/openfaas/faas-cli) (optional) * [faas-cli](https://github.com/openfaas/faas-cli) (optional)
## Backlog ## Backlog
@ -54,7 +44,6 @@ Pending:
* [ ] Monitor and restart any of the core components at runtime if the container stops * [ ] Monitor and restart any of the core components at runtime if the container stops
* [ ] Bundle/package/automate installation of containerd - [see bootstrap from k3s](https://github.com/rancher/k3s) * [ ] Bundle/package/automate installation of containerd - [see bootstrap from k3s](https://github.com/rancher/k3s)
* [ ] Provide ufw rules / example for blocking access to everything but a reverse proxy to the gateway container * [ ] Provide ufw rules / example for blocking access to everything but a reverse proxy to the gateway container
* [ ] Provide [simple Caddyfile example](https://blog.alexellis.io/https-inlets-local-endpoints/) in the README showing how to expose the faasd proxy on port 80/443 with TLS
Done: Done:
@ -66,13 +55,6 @@ Done:
* [x] Clear / remove containers and tasks with SIGTERM / SIGINT * [x] Clear / remove containers and tasks with SIGTERM / SIGINT
* [x] Determine armhf/arm64 containers to run for gateway * [x] Determine armhf/arm64 containers to run for gateway
* [x] Configure `basic_auth` to protect the OpenFaaS gateway and faas-containerd HTTP API * [x] Configure `basic_auth` to protect the OpenFaaS gateway and faas-containerd HTTP API
* [x] Setup custom working directory for faasd `/run/faasd/`
## Tutorial: Get started on armhf / Raspberry Pi
You can run this tutorial on your Raspberry Pi, or adapt the steps for a regular Linux VM/VPS host.
* [faasd - lightweight Serverless for your Raspberry Pi](https://blog.alexellis.io/faasd-for-lightweight-serverless/)
## Hacking (build from source) ## Hacking (build from source)
@ -104,24 +86,24 @@ go build
```sh ```sh
# For x86_64 # For x86_64
sudo curl -fSLs "https://github.com/alexellis/faasd/releases/download/0.4.2/faasd" \ sudo curl -fSLs "https://github.com/alexellis/faasd/releases/download/0.3.1/faasd" \
-o "/usr/local/bin/faasd" \ -o "/usr/local/bin/faasd" \
&& sudo chmod a+x "/usr/local/bin/faasd" && sudo chmod a+x "/usr/local/bin/faasd"
# armhf # armhf
sudo curl -fSLs "https://github.com/alexellis/faasd/releases/download/0.4.2/faasd-armhf" \ sudo curl -fSLs "https://github.com/alexellis/faasd/releases/download/0.3.1/faasd-armhf" \
-o "/usr/local/bin/faasd" \ -o "/usr/local/bin/faasd" \
&& sudo chmod a+x "/usr/local/bin/faasd" && sudo chmod a+x "/usr/local/bin/faasd"
# arm64 # arm64
sudo curl -fSLs "https://github.com/alexellis/faasd/releases/download/0.4.2/faasd-arm64" \ sudo curl -fSLs "https://github.com/alexellis/faasd/releases/download/0.3.1/faasd-arm64" \
-o "/usr/local/bin/faasd" \ -o "/usr/local/bin/faasd" \
&& sudo chmod a+x "/usr/local/bin/faasd" && sudo chmod a+x "/usr/local/bin/faasd"
``` ```
### At run-time ### At run-time
Look in `hosts` in the current working folder or in `/run/faasd/` to get the IP for the gateway or Prometheus Look in `hosts` in the current working folder to get the IP for the gateway or Prometheus
```sh ```sh
127.0.0.1 localhost 127.0.0.1 localhost
@ -143,12 +125,12 @@ Since faas-containerd uses containerd heavily it is not running as a container,
* basic-auth * basic-auth
You will then need to get the basic-auth password, it is written to `/run/faasd/secrets/basic-auth-password` if you followed the above instructions. You will then need to get the basic-auth password, it is written to `$GOPATH/src/github.com/alexellis/faasd/basic-auth-password` if you followed the above instructions.
The default Basic Auth username is `admin`, which is written to `/run/faasd/secrets/basic-auth-user`, if you wish to use a non-standard user then create this file and add your username (no newlines or other characters) The default Basic Auth username is `admin`, which is written to `$GOPATH/src/github.com/alexellis/faasd/basic-auth-user`, if you wish to use a non-standard user then create this file and add your username (no newlines or other characters)
#### Installation with systemd #### Installation with systemd
* `faasd install` - install faasd and containerd with systemd, this must be run from `$GOPATH/src/github.com/alexellis/faasd` * `faasd install` - install faasd and containerd with systemd, run in `$GOPATH/src/github.com/alexellis/faasd`
* `journalctl -u faasd` - faasd systemd logs * `journalctl -u faasd` - faasd systemd logs
* `journalctl -u faas-containerd` - faas-containerd systemd logs * `journalctl -u faas-containerd` - faas-containerd systemd logs

View File

@ -2,12 +2,10 @@ package cmd
import ( import (
"fmt" "fmt"
"io"
"os" "os"
"path" "path"
systemd "github.com/alexellis/faasd/pkg/systemd" systemd "github.com/alexellis/faasd/pkg/systemd"
"github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -18,31 +16,8 @@ var installCmd = &cobra.Command{
RunE: runInstall, RunE: runInstall,
} }
const faasdwd = "/run/faasd"
const faasContainerdwd = "/run/faas-containerd"
func runInstall(_ *cobra.Command, _ []string) error { func runInstall(_ *cobra.Command, _ []string) error {
if err := ensureWorkingDir(path.Join(faasdwd, "secrets")); err != nil {
return err
}
if err := ensureWorkingDir(faasContainerdwd); err != nil {
return err
}
if basicAuthErr := makeBasicAuthFiles(path.Join(faasdwd, "secrets")); basicAuthErr != nil {
return errors.Wrap(basicAuthErr, "cannot create basic-auth-* files")
}
if err := cp("prometheus.yml", faasdwd); err != nil {
return err
}
if err := cp("resolv.conf", faasdwd); err != nil {
return err
}
err := binExists("/usr/local/bin/", "faas-containerd") err := binExists("/usr/local/bin/", "faas-containerd")
if err != nil { if err != nil {
return err return err
@ -58,15 +33,12 @@ func runInstall(_ *cobra.Command, _ []string) error {
return err return err
} }
err = systemd.InstallUnit("faas-containerd", map[string]string{ err = systemd.InstallUnit("faas-containerd")
"Cwd": faasContainerdwd,
"SecretMountPath": path.Join(faasdwd, "secrets")})
if err != nil { if err != nil {
return err return err
} }
err = systemd.InstallUnit("faasd", map[string]string{"Cwd": faasdwd}) err = systemd.InstallUnit("faasd")
if err != nil { if err != nil {
return err return err
} }
@ -106,33 +78,3 @@ func binExists(folder, name string) error {
} }
return nil return nil
} }
func ensureWorkingDir(folder string) error {
if _, err := os.Stat(folder); err != nil {
err = os.MkdirAll(folder, 0600)
if err != nil {
return err
}
}
return nil
}
func cp(source, destFolder string) error {
file, err := os.Open(source)
if err != nil {
return err
}
defer file.Close()
out, err := os.Create(path.Join(destFolder, source))
if err != nil {
return err
}
defer out.Close()
_, err = io.Copy(out, file)
return err
}

View File

@ -2,18 +2,16 @@ package cmd
import ( import (
"fmt" "fmt"
"github.com/pkg/errors"
"io/ioutil" "io/ioutil"
"log" "log"
"os" "os"
"os/signal" "os/signal"
"path" "path"
"strings"
"sync" "sync"
"syscall" "syscall"
"time" "time"
"github.com/pkg/errors"
"github.com/alexellis/faasd/pkg" "github.com/alexellis/faasd/pkg"
"github.com/alexellis/k3sup/pkg/env" "github.com/alexellis/k3sup/pkg/env"
"github.com/sethvargo/go-password/password" "github.com/sethvargo/go-password/password"
@ -26,8 +24,6 @@ var upCmd = &cobra.Command{
RunE: runUp, RunE: runUp,
} }
const secretMountDir = "/run/secrets"
func runUp(_ *cobra.Command, _ []string) error { func runUp(_ *cobra.Command, _ []string) error {
clientArch, clientOS := env.GetClientArch() clientArch, clientOS := env.GetClientArch()
@ -49,8 +45,9 @@ func runUp(_ *cobra.Command, _ []string) error {
clientSuffix = "-arm64" clientSuffix = "-arm64"
} }
if basicAuthErr := makeBasicAuthFiles(path.Join(path.Join(faasdwd, "secrets"))); basicAuthErr != nil { authFileErr := errors.Wrap(makeBasicAuthFiles(), "Could not create gateway auth files")
return errors.Wrap(basicAuthErr, "cannot create basic-auth-* files") if authFileErr != nil {
return authFileErr
} }
services := makeServiceDefinitions(clientSuffix) services := makeServiceDefinitions(clientSuffix)
@ -77,7 +74,6 @@ func runUp(_ *cobra.Command, _ []string) error {
shutdownTimeout := time.Second * 1 shutdownTimeout := time.Second * 1
timeout := time.Second * 60 timeout := time.Second * 60
proxyDoneCh := make(chan bool)
wg := sync.WaitGroup{} wg := sync.WaitGroup{}
wg.Add(1) wg.Add(1)
@ -93,47 +89,23 @@ func runUp(_ *cobra.Command, _ []string) error {
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
} }
// Close proxy
proxyDoneCh <- true
time.AfterFunc(shutdownTimeout, func() { time.AfterFunc(shutdownTimeout, func() {
wg.Done() wg.Done()
}) })
}() }()
gatewayURLChan := make(chan string, 1)
proxyPort := 8080
proxy := pkg.NewProxy(proxyPort, timeout)
go proxy.Start(gatewayURLChan, proxyDoneCh)
go func() { go func() {
wd, _ := os.Getwd() wd, _ := os.Getwd()
proxy := pkg.NewProxy(path.Join(wd, "hosts"), timeout)
time.Sleep(3 * time.Second) proxy.Start()
fileData, fileErr := ioutil.ReadFile(path.Join(wd, "hosts"))
if fileErr != nil {
log.Println(fileErr)
return
}
host := ""
lines := strings.Split(string(fileData), "\n")
for _, line := range lines {
if strings.Index(line, "gateway") > -1 {
host = line[:strings.Index(line, "\t")]
}
}
log.Printf("[up] Sending %s to proxy\n", host)
gatewayURLChan <- host + ":8080"
close(gatewayURLChan)
}() }()
wg.Wait() wg.Wait()
return nil return nil
} }
func makeBasicAuthFiles(wd string) error { func makeBasicAuthFiles() error {
wd, _ := os.Getwd()
pwdFile := wd + "/basic-auth-password" pwdFile := wd + "/basic-auth-password"
authPassword, err := password.Generate(63, 10, 0, false, true) authPassword, err := password.Generate(63, 10, 0, false, true)
@ -158,10 +130,10 @@ func makeBasicAuthFiles(wd string) error {
func makeFile(filePath, fileContents string) error { func makeFile(filePath, fileContents string) error {
_, err := os.Stat(filePath) _, err := os.Stat(filePath)
if err == nil { if err == nil {
log.Printf("File exists: %q\n", filePath) log.Printf("File exists: %q, Using data from this file",filePath)
return nil return nil
} else if os.IsNotExist(err) { } else if os.IsNotExist(err) {
log.Printf("Writing to: %q\n", filePath) log.Printf("Writing file: %q", filePath)
return ioutil.WriteFile(filePath, []byte(fileContents), 0644) return ioutil.WriteFile(filePath, []byte(fileContents), 0644)
} else { } else {
return err return err
@ -171,10 +143,12 @@ func makeFile(filePath, fileContents string) error {
func makeServiceDefinitions(archSuffix string) []pkg.Service { func makeServiceDefinitions(archSuffix string) []pkg.Service {
wd, _ := os.Getwd() wd, _ := os.Getwd()
secretMountDir := "/run/secrets/"
return []pkg.Service{ return []pkg.Service{
pkg.Service{ pkg.Service{
Name: "basic-auth-plugin", Name: "basic-auth-plugin",
Image: "docker.io/openfaas/basic-auth-plugin:0.18.10" + archSuffix, Image: "docker.io/openfaas/basic-auth-plugin:0.18.10" + archSuffix,
Env: []string{ Env: []string{
"port=8080", "port=8080",
"secret_mount_path=" + secretMountDir, "secret_mount_path=" + secretMountDir,
@ -183,16 +157,16 @@ func makeServiceDefinitions(archSuffix string) []pkg.Service {
}, },
Mounts: []pkg.Mount{ Mounts: []pkg.Mount{
pkg.Mount{ pkg.Mount{
Src: path.Join(path.Join(wd, "secrets"), "basic-auth-password"), Src:path.Join(wd, "/basic-auth-password"),
Dest: path.Join(secretMountDir, "basic-auth-password"), Dest:secretMountDir + "basic-auth-password",
}, },
pkg.Mount{ pkg.Mount{
Src: path.Join(path.Join(wd, "secrets"), "basic-auth-user"), Src: path.Join(wd, "/basic-auth-user"),
Dest: path.Join(secretMountDir, "basic-auth-user"), Dest: secretMountDir + "basic-auth-user",
}, },
}, },
Caps: []string{"CAP_NET_RAW"}, Caps: []string{"CAP_NET_RAW"},
Args: nil, Args: nil,
}, },
pkg.Service{ pkg.Service{
Name: "nats", Name: "nats",
@ -228,18 +202,18 @@ func makeServiceDefinitions(archSuffix string) []pkg.Service {
"auth_proxy_pass_body=false", "auth_proxy_pass_body=false",
"secret_mount_path=" + secretMountDir, "secret_mount_path=" + secretMountDir,
}, },
Image: "docker.io/openfaas/gateway:0.18.8" + archSuffix, Image: "docker.io/openfaas/gateway:0.18.8" + archSuffix,
Mounts: []pkg.Mount{ Mounts: []pkg.Mount{
pkg.Mount{ pkg.Mount{
Src: path.Join(path.Join(wd, "secrets"), "basic-auth-password"), Src:path.Join(wd, "/basic-auth-password"),
Dest: path.Join(secretMountDir, "basic-auth-password"), Dest:secretMountDir + "basic-auth-password",
}, },
pkg.Mount{ pkg.Mount{
Src: path.Join(path.Join(wd, "secrets"), "basic-auth-user"), Src: path.Join(wd, "/basic-auth-user"),
Dest: path.Join(secretMountDir, "basic-auth-user"), Dest: secretMountDir + "basic-auth-user",
}, },
}, },
Caps: []string{"CAP_NET_RAW"}, Caps: []string{"CAP_NET_RAW"},
}, },
pkg.Service{ pkg.Service{
Name: "queue-worker", Name: "queue-worker",
@ -254,18 +228,18 @@ func makeServiceDefinitions(archSuffix string) []pkg.Service {
"basic_auth=true", "basic_auth=true",
"secret_mount_path=" + secretMountDir, "secret_mount_path=" + secretMountDir,
}, },
Image: "docker.io/openfaas/queue-worker:0.9.0", Image: "docker.io/openfaas/queue-worker:0.9.0",
Mounts: []pkg.Mount{ Mounts: []pkg.Mount{
pkg.Mount{ pkg.Mount{
Src: path.Join(path.Join(wd, "secrets"), "basic-auth-password"), Src:path.Join(wd, "/basic-auth-password"),
Dest: path.Join(secretMountDir, "basic-auth-password"), Dest:secretMountDir + "basic-auth-password",
}, },
pkg.Mount{ pkg.Mount{
Src: path.Join(path.Join(wd, "secrets"), "basic-auth-user"), Src: path.Join(wd, "/basic-auth-user"),
Dest: path.Join(secretMountDir, "basic-auth-user"), Dest: secretMountDir + "basic-auth-user",
}, },
}, },
Caps: []string{"CAP_NET_RAW"}, Caps: []string{"CAP_NET_RAW"},
}, },
} }
} }

View File

@ -3,11 +3,10 @@ Description=faasd-containerd
[Service] [Service]
MemoryLimit=500M MemoryLimit=500M
Environment="secret_mount_path={{.SecretMountPath}}"
ExecStart=/usr/local/bin/faas-containerd ExecStart=/usr/local/bin/faas-containerd
Restart=on-failure Restart=on-failure
RestartSec=10s RestartSec=10s
WorkingDirectory={{.Cwd}} WorkingDirectory=/usr/local/bin/
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target

View File

@ -1,122 +1,102 @@
package pkg package pkg
import ( import (
"context"
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"log" "log"
"net/http" "net/http"
"strings"
"time" "time"
) )
func NewProxy(port int, timeout time.Duration) *Proxy { func NewProxy(hosts string, timeout time.Duration) *Proxy {
return &Proxy{ return &Proxy{
Port: port, Hosts: hosts,
Timeout: timeout, Timeout: timeout,
} }
} }
type Proxy struct { type Proxy struct {
Hosts string
Timeout time.Duration Timeout time.Duration
Port int
} }
func (p *Proxy) Start(gatewayChan chan string, done chan bool) error { func (p *Proxy) Start() error {
tcp := p.Port tcp := 8080
http.DefaultClient.CheckRedirect = func(req *http.Request, via []*http.Request) error { http.DefaultClient.CheckRedirect = func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse return http.ErrUseLastResponse
} }
ps := proxyState{
Host: "", time.Sleep(3 * time.Second)
log.Printf("Starting faasd proxy on %d\n", tcp)
data := struct{ host string }{
host: "",
} }
ps.Host = <-gatewayChan fileData, fileErr := ioutil.ReadFile(p.Hosts)
if fileErr != nil {
return fileErr
}
log.Printf("Starting faasd proxy on %d\n", tcp) lines := strings.Split(string(fileData), "\n")
for _, line := range lines {
fmt.Printf("Gateway: %s\n", ps.Host) if strings.Index(line, "gateway") > -1 {
data.host = line[:strings.Index(line, "\t")]
}
}
fmt.Printf("Gateway: %s\n", data.host)
s := &http.Server{ s := &http.Server{
Addr: fmt.Sprintf(":%d", tcp), Addr: fmt.Sprintf(":%d", tcp),
ReadTimeout: p.Timeout, ReadTimeout: p.Timeout,
WriteTimeout: p.Timeout, WriteTimeout: p.Timeout,
MaxHeaderBytes: 1 << 20, // Max header of 1MB MaxHeaderBytes: 1 << 20, // Max header of 1MB
Handler: http.HandlerFunc(makeProxy(&ps)), Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
query := ""
if len(r.URL.RawQuery) > 0 {
query = "?" + r.URL.RawQuery
}
upstream := fmt.Sprintf("http://%s:8080%s%s", data.host, r.URL.Path, query)
fmt.Printf("[faasd] proxy: %s\n", upstream)
if r.Body != nil {
defer r.Body.Close()
}
wrapper := ioutil.NopCloser(r.Body)
upReq, upErr := http.NewRequest(r.Method, upstream, wrapper)
if upErr != nil {
log.Println(upErr)
http.Error(w, upErr.Error(), http.StatusInternalServerError)
return
}
upRes, upResErr := http.DefaultClient.Do(upReq)
if upResErr != nil {
log.Println(upResErr)
http.Error(w, upResErr.Error(), http.StatusInternalServerError)
return
}
for k, v := range upRes.Header {
w.Header().Set(k, v[0])
}
w.WriteHeader(upRes.StatusCode)
io.Copy(w, upRes.Body)
}),
} }
go func() { return s.ListenAndServe()
log.Printf("[proxy] Begin listen on %d\n", p.Port)
if err := s.ListenAndServe(); err != http.ErrServerClosed {
log.Printf("Error ListenAndServe: %v", err)
}
}()
log.Println("[proxy] Wait for done")
<-done
log.Println("[proxy] Done received")
if err := s.Shutdown(context.Background()); err != nil {
log.Printf("[proxy] Error in Shutdown: %v", err)
}
return nil
}
// copyHeaders clones the header values from the source into the destination.
func copyHeaders(destination http.Header, source *http.Header) {
for k, v := range *source {
vClone := make([]string, len(v))
copy(vClone, v)
destination[k] = vClone
}
}
type proxyState struct {
Host string
}
func makeProxy(ps *proxyState) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
query := ""
if len(r.URL.RawQuery) > 0 {
query = "?" + r.URL.RawQuery
}
upstream := fmt.Sprintf("http://%s%s%s", ps.Host, r.URL.Path, query)
fmt.Printf("[faasd] proxy: %s\n", upstream)
if r.Body != nil {
defer r.Body.Close()
}
wrapper := ioutil.NopCloser(r.Body)
upReq, upErr := http.NewRequest(r.Method, upstream, wrapper)
copyHeaders(upReq.Header, &r.Header)
if upErr != nil {
log.Println(upErr)
http.Error(w, upErr.Error(), http.StatusInternalServerError)
return
}
upRes, upResErr := http.DefaultClient.Do(upReq)
if upResErr != nil {
log.Println(upResErr)
http.Error(w, upResErr.Error(), http.StatusInternalServerError)
return
}
copyHeaders(w.Header(), &upRes.Header)
w.WriteHeader(upRes.StatusCode)
io.Copy(w, upRes.Body)
}
} }

View File

@ -1,73 +0,0 @@
package pkg
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"net/http/httptest"
"net/url"
"sync"
"testing"
"time"
)
func Test_Proxy_ToPrivateServer(t *testing.T) {
wantBodyText := "OK"
wantBody := []byte(wantBodyText)
upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Body != nil {
defer r.Body.Close()
}
w.WriteHeader(http.StatusOK)
w.Write(wantBody)
}))
defer upstream.Close()
port := 8080
proxy := NewProxy(port, time.Second*1)
gwChan := make(chan string, 1)
doneCh := make(chan bool)
go proxy.Start(gwChan, doneCh)
u, _ := url.Parse(upstream.URL)
log.Println("Host", u.Host)
wg := sync.WaitGroup{}
wg.Add(1)
go func() {
gwChan <- u.Host
wg.Done()
}()
wg.Wait()
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://127.0.0.1:%d", port), nil)
if err != nil {
t.Fatal(err)
}
for i := 1; i < 11; i++ {
res, err := http.DefaultClient.Do(req)
if err != nil {
t.Logf("Try %d, gave error: %s", i, err)
time.Sleep(time.Millisecond * 100)
} else {
resBody, _ := ioutil.ReadAll(res.Body)
if string(resBody) != string(wantBody) {
t.Errorf("want %s, but got %s in body", string(wantBody), string(resBody))
}
break
}
}
go func() {
doneCh <- true
}()
}

View File

@ -153,7 +153,7 @@ func (s *Supervisor) Start(svcs []Service) error {
) )
if containerCreateErr != nil { if containerCreateErr != nil {
log.Printf("Error creating container %s\n", containerCreateErr) log.Println(containerCreateErr)
return containerCreateErr return containerCreateErr
} }
@ -161,7 +161,7 @@ func (s *Supervisor) Start(svcs []Service) error {
task, err := newContainer.NewTask(ctx, cio.NewCreator(cio.WithStdio)) task, err := newContainer.NewTask(ctx, cio.NewCreator(cio.WithStdio))
if err != nil { if err != nil {
log.Printf("Error creating task: %s\n", err) log.Println(err)
return err return err
} }
@ -175,21 +175,19 @@ func (s *Supervisor) Start(svcs []Service) error {
writeErr := ioutil.WriteFile("hosts", hosts, 0644) writeErr := ioutil.WriteFile("hosts", hosts, 0644)
if writeErr != nil { if writeErr != nil {
log.Printf("Error writing file %s %s\n", "hosts", writeErr) log.Println("Error writing hosts file")
} }
// os.Chown("hosts", 101, 101) // os.Chown("hosts", 101, 101)
_, err = task.Wait(ctx) exitStatusC, err := task.Wait(ctx)
if err != nil { if err != nil {
log.Printf("Wait err: %s\n", err) log.Println(err)
return err return err
} }
log.Println("Exited: ", exitStatusC)
log.Printf("Task: %s\tContainer: %s\n", task.ID(), newContainer.ID()) if err := task.Start(ctx); err != nil {
// log.Println("Exited: ", exitStatusC) log.Println("Task err: ", err)
if err = task.Start(ctx); err != nil {
log.Printf("Task err: %s\n", err)
return err return err
} }
} }

View File

@ -64,11 +64,7 @@ func DaemonReload() error {
return nil return nil
} }
func InstallUnit(name string, tokens map[string]string) error { func InstallUnit(name string) error {
if len(tokens["Cwd"]) == 0 {
return fmt.Errorf("key Cwd expected in tokens parameter")
}
tmplName := "./hack/" + name + ".service" tmplName := "./hack/" + name + ".service"
tmpl, err := template.ParseFiles(tmplName) tmpl, err := template.ParseFiles(tmplName)
@ -76,9 +72,15 @@ func InstallUnit(name string, tokens map[string]string) error {
return fmt.Errorf("error loading template %s, error %s", tmplName, err) return fmt.Errorf("error loading template %s, error %s", tmplName, err)
} }
wd, _ := os.Getwd()
var tpl bytes.Buffer var tpl bytes.Buffer
userData := struct {
Cwd string
}{
Cwd: wd,
}
err = tmpl.Execute(&tpl, tokens) err = tmpl.Execute(&tpl, userData)
if err != nil { if err != nil {
return err return err
} }