mirror of
https://github.com/openfaas/faasd.git
synced 2025-06-20 23:06:33 +00:00
Compare commits
4 Commits
Author | SHA1 | Date | |
---|---|---|---|
5b92e7793d | |||
88f1aa0433 | |||
2b9efd29a0 | |||
db5312158c |
@ -16,13 +16,13 @@ runcmd:
|
|||||||
- mkdir -p /opt/cni/bin
|
- 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
|
- 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 /go/src/github.com/openfaas/
|
||||||
- cd /go/src/github.com/openfaas/ && git clone https://github.com/openfaas/faasd
|
- cd /go/src/github.com/openfaas/ && git clone https://github.com/openfaas/faasd && git checkout 0.9.0
|
||||||
- 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"
|
- curl -fSLs "https://github.com/openfaas/faasd/releases/download/0.9.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
|
- 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
|
||||||
- systemctl status -l faasd --no-pager
|
- systemctl status -l faasd --no-pager
|
||||||
- curl -sSLf https://cli.openfaas.com | sh
|
- curl -sSLf https://cli.openfaas.com | sh
|
||||||
- sleep 5 && journalctl -u faasd --no-pager
|
- sleep 60 && journalctl -u faasd --no-pager
|
||||||
- cat /var/lib/faasd/secrets/basic-auth-password | /usr/local/bin/faas-cli login --password-stdin
|
- cat /var/lib/faasd/secrets/basic-auth-password | /usr/local/bin/faas-cli login --password-stdin
|
||||||
|
@ -17,6 +17,7 @@ services:
|
|||||||
target: /run/secrets/basic-auth-user
|
target: /run/secrets/basic-auth-user
|
||||||
cap_add:
|
cap_add:
|
||||||
- CAP_NET_RAW
|
- CAP_NET_RAW
|
||||||
|
|
||||||
nats:
|
nats:
|
||||||
image: docker.io/library/nats-streaming:0.11.2
|
image: docker.io/library/nats-streaming:0.11.2
|
||||||
command:
|
command:
|
||||||
@ -25,6 +26,7 @@ services:
|
|||||||
- "8222"
|
- "8222"
|
||||||
- "--store=memory"
|
- "--store=memory"
|
||||||
- "--cluster_id=faas-cluster"
|
- "--cluster_id=faas-cluster"
|
||||||
|
|
||||||
prometheus:
|
prometheus:
|
||||||
image: docker.io/prom/prometheus:v2.14.0
|
image: docker.io/prom/prometheus:v2.14.0
|
||||||
volumes:
|
volumes:
|
||||||
@ -33,6 +35,7 @@ services:
|
|||||||
target: /etc/prometheus/prometheus.yml
|
target: /etc/prometheus/prometheus.yml
|
||||||
cap_add:
|
cap_add:
|
||||||
- CAP_NET_RAW
|
- CAP_NET_RAW
|
||||||
|
|
||||||
gateway:
|
gateway:
|
||||||
image: "docker.io/openfaas/gateway:0.18.17${ARCH_SUFFIX}"
|
image: "docker.io/openfaas/gateway:0.18.17${ARCH_SUFFIX}"
|
||||||
environment:
|
environment:
|
||||||
@ -58,6 +61,11 @@ services:
|
|||||||
target: /run/secrets/basic-auth-user
|
target: /run/secrets/basic-auth-user
|
||||||
cap_add:
|
cap_add:
|
||||||
- CAP_NET_RAW
|
- CAP_NET_RAW
|
||||||
|
depends_on:
|
||||||
|
- basic-auth-plugin
|
||||||
|
- nats
|
||||||
|
- prometheus
|
||||||
|
|
||||||
queue-worker:
|
queue-worker:
|
||||||
image: docker.io/openfaas/queue-worker:0.11.2
|
image: docker.io/openfaas/queue-worker:0.11.2
|
||||||
environment:
|
environment:
|
||||||
@ -80,3 +88,5 @@ services:
|
|||||||
target: /run/secrets/basic-auth-user
|
target: /run/secrets/basic-auth-user
|
||||||
cap_add:
|
cap_add:
|
||||||
- CAP_NET_RAW
|
- CAP_NET_RAW
|
||||||
|
depends_on:
|
||||||
|
- nats
|
||||||
|
@ -18,19 +18,6 @@ type Dev struct {
|
|||||||
CIDRs []*net.IPNet `json:"CIDRs,omitempty"`
|
CIDRs []*net.IPNet `json:"CIDRs,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func linkToNetDev(link netlink.Link) (Dev, error) {
|
|
||||||
addrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
|
|
||||||
if err != nil {
|
|
||||||
return Dev{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
netDev := Dev{Name: link.Attrs().Name, MAC: link.Attrs().HardwareAddr}
|
|
||||||
for _, addr := range addrs {
|
|
||||||
netDev.CIDRs = append(netDev.CIDRs, addr.IPNet)
|
|
||||||
}
|
|
||||||
return netDev, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConnectedToBridgeVethPeerIds returns peer indexes of veth links connected to
|
// ConnectedToBridgeVethPeerIds returns peer indexes of veth links connected to
|
||||||
// the given bridge. The peer index is used to query from a container netns
|
// the given bridge. The peer index is used to query from a container netns
|
||||||
// whether the container is connected to the bridge.
|
// whether the container is connected to the bridge.
|
||||||
|
19
pkg/cninetwork/weave_linux.go
Normal file
19
pkg/cninetwork/weave_linux.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// +build linux
|
||||||
|
|
||||||
|
package cninetwork
|
||||||
|
|
||||||
|
import "github.com/vishvananda/netlink"
|
||||||
|
|
||||||
|
func linkToNetDev(link netlink.Link) (Dev, error) {
|
||||||
|
|
||||||
|
addrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
|
||||||
|
if err != nil {
|
||||||
|
return Dev{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
netDev := Dev{Name: link.Attrs().Name, MAC: link.Attrs().HardwareAddr}
|
||||||
|
for _, addr := range addrs {
|
||||||
|
netDev.CIDRs = append(netDev.CIDRs, addr.IPNet)
|
||||||
|
}
|
||||||
|
return netDev, nil
|
||||||
|
}
|
10
pkg/cninetwork/weave_mac.go
Normal file
10
pkg/cninetwork/weave_mac.go
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
// +build darwin
|
||||||
|
|
||||||
|
package cninetwork
|
||||||
|
|
||||||
|
import "github.com/vishvananda/netlink"
|
||||||
|
|
||||||
|
func linkToNetDev(link netlink.Link) (Dev, error) {
|
||||||
|
|
||||||
|
return Dev{}, nil
|
||||||
|
}
|
106
pkg/depgraph/depgraph.go
Normal file
106
pkg/depgraph/depgraph.go
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
package depgraph
|
||||||
|
|
||||||
|
import "log"
|
||||||
|
|
||||||
|
// Node represents a node in a Graph with
|
||||||
|
// 0 to many edges
|
||||||
|
type Node struct {
|
||||||
|
Name string
|
||||||
|
Edges []*Node
|
||||||
|
}
|
||||||
|
|
||||||
|
// Graph is a collection of nodes
|
||||||
|
type Graph struct {
|
||||||
|
nodes []*Node
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDepgraph() *Graph {
|
||||||
|
return &Graph{
|
||||||
|
nodes: []*Node{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nodes returns the nodes within the graph
|
||||||
|
func (g *Graph) Nodes() []*Node {
|
||||||
|
return g.nodes
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contains returns true if the target Node is found
|
||||||
|
// in its list
|
||||||
|
func (g *Graph) Contains(target *Node) bool {
|
||||||
|
for _, g := range g.nodes {
|
||||||
|
if g.Name == target.Name {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add places a Node into the current Graph
|
||||||
|
func (g *Graph) Add(target *Node) {
|
||||||
|
g.nodes = append(g.nodes, target)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove deletes a target Node reference from the
|
||||||
|
// list of nodes in the graph
|
||||||
|
func (g *Graph) Remove(target *Node) {
|
||||||
|
var found *int
|
||||||
|
for i, n := range g.nodes {
|
||||||
|
if n == target {
|
||||||
|
found = &i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if found != nil {
|
||||||
|
g.nodes = append(g.nodes[:*found], g.nodes[*found+1:]...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve retruns a list of node names in order of their dependencies.
|
||||||
|
// A use case may be for determining the correct order to install
|
||||||
|
// software packages, or to start services.
|
||||||
|
// Based upon the algorithm described by Ferry Boender in the following article
|
||||||
|
// https://www.electricmonk.nl/log/2008/08/07/dependency-resolving-algorithm/
|
||||||
|
func (g *Graph) Resolve() []string {
|
||||||
|
resolved := &Graph{}
|
||||||
|
unresolved := &Graph{}
|
||||||
|
for _, node := range g.nodes {
|
||||||
|
resolve(node, resolved, unresolved)
|
||||||
|
}
|
||||||
|
|
||||||
|
order := []string{}
|
||||||
|
|
||||||
|
for _, node := range resolved.Nodes() {
|
||||||
|
order = append(order, node.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return order
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolve mutates the resolved graph for a given starting
|
||||||
|
// node. The unresolved graph is used to detect a circular graph
|
||||||
|
// error and will throw a panic. This can be caught with a resolve
|
||||||
|
// in a go routine.
|
||||||
|
func resolve(node *Node, resolved, unresolved *Graph) {
|
||||||
|
unresolved.Add(node)
|
||||||
|
|
||||||
|
for _, edge := range node.Edges {
|
||||||
|
|
||||||
|
if !resolved.Contains(edge) && unresolved.Contains(edge) {
|
||||||
|
log.Panicf("edge: %s may be a circular dependency", edge.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(edge, resolved, unresolved)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, r := range resolved.nodes {
|
||||||
|
if r.Name == node.Name {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resolved.Add(node)
|
||||||
|
unresolved.Remove(node)
|
||||||
|
}
|
41
pkg/depgraph/depgraph_test.go
Normal file
41
pkg/depgraph/depgraph_test.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package depgraph
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func Test_RemoveMedial(t *testing.T) {
|
||||||
|
g := Graph{nodes: []*Node{}}
|
||||||
|
a := &Node{Name: "A"}
|
||||||
|
b := &Node{Name: "B"}
|
||||||
|
c := &Node{Name: "C"}
|
||||||
|
|
||||||
|
g.nodes = append(g.nodes, a)
|
||||||
|
g.nodes = append(g.nodes, b)
|
||||||
|
g.nodes = append(g.nodes, c)
|
||||||
|
|
||||||
|
g.Remove(b)
|
||||||
|
|
||||||
|
for _, n := range g.nodes {
|
||||||
|
if n.Name == b.Name {
|
||||||
|
t.Fatalf("Found deleted node: %s", n.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_RemoveFinal(t *testing.T) {
|
||||||
|
g := Graph{nodes: []*Node{}}
|
||||||
|
a := &Node{Name: "A"}
|
||||||
|
b := &Node{Name: "B"}
|
||||||
|
c := &Node{Name: "C"}
|
||||||
|
|
||||||
|
g.nodes = append(g.nodes, a)
|
||||||
|
g.nodes = append(g.nodes, b)
|
||||||
|
g.nodes = append(g.nodes, c)
|
||||||
|
|
||||||
|
g.Remove(c)
|
||||||
|
|
||||||
|
for _, n := range g.nodes {
|
||||||
|
if n.Name == c.Name {
|
||||||
|
t.Fatalf("Found deleted node: %s", c.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
41
pkg/deployment_order.go
Normal file
41
pkg/deployment_order.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package pkg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/openfaas/faasd/pkg/depgraph"
|
||||||
|
)
|
||||||
|
|
||||||
|
func buildDeploymentOrder(svcs []Service) []string {
|
||||||
|
|
||||||
|
graph := buildServiceGraph(svcs)
|
||||||
|
|
||||||
|
order := graph.Resolve()
|
||||||
|
|
||||||
|
log.Printf("Start-up order:\n")
|
||||||
|
for _, node := range order {
|
||||||
|
log.Printf("- %s\n", node)
|
||||||
|
}
|
||||||
|
|
||||||
|
return order
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildServiceGraph(svcs []Service) *depgraph.Graph {
|
||||||
|
graph := depgraph.NewDepgraph()
|
||||||
|
|
||||||
|
nodeMap := map[string]*depgraph.Node{}
|
||||||
|
for _, s := range svcs {
|
||||||
|
n := &depgraph.Node{Name: s.Name}
|
||||||
|
nodeMap[s.Name] = n
|
||||||
|
graph.Add(n)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range svcs {
|
||||||
|
for _, d := range s.DependsOn {
|
||||||
|
nodeMap[s.Name].Edges = append(nodeMap[s.Name].Edges, nodeMap[d])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return graph
|
||||||
|
}
|
224
pkg/deployment_order_test.go
Normal file
224
pkg/deployment_order_test.go
Normal file
@ -0,0 +1,224 @@
|
|||||||
|
package pkg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_buildDeploymentOrder_ARequiresB(t *testing.T) {
|
||||||
|
svcs := []Service{
|
||||||
|
{
|
||||||
|
Name: "A",
|
||||||
|
DependsOn: []string{"B"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "B",
|
||||||
|
DependsOn: []string{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
order := buildDeploymentOrder(svcs)
|
||||||
|
|
||||||
|
if len(order) < len(svcs) {
|
||||||
|
t.Fatalf("length of order too short: %d", len(order))
|
||||||
|
}
|
||||||
|
|
||||||
|
got := order[0]
|
||||||
|
want := "B"
|
||||||
|
if got != want {
|
||||||
|
t.Fatalf("%s should be last to be installed, but was: %s", want, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_buildDeploymentOrder_ARequiresBAndC(t *testing.T) {
|
||||||
|
svcs := []Service{
|
||||||
|
{
|
||||||
|
Name: "A",
|
||||||
|
DependsOn: []string{"B", "C"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "B",
|
||||||
|
DependsOn: []string{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "C",
|
||||||
|
DependsOn: []string{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
order := buildDeploymentOrder(svcs)
|
||||||
|
|
||||||
|
if len(order) < len(svcs) {
|
||||||
|
t.Fatalf("length of order too short: %d", len(order))
|
||||||
|
}
|
||||||
|
|
||||||
|
a := indexStr(order, "a")
|
||||||
|
b := indexStr(order, "b")
|
||||||
|
c := indexStr(order, "c")
|
||||||
|
|
||||||
|
if a > b {
|
||||||
|
t.Fatalf("a should be after dependencies")
|
||||||
|
}
|
||||||
|
if a > c {
|
||||||
|
t.Fatalf("a should be after dependencies")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_buildDeploymentOrder_ARequiresBRequiresC(t *testing.T) {
|
||||||
|
svcs := []Service{
|
||||||
|
{
|
||||||
|
Name: "A",
|
||||||
|
DependsOn: []string{"B"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "B",
|
||||||
|
DependsOn: []string{"C"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "C",
|
||||||
|
DependsOn: []string{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
order := buildDeploymentOrder(svcs)
|
||||||
|
|
||||||
|
if len(order) < len(svcs) {
|
||||||
|
t.Fatalf("length of order too short: %d", len(order))
|
||||||
|
}
|
||||||
|
|
||||||
|
got := order[0]
|
||||||
|
want := "C"
|
||||||
|
if got != want {
|
||||||
|
t.Fatalf("%s should be last to be installed, but was: %s", want, got)
|
||||||
|
}
|
||||||
|
got = order[1]
|
||||||
|
want = "B"
|
||||||
|
if got != want {
|
||||||
|
t.Fatalf("%s should be last to be installed, but was: %s", want, got)
|
||||||
|
}
|
||||||
|
got = order[2]
|
||||||
|
want = "A"
|
||||||
|
if got != want {
|
||||||
|
t.Fatalf("%s should be last to be installed, but was: %s", want, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_buildDeploymentOrderCircularARequiresBRequiresA(t *testing.T) {
|
||||||
|
svcs := []Service{
|
||||||
|
{
|
||||||
|
Name: "A",
|
||||||
|
DependsOn: []string{"B"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "B",
|
||||||
|
DependsOn: []string{"A"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() { recover() }()
|
||||||
|
|
||||||
|
buildDeploymentOrder(svcs)
|
||||||
|
|
||||||
|
t.Fatalf("did not panic as expected")
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_buildDeploymentOrderComposeFile(t *testing.T) {
|
||||||
|
// svcs := []Service{}
|
||||||
|
file, err := LoadComposeFileWithArch("../", "docker-compose.yaml", func() (string, string) {
|
||||||
|
return "x86_64", "Linux"
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to load compose file: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
svcs, err := ParseCompose(file)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to parse compose file: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range svcs {
|
||||||
|
log.Printf("Service: %s\n", s.Name)
|
||||||
|
for _, d := range s.DependsOn {
|
||||||
|
log.Printf("Link: %s => %s\n", s.Name, d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
order := buildDeploymentOrder(svcs)
|
||||||
|
|
||||||
|
if len(order) < len(svcs) {
|
||||||
|
t.Fatalf("length of order too short: %d", len(order))
|
||||||
|
}
|
||||||
|
|
||||||
|
queueWorker := indexStr(order, "queue-worker")
|
||||||
|
nats := indexStr(order, "nats")
|
||||||
|
gateway := indexStr(order, "gateway")
|
||||||
|
prometheus := indexStr(order, "prometheus")
|
||||||
|
|
||||||
|
if prometheus > gateway {
|
||||||
|
t.Fatalf("Prometheus order was after gateway, and should be before")
|
||||||
|
}
|
||||||
|
if nats > gateway {
|
||||||
|
t.Fatalf("NATS order was after gateway, and should be before")
|
||||||
|
}
|
||||||
|
if nats > queueWorker {
|
||||||
|
t.Fatalf("NATS order was after queue-worker, and should be before")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_buildDeploymentOrderOpenFaaS(t *testing.T) {
|
||||||
|
svcs := []Service{
|
||||||
|
{
|
||||||
|
Name: "queue-worker",
|
||||||
|
DependsOn: []string{"nats"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "prometheus",
|
||||||
|
DependsOn: []string{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "gateway",
|
||||||
|
DependsOn: []string{"prometheus", "nats", "basic-auth-plugin"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "basic-auth-plugin",
|
||||||
|
DependsOn: []string{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "nats",
|
||||||
|
DependsOn: []string{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
order := buildDeploymentOrder(svcs)
|
||||||
|
|
||||||
|
if len(order) < len(svcs) {
|
||||||
|
t.Fatalf("length of order too short: %d", len(order))
|
||||||
|
}
|
||||||
|
|
||||||
|
queueWorker := indexStr(order, "queue-worker")
|
||||||
|
nats := indexStr(order, "nats")
|
||||||
|
gateway := indexStr(order, "gateway")
|
||||||
|
prometheus := indexStr(order, "prometheus")
|
||||||
|
|
||||||
|
if prometheus > gateway {
|
||||||
|
t.Fatalf("Prometheus order was after gateway, and should be before")
|
||||||
|
}
|
||||||
|
if nats > gateway {
|
||||||
|
t.Fatalf("NATS order was after gateway, and should be before")
|
||||||
|
}
|
||||||
|
if nats > queueWorker {
|
||||||
|
t.Fatalf("NATS order was after queue-worker, and should be before")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func indexStr(st []string, t string) int {
|
||||||
|
for n, s := range st {
|
||||||
|
if s == t {
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
@ -34,12 +34,13 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Service struct {
|
type Service struct {
|
||||||
Image string
|
Image string
|
||||||
Env []string
|
Env []string
|
||||||
Name string
|
Name string
|
||||||
Mounts []Mount
|
Mounts []Mount
|
||||||
Caps []string
|
Caps []string
|
||||||
Args []string
|
Args []string
|
||||||
|
DependsOn []string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Mount struct {
|
type Mount struct {
|
||||||
@ -92,7 +93,7 @@ func (s *Supervisor) Start(svcs []Service) error {
|
|||||||
images := map[string]containerd.Image{}
|
images := map[string]containerd.Image{}
|
||||||
|
|
||||||
for _, svc := range svcs {
|
for _, svc := range svcs {
|
||||||
fmt.Printf("Preparing: %s with image: %s\n", svc.Name, svc.Image)
|
fmt.Printf("Preparing %s with image: %s\n", svc.Name, svc.Image)
|
||||||
|
|
||||||
img, err := service.PrepareImage(ctx, s.client, svc.Image, defaultSnapshotter, faasServicesPullAlways)
|
img, err := service.PrepareImage(ctx, s.client, svc.Image, defaultSnapshotter, faasServicesPullAlways)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -104,12 +105,26 @@ func (s *Supervisor) Start(svcs []Service) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, svc := range svcs {
|
for _, svc := range svcs {
|
||||||
fmt.Printf("Reconciling: %s\n", svc.Name)
|
fmt.Printf("Removing old container for: %s\n", svc.Name)
|
||||||
|
|
||||||
containerErr := service.Remove(ctx, s.client, svc.Name)
|
containerErr := service.Remove(ctx, s.client, svc.Name)
|
||||||
if containerErr != nil {
|
if containerErr != nil {
|
||||||
return containerErr
|
return containerErr
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
order := buildDeploymentOrder(svcs)
|
||||||
|
|
||||||
|
for _, key := range order {
|
||||||
|
|
||||||
|
var svc *Service
|
||||||
|
for _, s := range svcs {
|
||||||
|
if s.Name == key {
|
||||||
|
svc = &s
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Starting: %s\n", svc.Name)
|
||||||
|
|
||||||
image := images[svc.Name]
|
image := images[svc.Name]
|
||||||
|
|
||||||
@ -123,7 +138,6 @@ func (s *Supervisor) Start(svcs []Service) error {
|
|||||||
Options: []string{"rbind", "rw"},
|
Options: []string{"rbind", "rw"},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mounts = append(mounts, specs.Mount{
|
mounts = append(mounts, specs.Mount{
|
||||||
@ -153,11 +167,11 @@ func (s *Supervisor) Start(svcs []Service) error {
|
|||||||
)
|
)
|
||||||
|
|
||||||
if containerCreateErr != nil {
|
if containerCreateErr != nil {
|
||||||
log.Printf("Error creating container %s\n", containerCreateErr)
|
log.Printf("Error creating container: %s\n", containerCreateErr)
|
||||||
return containerCreateErr
|
return containerCreateErr
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Created container %s\n", newContainer.ID())
|
log.Printf("Created container: %s\n", newContainer.ID())
|
||||||
|
|
||||||
task, err := newContainer.NewTask(ctx, cio.NewCreator(cio.WithStdio))
|
task, err := newContainer.NewTask(ctx, cio.NewCreator(cio.WithStdio))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -176,6 +190,7 @@ func (s *Supervisor) Start(svcs []Service) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("%s has IP: %s\n", newContainer.ID(), ip.String())
|
log.Printf("%s has IP: %s\n", newContainer.ID(), ip.String())
|
||||||
|
|
||||||
hosts, _ := ioutil.ReadFile("hosts")
|
hosts, _ := ioutil.ReadFile("hosts")
|
||||||
@ -277,10 +292,11 @@ func ParseCompose(config *compose.Config) ([]Service, error) {
|
|||||||
Name: s.Name,
|
Name: s.Name,
|
||||||
Image: s.Image,
|
Image: s.Image,
|
||||||
// ShellCommand is just an alias of string slice
|
// ShellCommand is just an alias of string slice
|
||||||
Args: []string(s.Command),
|
Args: []string(s.Command),
|
||||||
Caps: s.CapAdd,
|
Caps: s.CapAdd,
|
||||||
Env: env,
|
Env: env,
|
||||||
Mounts: mounts,
|
Mounts: mounts,
|
||||||
|
DependsOn: s.DependsOn,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -289,6 +305,12 @@ func ParseCompose(config *compose.Config) ([]Service, error) {
|
|||||||
|
|
||||||
// LoadComposeFile is a helper method for loading a docker-compose file
|
// LoadComposeFile is a helper method for loading a docker-compose file
|
||||||
func LoadComposeFile(wd string, file string) (*compose.Config, error) {
|
func LoadComposeFile(wd string, file string) (*compose.Config, error) {
|
||||||
|
return LoadComposeFileWithArch(wd, file, env.GetClientArch)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadComposeFileWithArch is a helper method for loading a docker-compose file
|
||||||
|
func LoadComposeFileWithArch(wd string, file string, archGetter ArchGetter) (*compose.Config, error) {
|
||||||
|
|
||||||
file = path.Join(wd, file)
|
file = path.Join(wd, file)
|
||||||
b, err := ioutil.ReadFile(file)
|
b, err := ioutil.ReadFile(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -300,7 +322,7 @@ func LoadComposeFile(wd string, file string) (*compose.Config, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
archSuffix, err := GetArchSuffix(env.GetClientArch)
|
archSuffix, err := GetArchSuffix(archGetter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func Test_ParseCompose(t *testing.T) {
|
func Test_ParseCompose(t *testing.T) {
|
||||||
|
|
||||||
wd := "testdata"
|
wd := "testdata"
|
||||||
expected := map[string]Service{
|
|
||||||
|
want := map[string]Service{
|
||||||
"basic-auth-plugin": {
|
"basic-auth-plugin": {
|
||||||
Name: "basic-auth-plugin",
|
Name: "basic-auth-plugin",
|
||||||
Image: "docker.io/openfaas/basic-auth-plugin:0.18.17",
|
Image: "docker.io/openfaas/basic-auth-plugin:0.18.17",
|
||||||
@ -73,7 +75,8 @@ func Test_ParseCompose(t *testing.T) {
|
|||||||
Dest: path.Join("/run/secrets", "basic-auth-user"),
|
Dest: path.Join("/run/secrets", "basic-auth-user"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Caps: []string{"CAP_NET_RAW"},
|
Caps: []string{"CAP_NET_RAW"},
|
||||||
|
DependsOn: []string{"nats"},
|
||||||
},
|
},
|
||||||
"queue-worker": {
|
"queue-worker": {
|
||||||
Name: "queue-worker",
|
Name: "queue-worker",
|
||||||
@ -103,32 +106,39 @@ func Test_ParseCompose(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
compose, err := LoadComposeFile(wd, "docker-compose.yaml")
|
compose, err := LoadComposeFileWithArch(wd, "docker-compose.yaml", func() (string, string) { return "x86_64", "Linux" })
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("can not read docker-compose fixture: %s", err)
|
t.Fatalf("can't read docker-compose file: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
services, err := ParseCompose(compose)
|
services, err := ParseCompose(compose)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("can not parse compose services: %s", err)
|
t.Fatalf("can't parse compose services: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(services) != len(expected) {
|
if len(services) != len(want) {
|
||||||
t.Fatalf("expected: %d services, got: %d", len(expected), len(services))
|
t.Fatalf("want: %d services, got: %d", len(want), len(services))
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, service := range services {
|
for _, service := range services {
|
||||||
exp, ok := expected[service.Name]
|
exp, ok := want[service.Name]
|
||||||
|
|
||||||
|
if service.Name == "gateway" {
|
||||||
|
if len(service.DependsOn) == 0 {
|
||||||
|
t.Fatalf("gateway should have at least one depends_on entry")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatalf("unexpected service: %s", service.Name)
|
t.Fatalf("incorrect service: %s", service.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
if service.Name != exp.Name {
|
if service.Name != exp.Name {
|
||||||
t.Fatalf("incorrect service Name:\n\texpected: %s,\n\tgot: %s", exp.Name, service.Name)
|
t.Fatalf("incorrect service Name:\n\twant: %s,\n\tgot: %s", exp.Name, service.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
if service.Image != exp.Image {
|
if service.Image != exp.Image {
|
||||||
t.Fatalf("incorrect service Image:\n\texpected: %s,\n\tgot: %s", exp.Image, service.Image)
|
t.Fatalf("incorrect service Image:\n\twant: %s,\n\tgot: %s", exp.Image, service.Image)
|
||||||
}
|
}
|
||||||
|
|
||||||
equalStringSlice(t, exp.Env, service.Env)
|
equalStringSlice(t, exp.Env, service.Env)
|
||||||
@ -136,41 +146,41 @@ func Test_ParseCompose(t *testing.T) {
|
|||||||
equalStringSlice(t, exp.Args, service.Args)
|
equalStringSlice(t, exp.Args, service.Args)
|
||||||
|
|
||||||
if !reflect.DeepEqual(exp.Mounts, service.Mounts) {
|
if !reflect.DeepEqual(exp.Mounts, service.Mounts) {
|
||||||
t.Fatalf("incorrect service Mounts:\n\texpected: %+v,\n\tgot: %+v", exp.Mounts, service.Mounts)
|
t.Fatalf("incorrect service Mounts:\n\twant: %+v,\n\tgot: %+v", exp.Mounts, service.Mounts)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func equalStringSlice(t *testing.T, expected, found []string) {
|
func equalStringSlice(t *testing.T, want, found []string) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
if (expected == nil) != (found == nil) {
|
if (want == nil) != (found == nil) {
|
||||||
t.Fatalf("unexpected nil slice: expected %+v, got %+v", expected, found)
|
t.Fatalf("unexpected nil slice: want %+v, got %+v", want, found)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(expected) != len(found) {
|
if len(want) != len(found) {
|
||||||
t.Fatalf("unequal slice length: expected %+v, got %+v", expected, found)
|
t.Fatalf("unequal slice length: want %+v, got %+v", want, found)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range expected {
|
for i := range want {
|
||||||
if expected[i] != found[i] {
|
if want[i] != found[i] {
|
||||||
t.Fatalf("unexpected value at postition %d: expected %s, got %s", i, expected[i], found[i])
|
t.Fatalf("unexpected value at postition %d: want %s, got %s", i, want[i], found[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func equalMountSlice(t *testing.T, expected, found []Mount) {
|
func equalMountSlice(t *testing.T, want, found []Mount) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
if (expected == nil) != (found == nil) {
|
if (want == nil) != (found == nil) {
|
||||||
t.Fatalf("unexpected nil slice: expected %+v, got %+v", expected, found)
|
t.Fatalf("unexpected nil slice: want %+v, got %+v", want, found)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(expected) != len(found) {
|
if len(want) != len(found) {
|
||||||
t.Fatalf("unequal slice length: expected %+v, got %+v", expected, found)
|
t.Fatalf("unequal slice length: want %+v, got %+v", want, found)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range expected {
|
for i := range want {
|
||||||
if !reflect.DeepEqual(expected[i], found[i]) {
|
if !reflect.DeepEqual(want[i], found[i]) {
|
||||||
t.Fatalf("unexpected value at postition %d: expected %s, got %s", i, expected[i], found[i])
|
t.Fatalf("unexpected value at postition %d: want %s, got %s", i, want[i], found[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -178,7 +188,7 @@ func equalMountSlice(t *testing.T, expected, found []Mount) {
|
|||||||
func Test_GetArchSuffix(t *testing.T) {
|
func Test_GetArchSuffix(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
name string
|
name string
|
||||||
expected string
|
want string
|
||||||
foundArch string
|
foundArch string
|
||||||
foundOS string
|
foundOS string
|
||||||
err string
|
err string
|
||||||
@ -192,37 +202,37 @@ func Test_GetArchSuffix(t *testing.T) {
|
|||||||
name: "x86 has no suffix",
|
name: "x86 has no suffix",
|
||||||
foundOS: "Linux",
|
foundOS: "Linux",
|
||||||
foundArch: "x86_64",
|
foundArch: "x86_64",
|
||||||
expected: "",
|
want: "",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "unknown arch has no suffix",
|
name: "unknown arch has no suffix",
|
||||||
foundOS: "Linux",
|
foundOS: "Linux",
|
||||||
foundArch: "anything_else",
|
foundArch: "anything_else",
|
||||||
expected: "",
|
want: "",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "armhf has armhf suffix",
|
name: "armhf has armhf suffix",
|
||||||
foundOS: "Linux",
|
foundOS: "Linux",
|
||||||
foundArch: "armhf",
|
foundArch: "armhf",
|
||||||
expected: "-armhf",
|
want: "-armhf",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "armv7l has armhf suffix",
|
name: "armv7l has armhf suffix",
|
||||||
foundOS: "Linux",
|
foundOS: "Linux",
|
||||||
foundArch: "armv7l",
|
foundArch: "armv7l",
|
||||||
expected: "-armhf",
|
want: "-armhf",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "arm64 has arm64 suffix",
|
name: "arm64 has arm64 suffix",
|
||||||
foundOS: "Linux",
|
foundOS: "Linux",
|
||||||
foundArch: "arm64",
|
foundArch: "arm64",
|
||||||
expected: "-arm64",
|
want: "-arm64",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "aarch64 has arm64 suffix",
|
name: "aarch64 has arm64 suffix",
|
||||||
foundOS: "Linux",
|
foundOS: "Linux",
|
||||||
foundArch: "aarch64",
|
foundArch: "aarch64",
|
||||||
expected: "-arm64",
|
want: "-arm64",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,15 +241,15 @@ func Test_GetArchSuffix(t *testing.T) {
|
|||||||
|
|
||||||
suffix, err := GetArchSuffix(testArchGetter(tc.foundArch, tc.foundOS))
|
suffix, err := GetArchSuffix(testArchGetter(tc.foundArch, tc.foundOS))
|
||||||
if tc.err != "" && err == nil {
|
if tc.err != "" && err == nil {
|
||||||
t.Fatalf("expected error %s but got nil", tc.err)
|
t.Fatalf("want error %s but got nil", tc.err)
|
||||||
} else if tc.err != "" && err.Error() != tc.err {
|
} else if tc.err != "" && err.Error() != tc.err {
|
||||||
t.Fatalf("expected error %s, got %s", tc.err, err.Error())
|
t.Fatalf("want error %s, got %s", tc.err, err.Error())
|
||||||
} else if tc.err == "" && err != nil {
|
} else if tc.err == "" && err != nil {
|
||||||
t.Fatalf("unexpected error %s", err.Error())
|
t.Fatalf("unexpected error %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
if suffix != tc.expected {
|
if suffix != tc.want {
|
||||||
t.Fatalf("expected suffix %s, got %s", tc.expected, suffix)
|
t.Fatalf("want suffix %s, got %s", tc.want, suffix)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
64
pkg/testdata/docker-compose.yaml
vendored
64
pkg/testdata/docker-compose.yaml
vendored
@ -1,12 +1,12 @@
|
|||||||
version: "3.7"
|
version: "3.7"
|
||||||
services:
|
services:
|
||||||
basic-auth-plugin:
|
basic-auth-plugin:
|
||||||
image: docker.io/openfaas/basic-auth-plugin:0.18.17
|
image: "docker.io/openfaas/basic-auth-plugin:0.18.17${ARCH_SUFFIX}"
|
||||||
environment:
|
environment:
|
||||||
port: "8080"
|
- port=8080
|
||||||
secret_mount_path: "/run/secrets"
|
- secret_mount_path=/run/secrets
|
||||||
user_filename: "basic-auth-user"
|
- user_filename=basic-auth-user
|
||||||
pass_filename: "basic-auth-password"
|
- pass_filename=basic-auth-password
|
||||||
volumes:
|
volumes:
|
||||||
# we assume cwd == /var/lib/faasd
|
# we assume cwd == /var/lib/faasd
|
||||||
- type: bind
|
- type: bind
|
||||||
@ -17,6 +17,7 @@ services:
|
|||||||
target: /run/secrets/basic-auth-user
|
target: /run/secrets/basic-auth-user
|
||||||
cap_add:
|
cap_add:
|
||||||
- CAP_NET_RAW
|
- CAP_NET_RAW
|
||||||
|
|
||||||
nats:
|
nats:
|
||||||
image: docker.io/library/nats-streaming:0.11.2
|
image: docker.io/library/nats-streaming:0.11.2
|
||||||
command:
|
command:
|
||||||
@ -25,6 +26,7 @@ services:
|
|||||||
- "8222"
|
- "8222"
|
||||||
- "--store=memory"
|
- "--store=memory"
|
||||||
- "--cluster_id=faas-cluster"
|
- "--cluster_id=faas-cluster"
|
||||||
|
|
||||||
prometheus:
|
prometheus:
|
||||||
image: docker.io/prom/prometheus:v2.14.0
|
image: docker.io/prom/prometheus:v2.14.0
|
||||||
volumes:
|
volumes:
|
||||||
@ -33,21 +35,22 @@ services:
|
|||||||
target: /etc/prometheus/prometheus.yml
|
target: /etc/prometheus/prometheus.yml
|
||||||
cap_add:
|
cap_add:
|
||||||
- CAP_NET_RAW
|
- CAP_NET_RAW
|
||||||
|
|
||||||
gateway:
|
gateway:
|
||||||
image: docker.io/openfaas/gateway:0.18.17
|
image: "docker.io/openfaas/gateway:0.18.17${ARCH_SUFFIX}"
|
||||||
environment:
|
environment:
|
||||||
basic_auth: "true"
|
- basic_auth=true
|
||||||
functions_provider_url: "http://faasd-provider:8081/"
|
- functions_provider_url=http://faasd-provider:8081/
|
||||||
direct_functions: "false"
|
- direct_functions=false
|
||||||
read_timeout: "60s"
|
- read_timeout=60s
|
||||||
write_timeout: "60s"
|
- write_timeout=60s
|
||||||
upstream_timeout: "65s"
|
- upstream_timeout=65s
|
||||||
faas_nats_address: "nats"
|
- faas_nats_address=nats
|
||||||
faas_nats_port: "4222"
|
- faas_nats_port=4222
|
||||||
auth_proxy_url: "http://basic-auth-plugin:8080/validate"
|
- auth_proxy_url=http://basic-auth-plugin:8080/validate
|
||||||
auth_proxy_pass_body: "false"
|
- auth_proxy_pass_body=false
|
||||||
secret_mount_path: "/run/secrets"
|
- secret_mount_path=/run/secrets
|
||||||
scale_from_zero: "true"
|
- scale_from_zero=true
|
||||||
volumes:
|
volumes:
|
||||||
# we assume cwd == /var/lib/faasd
|
# we assume cwd == /var/lib/faasd
|
||||||
- type: bind
|
- type: bind
|
||||||
@ -58,18 +61,23 @@ services:
|
|||||||
target: /run/secrets/basic-auth-user
|
target: /run/secrets/basic-auth-user
|
||||||
cap_add:
|
cap_add:
|
||||||
- CAP_NET_RAW
|
- CAP_NET_RAW
|
||||||
|
depends_on:
|
||||||
|
- basic-auth-plugin
|
||||||
|
- nats
|
||||||
|
- prometheus
|
||||||
|
|
||||||
queue-worker:
|
queue-worker:
|
||||||
image: docker.io/openfaas/queue-worker:0.11.2
|
image: docker.io/openfaas/queue-worker:0.11.2
|
||||||
environment:
|
environment:
|
||||||
faas_nats_address: "nats"
|
- faas_nats_address=nats
|
||||||
faas_nats_port: "4222"
|
- faas_nats_port=4222
|
||||||
gateway_invoke: "true"
|
- gateway_invoke=true
|
||||||
faas_gateway_address: "gateway"
|
- faas_gateway_address=gateway
|
||||||
ack_wait: "5m5s"
|
- ack_wait=5m5s
|
||||||
max_inflight: "1"
|
- max_inflight=1
|
||||||
write_debug: "false"
|
- write_debug=false
|
||||||
basic_auth: "true"
|
- basic_auth=true
|
||||||
secret_mount_path: "/run/secrets"
|
- secret_mount_path=/run/secrets
|
||||||
volumes:
|
volumes:
|
||||||
# we assume cwd == /var/lib/faasd
|
# we assume cwd == /var/lib/faasd
|
||||||
- type: bind
|
- type: bind
|
||||||
@ -80,3 +88,5 @@ services:
|
|||||||
target: /run/secrets/basic-auth-user
|
target: /run/secrets/basic-auth-user
|
||||||
cap_add:
|
cap_add:
|
||||||
- CAP_NET_RAW
|
- CAP_NET_RAW
|
||||||
|
depends_on:
|
||||||
|
- nats
|
||||||
|
Reference in New Issue
Block a user