Compare commits

...

8 Commits

Author SHA1 Message Date
8ac45f5379 Avoid providing memory limit if not set explicitly
Signed-off-by: Nitishkumar Singh <nitishkumarsingh71@gmail.com>
2022-01-25 16:17:30 +00:00
3579061423 Bump gateway version 0.21.3
Signed-off-by: Nitishkumar Singh <nitishkumarsingh71@gmail.com>
2022-01-25 16:12:12 +00:00
761d1847bf Update ISSUE_TEMPLATE.md 2022-01-21 09:12:27 +00:00
8003748b73 Review feedback for Labeller
Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2022-01-19 18:13:05 +00:00
a2ea804d2c Handled list secrets for no secret in namespaces
Signed-off-by: Nitishkumar Singh <nitishkumarsingh71@gmail.com>

Test case included for default and non-default

Signed-off-by: Nitishkumar Singh <nitishkumarsingh71@gmail.com>

Changed Fake Labeller Implementation

Signed-off-by: Nitishkumar Singh <nitishkumarsingh71@gmail.com>
2022-01-19 18:05:56 +00:00
551e6645b7 chore: improve multipass cloud-config and instructions
Improve the Multipass cloud-config.txt by using the install.sh script
instead of stale install instructions. This ensures that the latest
release is installed and reduces the number of install instructions we
need to maintain.

Also, improve the instructions for using multipass by including a
one-line command for setting the correct ssh key _and_ starting the VM.

Finally, improve the markdown formatting by indenting the paragraph
bodies of the list items. This ensures that the content is properly
aligned with the bullet list.

Signed-off-by: Lucas Roesler <roesler.lucas@gmail.com>
2022-01-19 12:44:47 +00:00
77867f17e3 Migrate to Go 1.17
Tested during local development and deployment to multipass
and Ubuntu. Worked as expected.

Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2021-12-21 13:26:38 +00:00
5aed707354 Create volumes automatically for NATS/Prometheus
Fixes: #223

Tested by checking the logs of Prometheus and NATS was tested
by running async requests with hey then restarting faasd,
which deletes the NATS and queue-worker containers. The work
left over in the queue was restarted as expected.

Pre-created volumes are read from docker-compose.yaml and
only numeric user IDs are supported at this time. Users
can still specify a textual username, if they create the
source directory for a mount before restarting faasd,
it won't try to overwrite the directory.

Add comment for workingDirectoryPermission

Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2021-12-21 13:26:38 +00:00
19 changed files with 270 additions and 107 deletions

View File

@ -1,3 +1,7 @@
## Due diligence
Before you for help or support, make sure that you've [consulted the faasd manual "Serverless For Everyone Else"](https://openfaas.gumroad.com/l/serverless-for-everyone-else).
<!--- Provide a general summary of the issue in the Title above --> <!--- Provide a general summary of the issue in the Title above -->
## Expected Behaviour ## Expected Behaviour

View File

@ -12,7 +12,7 @@ jobs:
GO111MODULE: off GO111MODULE: off
strategy: strategy:
matrix: matrix:
go-version: [1.16.x] go-version: [1.17.x]
os: [ubuntu-latest] os: [ubuntu-latest]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:

View File

@ -9,7 +9,7 @@ jobs:
publish: publish:
strategy: strategy:
matrix: matrix:
go-version: [ 1.16.x ] go-version: [ 1.17.x ]
os: [ ubuntu-latest ] os: [ ubuntu-latest ]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:

View File

@ -10,17 +10,7 @@ packages:
- git - git
runcmd: runcmd:
- curl -sLSf https://github.com/containerd/containerd/releases/download/v1.5.4/containerd-1.5.4-linux-amd64.tar.gz > /tmp/containerd.tar.gz && tar -xvf /tmp/containerd.tar.gz -C /usr/local/bin/ --strip-components=1 - curl -sfL https://raw.githubusercontent.com/openfaas/faasd/master/hack/install.sh | sh -s -
- curl -SLfs https://raw.githubusercontent.com/containerd/containerd/v1.5.4/containerd.service | tee /etc/systemd/system/containerd.service
- systemctl daemon-reload && systemctl start containerd
- systemctl enable 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/
- cd /go/src/github.com/openfaas/ && git clone --depth 1 --branch 0.13.0 https://github.com/openfaas/faasd
- curl -fSLs "https://github.com/openfaas/faasd/releases/download/0.13.0/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 - systemctl status -l containerd --no-pager
- journalctl -u faasd-provider --no-pager - journalctl -u faasd-provider --no-pager
- systemctl status -l faasd-provider --no-pager - systemctl status -l faasd-provider --no-pager

