mirror of
https://github.com/openfaas/faasd.git
synced 2025-06-09 00:16:46 +00:00
This is an optimization that uses the results cache created by CNI on the filesystem to store and fetch IP addresses for containers in the core services and for functions. As part of the change, the dependency on the syscall code from Weave net has been removed, and the code should compile on MacOS again. Updates and rebases the work in #38 by carlosedp Tested in the original PR, further testing in the incoming PR. Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
176 lines
4.0 KiB
Go
176 lines
4.0 KiB
Go
package handlers
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log"
|
|
"strings"
|
|
|
|
"github.com/opencontainers/runtime-spec/specs-go"
|
|
|
|
"github.com/containerd/containerd"
|
|
"github.com/containerd/containerd/namespaces"
|
|
"github.com/openfaas/faasd/pkg/cninetwork"
|
|
|
|
faasd "github.com/openfaas/faasd/pkg"
|
|
)
|
|
|
|
type Function struct {
|
|
name string
|
|
namespace string
|
|
image string
|
|
pid uint32
|
|
replicas int
|
|
IP string
|
|
labels map[string]string
|
|
annotations map[string]string
|
|
secrets []string
|
|
envVars map[string]string
|
|
envProcess string
|
|
}
|
|
|
|
// ListFunctions returns a map of all functions with running tasks on namespace
|
|
func ListFunctions(client *containerd.Client) (map[string]*Function, error) {
|
|
ctx := namespaces.WithNamespace(context.Background(), faasd.FunctionNamespace)
|
|
functions := make(map[string]*Function)
|
|
|
|
containers, err := client.Containers(ctx)
|
|
if err != nil {
|
|
return functions, err
|
|
}
|
|
|
|
for _, c := range containers {
|
|
name := c.ID()
|
|
f, err := GetFunction(client, name)
|
|
if err != nil {
|
|
log.Printf("error getting function %s: ", name)
|
|
return functions, err
|
|
}
|
|
functions[name] = &f
|
|
}
|
|
|
|
return functions, nil
|
|
}
|
|
|
|
// GetFunction returns a function that matches name
|
|
func GetFunction(client *containerd.Client, name string) (Function, error) {
|
|
ctx := namespaces.WithNamespace(context.Background(), faasd.FunctionNamespace)
|
|
fn := Function{}
|
|
|
|
c, err := client.LoadContainer(ctx, name)
|
|
if err != nil {
|
|
return Function{}, fmt.Errorf("unable to find function: %s, error %s", name, err)
|
|
}
|
|
|
|
image, err := c.Image(ctx)
|
|
if err != nil {
|
|
return fn, err
|
|
}
|
|
|
|
containerName := c.ID()
|
|
allLabels, labelErr := c.Labels(ctx)
|
|
|
|
if labelErr != nil {
|
|
log.Printf("cannot list container %s labels: %s", containerName, labelErr.Error())
|
|
}
|
|
|
|
labels, annotations := buildLabelsAndAnnotations(allLabels)
|
|
|
|
spec, err := c.Spec(ctx)
|
|
if err != nil {
|
|
return Function{}, fmt.Errorf("unable to load function spec for reading secrets: %s, error %s", name, err)
|
|
}
|
|
|
|
envVars, envProcess := readEnvFromProcessEnv(spec.Process.Env)
|
|
secrets := readSecretsFromMounts(spec.Mounts)
|
|
|
|
fn.name = containerName
|
|
fn.namespace = faasd.FunctionNamespace
|
|
fn.image = image.Name()
|
|
fn.labels = labels
|
|
fn.annotations = annotations
|
|
fn.secrets = secrets
|
|
fn.envVars = envVars
|
|
fn.envProcess = envProcess
|
|
|
|
replicas := 0
|
|
task, err := c.Task(ctx, nil)
|
|
if err == nil {
|
|
// Task for container exists
|
|
svc, err := task.Status(ctx)
|
|
if err != nil {
|
|
return Function{}, fmt.Errorf("unable to get task status for container: %s %s", name, err)
|
|
}
|
|
|
|
if svc.Status == "running" {
|
|
replicas = 1
|
|
fn.pid = task.Pid()
|
|
|
|
// Get container IP address
|
|
ip, err := cninetwork.GetIPAddress(name, task.Pid())
|
|
if err != nil {
|
|
return Function{}, err
|
|
}
|
|
fn.IP = ip
|
|
}
|
|
} else {
|
|
replicas = 0
|
|
}
|
|
|
|
fn.replicas = replicas
|
|
return fn, nil
|
|
}
|
|
|
|
func readEnvFromProcessEnv(env []string) (map[string]string, string) {
|
|
foundEnv := make(map[string]string)
|
|
fprocess := ""
|
|
for _, e := range env {
|
|
kv := strings.Split(e, "=")
|
|
if len(kv) == 1 {
|
|
continue
|
|
}
|
|
|
|
if kv[0] == "PATH" {
|
|
continue
|
|
}
|
|
|
|
if kv[0] == "fprocess" {
|
|
fprocess = kv[1]
|
|
continue
|
|
}
|
|
|
|
foundEnv[kv[0]] = kv[1]
|
|
}
|
|
|
|
return foundEnv, fprocess
|
|
}
|
|
|
|
func readSecretsFromMounts(mounts []specs.Mount) []string {
|
|
secrets := []string{}
|
|
for _, mnt := range mounts {
|
|
x := strings.Split(mnt.Destination, "/var/openfaas/secrets/")
|
|
if len(x) > 1 {
|
|
secrets = append(secrets, x[1])
|
|
}
|
|
|
|
}
|
|
return secrets
|
|
}
|
|
|
|
// buildLabelsAndAnnotations returns a separated list with labels first,
|
|
// followed by annotations by checking each key of ctrLabels for a prefix.
|
|
func buildLabelsAndAnnotations(ctrLabels map[string]string) (map[string]string, map[string]string) {
|
|
labels := make(map[string]string)
|
|
annotations := make(map[string]string)
|
|
|
|
for k, v := range ctrLabels {
|
|
if strings.HasPrefix(k, annotationLabelPrefix) {
|
|
annotations[strings.TrimPrefix(k, annotationLabelPrefix)] = v
|
|
} else {
|
|
labels[k] = v
|
|
}
|
|
}
|
|
|
|
return labels, annotations
|
|
}
|