mirror of
https://github.com/openfaas/faasd.git
synced 2025-06-09 08:26:47 +00:00
Enable multi namespace support
Signed-off-by: Nitishkumar Singh <nitishkumarsingh71@gmail.com> Included Test cases for utils Signed-off-by: Nitishkumar Singh <nitishkumarsingh71@gmail.com> Multi namespace handling in invoke Signed-off-by: Nitishkumar Singh <nitishkumarsingh71@gmail.com> List Namespaces capability included Signed-off-by: Nitishkumar Singh <nitishkumarsingh71@gmail.com> remove faasd namespace from list result Signed-off-by: Nitishkumar Singh <nitishkumarsingh71@gmail.com> Create Secret Folder Path Signed-off-by: Nitishkumar Singh <nitishkumarsingh71@gmail.com> Filter only namespaces with openfass label Signed-off-by: Nitishkumar Singh <nitishkumarsingh71@gmail.com> Include Testcase for utility function Signed-off-by: Nitishkumar Singh <nitishkumarsingh71@gmail.com> move default function secets to openfaas-fn namespace secrets Signed-off-by: Nitishkumar Singh <nitishkumarsingh71@gmail.com> Corrected issue with secret moving Signed-off-by: Nitishkumar Singh <nitishkumarsingh71@gmail.com>
This commit is contained in:
parent
73c7349e36
commit
dec02f3240
@ -1,7 +1,6 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
@ -14,6 +13,7 @@ import (
|
|||||||
"github.com/openfaas/faas-provider/logs"
|
"github.com/openfaas/faas-provider/logs"
|
||||||
"github.com/openfaas/faas-provider/proxy"
|
"github.com/openfaas/faas-provider/proxy"
|
||||||
"github.com/openfaas/faas-provider/types"
|
"github.com/openfaas/faas-provider/types"
|
||||||
|
faasd "github.com/openfaas/faasd/pkg"
|
||||||
"github.com/openfaas/faasd/pkg/cninetwork"
|
"github.com/openfaas/faasd/pkg/cninetwork"
|
||||||
faasdlogs "github.com/openfaas/faasd/pkg/logs"
|
faasdlogs "github.com/openfaas/faasd/pkg/logs"
|
||||||
"github.com/openfaas/faasd/pkg/provider/config"
|
"github.com/openfaas/faasd/pkg/provider/config"
|
||||||
@ -84,6 +84,11 @@ func makeProviderCmd() *cobra.Command {
|
|||||||
|
|
||||||
userSecretPath := path.Join(wd, "secrets")
|
userSecretPath := path.Join(wd, "secrets")
|
||||||
|
|
||||||
|
err = moveSecretsToDefaultNamespaceSecrets(userSecretPath, faasd.FunctionNamespace)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
bootstrapHandlers := types.FaaSHandlers{
|
bootstrapHandlers := types.FaaSHandlers{
|
||||||
FunctionProxy: proxy.NewHandlerFunc(*config, invokeResolver),
|
FunctionProxy: proxy.NewHandlerFunc(*config, invokeResolver),
|
||||||
DeleteHandler: handlers.MakeDeleteHandler(client, cni),
|
DeleteHandler: handlers.MakeDeleteHandler(client, cni),
|
||||||
@ -94,7 +99,7 @@ func makeProviderCmd() *cobra.Command {
|
|||||||
UpdateHandler: handlers.MakeUpdateHandler(client, cni, userSecretPath, alwaysPull),
|
UpdateHandler: handlers.MakeUpdateHandler(client, cni, userSecretPath, alwaysPull),
|
||||||
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: listNamespaces(),
|
ListNamespaceHandler: handlers.MakeNamespacesLister(client),
|
||||||
SecretHandler: handlers.MakeSecretHandler(client, userSecretPath),
|
SecretHandler: handlers.MakeSecretHandler(client, userSecretPath),
|
||||||
LogHandler: logs.NewLogHandlerFunc(faasdlogs.New(), config.ReadTimeout),
|
LogHandler: logs.NewLogHandlerFunc(faasdlogs.New(), config.ReadTimeout),
|
||||||
}
|
}
|
||||||
@ -107,10 +112,33 @@ func makeProviderCmd() *cobra.Command {
|
|||||||
return command
|
return command
|
||||||
}
|
}
|
||||||
|
|
||||||
func listNamespaces() func(w http.ResponseWriter, r *http.Request) {
|
/*
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
* Mutiple namespace support was added after release 0.13.0
|
||||||
list := []string{""}
|
* Function will help users to migrate on multiple namespace support of faasd
|
||||||
out, _ := json.Marshal(list)
|
*/
|
||||||
w.Write(out)
|
func moveSecretsToDefaultNamespaceSecrets(secretPath string, namespace string) error {
|
||||||
|
newSecretPath := path.Join(secretPath, namespace)
|
||||||
|
|
||||||
|
err := ensureWorkingDir(newSecretPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
files, err := ioutil.ReadDir(secretPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, f := range files {
|
||||||
|
if !f.IsDir() {
|
||||||
|
oldPath := path.Join(secretPath, f.Name())
|
||||||
|
newPath := path.Join(newSecretPath, f.Name())
|
||||||
|
err = os.Rename(oldPath, newPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -4,8 +4,8 @@ const (
|
|||||||
// FunctionNamespace is the default containerd namespace functions are created
|
// FunctionNamespace is the default containerd namespace functions are created
|
||||||
FunctionNamespace = "openfaas-fn"
|
FunctionNamespace = "openfaas-fn"
|
||||||
|
|
||||||
// faasdNamespace is the containerd namespace services are created
|
// FaasdNamespace is the containerd namespace services are created
|
||||||
faasdNamespace = "openfaas"
|
FaasdNamespace = "openfaas"
|
||||||
|
|
||||||
faasServicesPullAlways = false
|
faasServicesPullAlways = false
|
||||||
|
|
||||||
|
@ -13,7 +13,6 @@ import (
|
|||||||
gocni "github.com/containerd/go-cni"
|
gocni "github.com/containerd/go-cni"
|
||||||
"github.com/openfaas/faas/gateway/requests"
|
"github.com/openfaas/faas/gateway/requests"
|
||||||
|
|
||||||
faasd "github.com/openfaas/faasd/pkg"
|
|
||||||
cninetwork "github.com/openfaas/faasd/pkg/cninetwork"
|
cninetwork "github.com/openfaas/faasd/pkg/cninetwork"
|
||||||
"github.com/openfaas/faasd/pkg/service"
|
"github.com/openfaas/faasd/pkg/service"
|
||||||
)
|
)
|
||||||
@ -41,9 +40,11 @@ func MakeDeleteHandler(client *containerd.Client, cni gocni.CNI) func(w http.Res
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lookupNamespace := getRequestNamespace(readNamespaceFromQuery(r))
|
||||||
|
|
||||||
name := req.FunctionName
|
name := req.FunctionName
|
||||||
|
|
||||||
function, err := GetFunction(client, name)
|
function, err := GetFunction(client, name, lookupNamespace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
msg := fmt.Sprintf("service %s not found", name)
|
msg := fmt.Sprintf("service %s not found", name)
|
||||||
log.Printf("[Delete] %s\n", msg)
|
log.Printf("[Delete] %s\n", msg)
|
||||||
@ -51,7 +52,7 @@ func MakeDeleteHandler(client *containerd.Client, cni gocni.CNI) func(w http.Res
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := namespaces.WithNamespace(context.Background(), faasd.FunctionNamespace)
|
ctx := namespaces.WithNamespace(context.Background(), lookupNamespace)
|
||||||
|
|
||||||
// TODO: this needs to still happen if the task is paused
|
// TODO: this needs to still happen if the task is paused
|
||||||
if function.replicas != 0 {
|
if function.replicas != 0 {
|
||||||
|
@ -20,7 +20,6 @@ import (
|
|||||||
"github.com/docker/distribution/reference"
|
"github.com/docker/distribution/reference"
|
||||||
"github.com/opencontainers/runtime-spec/specs-go"
|
"github.com/opencontainers/runtime-spec/specs-go"
|
||||||
"github.com/openfaas/faas-provider/types"
|
"github.com/openfaas/faas-provider/types"
|
||||||
faasd "github.com/openfaas/faasd/pkg"
|
|
||||||
cninetwork "github.com/openfaas/faasd/pkg/cninetwork"
|
cninetwork "github.com/openfaas/faasd/pkg/cninetwork"
|
||||||
"github.com/openfaas/faasd/pkg/service"
|
"github.com/openfaas/faasd/pkg/service"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@ -52,15 +51,17 @@ func MakeDeployHandler(client *containerd.Client, cni gocni.CNI, secretMountPath
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = validateSecrets(secretMountPath, req.Secrets)
|
namespace := getRequestNamespace(req.Namespace)
|
||||||
|
namespaceSecretMountPath := getNamespaceSecretMountPath(secretMountPath, namespace)
|
||||||
|
err = validateSecrets(namespaceSecretMountPath, req.Secrets)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
name := req.Service
|
name := req.Service
|
||||||
ctx := namespaces.WithNamespace(context.Background(), faasd.FunctionNamespace)
|
ctx := namespaces.WithNamespace(context.Background(), namespace)
|
||||||
|
|
||||||
deployErr := deploy(ctx, req, client, cni, secretMountPath, alwaysPull)
|
deployErr := deploy(ctx, req, client, cni, namespaceSecretMountPath, alwaysPull)
|
||||||
if deployErr != nil {
|
if deployErr != nil {
|
||||||
log.Printf("[Deploy] error deploying %s, error: %s\n", name, deployErr)
|
log.Printf("[Deploy] error deploying %s, error: %s\n", name, deployErr)
|
||||||
http.Error(w, deployErr.Error(), http.StatusBadRequest)
|
http.Error(w, deployErr.Error(), http.StatusBadRequest)
|
||||||
|
@ -11,9 +11,8 @@ import (
|
|||||||
|
|
||||||
"github.com/containerd/containerd"
|
"github.com/containerd/containerd"
|
||||||
"github.com/containerd/containerd/namespaces"
|
"github.com/containerd/containerd/namespaces"
|
||||||
"github.com/openfaas/faasd/pkg/cninetwork"
|
|
||||||
|
|
||||||
faasd "github.com/openfaas/faasd/pkg"
|
faasd "github.com/openfaas/faasd/pkg"
|
||||||
|
"github.com/openfaas/faasd/pkg/cninetwork"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Function struct {
|
type Function struct {
|
||||||
@ -32,8 +31,8 @@ type Function struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ListFunctions returns a map of all functions with running tasks on namespace
|
// ListFunctions returns a map of all functions with running tasks on namespace
|
||||||
func ListFunctions(client *containerd.Client) (map[string]*Function, error) {
|
func ListFunctions(client *containerd.Client, namespace string) (map[string]*Function, error) {
|
||||||
ctx := namespaces.WithNamespace(context.Background(), faasd.FunctionNamespace)
|
ctx := namespaces.WithNamespace(context.Background(), namespace)
|
||||||
functions := make(map[string]*Function)
|
functions := make(map[string]*Function)
|
||||||
|
|
||||||
containers, err := client.Containers(ctx)
|
containers, err := client.Containers(ctx)
|
||||||
@ -43,7 +42,7 @@ func ListFunctions(client *containerd.Client) (map[string]*Function, error) {
|
|||||||
|
|
||||||
for _, c := range containers {
|
for _, c := range containers {
|
||||||
name := c.ID()
|
name := c.ID()
|
||||||
f, err := GetFunction(client, name)
|
f, err := GetFunction(client, name, namespace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("error getting function %s: ", name)
|
log.Printf("error getting function %s: ", name)
|
||||||
return functions, err
|
return functions, err
|
||||||
@ -55,8 +54,8 @@ func ListFunctions(client *containerd.Client) (map[string]*Function, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetFunction returns a function that matches name
|
// GetFunction returns a function that matches name
|
||||||
func GetFunction(client *containerd.Client, name string) (Function, error) {
|
func GetFunction(client *containerd.Client, name string, namespace string) (Function, error) {
|
||||||
ctx := namespaces.WithNamespace(context.Background(), faasd.FunctionNamespace)
|
ctx := namespaces.WithNamespace(context.Background(), namespace)
|
||||||
fn := Function{}
|
fn := Function{}
|
||||||
|
|
||||||
c, err := client.LoadContainer(ctx, name)
|
c, err := client.LoadContainer(ctx, name)
|
||||||
@ -92,7 +91,7 @@ func GetFunction(client *containerd.Client, name string) (Function, error) {
|
|||||||
secrets := readSecretsFromMounts(spec.Mounts)
|
secrets := readSecretsFromMounts(spec.Mounts)
|
||||||
|
|
||||||
fn.name = containerName
|
fn.name = containerName
|
||||||
fn.namespace = faasd.FunctionNamespace
|
fn.namespace = namespace
|
||||||
fn.image = image.Name()
|
fn.image = image.Name()
|
||||||
fn.labels = labels
|
fn.labels = labels
|
||||||
fn.annotations = annotations
|
fn.annotations = annotations
|
||||||
@ -181,3 +180,41 @@ func buildLabelsAndAnnotations(ctrLabels map[string]string) (map[string]string,
|
|||||||
|
|
||||||
return labels, annotations
|
return labels, annotations
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ListNamespaces(client *containerd.Client) []string {
|
||||||
|
set := []string{}
|
||||||
|
store := client.NamespaceService()
|
||||||
|
namespaces, err := store.List(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error listing namespaces: %s", err.Error())
|
||||||
|
set = append(set, faasd.FunctionNamespace)
|
||||||
|
return set
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, namespace := range namespaces {
|
||||||
|
labels, err := store.Labels(context.Background(), namespace)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error listing label for namespace %s: %s", namespace, err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, found := labels["openfaas"]; found {
|
||||||
|
set = append(set, namespace)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !findNamespace(faasd.FunctionNamespace, set) {
|
||||||
|
set = append(set, faasd.FunctionNamespace)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return set
|
||||||
|
}
|
||||||
|
|
||||||
|
func findNamespace(target string, items []string) bool {
|
||||||
|
for _, n := range items {
|
||||||
|
if n == target {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
@ -2,9 +2,10 @@ package handlers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/opencontainers/runtime-spec/specs-go"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/opencontainers/runtime-spec/specs-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_BuildLabelsAndAnnotationsFromServiceSpec_Annotations(t *testing.T) {
|
func Test_BuildLabelsAndAnnotationsFromServiceSpec_Annotations(t *testing.T) {
|
||||||
@ -84,3 +85,25 @@ func Test_ProcessEnvToEnvVars(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_findNamespace(t *testing.T) {
|
||||||
|
type test struct {
|
||||||
|
Name string
|
||||||
|
foundNamespaces []string
|
||||||
|
namespace string
|
||||||
|
Expected bool
|
||||||
|
}
|
||||||
|
tests := []test{
|
||||||
|
{Name: "Namespace Found", namespace: "fn", foundNamespaces: []string{"fn", "openfaas-fn"}, Expected: true},
|
||||||
|
{Name: "namespace Not Found", namespace: "fn", foundNamespaces: []string{"openfaas-fn"}, Expected: false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
t.Run(tc.Name, func(t *testing.T) {
|
||||||
|
got := findNamespace(tc.namespace, tc.foundNamespaces)
|
||||||
|
if got != tc.Expected {
|
||||||
|
t.Fatalf("expected %t, got %t", tc.Expected, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -4,8 +4,10 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/containerd/containerd"
|
"github.com/containerd/containerd"
|
||||||
|
faasd "github.com/openfaas/faasd/pkg"
|
||||||
)
|
)
|
||||||
|
|
||||||
const watchdogPort = 8080
|
const watchdogPort = 8080
|
||||||
@ -19,11 +21,18 @@ func NewInvokeResolver(client *containerd.Client) *InvokeResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *InvokeResolver) Resolve(functionName string) (url.URL, error) {
|
func (i *InvokeResolver) Resolve(functionName string) (url.URL, error) {
|
||||||
log.Printf("Resolve: %q\n", functionName)
|
actualFunctionName := functionName
|
||||||
|
log.Printf("Resolve: %q\n", actualFunctionName)
|
||||||
|
|
||||||
function, err := GetFunction(i.client, functionName)
|
namespace := getNamespace(functionName, faasd.FunctionNamespace)
|
||||||
|
|
||||||
|
if strings.Contains(functionName, ".") {
|
||||||
|
actualFunctionName = strings.TrimSuffix(functionName, "."+namespace)
|
||||||
|
}
|
||||||
|
|
||||||
|
function, err := GetFunction(i.client, actualFunctionName, namespace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return url.URL{}, fmt.Errorf("%s not found", functionName)
|
return url.URL{}, fmt.Errorf("%s not found", actualFunctionName)
|
||||||
}
|
}
|
||||||
|
|
||||||
serviceIP := function.IP
|
serviceIP := function.IP
|
||||||
@ -37,3 +46,11 @@ func (i *InvokeResolver) Resolve(functionName string) (url.URL, error) {
|
|||||||
|
|
||||||
return *urlRes, nil
|
return *urlRes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getNamespace(name, defaultNamespace string) string {
|
||||||
|
namespace := defaultNamespace
|
||||||
|
if strings.Contains(name, ".") {
|
||||||
|
namespace = name[strings.LastIndexAny(name, ".")+1:]
|
||||||
|
}
|
||||||
|
return namespace
|
||||||
|
}
|
||||||
|
18
pkg/provider/handlers/namespaces.go
Normal file
18
pkg/provider/handlers/namespaces.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/containerd/containerd"
|
||||||
|
)
|
||||||
|
|
||||||
|
func MakeNamespacesLister(client *containerd.Client) func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
list := ListNamespaces(client)
|
||||||
|
body, _ := json.Marshal(list)
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
w.Write(body)
|
||||||
|
}
|
||||||
|
}
|
@ -13,8 +13,10 @@ func MakeReadHandler(client *containerd.Client) func(w http.ResponseWriter, r *h
|
|||||||
|
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
lookupNamespace := getRequestNamespace(readNamespaceFromQuery(r))
|
||||||
|
|
||||||
res := []types.FunctionStatus{}
|
res := []types.FunctionStatus{}
|
||||||
fns, err := ListFunctions(client)
|
fns, err := ListFunctions(client, lookupNamespace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("[Read] error listing functions. Error: %s\n", err)
|
log.Printf("[Read] error listing functions. Error: %s\n", err)
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
@ -14,8 +14,9 @@ func MakeReplicaReaderHandler(client *containerd.Client) func(w http.ResponseWri
|
|||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
functionName := vars["name"]
|
functionName := vars["name"]
|
||||||
|
lookupNamespace := getRequestNamespace(readNamespaceFromQuery(r))
|
||||||
|
|
||||||
if f, err := GetFunction(client, functionName); err == nil {
|
if f, err := GetFunction(client, functionName, lookupNamespace); err == nil {
|
||||||
found := types.FunctionStatus{
|
found := types.FunctionStatus{
|
||||||
Name: functionName,
|
Name: functionName,
|
||||||
AvailableReplicas: uint64(f.replicas),
|
AvailableReplicas: uint64(f.replicas),
|
||||||
|
@ -13,7 +13,6 @@ import (
|
|||||||
gocni "github.com/containerd/go-cni"
|
gocni "github.com/containerd/go-cni"
|
||||||
|
|
||||||
"github.com/openfaas/faas-provider/types"
|
"github.com/openfaas/faas-provider/types"
|
||||||
faasd "github.com/openfaas/faasd/pkg"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func MakeReplicaUpdateHandler(client *containerd.Client, cni gocni.CNI) func(w http.ResponseWriter, r *http.Request) {
|
func MakeReplicaUpdateHandler(client *containerd.Client, cni gocni.CNI) func(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -40,16 +39,18 @@ func MakeReplicaUpdateHandler(client *containerd.Client, cni gocni.CNI) func(w h
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace := getRequestNamespace(readNamespaceFromQuery(r))
|
||||||
|
|
||||||
name := req.ServiceName
|
name := req.ServiceName
|
||||||
|
|
||||||
if _, err := GetFunction(client, name); err != nil {
|
if _, err := GetFunction(client, name, namespace); err != nil {
|
||||||
msg := fmt.Sprintf("service %s not found", name)
|
msg := fmt.Sprintf("service %s not found", name)
|
||||||
log.Printf("[Scale] %s\n", msg)
|
log.Printf("[Scale] %s\n", msg)
|
||||||
http.Error(w, msg, http.StatusNotFound)
|
http.Error(w, msg, http.StatusNotFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := namespaces.WithNamespace(context.Background(), faasd.FunctionNamespace)
|
ctx := namespaces.WithNamespace(context.Background(), namespace)
|
||||||
|
|
||||||
ctr, ctrErr := client.LoadContainer(ctx, name)
|
ctr, ctrErr := client.LoadContainer(ctx, name)
|
||||||
if ctrErr != nil {
|
if ctrErr != nil {
|
||||||
|
@ -15,6 +15,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const secretFilePermission = 0644
|
const secretFilePermission = 0644
|
||||||
|
const secretDirPermission = 0755
|
||||||
|
|
||||||
func MakeSecretHandler(c *containerd.Client, mountPath string) func(w http.ResponseWriter, r *http.Request) {
|
func MakeSecretHandler(c *containerd.Client, mountPath string) func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
@ -46,6 +47,10 @@ 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(c *containerd.Client, w http.ResponseWriter, r *http.Request, mountPath string) {
|
||||||
|
|
||||||
|
lookupNamespace := getRequestNamespace(readNamespaceFromQuery(r))
|
||||||
|
mountPath = getNamespaceSecretMountPath(mountPath, lookupNamespace)
|
||||||
|
|
||||||
files, err := ioutil.ReadDir(mountPath)
|
files, err := ioutil.ReadDir(mountPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
@ -54,7 +59,7 @@ func listSecrets(c *containerd.Client, w http.ResponseWriter, r *http.Request, m
|
|||||||
|
|
||||||
secrets := []types.Secret{}
|
secrets := []types.Secret{}
|
||||||
for _, f := range files {
|
for _, f := range files {
|
||||||
secrets = append(secrets, types.Secret{Name: f.Name()})
|
secrets = append(secrets, types.Secret{Name: f.Name(), Namespace: lookupNamespace})
|
||||||
}
|
}
|
||||||
|
|
||||||
bytesOut, _ := json.Marshal(secrets)
|
bytesOut, _ := json.Marshal(secrets)
|
||||||
@ -69,6 +74,16 @@ func createSecret(c *containerd.Client, w http.ResponseWriter, r *http.Request,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace := getRequestNamespace(secret.Namespace)
|
||||||
|
mountPath = getNamespaceSecretMountPath(mountPath, namespace)
|
||||||
|
|
||||||
|
err = os.MkdirAll(mountPath, secretDirPermission)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[secret] error %s", err.Error())
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
err = ioutil.WriteFile(path.Join(mountPath, secret.Name), []byte(secret.Value), secretFilePermission)
|
err = ioutil.WriteFile(path.Join(mountPath, secret.Name), []byte(secret.Value), secretFilePermission)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -86,6 +101,9 @@ func deleteSecret(c *containerd.Client, w http.ResponseWriter, r *http.Request,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace := getRequestNamespace(readNamespaceFromQuery(r))
|
||||||
|
mountPath = getNamespaceSecretMountPath(mountPath, namespace)
|
||||||
|
|
||||||
err = os.Remove(path.Join(mountPath, secret.Name))
|
err = os.Remove(path.Join(mountPath, secret.Name))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -13,7 +13,6 @@ import (
|
|||||||
gocni "github.com/containerd/go-cni"
|
gocni "github.com/containerd/go-cni"
|
||||||
"github.com/openfaas/faas-provider/types"
|
"github.com/openfaas/faas-provider/types"
|
||||||
|
|
||||||
faasd "github.com/openfaas/faasd/pkg"
|
|
||||||
"github.com/openfaas/faasd/pkg/cninetwork"
|
"github.com/openfaas/faasd/pkg/cninetwork"
|
||||||
"github.com/openfaas/faasd/pkg/service"
|
"github.com/openfaas/faasd/pkg/service"
|
||||||
)
|
)
|
||||||
@ -41,8 +40,10 @@ func MakeUpdateHandler(client *containerd.Client, cni gocni.CNI, secretMountPath
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
name := req.Service
|
name := req.Service
|
||||||
|
namespace := getRequestNamespace(req.Namespace)
|
||||||
|
namespaceSecretMountPath := getNamespaceSecretMountPath(secretMountPath, namespace)
|
||||||
|
|
||||||
function, err := GetFunction(client, name)
|
function, err := GetFunction(client, name, namespace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
msg := fmt.Sprintf("service %s not found", name)
|
msg := fmt.Sprintf("service %s not found", name)
|
||||||
log.Printf("[Update] %s\n", msg)
|
log.Printf("[Update] %s\n", msg)
|
||||||
@ -50,12 +51,12 @@ func MakeUpdateHandler(client *containerd.Client, cni gocni.CNI, secretMountPath
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = validateSecrets(secretMountPath, req.Secrets)
|
err = validateSecrets(namespaceSecretMountPath, req.Secrets)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := namespaces.WithNamespace(context.Background(), faasd.FunctionNamespace)
|
ctx := namespaces.WithNamespace(context.Background(), namespace)
|
||||||
|
|
||||||
if _, err := prepull(ctx, req, client, alwaysPull); err != nil {
|
if _, err := prepull(ctx, req, client, alwaysPull); err != nil {
|
||||||
log.Printf("[Update] error with pre-pull: %s, %s\n", name, err)
|
log.Printf("[Update] error with pre-pull: %s, %s\n", name, err)
|
||||||
@ -78,7 +79,7 @@ func MakeUpdateHandler(client *containerd.Client, cni gocni.CNI, secretMountPath
|
|||||||
// The pull has already been done in prepull, so we can force this pull to "false"
|
// The pull has already been done in prepull, so we can force this pull to "false"
|
||||||
pull := false
|
pull := false
|
||||||
|
|
||||||
if err := deploy(ctx, req, client, cni, secretMountPath, pull); err != nil {
|
if err := deploy(ctx, req, client, cni, namespaceSecretMountPath, pull); err != nil {
|
||||||
log.Printf("[Update] error deploying %s, error: %s\n", name, err)
|
log.Printf("[Update] error deploying %s, error: %s\n", name, err)
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
|
25
pkg/provider/handlers/utils.go
Normal file
25
pkg/provider/handlers/utils.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"path"
|
||||||
|
|
||||||
|
faasd "github.com/openfaas/faasd/pkg"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getRequestNamespace(namespace string) string {
|
||||||
|
|
||||||
|
if len(namespace) > 0 {
|
||||||
|
return namespace
|
||||||
|
}
|
||||||
|
return faasd.FunctionNamespace
|
||||||
|
}
|
||||||
|
|
||||||
|
func readNamespaceFromQuery(r *http.Request) string {
|
||||||
|
q := r.URL.Query()
|
||||||
|
return q.Get("namespace")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getNamespaceSecretMountPath(userSecretPath string, namespace string) string {
|
||||||
|
return path.Join(userSecretPath, namespace)
|
||||||
|
}
|
75
pkg/provider/handlers/utils_test.go
Normal file
75
pkg/provider/handlers/utils_test.go
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
faasd "github.com/openfaas/faasd/pkg"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_getRequestNamespace(t *testing.T) {
|
||||||
|
tables := []struct {
|
||||||
|
name string
|
||||||
|
requestNamespace string
|
||||||
|
expectedNamespace string
|
||||||
|
}{
|
||||||
|
{name: "RequestNamespace is not provided", requestNamespace: "", expectedNamespace: faasd.FunctionNamespace},
|
||||||
|
{name: "RequestNamespace is provided", requestNamespace: "user-namespace", expectedNamespace: "user-namespace"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tables {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
actualNamespace := getRequestNamespace(tc.requestNamespace)
|
||||||
|
if actualNamespace != tc.expectedNamespace {
|
||||||
|
t.Errorf("Got: %s, expected %s", actualNamespace, tc.expectedNamespace)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_getNamespaceSecretMountPath(t *testing.T) {
|
||||||
|
userSecretPath := "/var/openfaas/secrets"
|
||||||
|
tables := []struct {
|
||||||
|
name string
|
||||||
|
requestNamespace string
|
||||||
|
expectedSecretPath string
|
||||||
|
}{
|
||||||
|
{name: "Default Namespace is provided", requestNamespace: faasd.FunctionNamespace, expectedSecretPath: "/var/openfaas/secrets/" + faasd.FunctionNamespace},
|
||||||
|
{name: "User Namespace is provided", requestNamespace: "user-namespace", expectedSecretPath: "/var/openfaas/secrets/user-namespace"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tables {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
actualNamespace := getNamespaceSecretMountPath(userSecretPath, tc.requestNamespace)
|
||||||
|
if actualNamespace != tc.expectedSecretPath {
|
||||||
|
t.Errorf("Got: %s, expected %s", actualNamespace, tc.expectedSecretPath)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_readNamespaceFromQuery(t *testing.T) {
|
||||||
|
tables := []struct {
|
||||||
|
name string
|
||||||
|
queryNamespace string
|
||||||
|
expectedNamespace string
|
||||||
|
}{
|
||||||
|
{name: "No Namespace is provided", queryNamespace: "", expectedNamespace: ""},
|
||||||
|
{name: "User Namespace is provided", queryNamespace: "user-namespace", expectedNamespace: "user-namespace"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tables {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
|
||||||
|
url := fmt.Sprintf("/test?namespace=%s", tc.queryNamespace)
|
||||||
|
r := httptest.NewRequest(http.MethodGet, url, nil)
|
||||||
|
|
||||||
|
actualNamespace := readNamespaceFromQuery(r)
|
||||||
|
if actualNamespace != tc.expectedNamespace {
|
||||||
|
t.Errorf("Got: %s, expected %s", actualNamespace, tc.expectedNamespace)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -79,7 +79,7 @@ func NewSupervisor(sock string) (*Supervisor, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Supervisor) Start(svcs []Service) error {
|
func (s *Supervisor) Start(svcs []Service) error {
|
||||||
ctx := namespaces.WithNamespace(context.Background(), faasdNamespace)
|
ctx := namespaces.WithNamespace(context.Background(), FaasdNamespace)
|
||||||
|
|
||||||
wd, _ := os.Getwd()
|
wd, _ := os.Getwd()
|
||||||
|
|
||||||
@ -243,7 +243,7 @@ func (s *Supervisor) Close() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Supervisor) Remove(svcs []Service) error {
|
func (s *Supervisor) Remove(svcs []Service) error {
|
||||||
ctx := namespaces.WithNamespace(context.Background(), faasdNamespace)
|
ctx := namespaces.WithNamespace(context.Background(), FaasdNamespace)
|
||||||
|
|
||||||
for _, svc := range svcs {
|
for _, svc := range svcs {
|
||||||
err := cninetwork.DeleteCNINetwork(ctx, s.cni, s.client, svc.Name)
|
err := cninetwork.DeleteCNINetwork(ctx, s.cni, s.client, svc.Name)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user