View File

@ -103,7 +103,7 @@ func makeProviderCmd() *cobra.Command {
HealthHandler: func(w http.ResponseWriter, r *http.Request) {}, HealthHandler: func(w http.ResponseWriter, r *http.Request) {},
InfoHandler: handlers.MakeInfoHandler(Version, GitCommit), InfoHandler: handlers.MakeInfoHandler(Version, GitCommit),
ListNamespaceHandler: handlers.MakeNamespacesLister(client), ListNamespaceHandler: handlers.MakeNamespacesLister(client),
SecretHandler: handlers.MakeSecretHandler(client, baseUserSecretsPath), SecretHandler: handlers.MakeSecretHandler(client.NamespaceService(), baseUserSecretsPath),
LogHandler: logs.NewLogHandlerFunc(faasdlogs.New(), config.ReadTimeout), LogHandler: logs.NewLogHandlerFunc(faasdlogs.New(), config.ReadTimeout),
} }

View File

@ -20,28 +20,43 @@ services:
nats: nats:
image: docker.io/library/nats-streaming:0.22.0 image: docker.io/library/nats-streaming:0.22.0
# nobody
user: "65534"
command: command:
- "/nats-streaming-server" - "/nats-streaming-server"
- "-m" - "-m"
- "8222" - "8222"
- "--store=memory" - "--store=file"
- "--dir=/nats"
- "--cluster_id=faas-cluster" - "--cluster_id=faas-cluster"
volumes:
# Data directory
- type: bind
source: ./nats
target: /nats
# ports: # ports:
# - "127.0.0.1:8222:8222" # - "127.0.0.1:8222:8222"
prometheus: prometheus:
image: docker.io/prom/prometheus:v2.14.0 image: docker.io/prom/prometheus:v2.14.0
# nobody
user: "65534"
volumes: volumes:
# Config directory
- type: bind - type: bind
source: ./prometheus.yml source: ./prometheus.yml
target: /etc/prometheus/prometheus.yml target: /etc/prometheus/prometheus.yml
# Data directory
- type: bind
source: ./prometheus
target: /prometheus
cap_add: cap_add:
- CAP_NET_RAW - CAP_NET_RAW
ports: ports:
- "127.0.0.1:9090:9090" - "127.0.0.1:9090:9090"
gateway: gateway:
image: ghcr.io/openfaas/gateway:0.21.0 image: ghcr.io/openfaas/gateway:0.21.3
environment: environment:
- basic_auth=true - basic_auth=true
- functions_provider_url=http://faasd-provider:8081/ - functions_provider_url=http://faasd-provider:8081/

View File

