Skip to content

Commit

Permalink
Support auth via the cli auth config
Browse files Browse the repository at this point in the history
**What**
- Add the sdk Client to the Config object and refactor references to use
  the centrally configured client
- Update the request helpers to allow passing the auth setter
- Add cli/commands to the vendor so that we can reuse the NewCLIAuth
  constructor and the BasicAuth/TokenAuth implementations

Signed-off-by: Lucas Roesler <[email protected]>
  • Loading branch information
LucasRoesler authored and alexellis committed May 5, 2020
1 parent bc212b0 commit 30c5ee5
Show file tree
Hide file tree
Showing 12 changed files with 251 additions and 65 deletions.
21 changes: 20 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,32 @@ The purpose of this project is to certify that an OpenFaaS provider is doing wha

The tests assume a local environment with basic authentication turned off.

### Auth
The test _can_ use auth by setting an explicit Bearer token using the `-token` flag or by reading the CLI config when you set the `-enableAuth` flag.

```sh
echo -n $PASSWORD | faas-cli login --gateway=$OPENFAAS_URL --username admin --password-stdin
go test - v ./tests -enableAuth -gateway=$OPENFAAS_URL
```

### Kubernetes

Usage with local Kubernetes cluster:

```
```sh
export OPENFAAS_URL=http://127.0.0.1:31112/
make test-kubernetes
```

You will need to have access to `kubectl` for creating and cleaning state.

If you have enabled auth in your cluster, first login with the `faas-cli` and then use

```sh
export OPENFAAS_URL=http://127.0.0.1:31112/
make test-kubernetes .FEATURE_FLAGS='-enableAuth'
```

### Swarm

Usage with gateway on `http://127.0.0.1:8080/`:
Expand Down Expand Up @@ -72,6 +87,8 @@ make test-kubernetes .TEST_FLAGS='-run ^Test_SecretCRUD'
Some providers may not implement all features (yet) or an installation may have disabled a feature (e.g. scale to zero using the faas-idler)

```sh
-enableAuth
enable/disable authentication. The auth will be parsed from the default config in ~/.openfaas/config.yml
-gateway string
set the gateway URL, if empty use the gateway_url env variable
-scaleToZero
Expand All @@ -80,6 +97,8 @@ Some providers may not implement all features (yet) or an installation may have
enable/disable secret update tests (default true)
-swarm
helper flag to run only swarm-compatible tests only
-token string
authentication Bearer token override, enables auth automatically
```
These flags can be passed the the `Makefile` via the `.FEATURE_FLAGS` variable:
Expand Down
9 changes: 9 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,20 @@ module github.com/openfaas/certifier
go 1.13

require (
github.com/alexellis/go-execute v0.0.0-20200124154445-8697e4e28c5e // indirect
github.com/alexellis/hmac v0.0.0-20180624211220-5c52ab81c0de // indirect
github.com/docker/docker v1.13.1 // indirect
github.com/docker/docker-credential-helpers v0.6.3 // indirect
github.com/drone/envsubst v1.0.2 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/openfaas/faas v0.0.0-20200319193403-46b106878d2c // indirect
github.com/openfaas/faas-cli v0.0.0-20200403113215-3b98878aa1b6
github.com/openfaas/faas-provider v0.15.0
github.com/pkg/errors v0.9.1 // indirect
github.com/rakyll/hey v0.1.3
github.com/ryanuber/go-glob v1.0.0 // indirect
github.com/spf13/cobra v1.0.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/testify v1.5.1 // indirect
)
136 changes: 136 additions & 0 deletions go.sum

Large diffs are not rendered by default.

11 changes: 5 additions & 6 deletions tests/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,16 @@ package tests

import (
"net/http"
"time"

sdk "github.com/openfaas/faas-cli/proxy"
)

// Unauthenticated implements the sdk ClientAuthSetter as a noop, leaving
// the request unauthneticated
type Unauthenticated struct {
sdk.ClientAuth
}

func (auth *Unauthenticated) Set(req *http.Request) error {
return nil
}

var (
timeout = 5 * time.Second
defaultNamespace = ""
)
36 changes: 21 additions & 15 deletions tests/function_helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,30 @@ package tests
import (
"context"
"net/http"
"os"
"path"
"testing"

sdk "github.com/openfaas/faas-cli/proxy"
"github.com/openfaas/faas-provider/types"
)

var devnull = os.NewFile(0, os.DevNull)

