From 237a026b7946d1328bc9b1f65572e6a2a0d501cf Mon Sep 17 00:00:00 2001 From: Alistair Hey Date: Wed, 20 Jan 2021 19:23:46 +0000 Subject: [PATCH] Provider returns secrets for a function This commit allows the provider to return a list of the names of the secrets mapped into an openfaas function. This was tested by building and deploying faasd on multipass and curling the provider directly and seeing the returned secrets list! Signed-off-by: Alistair Hey --- docker-compose.yaml | 4 +- go.mod | 2 +- go.sum | 4 +- pkg/provider/handlers/functions.go | 23 ++- pkg/provider/handlers/functions_test.go | 26 ++++ pkg/provider/handlers/info.go | 6 +- pkg/provider/handlers/info_test.go | 6 +- pkg/provider/handlers/read.go | 1 + pkg/provider/handlers/replicas.go | 1 + .../openfaas/faas-provider/types/model.go | 147 ++++++++++-------- .../openfaas/faas-provider/types/requests.go | 27 ++-- vendor/modules.txt | 2 +- 12 files changed, 157 insertions(+), 92 deletions(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index c16c0a7..4444152 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,7 +1,7 @@ version: "3.7" services: basic-auth-plugin: - image: "docker.io/openfaas/basic-auth-plugin:0.18.18${ARCH_SUFFIX}" + image: ghcr.io/openfaas/basic-auth:0.20.5 environment: - port=8080 - secret_mount_path=/run/secrets @@ -41,7 +41,7 @@ services: - "127.0.0.1:9090:9090" gateway: - image: "docker.io/openfaas/gateway:0.19.1${ARCH_SUFFIX}" + image: ghcr.io/openfaas/gateway:0.20.5 environment: - basic_auth=true - functions_provider_url=http://faasd-provider:8081/ diff --git a/go.mod b/go.mod index ef434c0..9c5d7f7 100644 --- a/go.mod +++ b/go.mod @@ -30,7 +30,7 @@ require ( github.com/opencontainers/runc v1.0.0-rc9 // indirect github.com/opencontainers/runtime-spec v1.0.2 github.com/openfaas/faas v0.0.0-20201205125747-9bbb25e3c7c4 - github.com/openfaas/faas-provider v0.15.3 + github.com/openfaas/faas-provider v0.16.2 github.com/pkg/errors v0.9.1 github.com/prometheus/procfs v0.2.0 // indirect github.com/sethvargo/go-password v0.1.3 diff --git a/go.sum b/go.sum index e2a4ccc..8c1a275 100644 --- a/go.sum +++ b/go.sum @@ -181,8 +181,8 @@ github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/ github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= github.com/openfaas/faas v0.0.0-20201205125747-9bbb25e3c7c4 h1:JJjthDw7WziZQ7sC5C+M2872mIdud5R+s6Cb0cXyPuA= github.com/openfaas/faas v0.0.0-20201205125747-9bbb25e3c7c4/go.mod h1:E0m2rLup0Vvxg53BKxGgaYAGcZa3Xl+vvL7vSi5yQ14= -github.com/openfaas/faas-provider v0.15.3 h1:tfjuL5F/tdoUr1J65XrUADyoe59x38VzN1w0DvBaTRk= -github.com/openfaas/faas-provider v0.15.3/go.mod h1:fq1JL0mX4rNvVVvRLaLRJ3H6o667sHuyP5p/7SZEe98= +github.com/openfaas/faas-provider v0.16.2 h1:ChpiZh1RM8zFIzvp31OPlKpTbh5Lcm7f91WCFcpW4gA= +github.com/openfaas/faas-provider v0.16.2/go.mod h1:fq1JL0mX4rNvVVvRLaLRJ3H6o667sHuyP5p/7SZEe98= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= diff --git a/pkg/provider/handlers/functions.go b/pkg/provider/handlers/functions.go index dd58fc1..d236dbb 100644 --- a/pkg/provider/handlers/functions.go +++ b/pkg/provider/handlers/functions.go @@ -3,6 +3,7 @@ package handlers import ( "context" "fmt" + "github.com/opencontainers/runtime-spec/specs-go" "log" "strings" @@ -22,6 +23,7 @@ type Function struct { IP string labels map[string]string annotations map[string]string + secrets []string } // ListFunctions returns a map of all functions with running tasks on namespace @@ -71,12 +73,19 @@ func GetFunction(client *containerd.Client, name string) (Function, 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) + } + + secrets := readSecretsFromMounts(spec.Mounts) + fn.name = containerName fn.namespace = faasd.FunctionNamespace fn.image = image.Name() fn.labels = labels fn.annotations = annotations - + fn.secrets = secrets replicas := 0 task, err := c.Task(ctx, nil) if err == nil { @@ -105,6 +114,18 @@ func GetFunction(client *containerd.Client, name string) (Function, error) { return fn, nil } +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) { diff --git a/pkg/provider/handlers/functions_test.go b/pkg/provider/handlers/functions_test.go index bc54c9a..d6458a8 100644 --- a/pkg/provider/handlers/functions_test.go +++ b/pkg/provider/handlers/functions_test.go @@ -2,6 +2,8 @@ package handlers import ( "fmt" + "github.com/opencontainers/runtime-spec/specs-go" + "reflect" "testing" ) @@ -27,3 +29,27 @@ func Test_BuildLabelsAndAnnotationsFromServiceSpec_Annotations(t *testing.T) { t.Errorf("want: '%s' entry in annotation map got: key not found", "current-time") } } + +func Test_SplitMountToSecrets(t *testing.T) { + type test struct { + Name string + Input []specs.Mount + Expected []string + } + tests := []test{ + {Name: "No matching openfaas secrets", Input: []specs.Mount{{Destination: "/foo/"}}, Expected: []string{}}, + {Name: "No Mounts", Input: []specs.Mount{{Destination: "/foo/"}}, Expected: []string{}}, + {Name: "One Mounts IS secret", Input: []specs.Mount{{Destination: "/var/openfaas/secrets/secret1"}}, Expected: []string{"secret1"}}, + {Name: "Multiple Mounts 1 secret", Input: []specs.Mount{{Destination: "/var/openfaas/secrets/secret1"}, {Destination: "/some/other/path"}}, Expected: []string{"secret1"}}, + {Name: "Multiple Mounts all secrets", Input: []specs.Mount{{Destination: "/var/openfaas/secrets/secret1"}, {Destination: "/var/openfaas/secrets/secret2"}}, Expected: []string{"secret1", "secret2"}}, + } + + for _, tc := range tests { + t.Run(tc.Name, func(t *testing.T) { + got := readSecretsFromMounts(tc.Input) + if !reflect.DeepEqual(got, tc.Expected) { + t.Fatalf("expected %s, got %s", tc.Expected, got) + } + }) + } +} diff --git a/pkg/provider/handlers/info.go b/pkg/provider/handlers/info.go index aa1d13f..19a8488 100644 --- a/pkg/provider/handlers/info.go +++ b/pkg/provider/handlers/info.go @@ -22,10 +22,10 @@ func MakeInfoHandler(version, sha string) http.HandlerFunc { defer r.Body.Close() } - infoResponse := types.InfoResponse{ + infoResponse := types.ProviderInfo{ Orchestration: OrchestrationIdentifier, - Provider: ProviderName, - Version: types.ProviderVersion{ + Name: ProviderName, + Version: &types.VersionInfo{ Release: version, SHA: sha, }, diff --git a/pkg/provider/handlers/info_test.go b/pkg/provider/handlers/info_test.go index 0576f85..e7ea471 100644 --- a/pkg/provider/handlers/info_test.go +++ b/pkg/provider/handlers/info_test.go @@ -15,14 +15,14 @@ func Test_InfoHandler(t *testing.T) { r := httptest.NewRequest("GET", "/", nil) handler(w, r) - resp := types.InfoResponse{} + resp := types.ProviderInfo{} err := json.Unmarshal(w.Body.Bytes(), &resp) if err != nil { t.Fatalf("unexpected error unmarshalling the response") } - if resp.Provider != ProviderName { - t.Fatalf("expected provider %q, got %q", ProviderName, resp.Provider) + if resp.Name != ProviderName { + t.Fatalf("expected provider %q, got %q", ProviderName, resp.Name) } if resp.Orchestration != OrchestrationIdentifier { diff --git a/pkg/provider/handlers/read.go b/pkg/provider/handlers/read.go index 1c1a74f..943dbed 100644 --- a/pkg/provider/handlers/read.go +++ b/pkg/provider/handlers/read.go @@ -31,6 +31,7 @@ func MakeReadHandler(client *containerd.Client) func(w http.ResponseWriter, r *h Namespace: fn.namespace, Labels: labels, Annotations: annotations, + Secrets: fn.secrets, }) } diff --git a/pkg/provider/handlers/replicas.go b/pkg/provider/handlers/replicas.go index 934afd4..52e2137 100644 --- a/pkg/provider/handlers/replicas.go +++ b/pkg/provider/handlers/replicas.go @@ -23,6 +23,7 @@ func MakeReplicaReaderHandler(client *containerd.Client) func(w http.ResponseWri Namespace: f.namespace, Labels: &f.labels, Annotations: &f.annotations, + Secrets: f.secrets, } functionBytes, _ := json.Marshal(found) diff --git a/vendor/github.com/openfaas/faas-provider/types/model.go b/vendor/github.com/openfaas/faas-provider/types/model.go index 9e73c21..5a09e3d 100644 --- a/vendor/github.com/openfaas/faas-provider/types/model.go +++ b/vendor/github.com/openfaas/faas-provider/types/model.go @@ -3,92 +3,45 @@ package types // FunctionDeployment represents a request to create or update a Function. type FunctionDeployment struct { - // Service corresponds to a Service + // Service is the name of the function deployment Service string `json:"service"` - // Image corresponds to a Docker image + // Image is a fully-qualified container image Image string `json:"image"` - // Network is specific to Docker Swarm - default overlay network is: func_functions - Network string `json:"network"` + // Namespace for the function, if supported by the faas-provider + Namespace string `json:"namespace,omitempty"` - // EnvProcess corresponds to the fprocess variable for your container watchdog. - EnvProcess string `json:"envProcess"` + // EnvProcess overrides the fprocess environment variable and can be used + // with the watchdog + EnvProcess string `json:"envProcess,omitempty"` - // EnvVars provides overrides for functions. - EnvVars map[string]string `json:"envVars"` + // EnvVars can be provided to set environment variables for the function runtime. + EnvVars map[string]string `json:"envVars,omitempty"` - // RegistryAuth is the registry authentication (optional) - // in the same encoded format as Docker native credentials - // (see ~/.docker/config.json) - RegistryAuth string `json:"registryAuth,omitempty"` - - // Constraints are specific to back-end orchestration platform - Constraints []string `json:"constraints"` + // Constraints are specific to the faas-provider. + Constraints []string `json:"constraints,omitempty"` // Secrets list of secrets to be made available to function - Secrets []string `json:"secrets"` + Secrets []string `json:"secrets,omitempty"` // Labels are metadata for functions which may be used by the - // back-end for making scheduling or routing decisions - Labels *map[string]string `json:"labels"` + // faas-provider or the gateway + Labels *map[string]string `json:"labels,omitempty"` // Annotations are metadata for functions which may be used by the - // back-end for management, orchestration, events and build tasks - Annotations *map[string]string `json:"annotations"` + // faas-provider or the gateway + Annotations *map[string]string `json:"annotations,omitempty"` // Limits for function - Limits *FunctionResources `json:"limits"` + Limits *FunctionResources `json:"limits,omitempty"` // Requests of resources requested by function - Requests *FunctionResources `json:"requests"` + Requests *FunctionResources `json:"requests,omitempty"` // ReadOnlyRootFilesystem removes write-access from the root filesystem // mount-point. - ReadOnlyRootFilesystem bool `json:"readOnlyRootFilesystem"` - - // Namespace for the function to be deployed into - Namespace string `json:"namespace,omitempty"` -} - -// FunctionResources Memory and CPU -type FunctionResources struct { - Memory string `json:"memory"` - CPU string `json:"cpu"` -} - -// FunctionStatus exported for system/functions endpoint -type FunctionStatus struct { - - // Name corresponds to a Service - Name string `json:"name"` - - // Image corresponds to a Docker image - Image string `json:"image"` - - // InvocationCount count of invocations - InvocationCount float64 `json:"invocationCount"` - - // Replicas desired within the cluster - Replicas uint64 `json:"replicas"` - - // EnvProcess is the process to pass to the watchdog, if in use - EnvProcess string `json:"envProcess"` - - // AvailableReplicas is the count of replicas ready to receive - // invocations as reported by the backend - AvailableReplicas uint64 `json:"availableReplicas"` - - // Labels are metadata for functions which may be used by the - // backend for making scheduling or routing decisions - Labels *map[string]string `json:"labels"` - - // Annotations are metadata for functions which may be used by the - // backend for management, orchestration, events and build tasks - Annotations *map[string]string `json:"annotations"` - - // Namespace where the function can be accessed - Namespace string `json:"namespace,omitempty"` + ReadOnlyRootFilesystem bool `json:"readOnlyRootFilesystem,omitempty"` } // Secret for underlying orchestrator @@ -97,3 +50,65 @@ type Secret struct { Namespace string `json:"namespace,omitempty"` Value string `json:"value,omitempty"` } + +// FunctionResources Memory and CPU +type FunctionResources struct { + Memory string `json:"memory,omitempty"` + CPU string `json:"cpu,omitempty"` +} + +// FunctionStatus exported for system/functions endpoint +type FunctionStatus struct { + + // Name is the name of the function deployment + Name string `json:"name"` + + // Image is a fully-qualified container image + Image string `json:"image"` + + // Namespace for the function, if supported by the faas-provider + Namespace string `json:"namespace,omitempty"` + + // EnvProcess overrides the fprocess environment variable and can be used + // with the watchdog + EnvProcess string `json:"envProcess,omitempty"` + + // EnvVars set environment variables for the function runtime + EnvVars map[string]string `json:"envVars,omitempty"` + + // Constraints are specific to the faas-provider + Constraints []string `json:"constraints,omitempty"` + + // Secrets list of secrets to be made available to function + Secrets []string `json:"secrets,omitempty"` + + // Labels are metadata for functions which may be used by the + // faas-provider or the gateway + Labels *map[string]string `json:"labels,omitempty"` + + // Annotations are metadata for functions which may be used by the + // faas-provider or the gateway + Annotations *map[string]string `json:"annotations,omitempty"` + + // Limits for function + Limits *FunctionResources `json:"limits,omitempty"` + + // Requests of resources requested by function + Requests *FunctionResources `json:"requests,omitempty"` + + // ReadOnlyRootFilesystem removes write-access from the root filesystem + // mount-point. + ReadOnlyRootFilesystem bool `json:"readOnlyRootFilesystem,omitempty"` + + // ** Status fields *8 + + // InvocationCount count of invocations + InvocationCount float64 `json:"invocationCount,omitempty"` + + // Replicas desired within the cluster + Replicas uint64 `json:"replicas,omitempty"` + + // AvailableReplicas is the count of replicas ready to receive + // invocations as reported by the faas-provider + AvailableReplicas uint64 `json:"availableReplicas,omitempty"` +} diff --git a/vendor/github.com/openfaas/faas-provider/types/requests.go b/vendor/github.com/openfaas/faas-provider/types/requests.go index 1963c79..752893e 100644 --- a/vendor/github.com/openfaas/faas-provider/types/requests.go +++ b/vendor/github.com/openfaas/faas-provider/types/requests.go @@ -9,20 +9,21 @@ type ScaleServiceRequest struct { Replicas uint64 `json:"replicas"` } -// InfoResponse provides information about the underlying provider -type InfoResponse struct { - Provider string `json:"provider"` - Version ProviderVersion `json:"version"` - Orchestration string `json:"orchestration"` -} - -// ProviderVersion provides the commit sha and release version number of the underlying provider -type ProviderVersion struct { - SHA string `json:"sha"` - Release string `json:"release"` -} - // DeleteFunctionRequest delete a deployed function type DeleteFunctionRequest struct { FunctionName string `json:"functionName"` } + +// ProviderInfo provides information about the configured provider +type ProviderInfo struct { + Name string `json:"provider"` + Version *VersionInfo `json:"version"` + Orchestration string `json:"orchestration"` +} + +// VersionInfo provides the commit message, sha and release version number +type VersionInfo struct { + CommitMessage string `json:"commit_message,omitempty"` + SHA string `json:"sha"` + Release string `json:"release"` +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 4a4744d..f95aa83 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -167,7 +167,7 @@ github.com/opencontainers/runc/libcontainer/user github.com/opencontainers/runtime-spec/specs-go # github.com/openfaas/faas v0.0.0-20201205125747-9bbb25e3c7c4 github.com/openfaas/faas/gateway/requests -# github.com/openfaas/faas-provider v0.15.3 +# github.com/openfaas/faas-provider v0.16.2 github.com/openfaas/faas-provider github.com/openfaas/faas-provider/auth github.com/openfaas/faas-provider/httputil