@ -27,111 +27,112 @@ It took me about 2-3 minutes to run through everything after installing multipas
* Get my cloud-config.txt file * Get my cloud-config.txt file
```sh ```sh
curl -sSLO https://raw.githubusercontent.com/openfaas/faasd/master/cloud-config.txt curl -sSLO https://raw.githubusercontent.com/openfaas/faasd/master/cloud-config.txt
``` ```
* Update the SSH key to match your own, edit `cloud-config.txt`:
Replace the 2nd line with the contents of `~/.ssh/id_rsa.pub`:
```
ssh_authorized_keys:
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC8Q/aUYUr3P1XKVucnO9mlWxOjJm+K01lHJR90MkHC9zbfTqlp8P7C3J26zKAuzHXOeF+VFxETRr6YedQKW9zp5oP7sN+F2gr/pO7GV3VmOqHMV7uKfyUQfq7H1aVzLfCcI7FwN2Zekv3yB7kj35pbsMa1Za58aF6oHRctZU6UWgXXbRxP+B04DoVU7jTstQ4GMoOCaqYhgPHyjEAS3DW0kkPW6HzsvJHkxvVcVlZ/wNJa1Ie/yGpzOzWIN0Ol0t2QT/RSWOhfzO1A2P0XbPuZ04NmriBonO9zR7T1fMNmmtTuK7WazKjQT3inmYRAqU6pe8wfX8WIWNV7OowUjUsv alex@alexr.local
```
* Boot the VM * Boot the VM
```sh The `cloud-config.txt` contains an ssh key to allow your local machine to access the VM. However, this must be updated with your local ssh key.
multipass launch --cloud-init cloud-config.txt --name faasd This command will update the key with your local public key value and start the VM.
```
```sh
sed "s/ssh-rsa.*/$(cat $HOME/.ssh/id_*.pub)/" cloud-config.txt | multipass launch --name faasd --cloud-init -
```
This can also be done manually, just replace the 2nd line of the `cloud-config.txt` with the coPntents of your public ssh key, usually either `~/.ssh/id_rsa.pub` or `~/.ssh/id_ed25519.pub`
```
ssh_authorized_keys:
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC8Q/aUYUr3P1XKVucnO9mlWxOjJm+K01lHJR90MkHC9zbfTqlp8P7C3J26zKAuzHXOeF+VFxETRr6YedQKW9zp5oP7sN+F2gr/pO7GV3VmOqHMV7uKfyUQfq7H1aVzLfCcI7FwN2Zekv3yB7kj35pbsMa1Za58aF6oHRctZU6UWgXXbRxP+B04DoVU7jTstQ4GMoOCaqYhgPHyjEAS3DW0kkPW6HzsvJHkxvVcVlZ/wNJa1Ie/yGpzOzWIN0Ol0t2QT/RSWOhfzO1A2P0XbPuZ04NmriBonO9zR7T1fMNmmtTuK7WazKjQT3inmYRAqU6pe8wfX8WIWNV7OowUjUsv alex@alexr.local
```
* Get the VM's IP and connect with `ssh` * Get the VM's IP and connect with `ssh`
```sh ```sh
multipass info faasd multipass info faasd
Name: faasd Name: faasd
State: Running State: Running
IPv4: 192.168.64.14 IPv4: 192.168.64.14
Release: Ubuntu 18.04.3 LTS Release: Ubuntu 18.04.3 LTS
Image hash: a720c34066dc (Ubuntu 18.04 LTS) Image hash: a720c34066dc (Ubuntu 18.04 LTS)
Load: 0.79 0.19 0.06 Load: 0.79 0.19 0.06
Disk usage: 1.1G out of 4.7G Disk usage: 1.1G out of 4.7G
Memory usage: 145.6M out of 985.7M Memory usage: 145.6M out of 985.7M
``` ```
Set the variable `IP`: Set the variable `IP`:
``` ```
export IP="192.168.64.14" export IP="192.168.64.14"
``` ```
You can also try to use `jq` to get the IP into a variable: You can also try to use `jq` to get the IP into a variable:
```sh ```sh
export IP=$(multipass info faasd --format json| jq -r '.info.faasd.ipv4[0]') export IP=$(multipass info faasd --format json| jq -r '.info.faasd.ipv4[0]')
``` ```
Connect to the IP listed: Connect to the IP listed:
```sh ```sh
ssh ubuntu@$IP ssh ubuntu@$IP
``` ```
Log out once you know it works. Log out once you know it works.
* Let's capture the authentication password into a file for use with `faas-cli` * Let's capture the authentication password into a file for use with `faas-cli`
``` ```
ssh ubuntu@$IP "sudo cat /var/lib/faasd/secrets/basic-auth-password" > basic-auth-password ssh ubuntu@$IP "sudo cat /var/lib/faasd/secrets/basic-auth-password" > basic-auth-password
``` ```
## Try faasd (OpenFaaS) ## Try faasd (OpenFaaS)
* Login from your laptop (the host) * Login from your laptop (the host)
``` ```
export OPENFAAS_URL=http://$IP:8080 export OPENFAAS_URL=http://$IP:8080
cat basic-auth-password | faas-cli login -s cat basic-auth-password | faas-cli login -s
``` ```
* Deploy a function and invoke it * Deploy a function and invoke it
``` ```
faas-cli store deploy figlet --env write_timeout=1s faas-cli store deploy figlet --env write_timeout=1s
echo "faasd" | faas-cli invoke figlet echo "faasd" | faas-cli invoke figlet
faas-cli describe figlet faas-cli describe figlet
# Run async # Run async
curl -i -d "faasd-async" $OPENFAAS_URL/async-function/figlet curl -i -d "faasd-async" $OPENFAAS_URL/async-function/figlet
# Run async with a callback # Run async with a callback
curl -i -d "faasd-async" -H "X-Callback-Url: http://some-request-bin.com/path" $OPENFAAS_URL/async-function/figlet curl -i -d "faasd-async" -H "X-Callback-Url: http://some-request-bin.com/path" $OPENFAAS_URL/async-function/figlet
``` ```
You can also checkout the other store functions: `faas-cli store list` You can also checkout the other store functions: `faas-cli store list`
* Try the UI * Try the UI
Head over to the UI from your laptop and remember that your password is in the `basic-auth-password` file. The username is `admin`: Head over to the UI from your laptop and remember that your password is in the `basic-auth-password` file. The username is `admin`:
``` ```
echo http://$IP:8080 echo http://$IP:8080
``` ```
* Stop/start the instance * Stop/start the instance
```sh ```sh
multipass stop faasd multipass stop faasd
``` ```
* Delete, if you want to: * Delete, if you want to:
``` ```
multipass delete --purge faasd multipass delete --purge faasd
``` ```
You now have a faasd appliance on your Mac. You can also use this cloud-init file with public cloud like AWS or DigitalOcean. You now have a faasd appliance on your Mac. You can also use this cloud-init file with public cloud like AWS or DigitalOcean.

