mirror of
https://github.com/openfaas/faasd.git
synced 2025-06-18 12:06:36 +00:00
Compare commits
14 Commits
Author | SHA1 | Date | |
---|---|---|---|
d64edeb648 | |||
42b9cc6b71 | |||
25c553a87c | |||
8bc39f752e | |||
cbff6fa8f6 | |||
3e29408518 | |||
04f1807d92 | |||
35e017b526 | |||
e54da61283 | |||
84353d0cae | |||
e33a60862d | |||
7b67ff22e6 | |||
19abc9f7b9 | |||
480f566819 |
@ -10,6 +10,7 @@ addons:
|
||||
- runc
|
||||
|
||||
script:
|
||||
- make test
|
||||
- make dist
|
||||
- make prepare-test
|
||||
- make test-e2e
|
||||
|
4
Makefile
4
Makefile
@ -11,6 +11,10 @@ all: local
|
||||
local:
|
||||
CGO_ENABLED=0 GOOS=linux go build -o bin/faasd
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
CGO_ENABLED=0 GOOS=linux go test -ldflags $(LDFLAGS) ./...
|
||||
|
||||
.PHONY: dist
|
||||
dist:
|
||||
CGO_ENABLED=0 GOOS=linux go build -ldflags $(LDFLAGS) -a -installsuffix cgo -o bin/faasd
|
||||
|
@ -1,10 +1,11 @@
|
||||
# faasd - serverless with containerd and CNI 🐳
|
||||
# faasd - lightweight OSS serverless 🐳
|
||||
|
||||
[](https://travis-ci.com/openfaas/faasd)
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
[](https://www.openfaas.com)
|
||||

|
||||
|
||||
faasd is the same OpenFaaS experience and ecosystem, but without Kubernetes. Functions and microservices can be deployed anywhere with reduced overheads whilst retaining the portability of containers and cloud-native tooling.
|
||||
faasd is the same OpenFaaS experience and ecosystem, but without Kubernetes. Functions and microservices can be deployed anywhere with reduced overheads whilst retaining the portability of containers and cloud-native tooling such as containerd and CNI.
|
||||
|
||||
## About faasd
|
||||
|
||||
@ -55,7 +56,9 @@ You can run this tutorial on your Raspberry Pi, or adapt the steps for a regular
|
||||
|
||||
Automate everything within < 60 seconds and get a public URL and IP address back. Customise as required, or adapt to your preferred cloud such as AWS EC2.
|
||||
|
||||
* [Provision faasd 0.7.5 on DigitalOcean with Terraform 0.12.0](https://gist.github.com/alexellis/fd618bd2f957eb08c44d086ef2fc3906)
|
||||
* [Provision faasd 0.8.1 on DigitalOcean with Terraform 0.12.0](docs/bootstrap/README.md)
|
||||
|
||||
* [Provision faasd on DigitalOcean with built-in TLS support](docs/bootstrap/digitalocean-terraform/README.md)
|
||||
|
||||
### A note on private repos / registries
|
||||
|
||||
|
@ -17,7 +17,7 @@ runcmd:
|
||||
- curl -sSL https://github.com/containernetworking/plugins/releases/download/v0.8.5/cni-plugins-linux-amd64-v0.8.5.tgz | tar -xz -C /opt/cni/bin
|
||||
- mkdir -p /go/src/github.com/openfaas/
|
||||
- cd /go/src/github.com/openfaas/ && git clone https://github.com/openfaas/faasd
|
||||
- curl -fSLs "https://github.com/openfaas/faasd/releases/download/0.7.7/faasd" --output "/usr/local/bin/faasd" && chmod a+x "/usr/local/bin/faasd"
|
||||
- curl -fSLs "https://github.com/openfaas/faasd/releases/download/0.8.2/faasd" --output "/usr/local/bin/faasd" && chmod a+x "/usr/local/bin/faasd"
|
||||
- cd /go/src/github.com/openfaas/faasd/ && /usr/local/bin/faasd install
|
||||
- systemctl status -l containerd --no-pager
|
||||
- journalctl -u faasd-provider --no-pager
|
||||
|
@ -175,7 +175,7 @@ func makeServiceDefinitions(archSuffix string) []pkg.Service {
|
||||
return []pkg.Service{
|
||||
{
|
||||
Name: "basic-auth-plugin",
|
||||
Image: "docker.io/openfaas/basic-auth-plugin:0.18.10" + archSuffix,
|
||||
Image: "docker.io/openfaas/basic-auth-plugin:0.18.17" + archSuffix,
|
||||
Env: []string{
|
||||
"port=8080",
|
||||
"secret_mount_path=" + containerSecretMountDir,
|
||||
@ -230,7 +230,7 @@ func makeServiceDefinitions(archSuffix string) []pkg.Service {
|
||||
"secret_mount_path=" + containerSecretMountDir,
|
||||
"scale_from_zero=true",
|
||||
},
|
||||
Image: "docker.io/openfaas/gateway:0.18.8" + archSuffix,
|
||||
Image: "docker.io/openfaas/gateway:0.18.17" + archSuffix,
|
||||
Mounts: []pkg.Mount{
|
||||
{
|
||||
Src: path.Join(path.Join(wd, "secrets"), "basic-auth-password"),
|
||||
@ -256,7 +256,7 @@ func makeServiceDefinitions(archSuffix string) []pkg.Service {
|
||||
"basic_auth=true",
|
||||
"secret_mount_path=" + containerSecretMountDir,
|
||||
},
|
||||
Image: "docker.io/openfaas/queue-worker:0.9.0",
|
||||
Image: "docker.io/openfaas/queue-worker:0.11.2",
|
||||
Mounts: []pkg.Mount{
|
||||
{
|
||||
Src: path.Join(path.Join(wd, "secrets"), "basic-auth-password"),
|
||||
|
18
docs/DEV.md
18
docs/DEV.md
@ -174,19 +174,19 @@ make local
|
||||
|
||||
```sh
|
||||
# For x86_64
|
||||
sudo curl -fSLs "https://github.com/openfaas/faasd/releases/download/0.8.0/faasd" \
|
||||
-o "/usr/local/bin/faasd" \
|
||||
&& sudo chmod a+x "/usr/local/bin/faasd"
|
||||
export SUFFIX=""
|
||||
|
||||
# armhf
|
||||
sudo curl -fSLs "https://github.com/openfaas/faasd/releases/download/0.8.0/faasd-armhf" \
|
||||
-o "/usr/local/bin/faasd" \
|
||||
&& sudo chmod a+x "/usr/local/bin/faasd"
|
||||
export SUFFIX="-armhf"
|
||||
|
||||
# arm64
|
||||
sudo curl -fSLs "https://github.com/openfaas/faasd/releases/download/0.8.0/faasd-arm64" \
|
||||
-o "/usr/local/bin/faasd" \
|
||||
&& sudo chmod a+x "/usr/local/bin/faasd"
|
||||
export SUFFIX="-arm64"
|
||||
|
||||
# Then download
|
||||
curl -fSLs "https://github.com/openfaas/faasd/releases/download/0.8.2/faasd$SUFFIX" \
|
||||
-o "/tmp/faasd" \
|
||||
&& chmod +x "/tmp/faasd"
|
||||
sudo mv /tmp/faasd /usr/local/bin/
|
||||
```
|
||||
|
||||
#### Install `faasd`
|
||||
|
20
docs/bootstrap/README.md
Normal file
20
docs/bootstrap/README.md
Normal file
@ -0,0 +1,20 @@
|
||||
# Bootstrap faasd on Digitalocean
|
||||
|
||||
1) [Sign up to DigitalOcean](https://www.digitalocean.com/?refcode=2962aa9e56a1&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=CopyPaste)
|
||||
2) [Download Terraform](https://www.terraform.io)
|
||||
3) Clone this gist using the URL from the address bar
|
||||
4) Run `terraform init`
|
||||
5) Run `terraform apply -var="do_token=$(cat $HOME/digitalocean-access-token)"`
|
||||
6) View the output for the login command and gateway URL i.e.
|
||||
|
||||
```
|
||||
gateway_url = http://178.128.39.201:8080/
|
||||
login_cmd = faas-cli login -g http://178.128.39.201:8080/ -p rvIU49CEcFcHmqxj
|
||||
password = rvIU49CEcFcHmqxj
|
||||
```
|
||||
|
||||
Note that the user-data may take a couple of minutes to come up since it will be pulling in various components and preparing the machine.
|
||||
|
||||
A single host with 1GB of RAM will be deployed for you, to remove at a later date simply use `terraform destroy`.
|
||||
|
||||
If required, you can remove the VM via `terraform destroy -var="do_token=$(cat $HOME/digitalocean-access-token)"`
|
29
docs/bootstrap/cloud-config.tpl
Normal file
29
docs/bootstrap/cloud-config.tpl
Normal file
@ -0,0 +1,29 @@
|
||||
#cloud-config
|
||||
ssh_authorized_keys:
|
||||
- ${ssh_key}
|
||||
|
||||
package_update: true
|
||||
|
||||
packages:
|
||||
- runc
|
||||
|
||||
runcmd:
|
||||
- curl -sLSf https://github.com/containerd/containerd/releases/download/v1.3.2/containerd-1.3.2.linux-amd64.tar.gz > /tmp/containerd.tar.gz && 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 | tee /etc/systemd/system/containerd.service
|
||||
- systemctl daemon-reload && systemctl start containerd
|
||||
- /sbin/sysctl -w net.ipv4.conf.all.forwarding=1
|
||||
- mkdir -p /opt/cni/bin
|
||||
- curl -sSL https://github.com/containernetworking/plugins/releases/download/v0.8.5/cni-plugins-linux-amd64-v0.8.5.tgz | tar -xz -C /opt/cni/bin
|
||||
- mkdir -p /go/src/github.com/openfaas/
|
||||
- mkdir -p /var/lib/faasd/secrets/
|
||||
- echo ${gw_password} > /var/lib/faasd/secrets/basic-auth-password
|
||||
- echo admin > /var/lib/faasd/secrets/basic-auth-user
|
||||
- cd /go/src/github.com/openfaas/ && git clone https://github.com/openfaas/faasd
|
||||
- curl -fSLs "https://github.com/openfaas/faasd/releases/download/0.8.1/faasd" --output "/usr/local/bin/faasd" && chmod a+x "/usr/local/bin/faasd"
|
||||
- cd /go/src/github.com/openfaas/faasd/ && /usr/local/bin/faasd install
|
||||
- systemctl status -l containerd --no-pager
|
||||
- journalctl -u faasd-provider --no-pager
|
||||
- systemctl status -l faasd-provider --no-pager
|
||||
- systemctl status -l faasd --no-pager
|
||||
- curl -sSLf https://cli.openfaas.com | sh
|
||||
- sleep 5 && journalctl -u faasd --no-pager
|
37
docs/bootstrap/digitalocean-terraform/README.md
Normal file
37
docs/bootstrap/digitalocean-terraform/README.md
Normal file
@ -0,0 +1,37 @@
|
||||
# Bootstrap faasd with TLS support on Digitalocean
|
||||
|
||||
1) [Sign up to DigitalOcean](https://www.digitalocean.com/?refcode=2962aa9e56a1&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=CopyPaste)
|
||||
2) [Download Terraform](https://www.terraform.io)
|
||||
3) Clone this gist using the URL from the address bar
|
||||
4) Run `terraform init`
|
||||
5) Configure terraform variables as needed by updating the `main.tfvars` file:
|
||||
|
||||
| Variable | Description | Default |
|
||||
| ------------ | ------------------- | --------------- |
|
||||
| `do_token` | Digitalocean API token | None |
|
||||
| `do_domain` | Public domain used for the faasd gateway | None |
|
||||
| `letsencrypt_email` | Email used by when ordering TLS certificate from Letsencrypt | `""` |
|
||||
| `do_create_record` | When set to `true`, a new DNS record will be created. This works only if your domain (`do_domain`) is managed by Digitalocean | `false` |
|
||||
| `do_region` | Digitalocean region for creating the droplet | `fra1` |
|
||||
| `ssh_key_file` | Path to public SSH key file |`~/.ssh/id_rsa.pub` |
|
||||
|
||||
> Environment variables can also be used to set terraform variables when running the `terraform apply` command using the format `TF_VAR_name`.
|
||||
|
||||
6) Run `terraform apply`
|
||||
1) Add `-var-file=main.tfvars` if you have set the variables in `main.tfvars`.
|
||||
2) OR [use environment variables](https://www.terraform.io/docs/commands/environment-variables.html#tf_var_name) for setting the terraform variables when running the `apply` command
|
||||
|
||||
7) View the output for the login command and gateway URL i.e.
|
||||
|
||||
```
|
||||
droplet_ip = 178.128.39.201
|
||||
gateway_url = https://faasd.example.com/
|
||||
login_cmd = faas-cli login -g https://faasd.example.com/ -p rvIU49CEcFcHmqxj
|
||||
password = rvIU49CEcFcHmqxj
|
||||
```
|
||||
8) Use your browser to access the OpenFaaS interface
|
||||
|
||||
Note that the user-data may take a couple of minutes to come up since it will be pulling in various components and preparing the machine.
|
||||
Also take into consideration the DNS propagation time for the new DNS record.
|
||||
|
||||
A single host with 1GB of RAM will be deployed for you, to remove at a later date simply use `terraform destroy`.
|
57
docs/bootstrap/digitalocean-terraform/cloud-config.tpl
Normal file
57
docs/bootstrap/digitalocean-terraform/cloud-config.tpl
Normal file
@ -0,0 +1,57 @@
|
||||
#cloud-config
|
||||
ssh_authorized_keys:
|
||||
- ${ssh_key}
|
||||
|
||||
groups:
|
||||
- caddy
|
||||
|
||||
users:
|
||||
- name: caddy
|
||||
gecos: Caddy web server
|
||||
primary_group: caddy
|
||||
groups: caddy
|
||||
shell: /usr/sbin/nologin
|
||||
homedir: /var/lib/caddy
|
||||
|
||||
write_files:
|
||||
- content: |
|
||||
{
|
||||
email ${letsencrypt_email}
|
||||
}
|
||||
|
||||
${faasd_domain_name} {
|
||||
reverse_proxy 127.0.0.1:8080
|
||||
}
|
||||
|
||||
path: /etc/caddy/Caddyfile
|
||||
|
||||
package_update: true
|
||||
|
||||
packages:
|
||||
- runc
|
||||
|
||||
runcmd:
|
||||
- curl -sLSf https://github.com/containerd/containerd/releases/download/v1.3.2/containerd-1.3.2.linux-amd64.tar.gz > /tmp/containerd.tar.gz && 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 | tee /etc/systemd/system/containerd.service
|
||||
- systemctl daemon-reload && systemctl start containerd
|
||||
- /sbin/sysctl -w net.ipv4.conf.all.forwarding=1
|
||||
- mkdir -p /opt/cni/bin
|
||||
- curl -sSL https://github.com/containernetworking/plugins/releases/download/v0.8.5/cni-plugins-linux-amd64-v0.8.5.tgz | tar -xz -C /opt/cni/bin
|
||||
- mkdir -p /go/src/github.com/openfaas/
|
||||
- mkdir -p /var/lib/faasd/secrets/
|
||||
- echo ${gw_password} > /var/lib/faasd/secrets/basic-auth-password
|
||||
- echo admin > /var/lib/faasd/secrets/basic-auth-user
|
||||
- cd /go/src/github.com/openfaas/ && git clone https://github.com/openfaas/faasd
|
||||
- curl -fSLs "https://github.com/openfaas/faasd/releases/download/0.8.1/faasd" --output "/usr/local/bin/faasd" && chmod a+x "/usr/local/bin/faasd"
|
||||
- cd /go/src/github.com/openfaas/faasd/ && /usr/local/bin/faasd install
|
||||
- systemctl status -l containerd --no-pager
|
||||
- journalctl -u faasd-provider --no-pager
|
||||
- systemctl status -l faasd-provider --no-pager
|
||||
- systemctl status -l faasd --no-pager
|
||||
- curl -sSLf https://cli.openfaas.com | sh
|
||||
- sleep 5 && journalctl -u faasd --no-pager
|
||||
- wget https://github.com/caddyserver/caddy/releases/download/v2.0.0-rc.2/caddy_2.0.0-rc.2_linux_amd64.tar.gz -O /tmp/caddy.tar.gz && tar -zxvf /tmp/caddy.tar.gz -C /usr/bin/ caddy
|
||||
- wget https://raw.githubusercontent.com/caddyserver/dist/master/init/caddy.service -O /etc/systemd/system/caddy.service
|
||||
- systemctl daemon-reload
|
||||
- systemctl enable caddy
|
||||
- systemctl start caddy
|
82
docs/bootstrap/digitalocean-terraform/main.tf
Normal file
82
docs/bootstrap/digitalocean-terraform/main.tf
Normal file
@ -0,0 +1,82 @@
|
||||
terraform {
|
||||
required_version = ">= 0.12"
|
||||
}
|
||||
|
||||
variable "do_token" {
|
||||
description = "Digitalocean API token"
|
||||
}
|
||||
variable "do_domain" {
|
||||
description = "Your public domain"
|
||||
}
|
||||
variable "letsencrypt_email" {
|
||||
description = "Email used to order a certificate from Letsencrypt"
|
||||
}
|
||||
variable "do_create_record" {
|
||||
default = false
|
||||
description = "Whether to create a DNS record on Digitalocean"
|
||||
}
|
||||
variable "do_region" {
|
||||
default = "fra1"
|
||||
description = "The Digitalocean region where the faasd droplet will be created."
|
||||
}
|
||||
variable "ssh_key_file" {
|
||||
default = "~/.ssh/id_rsa.pub"
|
||||
description = "Path to the SSH public key file"
|
||||
}
|
||||
|
||||
provider "digitalocean" {
|
||||
token = var.do_token
|
||||
}
|
||||
|
||||
data "local_file" "ssh_key"{
|
||||
filename = pathexpand(var.ssh_key_file)
|
||||
}
|
||||
|
||||
resource "random_password" "password" {
|
||||
length = 16
|
||||
special = true
|
||||
override_special = "_-#"
|
||||
}
|
||||
|
||||
data "template_file" "cloud_init" {
|
||||
template = "${file("cloud-config.tpl")}"
|
||||
vars = {
|
||||
gw_password=random_password.password.result,
|
||||
ssh_key=data.local_file.ssh_key.content,
|
||||
faasd_domain_name="faasd.${var.do_domain}"
|
||||
letsencrypt_email=var.letsencrypt_email
|
||||
}
|
||||
}
|
||||
|
||||
resource "digitalocean_droplet" "faasd" {
|
||||
region = var.do_region
|
||||
image = "ubuntu-18-04-x64"
|
||||
name = "faasd"
|
||||
size = "s-1vcpu-1gb"
|
||||
user_data = data.template_file.cloud_init.rendered
|
||||
}
|
||||
|
||||
resource "digitalocean_record" "faasd" {
|
||||
domain = var.do_domain
|
||||
type = "A"
|
||||
name = "faasd"
|
||||
value = digitalocean_droplet.faasd.ipv4_address
|
||||
# Only creates record if do_create_record is true
|
||||
count = var.do_create_record == true ? 1 : 0
|
||||
}
|
||||
|
||||
output "droplet_ip" {
|
||||
value = digitalocean_droplet.faasd.ipv4_address
|
||||
}
|
||||
|
||||
output "gateway_url" {
|
||||
value = "https://faasd.${var.do_domain}/"
|
||||
}
|
||||
|
||||
output "password" {
|
||||
value = random_password.password.result
|
||||
}
|
||||
|
||||
output "login_cmd" {
|
||||
value = "faas-cli login -g https://faasd.${var.do_domain}/ -p ${random_password.password.result}"
|
||||
}
|
3
docs/bootstrap/digitalocean-terraform/main.tfvars
Normal file
3
docs/bootstrap/digitalocean-terraform/main.tfvars
Normal file
@ -0,0 +1,3 @@
|
||||
do_token = ""
|
||||
do_domain = ""
|
||||
letsencrypt_email = ""
|
56
docs/bootstrap/main.tf
Normal file
56
docs/bootstrap/main.tf
Normal file
@ -0,0 +1,56 @@
|
||||
terraform {
|
||||
required_version = ">= 0.12"
|
||||
}
|
||||
|
||||
variable "do_token" {}
|
||||
|
||||
variable "ssh_key_file" {
|
||||
default = "~/.ssh/id_rsa.pub"
|
||||
description = "Path to the SSH public key file"
|
||||
}
|
||||
|
||||
provider "digitalocean" {
|
||||
token = var.do_token
|
||||
}
|
||||
|
||||
resource "random_password" "password" {
|
||||
length = 16
|
||||
special = true
|
||||
override_special = "_-#"
|
||||
}
|
||||
|
||||
data "local_file" "ssh_key"{
|
||||
filename = pathexpand(var.ssh_key_file)
|
||||
}
|
||||
|
||||
data "template_file" "cloud_init" {
|
||||
template = "${file("cloud-config.tpl")}"
|
||||
vars = {
|
||||
gw_password=random_password.password.result,
|
||||
ssh_key=data.local_file.ssh_key.content,
|
||||
}
|
||||
}
|
||||
|
||||
resource "digitalocean_droplet" "faasd" {
|
||||
|
||||
region = "lon1"
|
||||
image = "ubuntu-18-04-x64"
|
||||
name = "faasd"
|
||||
# Plans: https://developers.digitalocean.com/documentation/changelog/api-v2/new-size-slugs-for-droplet-plan-changes/
|
||||
#size = "512mb"
|
||||
size = "s-1vcpu-1gb"
|
||||
user_data = data.template_file.cloud_init.rendered
|
||||
}
|
||||
|
||||
output "password" {
|
||||
value = random_password.password.result
|
||||
}
|
||||
|
||||
output "gateway_url" {
|
||||
value = "http://${digitalocean_droplet.faasd.ipv4_address}:8080/"
|
||||
}
|
||||
|
||||
output "login_cmd" {
|
||||
value = "faas-cli login -g http://${digitalocean_droplet.faasd.ipv4_address}:8080/ -p ${random_password.password.result}"
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ func (r *requester) Query(ctx context.Context, req logs.Request) (<-chan logs.Me
|
||||
// call start and get the stdout prior to streaming so that we can return a meaningful
|
||||
// error for as long as possible. If the cmd starts correctly, we are highly likely to
|
||||
// succeed anyway
|
||||
msgs := make(chan logs.Message, 100)
|
||||
msgs := make(chan logs.Message)
|
||||
go streamLogs(ctx, cmd, stdout, msgs)
|
||||
go logErrOut(stderr)
|
||||
|
||||
@ -62,7 +62,6 @@ func (r *requester) Query(ctx context.Context, req logs.Request) (<-chan logs.Me
|
||||
// --output=json \
|
||||
// --since=<timestamp> \
|
||||
// <--follow> \
|
||||
// --output-fields=SYSLOG_IDENTIFIER,MESSAGE,_PID,_SOURCE_REALTIME_TIMESTAMP
|
||||
func buildCmd(ctx context.Context, req logs.Request) *exec.Cmd {
|
||||
// // set the cursor position based on req, default to 5m
|
||||
since := time.Now().Add(-5 * time.Minute)
|
||||
@ -102,10 +101,6 @@ func buildCmd(ctx context.Context, req logs.Request) *exec.Cmd {
|
||||
// the loop is based on the Decoder example in the docs
|
||||
// https://golang.org/pkg/encoding/json/#Decoder.Decode
|
||||
func streamLogs(ctx context.Context, cmd *exec.Cmd, out io.ReadCloser, msgs chan logs.Message) {
|
||||
// without this sleep the channel seems to get stuck. This results in either no log messages
|
||||
// being read by the Handler _or_ the messages are read but only flushed when the request
|
||||
// timesout
|
||||
time.Sleep(time.Millisecond)
|
||||
log.Println("starting journal stream using ", cmd.String())
|
||||
|
||||
// will ensure `out` is closed and all related resources cleaned up
|
||||
|
@ -29,19 +29,19 @@ func Test_parseEntry(t *testing.T) {
|
||||
}
|
||||
|
||||
if entry.Name != expectedEntry.Name {
|
||||
t.Fatalf("expected Name %s, got %s", expectedEntry.Name, entry.Name)
|
||||
t.Fatalf("want Name: %q, got %q", expectedEntry.Name, entry.Name)
|
||||
}
|
||||
|
||||
if entry.Namespace != expectedEntry.Namespace {
|
||||
t.Fatalf("expected Namespace %s, got %s", expectedEntry.Namespace, entry.Namespace)
|
||||
t.Fatalf("want Namespace: %q, got %q", expectedEntry.Namespace, entry.Namespace)
|
||||
}
|
||||
|
||||
if entry.Timestamp != expectedEntry.Timestamp {
|
||||
t.Fatalf("expected Timestamp %s, got %s", expectedEntry.Timestamp, entry.Timestamp)
|
||||
t.Fatalf("want Timestamp: %q, got %q", expectedEntry.Timestamp, entry.Timestamp)
|
||||
}
|
||||
|
||||
if entry.Text != expectedEntry.Text {
|
||||
t.Fatalf("expected Text %s, got %s", expectedEntry.Text, entry.Text)
|
||||
t.Fatalf("want Text: %q, got %q", expectedEntry.Text, entry.Text)
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,17 +57,17 @@ func Test_buildCmd(t *testing.T) {
|
||||
}
|
||||
|
||||
expectedArgs := fmt.Sprintf(
|
||||
"--utc --no-pager --output=json --output-fields=SYSLOG_IDENTIFIER,MESSAGE,_PID,_SOURCE_REALTIME_TIMESTAMP --identifier=spacetwo:loggyfunc --since=%s --follow --lines=5",
|
||||
"--utc --no-pager --output=json --identifier=spacetwo:loggyfunc --since=%s --follow --lines=5",
|
||||
now.UTC().Format("2006-01-02 15:04:05"),
|
||||
)
|
||||
|
||||
cmd := buildCmd(ctx, req).String()
|
||||
|
||||
if !strings.Contains(cmd, "journalctl") {
|
||||
t.Fatalf("expected journalctl cmd, got cmd %s", cmd)
|
||||
wantCmd := "journalctl"
|
||||
if !strings.Contains(cmd, wantCmd) {
|
||||
t.Fatalf("cmd want: %q, got: %q", wantCmd, cmd)
|
||||
}
|
||||
|
||||
if !strings.HasSuffix(cmd, expectedArgs) {
|
||||
t.Fatalf("expected arg %s,\ngot cmd %s", expectedArgs, cmd)
|
||||
t.Fatalf("arg want: %q\ngot: %q", expectedArgs, cmd)
|
||||
}
|
||||
}
|
||||
|
@ -2,11 +2,13 @@ package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/containerd/containerd"
|
||||
"github.com/openfaas/faas-provider/types"
|
||||
@ -76,17 +78,6 @@ func createSecret(c *containerd.Client, w http.ResponseWriter, r *http.Request,
|
||||
}
|
||||
}
|
||||
|
||||
func parseSecret(r *http.Request) (types.Secret, error) {
|
||||
secret := types.Secret{}
|
||||
bytesOut, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return secret, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(bytesOut, &secret)
|
||||
return secret, err
|
||||
}
|
||||
|
||||
func deleteSecret(c *containerd.Client, w http.ResponseWriter, r *http.Request, mountPath string) {
|
||||
secret, err := parseSecret(r)
|
||||
if err != nil {
|
||||
@ -103,3 +94,29 @@ func deleteSecret(c *containerd.Client, w http.ResponseWriter, r *http.Request,
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func parseSecret(r *http.Request) (types.Secret, error) {
|
||||
secret := types.Secret{}
|
||||
bytesOut, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return secret, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(bytesOut, &secret)
|
||||
if err != nil {
|
||||
return secret, err
|
||||
}
|
||||
|
||||
if isTraversal(secret.Name) {
|
||||
return secret, fmt.Errorf(traverseErrorSt)
|
||||
}
|
||||
|
||||
return secret, err
|
||||
}
|
||||
|
||||
const traverseErrorSt = "directory traversal found in name"
|
||||
|
||||
func isTraversal(name string) bool {
|
||||
return strings.Contains(name, fmt.Sprintf("%s", string(os.PathSeparator))) ||
|
||||
strings.Contains(name, "..")
|
||||
}
|
||||
|
63
pkg/provider/handlers/secret_test.go
Normal file
63
pkg/provider/handlers/secret_test.go
Normal file
@ -0,0 +1,63 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/openfaas/faas-provider/types"
|
||||
)
|
||||
|
||||
func Test_parseSecretValidName(t *testing.T) {
|
||||
|
||||
s := types.Secret{Name: "authorized_keys"}
|
||||
body, _ := json.Marshal(s)
|
||||
reader := bytes.NewReader(body)
|
||||
r := httptest.NewRequest(http.MethodPost, "/", reader)
|
||||
_, err := parseSecret(r)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("secret name is valid with no traversal characters")
|
||||
}
|
||||
}
|
||||
|
||||
func Test_parseSecretValidNameWithDot(t *testing.T) {
|
||||
|
||||
s := types.Secret{Name: "authorized.keys"}
|
||||
body, _ := json.Marshal(s)
|
||||
reader := bytes.NewReader(body)
|
||||
r := httptest.NewRequest(http.MethodPost, "/", reader)
|
||||
_, err := parseSecret(r)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("secret name is valid with no traversal characters")
|
||||
}
|
||||
}
|
||||
|
||||
func Test_parseSecretWithTraversalWithSlash(t *testing.T) {
|
||||
|
||||
s := types.Secret{Name: "/root/.ssh/authorized_keys"}
|
||||
body, _ := json.Marshal(s)
|
||||
reader := bytes.NewReader(body)
|
||||
r := httptest.NewRequest(http.MethodPost, "/", reader)
|
||||
_, err := parseSecret(r)
|
||||
|
||||
if err == nil {
|
||||
t.Fatalf("secret name should fail due to path traversal")
|
||||
}
|
||||
}
|
||||
|
||||
func Test_parseSecretWithTraversalWithDoubleDot(t *testing.T) {
|
||||
|
||||
s := types.Secret{Name: ".."}
|
||||
body, _ := json.Marshal(s)
|
||||
reader := bytes.NewReader(body)
|
||||
r := httptest.NewRequest(http.MethodPost, "/", reader)
|
||||
_, err := parseSecret(r)
|
||||
|
||||
if err == nil {
|
||||
t.Fatalf("secret name should fail due to path traversal")
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user