Compare commits

..

3 Commits

Author SHA1 Message Date
3fe0d8d8d3 Update messages to want/got for unit tests
This is the style used in the openfaas project.

Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2021-09-16 10:44:36 +01:00
5aa4c69e03 Inline namespace check and create const for label
* Inlines the namespace check for valid faasd namespaces
* Creates a const for the namespace label applied to faasd
namespaces

Tested with go build and go test.

Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2021-09-16 10:43:21 +01:00
12b5e8ca7f Add check for namespace label openfaas=true
This commit adds the checks that the namespace supplied by the user has
the `openfaas=true` label. Without this check the user can
deploy/update/read functions in any namespace  using the CLI.

The UI is not effected because it calls the listnamesaces endpoint,
which has the check for the label

Signed-off-by: Alistair Hey <alistair@heyal.co.uk>
2021-09-16 10:37:32 +01:00
15 changed files with 143 additions and 16 deletions

View File

@ -88,7 +88,7 @@ func makeProviderCmd() *cobra.Command {
baseUserSecretsPath := path.Join(wd, "secrets") baseUserSecretsPath := path.Join(wd, "secrets")
if err := moveSecretsToDefaultNamespaceSecrets( if err := moveSecretsToDefaultNamespaceSecrets(
baseUserSecretsPath, baseUserSecretsPath,
faasd.FunctionNamespace); err != nil { faasd.DefaultFunctionNamespace); err != nil {
return err return err
} }

View File

@ -1,8 +1,11 @@
package pkg package pkg
const ( const (
// FunctionNamespace is the default containerd namespace functions are created // DefaultFunctionNamespace is the default containerd namespace functions are created
FunctionNamespace = "openfaas-fn" DefaultFunctionNamespace = "openfaas-fn"
// NamespaceLabel indicates that a namespace is managed by faasd
NamespaceLabel = "openfaas"
// FaasdNamespace is the containerd namespace services are created // FaasdNamespace is the containerd namespace services are created
FaasdNamespace = "openfaas" FaasdNamespace = "openfaas"

View File

@ -71,7 +71,7 @@ func buildCmd(ctx context.Context, req logs.Request) *exec.Cmd {
namespace := req.Namespace namespace := req.Namespace
if namespace == "" { if namespace == "" {
namespace = faasd.FunctionNamespace namespace = faasd.DefaultFunctionNamespace
} }
// find the description of the fields here // find the description of the fields here

View File

@ -42,6 +42,18 @@ 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
valid, err := validNamespace(client, lookupNamespace)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
if !valid {
http.Error(w, "namespace not valid", http.StatusBadRequest)
return
}
name := req.FunctionName name := req.FunctionName
function, err := GetFunction(client, name, lookupNamespace) function, err := GetFunction(client, name, lookupNamespace)

View File

@ -52,10 +52,25 @@ 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
valid, err := validNamespace(client, namespace)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
if !valid {
http.Error(w, "namespace not valid", http.StatusBadRequest)
return
}
namespaceSecretMountPath := getNamespaceSecretMountPath(secretMountPath, namespace) namespaceSecretMountPath := getNamespaceSecretMountPath(secretMountPath, namespace)
err = validateSecrets(namespaceSecretMountPath, 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)
return
} }
name := req.Service name := req.Service

View File

@ -53,7 +53,7 @@ func Test_BuildLabels_WithAnnotations(t *testing.T) {
} }
if !reflect.DeepEqual(val, tc.result) { if !reflect.DeepEqual(val, tc.result) {
t.Errorf("Got: %s, expected %s", val, tc.result) t.Errorf("Want: %s, got: %s", val, tc.result)
} }
}) })
} }

View File

@ -2,6 +2,7 @@ package handlers
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"log" "log"
"strings" "strings"
@ -11,6 +12,7 @@ import (
"github.com/containerd/containerd" "github.com/containerd/containerd"
"github.com/containerd/containerd/namespaces" "github.com/containerd/containerd/namespaces"
"github.com/openfaas/faasd/pkg"
faasd "github.com/openfaas/faasd/pkg" faasd "github.com/openfaas/faasd/pkg"
"github.com/openfaas/faasd/pkg/cninetwork" "github.com/openfaas/faasd/pkg/cninetwork"
) )
@ -32,6 +34,17 @@ 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, 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
valid, err := validNamespace(client, namespace)
if err != nil {
return nil, err
}
if !valid {
return nil, errors.New("namespace not valid")
}
ctx := namespaces.WithNamespace(context.Background(), namespace) ctx := namespaces.WithNamespace(context.Background(), namespace)
functions := make(map[string]*Function) functions := make(map[string]*Function)
@ -187,7 +200,7 @@ func ListNamespaces(client *containerd.Client) []string {
namespaces, err := store.List(context.Background()) namespaces, err := store.List(context.Background())
if err != nil { if err != nil {
log.Printf("Error listing namespaces: %s", err.Error()) log.Printf("Error listing namespaces: %s", err.Error())
set = append(set, faasd.FunctionNamespace) set = append(set, faasd.DefaultFunctionNamespace)
return set return set
} }
@ -198,12 +211,12 @@ func ListNamespaces(client *containerd.Client) []string {
continue continue
} }
if _, found := labels["openfaas"]; found { if _, found := labels[pkg.NamespaceLabel]; found {
set = append(set, namespace) set = append(set, namespace)
} }
if !findNamespace(faasd.FunctionNamespace, set) { if !findNamespace(faasd.DefaultFunctionNamespace, set) {
set = append(set, faasd.FunctionNamespace) set = append(set, faasd.DefaultFunctionNamespace)
} }
} }

