mirror of
https://github.com/openfaas/faasd.git
synced 2025-06-09 08:26:47 +00:00
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>
This commit is contained in:
parent
551e6645b7
commit
a2ea804d2c
@ -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),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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())
|
||||||
|
@ -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"
|
||||||
|
mock "github.com/openfaas/faasd/pkg/provider"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_parseSecret(t *testing.T) {
|
func Test_parseSecret(t *testing.T) {
|
||||||
@ -161,3 +166,90 @@ 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) {
|
||||||
|
fakeStore := &mock.FakeLabeller{}
|
||||||
|
fakeStore.FakeLabels = tc.labels
|
||||||
|
|
||||||
|
handler := MakeSecretHandler(fakeStore, 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.Logf("response body: %s", w.Body.String())
|
||||||
|
t.Fatalf("expected status: %d, got: %d", tc.status, resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK && w.Body.String() != tc.err {
|
||||||
|
t.Fatalf("expected error message: %q, got %q", tc.err, w.Body.String())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("can not read response of list %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var res []types.Secret
|
||||||
|
err = json.Unmarshal(body, &res)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("can not read response of list %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(res, tc.expected) {
|
||||||
|
t.Fatalf("expected response: %v, got: %v", tc.expected, res)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
18
pkg/provider/labeller.go
Normal file
18
pkg/provider/labeller.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package provider
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
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 {
|
||||||
|
FakeLabels map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *FakeLabeller) Labels(ctx context.Context, namespace string) (map[string]string, error) {
|
||||||
|
return s.FakeLabels, nil
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user