View File

@ -43,7 +43,7 @@ func MakeDeleteHandler(client *containerd.Client, cni gocni.CNI) func(w http.Res
lookupNamespace := getRequestNamespace(readNamespaceFromQuery(r)) lookupNamespace := getRequestNamespace(readNamespaceFromQuery(r))
// Check if namespace exists, and it has the openfaas label // Check if namespace exists, and it has the openfaas label
valid, err := validNamespace(client, lookupNamespace) valid, err := validNamespace(client.NamespaceService(), lookupNamespace)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest) http.Error(w, err.Error(), http.StatusBadRequest)
return return

View File

@ -54,7 +54,7 @@ func MakeDeployHandler(client *containerd.Client, cni gocni.CNI, secretMountPath
namespace := getRequestNamespace(req.Namespace) namespace := getRequestNamespace(req.Namespace)
// Check if namespace exists, and it has the openfaas label // Check if namespace exists, and it has the openfaas label
valid, err := validNamespace(client, namespace) valid, err := validNamespace(client.NamespaceService(), namespace)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest) http.Error(w, err.Error(), http.StatusBadRequest)

View File

@ -37,7 +37,7 @@ type Function struct {
func ListFunctions(client *containerd.Client, namespace string) (map[string]*Function, error) { func ListFunctions(client *containerd.Client, namespace string) (map[string]*Function, error) {
// Check if namespace exists, and it has the openfaas label // Check if namespace exists, and it has the openfaas label
valid, err := validNamespace(client, namespace) valid, err := validNamespace(client.NamespaceService(), namespace)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -2,10 +2,11 @@ package handlers
import ( import (
"encoding/json" "encoding/json"
"k8s.io/apimachinery/pkg/api/resource"
"log" "log"
"net/http" "net/http"
"k8s.io/apimachinery/pkg/api/resource"
"github.com/containerd/containerd" "github.com/containerd/containerd"
"github.com/openfaas/faas-provider/types" "github.com/openfaas/faas-provider/types"
) )
@ -16,7 +17,7 @@ func MakeReadHandler(client *containerd.Client) func(w http.ResponseWriter, r *h
lookupNamespace := getRequestNamespace(readNamespaceFromQuery(r)) lookupNamespace := getRequestNamespace(readNamespaceFromQuery(r))
// Check if namespace exists, and it has the openfaas label // Check if namespace exists, and it has the openfaas label
valid, err := validNamespace(client, lookupNamespace) valid, err := validNamespace(client.NamespaceService(), lookupNamespace)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest) http.Error(w, err.Error(), http.StatusBadRequest)
return return
@ -39,7 +40,7 @@ func MakeReadHandler(client *containerd.Client) func(w http.ResponseWriter, r *h
annotations := &fn.annotations annotations := &fn.annotations
labels := &fn.labels labels := &fn.labels
memory := resource.NewQuantity(fn.memoryLimit, resource.BinarySI) memory := resource.NewQuantity(fn.memoryLimit, resource.BinarySI)
res = append(res, types.FunctionStatus{ status := types.FunctionStatus{
Name: fn.name, Name: fn.name,
Image: fn.image, Image: fn.image,
Replicas: uint64(fn.replicas), Replicas: uint64(fn.replicas),
@ -49,9 +50,17 @@ func MakeReadHandler(client *containerd.Client) func(w http.ResponseWriter, r *h
Secrets: fn.secrets, Secrets: fn.secrets,
EnvVars: fn.envVars, EnvVars: fn.envVars,
EnvProcess: fn.envProcess, EnvProcess: fn.envProcess,
Limits: &types.FunctionResources{Memory: memory.String()},
CreatedAt: fn.createdAt, CreatedAt: fn.createdAt,
}) }
// Do not remove below memory check for 0
// Memory limit should not be included in status until set explicitly
limit := &types.FunctionResources{Memory: memory.String()}
if limit.Memory != "0" {
status.Limits = limit
}
res = append(res, status)
} }
body, _ := json.Marshal(res) body, _ := json.Marshal(res)

View File

@ -17,7 +17,7 @@ func MakeReplicaReaderHandler(client *containerd.Client) func(w http.ResponseWri
lookupNamespace := getRequestNamespace(readNamespaceFromQuery(r)) lookupNamespace := getRequestNamespace(readNamespaceFromQuery(r))
// Check if namespace exists, and it has the openfaas label // Check if namespace exists, and it has the openfaas label
valid, err := validNamespace(client, lookupNamespace) valid, err := validNamespace(client.NamespaceService(), lookupNamespace)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest) http.Error(w, err.Error(), http.StatusBadRequest)
return return

View File

@ -42,7 +42,7 @@ func MakeReplicaUpdateHandler(client *containerd.Client, cni gocni.CNI) func(w h
namespace := getRequestNamespace(readNamespaceFromQuery(r)) namespace := getRequestNamespace(readNamespaceFromQuery(r))
// Check if namespace exists, and it has the openfaas label // Check if namespace exists, and it has the openfaas label
valid, err := validNamespace(client, namespace) valid, err := validNamespace(client.NamespaceService(), namespace)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest) http.Error(w, err.Error(), http.StatusBadRequest)
return return

View File

@ -10,14 +10,14 @@ import (
"path" "path"
"strings" "strings"
"github.com/containerd/containerd"
"github.com/openfaas/faas-provider/types" "github.com/openfaas/faas-provider/types"
provider "github.com/openfaas/faasd/pkg/provider"
) )
const secretFilePermission = 0644 const secretFilePermission = 0644
const secretDirPermission = 0755 const secretDirPermission = 0755
func MakeSecretHandler(c *containerd.Client, mountPath string) func(w http.ResponseWriter, r *http.Request) { func MakeSecretHandler(store provider.Labeller, mountPath string) func(w http.ResponseWriter, r *http.Request) {
err := os.MkdirAll(mountPath, secretFilePermission) err := os.MkdirAll(mountPath, secretFilePermission)
if err != nil { if err != nil {
@ -31,13 +31,13 @@ func MakeSecretHandler(c *containerd.Client, mountPath string) func(w http.Respo
switch r.Method { switch r.Method {
case http.MethodGet: case http.MethodGet:
listSecrets(c, w, r, mountPath) listSecrets(store, w, r, mountPath)
case http.MethodPost: case http.MethodPost:
createSecret(c, w, r, mountPath) createSecret(w, r, mountPath)
case http.MethodPut: case http.MethodPut:
createSecret(c, w, r, mountPath) createSecret(w, r, mountPath)
case http.MethodDelete: case http.MethodDelete:
deleteSecret(c, w, r, mountPath) deleteSecret(w, r, mountPath)
default: default:
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
return return
@ -46,11 +46,11 @@ func MakeSecretHandler(c *containerd.Client, mountPath string) func(w http.Respo
} }
} }
func listSecrets(c *containerd.Client, w http.ResponseWriter, r *http.Request, mountPath string) { func listSecrets(store provider.Labeller, w http.ResponseWriter, r *http.Request, mountPath string) {
lookupNamespace := getRequestNamespace(readNamespaceFromQuery(r)) lookupNamespace := getRequestNamespace(readNamespaceFromQuery(r))
// Check if namespace exists, and it has the openfaas label // Check if namespace exists, and it has the openfaas label
valid, err := validNamespace(c, lookupNamespace) valid, err := validNamespace(store, lookupNamespace)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest) http.Error(w, err.Error(), http.StatusBadRequest)
return return
@ -63,8 +63,15 @@ func listSecrets(c *containerd.Client, w http.ResponseWriter, r *http.Request, m
mountPath = getNamespaceSecretMountPath(mountPath, lookupNamespace) mountPath = getNamespaceSecretMountPath(mountPath, lookupNamespace)
files, err := ioutil.ReadDir(mountPath) files, err := os.ReadDir(mountPath)
if os.IsNotExist(err) {
bytesOut, _ := json.Marshal([]types.Secret{})
w.Write(bytesOut)
return
}
if err != nil { if err != nil {
fmt.Printf("Error Occured: %s \n", err)
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
@ -78,7 +85,7 @@ func listSecrets(c *containerd.Client, w http.ResponseWriter, r *http.Request, m
w.Write(bytesOut) w.Write(bytesOut)
} }
func createSecret(c *containerd.Client, w http.ResponseWriter, r *http.Request, mountPath string) { func createSecret(w http.ResponseWriter, r *http.Request, mountPath string) {
secret, err := parseSecret(r) secret, err := parseSecret(r)
if err != nil { if err != nil {
log.Printf("[secret] error %s", err.Error()) log.Printf("[secret] error %s", err.Error())
@ -118,7 +125,7 @@ func createSecret(c *containerd.Client, w http.ResponseWriter, r *http.Request,
} }
} }
func deleteSecret(c *containerd.Client, w http.ResponseWriter, r *http.Request, mountPath string) { func deleteSecret(w http.ResponseWriter, r *http.Request, mountPath string) {
secret, err := parseSecret(r) secret, err := parseSecret(r)
if err != nil { if err != nil {
log.Printf("[secret] error %s", err.Error()) log.Printf("[secret] error %s", err.Error())

View File

@ -1,6 +1,9 @@
package handlers package handlers
import ( import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"os" "os"
@ -10,6 +13,8 @@ import (
"testing" "testing"
"github.com/openfaas/faas-provider/types" "github.com/openfaas/faas-provider/types"
"github.com/openfaas/faasd/pkg"
provider "github.com/openfaas/faasd/pkg/provider"
) )
func Test_parseSecret(t *testing.T) { func Test_parseSecret(t *testing.T) {
@ -161,3 +166,87 @@ func TestSecretCreation(t *testing.T) {
}) })
} }
} }
func TestListSecrets(t *testing.T) {
mountPath, err := os.MkdirTemp("", "test_secret_creation")
if err != nil {
t.Fatalf("unexpected error while creating temp directory: %s", err)
}
defer os.RemoveAll(mountPath)
cases := []struct {
name string
verb string
namespace string
labels map[string]string
status int
secretPath string
secret string
err string
expected []types.Secret
}{
{
name: "Get empty secret list for default namespace having no secret",
verb: http.MethodGet,
status: http.StatusOK,
secretPath: "/test-fn/foo",
secret: "bar",
expected: make([]types.Secret, 0),
},
{
name: "Get empty secret list for non-default namespace having no secret",
verb: http.MethodGet,
status: http.StatusOK,
secretPath: "/test-fn/foo",
secret: "bar",
expected: make([]types.Secret, 0),
namespace: "other-ns",
labels: map[string]string{
pkg.NamespaceLabel: "true",
},
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
labelStore := provider.NewFakeLabeller(tc.labels)
handler := MakeSecretHandler(labelStore, mountPath)
path := "http://example.com/foo"
if len(tc.namespace) > 0 {
path = path + fmt.Sprintf("?namespace=%s", tc.namespace)
}
req := httptest.NewRequest(tc.verb, path, nil)
w := httptest.NewRecorder()
handler(w, req)
resp := w.Result()
if resp.StatusCode != tc.status {
t.Fatalf("want status: %d, but got: %d", tc.status, resp.StatusCode)
}
if resp.StatusCode != http.StatusOK && w.Body.String() != tc.err {
t.Fatalf("want error message: %q, but got %q", tc.err, w.Body.String())
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Fatalf("can't read response of list %v", err)
}
var res []types.Secret
err = json.Unmarshal(body, &res)
if err != nil {
t.Fatalf("unable to unmarshal %q, error: %v", string(body), err)
}
if !reflect.DeepEqual(res, tc.expected) {
t.Fatalf("want response: %v, but got: %v", tc.expected, res)
}
})
}
}

View File

@ -43,7 +43,7 @@ func MakeUpdateHandler(client *containerd.Client, cni gocni.CNI, secretMountPath
namespace := getRequestNamespace(req.Namespace) namespace := getRequestNamespace(req.Namespace)
// Check if namespace exists, and it has the openfaas label // Check if namespace exists, and it has the openfaas label
valid, err := validNamespace(client, namespace) valid, err := validNamespace(client.NamespaceService(), namespace)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest) http.Error(w, err.Error(), http.StatusBadRequest)
return return

View File

@ -5,10 +5,9 @@ import (
"net/http" "net/http"
"path" "path"
"github.com/containerd/containerd"
"github.com/openfaas/faasd/pkg" "github.com/openfaas/faasd/pkg"
faasd "github.com/openfaas/faasd/pkg" faasd "github.com/openfaas/faasd/pkg"
provider "github.com/openfaas/faasd/pkg/provider"
) )
func getRequestNamespace(namespace string) string { func getRequestNamespace(namespace string) string {
@ -30,12 +29,11 @@ func getNamespaceSecretMountPath(userSecretPath string, namespace string) string
// validNamespace indicates whether the namespace is eligable to be // validNamespace indicates whether the namespace is eligable to be
// used for OpenFaaS functions. // used for OpenFaaS functions.
func validNamespace(client *containerd.Client, namespace string) (bool, error) { func validNamespace(store provider.Labeller, namespace string) (bool, error) {
if namespace == faasd.DefaultFunctionNamespace { if namespace == faasd.DefaultFunctionNamespace {
return true, nil return true, nil
} }
store := client.NamespaceService()
labels, err := store.Labels(context.Background(), namespace) labels, err := store.Labels(context.Background(), namespace)
if err != nil { if err != nil {
return false, err return false, err

25
pkg/provider/labeller.go Normal file
View File

@ -0,0 +1,25 @@
package provider
import "context"
// Labeller can return labels for a namespace from containerd.
type Labeller interface {
Labels(ctx context.Context, namespace string) (map[string]string, error)
}
//
// FakeLabeller can be used to fake labels applied on namespace to mark
// them valid/invalid for openfaas functions
type FakeLabeller struct {
labels map[string]string
}
func NewFakeLabeller(labels map[string]string) Labeller {
return &FakeLabeller{
labels: labels,
}
}
func (s *FakeLabeller) Labels(ctx context.Context, namespace string) (map[string]string, error) {
return s.labels, nil
}

View File

@ -8,6 +8,8 @@ import (
"os" "os"
"path" "path"
"sort" "sort"
"strconv"
"strings"
"github.com/alexellis/k3sup/pkg/env" "github.com/alexellis/k3sup/pkg/env"
"github.com/compose-spec/compose-go/loader" "github.com/compose-spec/compose-go/loader"
@ -26,7 +28,8 @@ import (
) )
const ( const (
workingDirectoryPermission = 0644 // workingDirectoryPermission user read/write/execute, group and others: read-only
workingDirectoryPermission = 0744
) )
type Service struct { type Service struct {
@ -145,6 +148,28 @@ func (s *Supervisor) Start(svcs []Service) error {
Type: "bind", Type: "bind",
Options: []string{"rbind", "rw"}, Options: []string{"rbind", "rw"},
}) })
// Only create directories, not files.
// Some files don't have a suffix, such as secrets.
if len(path.Ext(mnt.Src)) == 0 &&
!strings.HasPrefix(mnt.Src, "/var/lib/faasd/secrets/") {
// src is already prefixed with wd from an earlier step
src := mnt.Src
fmt.Printf("Creating local directory: %s\n", src)
if err := os.MkdirAll(src, workingDirectoryPermission); err != nil {
if !errors.Is(os.ErrExist, err) {
fmt.Printf("Unable to create: %s, %s\n", src, err)
}
}
if len(svc.User) > 0 {
uid, err := strconv.Atoi(svc.User)
if err == nil {
if err := os.Chown(src, uid, -1); err != nil {
fmt.Printf("Unable to chown: %s to %d, error: %s\n", src, uid, err)
}
}
}
}
} }
} }