func deploy(t *testing.T, createRequest *sdk.DeployFunctionSpec) int {
t.Helper()
gwURL := gatewayURL(t)
var stdout *os.File

// suppress the sdk fmt.Println, this hides statements like this that provide no
// useful information to the tests and clutter the output
// Deployed. 202 Accepted.
// URL: http://127.0.0.1:8080/function/test-throughput-scaling
stdout, os.Stdout = os.Stdout, devnull
defer func() {
os.Stdout = stdout
}()

client := sdk.NewClient(&Unauthenticated{}, gwURL, nil, &timeout)
statusCode := client.DeployFunction(context.Background(), createRequest)
statusCode := config.Client.DeployFunction(context.Background(), createRequest)
if statusCode >= 400 {
t.Fatalf("unable to deploy function: %d", statusCode)
}
Expand All @@ -24,10 +35,7 @@ func deploy(t *testing.T, createRequest *sdk.DeployFunctionSpec) int {
}

func list(t *testing.T, expectedStatusCode int) {
gwURL := gatewayURL(t)

client := sdk.NewClient(&Unauthenticated{}, gwURL, nil, &timeout)
functions, err := client.ListFunctions(context.Background(), defaultNamespace)
functions, err := config.Client.ListFunctions(context.Background(), defaultNamespace)
if err != nil {
t.Fatal(err)
}
Expand All @@ -38,10 +46,7 @@ func list(t *testing.T, expectedStatusCode int) {
}

func get(t *testing.T, name string) types.FunctionStatus {
gwURL := gatewayURL(t)

client := sdk.NewClient(&Unauthenticated{}, gwURL, nil, &timeout)
function, err := client.GetFunctionInfo(context.Background(), name, defaultNamespace)
function, err := config.Client.GetFunctionInfo(context.Background(), name, defaultNamespace)
if err != nil {
t.Fatal(err)
}
Expand All @@ -51,21 +56,22 @@ func get(t *testing.T, name string) types.FunctionStatus {

func deleteFunction(t *testing.T, name string) {
t.Helper()
gwURL := gatewayURL(t)

client := sdk.NewClient(&Unauthenticated{}, gwURL, nil, &timeout)
err := client.DeleteFunction(context.Background(), name, defaultNamespace)
err := config.Client.DeleteFunction(context.Background(), name, defaultNamespace)
if err != nil {
t.Fatal(err)
}
}

func scaleFunction(t *testing.T, name string, count int) {
t.Helper()

// the CLI sdk does not currently support manually scaling
gwURL := resourceURL(t, path.Join("system", "scale-function", name), "")
payload := makeReader(map[string]interface{}{"service": name, "replicas": count})

_, res := request(t, gwURL, http.MethodPost, payload)
// TODO : enable auth
_, res := request(t, gwURL, http.MethodPost, config.Auth, payload)
if res.StatusCode != http.StatusAccepted && res.StatusCode != http.StatusOK {
t.Fatalf("scale got %d, wanted %d (or %d)", res.StatusCode, http.StatusAccepted, http.StatusOK)
}
Expand Down
3 changes: 2 additions & 1 deletion tests/health_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import (

func Test_HealthEndpoint(t *testing.T) {
gwURL := resourceURL(t, "healthz", "")
_, res := request(t, gwURL, http.MethodGet, nil)
// TODO: enable auth
_, res := request(t, gwURL, http.MethodGet, config.Auth, nil)
if res.StatusCode != http.StatusOK {
t.Fatalf("error with /healthz, got %d, but want %d", res.StatusCode, http.StatusOK)
}
Expand Down
26 changes: 12 additions & 14 deletions tests/http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,9 @@ import (
"net/url"
"path"
"testing"
)

// gatewayURL returns valid gateway url from the `gateway_url`
// in the ENV.
func gatewayURL(t *testing.T) string {
t.Helper()
uri, err := url.Parse(os.Getenv("gateway_url"))
if err != nil {
t.Fatalf("invalid gateway url %s", err)
}
return uri.String()
}
sdk "github.com/openfaas/faas-cli/proxy"
)

// resourceURL safely constructs the API url based on the `gateway_url`
// in the ENV.
Expand All @@ -42,12 +33,12 @@ func makeReader(input interface{}) *bytes.Buffer {
return bytes.NewBuffer(res)
}

func request(t *testing.T, url, method string, reader io.Reader) ([]byte, *http.Response) {
func request(t *testing.T, url, method string, auth sdk.ClientAuth, reader io.Reader) ([]byte, *http.Response) {
t.Helper()
return requestContext(t, context.Background(), url, method, reader)
return requestContext(t, context.Background(), url, method, auth, reader)
}

func requestContext(t *testing.T, ctx context.Context, url, method string, reader io.Reader) ([]byte, *http.Response) {
func requestContext(t *testing.T, ctx context.Context, url, method string, auth sdk.ClientAuth, reader io.Reader) ([]byte, *http.Response) {
t.Helper()

c := http.Client{
Expand All @@ -61,6 +52,13 @@ func requestContext(t *testing.T, ctx context.Context, url, method string, reade
t.Fatalf("error with request %s ", makeReqErr)
}

if auth != nil {
err := auth.Set(req)
if err != nil {
t.Fatalf("error setting the request auth %s ", err)
}
}

req = req.WithContext(ctx)

res, callErr := c.Do(req)
Expand Down
7 changes: 1 addition & 6 deletions tests/info_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,12 @@ package tests
import (
"context"
"testing"

// please only use the gateway types in this test
// other other tests should use the provider types
sdk "github.com/openfaas/faas-cli/proxy"
)

func Test_ProviderInfo(t *testing.T) {
gwURL := gatewayURL(t)

client := sdk.NewClient(&Unauthenticated{}, gwURL, nil, &timeout)
systeminfo, err := client.GetSystemInfo(context.Background())
systeminfo, err := config.Client.GetSystemInfo(context.Background())

if err != nil {
t.Fatal(err)
Expand Down
4 changes: 1 addition & 3 deletions tests/logs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,12 @@ func Test_FunctionLogs(t *testing.T) {
_ = invoke(t, functionName, "", http.StatusOK)

logRequest := logs.Request{Name: functionName, Tail: 2, Follow: false}
gwURL := gatewayURL(t)

// use context with timeout here to ensure we don't hang waiting for logs too long
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

client := sdk.NewClient(&Unauthenticated{}, gwURL, nil, &timeout)
logChan, err := client.GetLogs(ctx, logRequest)
logChan, err := config.Client.GetLogs(ctx, logRequest)
if err != nil {
t.Fatal(err)
}
Expand Down
45 changes: 37 additions & 8 deletions tests/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,39 @@ package tests

import (
"flag"
"fmt"
"log"
"net/url"
"os"
"path/filepath"
"strings"
"testing"
"time"

"github.com/openfaas/faas-cli/proxy"
"github.com/openfaas/faas-cli/commands"

sdkConfig "github.com/openfaas/faas-cli/config"

sdk "github.com/openfaas/faas-cli/proxy"
)

var (
config = Config{}
swarm = flag.Bool("swarm", false, "helper flag to run only swarm-compatible tests only")
config = Config{}
defaultNamespace = ""
swarm = flag.Bool("swarm", false, "helper flag to run only swarm-compatible tests only")
token = flag.String("token", "", "authentication Bearer token override, enables auth automatically")
)

func init() {

flag.StringVar(&config.Gateway, "gateway", "", "set the gateway URL, if empty use the gateway_url env variable")

flag.BoolVar(
&config.AuthEnabled,
"enableAuth",
false,
fmt.Sprintf("enable/disable authentication. The auth will be parsed from the default config in %s", filepath.Join(sdkConfig.DefaultDir, sdkConfig.DefaultFile)),
)
flag.BoolVar(&config.SecretUpdate, "secretUpdate", true, "enable/disable secret update tests")
flag.BoolVar(&config.ScaleToZero, "scaleToZero", true, "enable/disable scale from zero tests")
}
Expand All @@ -36,15 +52,23 @@ func TestMain(m *testing.M) {
config.Gateway = uri.String()
}

// make sure to trim any trailing slash because this is how the gateway is modified when
// saved to the config. if we don't do this, we wont find the saved auth.
config.Gateway = strings.TrimRight(config.Gateway, "/")

if *swarm {
config.SecretUpdate = false
config.ScaleToZero = false
}

// auth, err := cliConfig.LookupAuthConfig(config.Gateway)
// if err != nil {
// log.Fatalf("invalid gateway url %s", err)
// }
config.Auth = &Unauthenticated{}
if config.AuthEnabled || *token != "" {
// TODO : NewCLIAuth should return the error from LookupAuthConfig!
config.Auth = commands.NewCLIAuth(*token, config.Gateway)
}

timeout := 5 * time.Second
config.Client = sdk.NewClient(config.Auth, config.Gateway, nil, &timeout)

os.Exit(m.Run())
}
Expand All @@ -56,7 +80,12 @@ type Config struct {
// Gateway is the URL for the gateway that will be tested
Gateway string
// Auth contains the parsed proxy client auth
Auth proxy.ClientAuth
Auth sdk.ClientAuth
// Client is a preconfigured gateway client, including auth
Client *sdk.Client

// AuthEnabled
AuthEnabled bool

// SecretUpdate enables/disables the secret update test
SecretUpdate bool
Expand Down
Loading

0 comments on commit 30c5ee5

Please sign in to comment.