View File

@ -24,7 +24,7 @@ func (i *InvokeResolver) Resolve(functionName string) (url.URL, error) {
actualFunctionName := functionName actualFunctionName := functionName
log.Printf("Resolve: %q\n", actualFunctionName) log.Printf("Resolve: %q\n", actualFunctionName)
namespace := getNamespace(functionName, faasd.FunctionNamespace) namespace := getNamespace(functionName, faasd.DefaultFunctionNamespace)
if strings.Contains(functionName, ".") { if strings.Contains(functionName, ".") {
actualFunctionName = strings.TrimSuffix(functionName, "."+namespace) actualFunctionName = strings.TrimSuffix(functionName, "."+namespace)

View File

@ -14,6 +14,17 @@ 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)) lookupNamespace := getRequestNamespace(readNamespaceFromQuery(r))
// Check if namespace exists, and it has the openfaas label
valid, err := validNamespace(client, lookupNamespace)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
if !valid {
http.Error(w, "namespace not valid", http.StatusBadRequest)
return
}
res := []types.FunctionStatus{} res := []types.FunctionStatus{}
fns, err := ListFunctions(client, lookupNamespace) fns, err := ListFunctions(client, lookupNamespace)

View File

@ -16,6 +16,18 @@ func MakeReplicaReaderHandler(client *containerd.Client) func(w http.ResponseWri
functionName := vars["name"] functionName := vars["name"]
lookupNamespace := getRequestNamespace(readNamespaceFromQuery(r)) lookupNamespace := getRequestNamespace(readNamespaceFromQuery(r))
// Check if namespace exists, and it has the openfaas label
valid, err := validNamespace(client, lookupNamespace)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
if !valid {
http.Error(w, "namespace not valid", http.StatusBadRequest)
return
}
if f, err := GetFunction(client, functionName, lookupNamespace); err == nil { if f, err := GetFunction(client, functionName, lookupNamespace); err == nil {
found := types.FunctionStatus{ found := types.FunctionStatus{
Name: functionName, Name: functionName,

View File

@ -41,6 +41,18 @@ 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
valid, err := validNamespace(client, namespace)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
if !valid {
http.Error(w, "namespace not valid", http.StatusBadRequest)
return
}
name := req.ServiceName name := req.ServiceName
if _, err := GetFunction(client, name, namespace); err != nil { if _, err := GetFunction(client, name, namespace); err != nil {

View File

@ -49,6 +49,18 @@ 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)) lookupNamespace := getRequestNamespace(readNamespaceFromQuery(r))
// Check if namespace exists, and it has the openfaas label
valid, err := validNamespace(c, lookupNamespace)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
if !valid {
http.Error(w, "namespace not valid", http.StatusBadRequest)
return
}
mountPath = getNamespaceSecretMountPath(mountPath, lookupNamespace) mountPath = getNamespaceSecretMountPath(mountPath, lookupNamespace)
files, err := ioutil.ReadDir(mountPath) files, err := ioutil.ReadDir(mountPath)

View File

@ -41,6 +41,19 @@ func MakeUpdateHandler(client *containerd.Client, cni gocni.CNI, secretMountPath
} }
name := req.Service name := req.Service
namespace := getRequestNamespace(req.Namespace) namespace := getRequestNamespace(req.Namespace)
// Check if namespace exists, and it has the openfaas label
valid, err := validNamespace(client, namespace)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
if !valid {
http.Error(w, "namespace not valid", http.StatusBadRequest)
return
}
namespaceSecretMountPath := getNamespaceSecretMountPath(secretMountPath, namespace) namespaceSecretMountPath := getNamespaceSecretMountPath(secretMountPath, namespace)
function, err := GetFunction(client, name, namespace) function, err := GetFunction(client, name, namespace)

View File

@ -1,9 +1,13 @@
package handlers package handlers
import ( import (
"context"
"net/http" "net/http"
"path" "path"
"github.com/containerd/containerd"
"github.com/openfaas/faasd/pkg"
faasd "github.com/openfaas/faasd/pkg" faasd "github.com/openfaas/faasd/pkg"
) )
@ -12,7 +16,7 @@ func getRequestNamespace(namespace string) string {
if len(namespace) > 0 { if len(namespace) > 0 {
return namespace return namespace
} }
return faasd.FunctionNamespace return faasd.DefaultFunctionNamespace
} }
func readNamespaceFromQuery(r *http.Request) string { func readNamespaceFromQuery(r *http.Request) string {
@ -23,3 +27,23 @@ func readNamespaceFromQuery(r *http.Request) string {
func getNamespaceSecretMountPath(userSecretPath string, namespace string) string { func getNamespaceSecretMountPath(userSecretPath string, namespace string) string {
return path.Join(userSecretPath, namespace) return path.Join(userSecretPath, namespace)
} }
// validNamespace indicates whether the namespace is eligable to be
// used for OpenFaaS functions.
func validNamespace(client *containerd.Client, namespace string) (bool, error) {
if namespace == faasd.DefaultFunctionNamespace {
return true, nil
}
store := client.NamespaceService()
labels, err := store.Labels(context.Background(), namespace)
if err != nil {
return false, err
}
if value, found := labels[pkg.NamespaceLabel]; found && value == "true" {
return true, nil
}
return false, nil
}

View File

@ -15,7 +15,7 @@ func Test_getRequestNamespace(t *testing.T) {
requestNamespace string requestNamespace string
expectedNamespace string expectedNamespace string
}{ }{
{name: "RequestNamespace is not provided", requestNamespace: "", expectedNamespace: faasd.FunctionNamespace}, {name: "RequestNamespace is not provided", requestNamespace: "", expectedNamespace: faasd.DefaultFunctionNamespace},
{name: "RequestNamespace is provided", requestNamespace: "user-namespace", expectedNamespace: "user-namespace"}, {name: "RequestNamespace is provided", requestNamespace: "user-namespace", expectedNamespace: "user-namespace"},
} }
@ -23,7 +23,7 @@ func Test_getRequestNamespace(t *testing.T) {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
actualNamespace := getRequestNamespace(tc.requestNamespace) actualNamespace := getRequestNamespace(tc.requestNamespace)
if actualNamespace != tc.expectedNamespace { if actualNamespace != tc.expectedNamespace {
t.Errorf("Got: %s, expected %s", actualNamespace, tc.expectedNamespace) t.Errorf("Want: %s, got: %s", actualNamespace, tc.expectedNamespace)
} }
}) })
} }
@ -36,7 +36,7 @@ func Test_getNamespaceSecretMountPath(t *testing.T) {
requestNamespace string requestNamespace string
expectedSecretPath string expectedSecretPath string
}{ }{
{name: "Default Namespace is provided", requestNamespace: faasd.FunctionNamespace, expectedSecretPath: "/var/openfaas/secrets/" + faasd.FunctionNamespace}, {name: "Default Namespace is provided", requestNamespace: faasd.DefaultFunctionNamespace, expectedSecretPath: "/var/openfaas/secrets/" + faasd.DefaultFunctionNamespace},
{name: "User Namespace is provided", requestNamespace: "user-namespace", expectedSecretPath: "/var/openfaas/secrets/user-namespace"}, {name: "User Namespace is provided", requestNamespace: "user-namespace", expectedSecretPath: "/var/openfaas/secrets/user-namespace"},
} }
@ -44,7 +44,7 @@ func Test_getNamespaceSecretMountPath(t *testing.T) {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
actualNamespace := getNamespaceSecretMountPath(userSecretPath, tc.requestNamespace) actualNamespace := getNamespaceSecretMountPath(userSecretPath, tc.requestNamespace)
if actualNamespace != tc.expectedSecretPath { if actualNamespace != tc.expectedSecretPath {
t.Errorf("Got: %s, expected %s", actualNamespace, tc.expectedSecretPath) t.Errorf("Want: %s, got: %s", actualNamespace, tc.expectedSecretPath)
} }
}) })
} }
@ -68,7 +68,7 @@ func Test_readNamespaceFromQuery(t *testing.T) {
actualNamespace := readNamespaceFromQuery(r) actualNamespace := readNamespaceFromQuery(r)
if actualNamespace != tc.expectedNamespace { if actualNamespace != tc.expectedNamespace {
t.Errorf("Got: %s, expected %s", actualNamespace, tc.expectedNamespace) t.Errorf("Want: %s, got: %s", actualNamespace, tc.expectedNamespace)
} }
}) })
} }