Add webhook server to tests (#2873)
* Add webhook server to tests * fix config for webhoook service * Fix logger in webhook manager * Use interface for webhook manager * single reference for router url * Cleanup token code --------- Signed-off-by: Sanket Sudake <sanketsudake@gmail.com>
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
ignore:
|
ignore:
|
||||||
- "charts"
|
- "charts"
|
||||||
- "test/tests"
|
- "test/"
|
||||||
- "tools"
|
- "tools"
|
||||||
- "pkg/apis/genclient"
|
- "pkg/apis/genclient"
|
||||||
coverage:
|
coverage:
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ env:
|
|||||||
jobs:
|
jobs:
|
||||||
lint:
|
lint:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: ${{ !contains(github.event.pull_request.labels.*.name, 'skip-ci') }}
|
# if: ${{ !contains(github.event.pull_request.labels.*.name, 'skip-ci') }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import (
|
|||||||
docopt "github.com/docopt/docopt-go"
|
docopt "github.com/docopt/docopt-go"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/manager/signals"
|
"sigs.k8s.io/controller-runtime/pkg/manager/signals"
|
||||||
|
cnwebhook "sigs.k8s.io/controller-runtime/pkg/webhook"
|
||||||
|
|
||||||
"github.com/fission/fission/cmd/fission-bundle/mqtrigger"
|
"github.com/fission/fission/cmd/fission-bundle/mqtrigger"
|
||||||
"github.com/fission/fission/pkg/buildermgr"
|
"github.com/fission/fission/pkg/buildermgr"
|
||||||
@@ -48,8 +49,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// runWebhook starts admission webhook server
|
// runWebhook starts admission webhook server
|
||||||
func runWebhook(ctx context.Context, logger *zap.Logger, port int) error {
|
func runWebhook(ctx context.Context, clientGen crd.ClientGeneratorInterface, logger *zap.Logger, port int) error {
|
||||||
return webhook.Start(ctx, logger, port)
|
return webhook.Start(ctx, clientGen, logger, cnwebhook.Options{
|
||||||
|
Port: port,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func runCanaryConfigServer(ctx context.Context, clientGen crd.ClientGeneratorInterface, logger *zap.Logger, mgr manager.Interface) error {
|
func runCanaryConfigServer(ctx context.Context, clientGen crd.ClientGeneratorInterface, logger *zap.Logger, mgr manager.Interface) error {
|
||||||
@@ -235,7 +238,7 @@ Options:
|
|||||||
|
|
||||||
if arguments["--webhookPort"] != nil {
|
if arguments["--webhookPort"] != nil {
|
||||||
port := getPort(logger, arguments["--webhookPort"])
|
port := getPort(logger, arguments["--webhookPort"])
|
||||||
err = runWebhook(ctx, logger, port)
|
err = runWebhook(ctx, clientGen, logger, port)
|
||||||
logger.Error("webhook server exited:", zap.Error(err))
|
logger.Error("webhook server exited:", zap.Error(err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ func App(clientOptions cmd.ClientOptions) *cobra.Command {
|
|||||||
})
|
})
|
||||||
|
|
||||||
wrapper.SetFlags(rootCmd, flag.FlagSet{
|
wrapper.SetFlags(rootCmd, flag.FlagSet{
|
||||||
Global: []flag.Flag{flag.GlobalServer, flag.GlobalVerbosity, flag.KubeContext, flag.Namespace},
|
Global: []flag.Flag{flag.GlobalVerbosity, flag.KubeContext, flag.Namespace},
|
||||||
})
|
})
|
||||||
|
|
||||||
groups := helptemplate.CommandGroups{}
|
groups := helptemplate.CommandGroups{}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ require (
|
|||||||
github.com/fatih/color v1.15.0
|
github.com/fatih/color v1.15.0
|
||||||
github.com/fsnotify/fsnotify v1.7.0
|
github.com/fsnotify/fsnotify v1.7.0
|
||||||
github.com/go-git/go-git/v5 v5.10.0
|
github.com/go-git/go-git/v5 v5.10.0
|
||||||
|
github.com/go-logr/zapr v1.2.4
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.0
|
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||||
github.com/google/go-cmp v0.6.0
|
github.com/google/go-cmp v0.6.0
|
||||||
github.com/google/uuid v1.4.0
|
github.com/google/uuid v1.4.0
|
||||||
@@ -90,7 +91,6 @@ require (
|
|||||||
github.com/go-ini/ini v1.66.4 // indirect
|
github.com/go-ini/ini v1.66.4 // indirect
|
||||||
github.com/go-logr/logr v1.2.4 // indirect
|
github.com/go-logr/logr v1.2.4 // indirect
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/go-logr/zapr v1.2.4 // indirect
|
|
||||||
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
||||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||||
github.com/go-openapi/swag v0.22.3 // indirect
|
github.com/go-openapi/swag v0.22.3 // indirect
|
||||||
|
|||||||
+7
-3
@@ -37,6 +37,7 @@ import (
|
|||||||
|
|
||||||
type (
|
type (
|
||||||
ClientGeneratorInterface interface {
|
ClientGeneratorInterface interface {
|
||||||
|
GetRestConfig() (*rest.Config, error)
|
||||||
GetFissionClient() (versioned.Interface, error)
|
GetFissionClient() (versioned.Interface, error)
|
||||||
GetKubernetesClient() (kubernetes.Interface, error)
|
GetKubernetesClient() (kubernetes.Interface, error)
|
||||||
GetApiExtensionsClient() (apiextensionsclient.Interface, error)
|
GetApiExtensionsClient() (apiextensionsclient.Interface, error)
|
||||||
@@ -62,6 +63,10 @@ func (cg *ClientGenerator) getRestConfig() (*rest.Config, error) {
|
|||||||
return cg.restConfig, nil
|
return cg.restConfig, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cg *ClientGenerator) GetRestConfig() (*rest.Config, error) {
|
||||||
|
return cg.getRestConfig()
|
||||||
|
}
|
||||||
|
|
||||||
func (cg *ClientGenerator) GetFissionClient() (versioned.Interface, error) {
|
func (cg *ClientGenerator) GetFissionClient() (versioned.Interface, error) {
|
||||||
config, err := cg.getRestConfig()
|
config, err := cg.getRestConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -121,11 +126,10 @@ func WaitForFunctionCRDs(ctx context.Context, logger *zap.Logger, fissionClient
|
|||||||
for {
|
for {
|
||||||
fi := fissionClient.CoreV1().Functions(defaultNs)
|
fi := fissionClient.CoreV1().Functions(defaultNs)
|
||||||
_, err := fi.List(ctx, metav1.ListOptions{})
|
_, err := fi.List(ctx, metav1.ListOptions{})
|
||||||
if err != nil {
|
if err == nil {
|
||||||
time.Sleep(100 * time.Millisecond)
|
|
||||||
} else {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
|
||||||
if time.Since(start) > 30*time.Second {
|
if time.Since(start) > 30*time.Second {
|
||||||
return fmt.Errorf("timeout waiting for function CRD access")
|
return fmt.Errorf("timeout waiting for function CRD access")
|
||||||
|
|||||||
@@ -66,31 +66,21 @@ func (opts *TestSubCommand) do(input cli.Input) error {
|
|||||||
Namespace: namespace,
|
Namespace: namespace,
|
||||||
}
|
}
|
||||||
|
|
||||||
routerURL := os.Getenv("FISSION_ROUTER_URL")
|
routerURL, err := util.GetRouterURL(input.Context(), opts.Client())
|
||||||
if len(routerURL) == 0 {
|
if err != nil {
|
||||||
// Portforward to the fission router
|
return errors.Wrap(err, "error getting router URL")
|
||||||
localRouterPort, err := util.SetupPortForward(input.Context(), opts.Client(), util.GetFissionNamespace(), "application=fission-router")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
routerURL = "http://127.0.0.1:" + localRouterPort
|
|
||||||
}
|
}
|
||||||
fnURL := routerURL + util.UrlForFunction(m.Name, m.Namespace)
|
fnURI := util.UrlForFunction(m.Name, m.Namespace)
|
||||||
if input.IsSet(flagkey.FnSubPath) {
|
if input.IsSet(flagkey.FnSubPath) {
|
||||||
subPath := input.String(flagkey.FnSubPath)
|
subPath := input.String(flagkey.FnSubPath)
|
||||||
if !strings.HasPrefix(subPath, "/") {
|
if !strings.HasPrefix(subPath, "/") {
|
||||||
fnURL = fnURL + "/" + subPath
|
fnURI = fnURI + "/" + subPath
|
||||||
} else {
|
} else {
|
||||||
fnURL = fnURL + subPath
|
fnURI = fnURI + subPath
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fnURL := routerURL.JoinPath(fnURI)
|
||||||
functionUrl, err := url.Parse(fnURL)
|
console.Verbose(2, "Function test url: %v", fnURL.String())
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
console.Verbose(2, "Function test url: %v", functionUrl.String())
|
|
||||||
|
|
||||||
queryParams := input.StringSlice(flagkey.FnTestQuery)
|
queryParams := input.StringSlice(flagkey.FnTestQuery)
|
||||||
if len(queryParams) > 0 {
|
if len(queryParams) > 0 {
|
||||||
@@ -109,7 +99,7 @@ func (opts *TestSubCommand) do(input cli.Input) error {
|
|||||||
}
|
}
|
||||||
query.Set(key, value)
|
query.Set(key, value)
|
||||||
}
|
}
|
||||||
functionUrl.RawQuery = query.Encode()
|
fnURL.RawQuery = query.Encode()
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -145,7 +135,7 @@ func (opts *TestSubCommand) do(input cli.Input) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
resp, err := doHTTPRequest(ctx, functionUrl.String(),
|
resp, err := doHTTPRequest(ctx, fnURL.String(),
|
||||||
input.StringSlice(flagkey.FnTestHeader),
|
input.StringSlice(flagkey.FnTestHeader),
|
||||||
method,
|
method,
|
||||||
input.String(flagkey.FnTestBody))
|
input.String(flagkey.FnTestBody))
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@@ -30,6 +29,7 @@ import (
|
|||||||
fv1 "github.com/fission/fission/pkg/apis/core/v1"
|
fv1 "github.com/fission/fission/pkg/apis/core/v1"
|
||||||
"github.com/fission/fission/pkg/fission-cli/cliwrapper/cli"
|
"github.com/fission/fission/pkg/fission-cli/cliwrapper/cli"
|
||||||
"github.com/fission/fission/pkg/fission-cli/cmd"
|
"github.com/fission/fission/pkg/fission-cli/cmd"
|
||||||
|
"github.com/fission/fission/pkg/fission-cli/console"
|
||||||
flagkey "github.com/fission/fission/pkg/fission-cli/flag/key"
|
flagkey "github.com/fission/fission/pkg/fission-cli/flag/key"
|
||||||
"github.com/fission/fission/pkg/fission-cli/util"
|
"github.com/fission/fission/pkg/fission-cli/util"
|
||||||
)
|
)
|
||||||
@@ -64,26 +64,19 @@ func (opts *CreateSubCommand) run(input cli.Input) error {
|
|||||||
|
|
||||||
jsonValue, _ := json.Marshal(values)
|
jsonValue, _ := json.Marshal(values)
|
||||||
|
|
||||||
// Portforward to the fission router
|
|
||||||
localRouterPort, err := util.SetupPortForward(input.Context(), opts.Client(), util.GetFissionNamespace(), "application=fission-router")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
authURI, _ := os.LookupEnv("FISSION_AUTH_URI")
|
authURI, _ := os.LookupEnv("FISSION_AUTH_URI")
|
||||||
|
|
||||||
if input.IsSet(flagkey.TokAuthURI) {
|
if input.IsSet(flagkey.TokAuthURI) {
|
||||||
authURI = input.String(flagkey.TokAuthURI)
|
authURI = input.String(flagkey.TokAuthURI)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(authURI) == 0 {
|
if len(authURI) == 0 {
|
||||||
authURI = util.FISSION_AUTH_URI
|
authURI = util.FISSION_AUTH_URI
|
||||||
}
|
}
|
||||||
|
routerURL, err := util.GetRouterURL(input.Context(), opts.Client())
|
||||||
relativeURL, _ := url.Parse(authURI)
|
if err != nil {
|
||||||
serverURL, _ := url.Parse("http://127.0.0.1:" + localRouterPort)
|
return errors.Wrap(err, "error getting router URL")
|
||||||
authAuthenticatorUrl := serverURL.ResolveReference(relativeURL)
|
}
|
||||||
|
authAuthenticatorUrl := routerURL.JoinPath(authURI)
|
||||||
|
console.Verbose(2, "Auth URI: %s", authAuthenticatorUrl.String())
|
||||||
resp, err := http.Post(authAuthenticatorUrl.String(), "application/json", bytes.NewBuffer(jsonValue))
|
resp, err := http.Post(authAuthenticatorUrl.String(), "application/json", bytes.NewBuffer(jsonValue))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -68,7 +68,6 @@ const (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
GlobalVerbosity = Flag{Type: Int, Name: flagkey.Verbosity, Short: "v", Usage: "CLI verbosity (0 is quiet, 1 is the default, 2 is verbose)", DefaultValue: 1}
|
GlobalVerbosity = Flag{Type: Int, Name: flagkey.Verbosity, Short: "v", Usage: "CLI verbosity (0 is quiet, 1 is the default, 2 is verbose)", DefaultValue: 1}
|
||||||
GlobalServer = Flag{Type: String, Name: flagkey.Server, Usage: "Server URL"}
|
|
||||||
|
|
||||||
ClientOnly = Flag{Type: Bool, Name: flagkey.ClientOnly, Usage: "If set, the CLI won't connect to remote server"}
|
ClientOnly = Flag{Type: Bool, Name: flagkey.ClientOnly, Usage: "If set, the CLI won't connect to remote server"}
|
||||||
|
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ func GetVersion(ctx context.Context, input cli.Input, cmdClient cmd.Client) info
|
|||||||
func GetServerInfo(input cli.Input, cmdClient cmd.Client) *info.ServerInfo {
|
func GetServerInfo(input cli.Input, cmdClient cmd.Client) *info.ServerInfo {
|
||||||
|
|
||||||
var serverInfo info.ServerInfo
|
var serverInfo info.ServerInfo
|
||||||
serverURL, err := getRouterURL(input.Context(), cmdClient)
|
serverURL, err := GetRouterURL(input.Context(), cmdClient)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
console.Warn("could not connect to server")
|
console.Warn("could not connect to server")
|
||||||
return &serverInfo
|
return &serverInfo
|
||||||
@@ -212,7 +212,12 @@ func GetServerInfo(input cli.Input, cmdClient cmd.Client) *info.ServerInfo {
|
|||||||
return &serverInfo
|
return &serverInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
func getRouterURL(ctx context.Context, cmdClient cmd.Client) (serverURL *url.URL, err error) {
|
func GetRouterURL(ctx context.Context, cmdClient cmd.Client) (serverURL *url.URL, err error) {
|
||||||
|
routerURL := os.Getenv("FISSION_ROUTER_URL")
|
||||||
|
if len(routerURL) > 0 {
|
||||||
|
return url.Parse(routerURL)
|
||||||
|
}
|
||||||
|
|
||||||
// Portforward to the fission router
|
// Portforward to the fission router
|
||||||
localRouterPort, err := SetupPortForward(ctx, cmdClient, GetFissionNamespace(), "application=fission-router")
|
localRouterPort, err := SetupPortForward(ctx, cmdClient, GetFissionNamespace(), "application=fission-router")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -226,26 +231,6 @@ func getRouterURL(ctx context.Context, cmdClient cmd.Client) (serverURL *url.URL
|
|||||||
return serverURL, err
|
return serverURL, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetServerURL(input cli.Input, client cmd.Client) (serverUrl string, err error) {
|
|
||||||
serverUrl = input.GlobalString(flagkey.Server)
|
|
||||||
if len(serverUrl) == 0 {
|
|
||||||
// starts local portforwarder etc.
|
|
||||||
serverUrl, err = GetApplicationUrl(input.Context(), client, "application=fission-api")
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
isHTTPS := strings.Index(serverUrl, "https://") == 0
|
|
||||||
isHTTP := strings.Index(serverUrl, "http://") == 0
|
|
||||||
|
|
||||||
if !(isHTTP || isHTTPS) {
|
|
||||||
serverUrl = "http://" + serverUrl
|
|
||||||
}
|
|
||||||
|
|
||||||
return serverUrl, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetResourceReqs(input cli.Input, resReqs *v1.ResourceRequirements) (*v1.ResourceRequirements, error) {
|
func GetResourceReqs(input cli.Input, resReqs *v1.ResourceRequirements) (*v1.ResourceRequirements, error) {
|
||||||
r := &v1.ResourceRequirements{}
|
r := &v1.ResourceRequirements{}
|
||||||
|
|
||||||
|
|||||||
@@ -18,19 +18,23 @@ package webhook
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
|
// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
|
||||||
// to ensure that exec-entrypoint and run can make use of them.
|
// to ensure that exec-entrypoint and run can make use of them.
|
||||||
|
|
||||||
|
"github.com/go-logr/zapr"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
_ "k8s.io/client-go/plugin/pkg/client/auth"
|
_ "k8s.io/client-go/plugin/pkg/client/auth"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client/config"
|
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/manager"
|
"sigs.k8s.io/controller-runtime/pkg/manager"
|
||||||
|
|
||||||
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
|
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/webhook"
|
"sigs.k8s.io/controller-runtime/pkg/webhook"
|
||||||
|
|
||||||
v1 "github.com/fission/fission/pkg/apis/core/v1"
|
v1 "github.com/fission/fission/pkg/apis/core/v1"
|
||||||
|
"github.com/fission/fission/pkg/crd"
|
||||||
"github.com/fission/fission/pkg/generated/clientset/versioned/scheme"
|
"github.com/fission/fission/pkg/generated/clientset/versioned/scheme"
|
||||||
//+kubebuilder:scaffold:imports
|
//+kubebuilder:scaffold:imports
|
||||||
)
|
)
|
||||||
@@ -39,26 +43,33 @@ type WebhookInjector interface {
|
|||||||
SetupWebhookWithManager(mgr manager.Manager) error
|
SetupWebhookWithManager(mgr manager.Manager) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func Start(ctx context.Context, logger *zap.Logger, port int) (err error) {
|
func Start(ctx context.Context, clientGen crd.ClientGeneratorInterface, logger *zap.Logger, options webhook.Options) (err error) {
|
||||||
|
|
||||||
wLogger := logger.Named("webhook")
|
wLogger := logger.Named("webhook")
|
||||||
|
zaprLogger := zapr.NewLogger(logger)
|
||||||
|
log.SetLogger(zaprLogger)
|
||||||
|
|
||||||
metricsAddr := os.Getenv("METRICS_ADDR")
|
metricsAddr := os.Getenv("METRICS_ADDR")
|
||||||
if metricsAddr == "" {
|
if metricsAddr == "" {
|
||||||
metricsAddr = ":8080"
|
metricsAddr = ":8080"
|
||||||
}
|
}
|
||||||
|
if metricsAddr[0] != ':' {
|
||||||
|
metricsAddr = fmt.Sprintf(":%s", metricsAddr)
|
||||||
|
}
|
||||||
mgrOpt := manager.Options{
|
mgrOpt := manager.Options{
|
||||||
Scheme: scheme.Scheme,
|
Scheme: scheme.Scheme,
|
||||||
Metrics: metricsserver.Options{
|
Metrics: metricsserver.Options{
|
||||||
BindAddress: metricsAddr,
|
BindAddress: metricsAddr,
|
||||||
},
|
},
|
||||||
WebhookServer: webhook.NewServer(webhook.Options{
|
WebhookServer: webhook.NewServer(options),
|
||||||
Port: port,
|
Logger: zaprLogger,
|
||||||
}),
|
}
|
||||||
|
restConfig, err := clientGen.GetRestConfig()
|
||||||
|
if err != nil {
|
||||||
|
wLogger.Error("unable to get rest config", zap.Error(err))
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
// Setup a Manager
|
// Setup a Manager
|
||||||
mgr, err := manager.New(config.GetConfigOrDie(), mgrOpt)
|
mgr, err := manager.New(restConfig, mgrOpt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wLogger.Error("unable to set up overall controller manager", zap.Error(err))
|
wLogger.Error("unable to set up overall controller manager", zap.Error(err))
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -3,9 +3,11 @@ package cli_test
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
|
|
||||||
v1 "github.com/fission/fission/pkg/apis/core/v1"
|
v1 "github.com/fission/fission/pkg/apis/core/v1"
|
||||||
"github.com/fission/fission/pkg/utils/manager"
|
"github.com/fission/fission/pkg/utils/manager"
|
||||||
@@ -20,6 +22,7 @@ func TestFissionCLI(t *testing.T) {
|
|||||||
defer mgr.Wait()
|
defer mgr.Wait()
|
||||||
|
|
||||||
f := framework.NewFramework()
|
f := framework.NewFramework()
|
||||||
|
defer f.Logger().Sync()
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
err := f.Start(ctx)
|
err := f.Start(ctx)
|
||||||
@@ -28,6 +31,14 @@ func TestFissionCLI(t *testing.T) {
|
|||||||
err = services.StartServices(ctx, f, mgr)
|
err = services.StartServices(ctx, f, mgr)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = wait.PollUntilContextTimeout(ctx, time.Second*5, time.Second*50, true, func(_ context.Context) (bool, error) {
|
||||||
|
if err := f.CheckService("webhook"); err != nil {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
fissionClient, err := f.ClientGen().GetFissionClient()
|
fissionClient, err := f.ClientGen().GetFissionClient()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,13 @@ package framework
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
@@ -29,10 +33,34 @@ type Framework struct {
|
|||||||
env *envtest.Environment
|
env *envtest.Environment
|
||||||
config *rest.Config
|
config *rest.Config
|
||||||
logger *zap.Logger
|
logger *zap.Logger
|
||||||
ServiceInfo map[string]ServiceInfo
|
serviceInfo map[string]ServiceInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWebhookOptions() (*envtest.WebhookInstallOptions, error) {
|
||||||
|
webhookPort, err := utils.FindFreePort()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error finding unused port: %v", err)
|
||||||
|
}
|
||||||
|
_, filename, _, _ := runtime.Caller(0) //nolint
|
||||||
|
root := filepath.Dir(filename)
|
||||||
|
|
||||||
|
options := &envtest.WebhookInstallOptions{
|
||||||
|
LocalServingHost: "localhost",
|
||||||
|
LocalServingPort: webhookPort,
|
||||||
|
Paths: []string{filepath.Join(root, "webhook-manifest.yaml")},
|
||||||
|
}
|
||||||
|
err = options.PrepWithoutInstalling()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error preparing webhook install options: %v", err)
|
||||||
|
}
|
||||||
|
return options, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFramework() *Framework {
|
func NewFramework() *Framework {
|
||||||
|
webhookOptions, err := NewWebhookOptions()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
return &Framework{
|
return &Framework{
|
||||||
logger: loggerfactory.GetLogger(),
|
logger: loggerfactory.GetLogger(),
|
||||||
env: &envtest.Environment{
|
env: &envtest.Environment{
|
||||||
@@ -41,12 +69,17 @@ func NewFramework() *Framework {
|
|||||||
CRDInstallOptions: envtest.CRDInstallOptions{
|
CRDInstallOptions: envtest.CRDInstallOptions{
|
||||||
MaxTime: 60 * time.Second,
|
MaxTime: 60 * time.Second,
|
||||||
},
|
},
|
||||||
|
WebhookInstallOptions: *webhookOptions,
|
||||||
BinaryAssetsDirectory: os.Getenv("KUBEBUILDER_ASSETS"),
|
BinaryAssetsDirectory: os.Getenv("KUBEBUILDER_ASSETS"),
|
||||||
},
|
},
|
||||||
ServiceInfo: make(map[string]ServiceInfo),
|
serviceInfo: make(map[string]ServiceInfo),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *Framework) GetEnv() *envtest.Environment {
|
||||||
|
return f.env
|
||||||
|
}
|
||||||
|
|
||||||
func (f *Framework) Start(ctx context.Context) error {
|
func (f *Framework) Start(ctx context.Context) error {
|
||||||
var err error
|
var err error
|
||||||
f.config, err = f.env.Start()
|
f.config, err = f.env.Start()
|
||||||
@@ -84,3 +117,41 @@ func (f *Framework) Stop() error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *Framework) AddServiceInfo(name string, info ServiceInfo) {
|
||||||
|
f.serviceInfo[name] = info
|
||||||
|
f.logger.Info("Added service", zap.String("name", name), zap.Any("info", info))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Framework) GetServiceURL(name string) (string, error) {
|
||||||
|
info, ok := f.serviceInfo[name]
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("service %s not found", name)
|
||||||
|
}
|
||||||
|
if info.Port == 0 {
|
||||||
|
return "", fmt.Errorf("service %s port not set", name)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("http://localhost:%d", info.Port), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Framework) CheckService(name string) error {
|
||||||
|
_, err := f.GetServiceURL(name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
config := &tls.Config{
|
||||||
|
InsecureSkipVerify: true, //nolint:gosec // config is used to connect to our own webhook port.
|
||||||
|
}
|
||||||
|
|
||||||
|
d := &net.Dialer{Timeout: 10 * time.Second}
|
||||||
|
conn, err := tls.DialWithDialer(d, "tcp", net.JoinHostPort("localhost", strconv.Itoa(f.serviceInfo[name].Port)), config)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("webhook server is not reachable: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := conn.Close(); err != nil {
|
||||||
|
return fmt.Errorf("webhook server is not reachable: closing connection: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"go.uber.org/zap"
|
||||||
|
cnwebhook "sigs.k8s.io/controller-runtime/pkg/webhook"
|
||||||
|
|
||||||
"github.com/fission/fission/pkg/buildermgr"
|
"github.com/fission/fission/pkg/buildermgr"
|
||||||
"github.com/fission/fission/pkg/executor"
|
"github.com/fission/fission/pkg/executor"
|
||||||
eclient "github.com/fission/fission/pkg/executor/client"
|
eclient "github.com/fission/fission/pkg/executor/client"
|
||||||
@@ -15,10 +18,28 @@ import (
|
|||||||
"github.com/fission/fission/pkg/timer"
|
"github.com/fission/fission/pkg/timer"
|
||||||
"github.com/fission/fission/pkg/utils"
|
"github.com/fission/fission/pkg/utils"
|
||||||
"github.com/fission/fission/pkg/utils/manager"
|
"github.com/fission/fission/pkg/utils/manager"
|
||||||
|
"github.com/fission/fission/pkg/webhook"
|
||||||
"github.com/fission/fission/test/e2e/framework"
|
"github.com/fission/fission/test/e2e/framework"
|
||||||
)
|
)
|
||||||
|
|
||||||
func StartServices(ctx context.Context, f *framework.Framework, mgr manager.Interface) error {
|
func StartServices(ctx context.Context, f *framework.Framework, mgr manager.Interface) error {
|
||||||
|
os.Setenv("DEBUG_ENV", "true")
|
||||||
|
env := f.GetEnv()
|
||||||
|
webhookPort := env.WebhookInstallOptions.LocalServingPort
|
||||||
|
err := f.ToggleMetricAddr()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error toggling metric address: %v", err)
|
||||||
|
}
|
||||||
|
mgr.Add(ctx, func(ctx context.Context) {
|
||||||
|
err = webhook.Start(ctx, f.ClientGen(), f.Logger(), cnwebhook.Options{
|
||||||
|
Port: webhookPort,
|
||||||
|
CertDir: env.WebhookInstallOptions.LocalServingCertDir,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
f.Logger().Fatal("error starting webhook", zap.Error(err))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
f.AddServiceInfo("webhook", framework.ServiceInfo{Port: webhookPort})
|
||||||
|
|
||||||
executorPort, err := utils.FindFreePort()
|
executorPort, err := utils.FindFreePort()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -44,9 +65,7 @@ func StartServices(ctx context.Context, f *framework.Framework, mgr manager.Inte
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error starting executor: %v", err)
|
return fmt.Errorf("error starting executor: %v", err)
|
||||||
}
|
}
|
||||||
f.ServiceInfo["executor"] = framework.ServiceInfo{
|
f.AddServiceInfo("executor", framework.ServiceInfo{Port: executorPort})
|
||||||
Port: executorPort,
|
|
||||||
}
|
|
||||||
|
|
||||||
os.Setenv("PRUNE_ENABLED", "true")
|
os.Setenv("PRUNE_ENABLED", "true")
|
||||||
os.Setenv("PRUNE_INTERVAL", "60")
|
os.Setenv("PRUNE_INTERVAL", "60")
|
||||||
@@ -67,9 +86,7 @@ func StartServices(ctx context.Context, f *framework.Framework, mgr manager.Inte
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error starting storage service: %v", err)
|
return fmt.Errorf("error starting storage service: %v", err)
|
||||||
}
|
}
|
||||||
f.ServiceInfo["storagesvc"] = framework.ServiceInfo{
|
f.AddServiceInfo("storagesvc", framework.ServiceInfo{Port: storageSvcPort})
|
||||||
Port: storageSvcPort,
|
|
||||||
}
|
|
||||||
err = f.ToggleMetricAddr()
|
err = f.ToggleMetricAddr()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error toggling metric address: %v", err)
|
return fmt.Errorf("error toggling metric address: %v", err)
|
||||||
@@ -78,7 +95,7 @@ func StartServices(ctx context.Context, f *framework.Framework, mgr manager.Inte
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error starting builder manager: %v", err)
|
return fmt.Errorf("error starting builder manager: %v", err)
|
||||||
}
|
}
|
||||||
f.ServiceInfo["buildermgr"] = framework.ServiceInfo{}
|
f.AddServiceInfo("buildermgr", framework.ServiceInfo{})
|
||||||
|
|
||||||
os.Setenv("ROUTER_ROUND_TRIP_TIMEOUT", "50ms")
|
os.Setenv("ROUTER_ROUND_TRIP_TIMEOUT", "50ms")
|
||||||
os.Setenv("ROUTER_ROUNDTRIP_TIMEOUT_EXPONENT", "2")
|
os.Setenv("ROUTER_ROUNDTRIP_TIMEOUT_EXPONENT", "2")
|
||||||
@@ -89,8 +106,8 @@ func StartServices(ctx context.Context, f *framework.Framework, mgr manager.Inte
|
|||||||
os.Setenv("ROUTER_SVC_ADDRESS_UPDATE_TIMEOUT", "30s")
|
os.Setenv("ROUTER_SVC_ADDRESS_UPDATE_TIMEOUT", "30s")
|
||||||
os.Setenv("ROUTER_UNTAP_SERVICE_TIMEOUT", "3600s")
|
os.Setenv("ROUTER_UNTAP_SERVICE_TIMEOUT", "3600s")
|
||||||
os.Setenv("USE_ENCODED_PATH", "false")
|
os.Setenv("USE_ENCODED_PATH", "false")
|
||||||
os.Setenv("DISPLAY_ACCESS_LOG", "false")
|
os.Setenv("DISPLAY_ACCESS_LOG", "true")
|
||||||
os.Setenv("DEBUG_ENV", "false")
|
//os.Setenv("DEBUG_ENV", "false")
|
||||||
routerPort, err := utils.FindFreePort()
|
routerPort, err := utils.FindFreePort()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error finding unused port: %v", err)
|
return fmt.Errorf("error finding unused port: %v", err)
|
||||||
@@ -104,9 +121,8 @@ func StartServices(ctx context.Context, f *framework.Framework, mgr manager.Inte
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error starting router: %v", err)
|
return fmt.Errorf("error starting router: %v", err)
|
||||||
}
|
}
|
||||||
f.ServiceInfo["router"] = framework.ServiceInfo{
|
f.AddServiceInfo("router", framework.ServiceInfo{Port: routerPort})
|
||||||
Port: routerPort,
|
|
||||||
}
|
|
||||||
routerURL := fmt.Sprintf("http://localhost:%d", routerPort)
|
routerURL := fmt.Sprintf("http://localhost:%d", routerPort)
|
||||||
os.Setenv("FISSION_ROUTER_URL", routerURL)
|
os.Setenv("FISSION_ROUTER_URL", routerURL)
|
||||||
|
|
||||||
@@ -114,18 +130,19 @@ func StartServices(ctx context.Context, f *framework.Framework, mgr manager.Inte
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error starting timer: %v", err)
|
return fmt.Errorf("error starting timer: %v", err)
|
||||||
}
|
}
|
||||||
f.ServiceInfo["timer"] = framework.ServiceInfo{}
|
f.AddServiceInfo("timer", framework.ServiceInfo{})
|
||||||
|
|
||||||
err = mqtrigger.StartScalerManager(ctx, f.ClientGen(), f.Logger(), mgr, routerURL)
|
err = mqtrigger.StartScalerManager(ctx, f.ClientGen(), f.Logger(), mgr, routerURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error starting mqt scaler manager: %v", err)
|
return fmt.Errorf("error starting mqt scaler manager: %v", err)
|
||||||
}
|
}
|
||||||
f.ServiceInfo["mqtrigger-keda"] = framework.ServiceInfo{}
|
f.AddServiceInfo("mqtrigger-keda", framework.ServiceInfo{})
|
||||||
|
|
||||||
err = kubewatcher.Start(ctx, f.ClientGen(), f.Logger(), mgr, routerURL)
|
err = kubewatcher.Start(ctx, f.ClientGen(), f.Logger(), mgr, routerURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error starting kubewatcher: %v", err)
|
return fmt.Errorf("error starting kubewatcher: %v", err)
|
||||||
}
|
}
|
||||||
f.ServiceInfo["kubewatcher"] = framework.ServiceInfo{}
|
f.AddServiceInfo("kubewatcher", framework.ServiceInfo{})
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,170 @@
|
|||||||
|
---
|
||||||
|
apiVersion: admissionregistration.k8s.io/v1
|
||||||
|
kind: MutatingWebhookConfiguration
|
||||||
|
metadata:
|
||||||
|
name: mutating-webhook-configuration
|
||||||
|
webhooks:
|
||||||
|
- admissionReviewVersions:
|
||||||
|
- v1
|
||||||
|
clientConfig:
|
||||||
|
service:
|
||||||
|
name: webhook-service
|
||||||
|
namespace: system
|
||||||
|
path: /mutate-fission-io-v1-package
|
||||||
|
failurePolicy: Fail
|
||||||
|
name: mpackage.fission.io
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- fission.io
|
||||||
|
apiVersions:
|
||||||
|
- v1
|
||||||
|
operations:
|
||||||
|
- CREATE
|
||||||
|
- UPDATE
|
||||||
|
resources:
|
||||||
|
- packages
|
||||||
|
sideEffects: None
|
||||||
|
---
|
||||||
|
apiVersion: admissionregistration.k8s.io/v1
|
||||||
|
kind: ValidatingWebhookConfiguration
|
||||||
|
metadata:
|
||||||
|
name: validating-webhook-configuration
|
||||||
|
webhooks:
|
||||||
|
- admissionReviewVersions:
|
||||||
|
- v1
|
||||||
|
clientConfig:
|
||||||
|
service:
|
||||||
|
name: webhook-service
|
||||||
|
namespace: system
|
||||||
|
path: /validate-fission-io-v1-environment
|
||||||
|
failurePolicy: Fail
|
||||||
|
name: venvironment.fission.io
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- fission.io
|
||||||
|
apiVersions:
|
||||||
|
- v1
|
||||||
|
operations:
|
||||||
|
- CREATE
|
||||||
|
resources:
|
||||||
|
- environments
|
||||||
|
sideEffects: None
|
||||||
|
- admissionReviewVersions:
|
||||||
|
- v1
|
||||||
|
clientConfig:
|
||||||
|
service:
|
||||||
|
name: webhook-service
|
||||||
|
namespace: system
|
||||||
|
path: /validate-fission-io-v1-function
|
||||||
|
failurePolicy: Fail
|
||||||
|
name: vfunction.fission.io
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- fission.io
|
||||||
|
apiVersions:
|
||||||
|
- v1
|
||||||
|
operations:
|
||||||
|
- CREATE
|
||||||
|
- UPDATE
|
||||||
|
resources:
|
||||||
|
- functions
|
||||||
|
sideEffects: None
|
||||||
|
- admissionReviewVersions:
|
||||||
|
- v1
|
||||||
|
clientConfig:
|
||||||
|
service:
|
||||||
|
name: webhook-service
|
||||||
|
namespace: system
|
||||||
|
path: /validate-fission-io-v1-httptrigger
|
||||||
|
failurePolicy: Fail
|
||||||
|
name: vhttptrigger.fission.io
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- fission.io
|
||||||
|
apiVersions:
|
||||||
|
- v1
|
||||||
|
operations:
|
||||||
|
- CREATE
|
||||||
|
- UPDATE
|
||||||
|
resources:
|
||||||
|
- httptriggers
|
||||||
|
sideEffects: None
|
||||||
|
- admissionReviewVersions:
|
||||||
|
- v1
|
||||||
|
clientConfig:
|
||||||
|
service:
|
||||||
|
name: webhook-service
|
||||||
|
namespace: system
|
||||||
|
path: /validate-fission-io-v1-kuberneteswatchtrigger
|
||||||
|
failurePolicy: Fail
|
||||||
|
name: vkuberneteswatchtrigger.fission.io
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- fission.io
|
||||||
|
apiVersions:
|
||||||
|
- v1
|
||||||
|
operations:
|
||||||
|
- CREATE
|
||||||
|
resources:
|
||||||
|
- kuberneteswatchtriggers
|
||||||
|
sideEffects: None
|
||||||
|
- admissionReviewVersions:
|
||||||
|
- v1
|
||||||
|
clientConfig:
|
||||||
|
service:
|
||||||
|
name: webhook-service
|
||||||
|
namespace: system
|
||||||
|
path: /validate-fission-io-v1-messagequeuetrigger
|
||||||
|
failurePolicy: Fail
|
||||||
|
name: vmessagequeuetrigger.fission.io
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- fission.io
|
||||||
|
apiVersions:
|
||||||
|
- v1
|
||||||
|
operations:
|
||||||
|
- CREATE
|
||||||
|
- UPDATE
|
||||||
|
resources:
|
||||||
|
- messagequeuetriggers
|
||||||
|
sideEffects: None
|
||||||
|
- admissionReviewVersions:
|
||||||
|
- v1
|
||||||
|
clientConfig:
|
||||||
|
service:
|
||||||
|
name: webhook-service
|
||||||
|
namespace: system
|
||||||
|
path: /validate-fission-io-v1-package
|
||||||
|
failurePolicy: Fail
|
||||||
|
name: vpackage.fission.io
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- fission.io
|
||||||
|
apiVersions:
|
||||||
|
- v1
|
||||||
|
operations:
|
||||||
|
- CREATE
|
||||||
|
- UPDATE
|
||||||
|
resources:
|
||||||
|
- packages
|
||||||
|
sideEffects: None
|
||||||
|
- admissionReviewVersions:
|
||||||
|
- v1
|
||||||
|
clientConfig:
|
||||||
|
service:
|
||||||
|
name: webhook-service
|
||||||
|
namespace: system
|
||||||
|
path: /validate-fission-io-v1-timetrigger
|
||||||
|
failurePolicy: Fail
|
||||||
|
name: vtimetrigger.fission.io
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- fission.io
|
||||||
|
apiVersions:
|
||||||
|
- v1
|
||||||
|
operations:
|
||||||
|
- CREATE
|
||||||
|
- UPDATE
|
||||||
|
resources:
|
||||||
|
- timetriggers
|
||||||
|
sideEffects: None
|
||||||
Reference in New Issue
Block a user