<commit_before>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package server

import (
	"fmt"
	"io"
	"net"

	"github.com/spf13/cobra"

	utilerrors "k8s.io/apimachinery/pkg/util/errors"
	genericapiserver "k8s.io/apiserver/pkg/server"
	genericoptions "k8s.io/apiserver/pkg/server/options"
	"k8s.io/sample-apiserver/pkg/admission/plugin/banflunder"
	"k8s.io/sample-apiserver/pkg/admission/wardleinitializer"
	"k8s.io/sample-apiserver/pkg/apis/wardle/v1alpha1"
	"k8s.io/sample-apiserver/pkg/apiserver"
	clientset "k8s.io/sample-apiserver/pkg/client/clientset/internalversion"
	informers "k8s.io/sample-apiserver/pkg/client/informers/internalversion"
)

const defaultEtcdPathPrefix = "/registry/wardle.kubernetes.io"

type WardleServerOptions struct {
	RecommendedOptions *genericoptions.RecommendedOptions
	Admission          *genericoptions.AdmissionOptions

	StdOut io.Writer
	StdErr io.Writer
}

func NewWardleServerOptions(out, errOut io.Writer) *WardleServerOptions {
	o := &WardleServerOptions{
		RecommendedOptions: genericoptions.NewRecommendedOptions(defaultEtcdPathPrefix, apiserver.Codecs.LegacyCodec(v1alpha1.SchemeGroupVersion)),
		Admission:          genericoptions.NewAdmissionOptions(),

		StdOut: out,
		StdErr: errOut,
	}

	return o
}

// NewCommandStartMaster provides a CLI handler for 'start master' command
func NewCommandStartWardleServer(out, errOut io.Writer, stopCh <-chan struct{}) *cobra.Command {
	o := NewWardleServerOptions(out, errOut)

	cmd := &cobra.Command{
		Short: "Launch a wardle API server",
		Long:  "Launch a wardle API server",
		RunE: func(c *cobra.Command, args []string) error {
			if err := o.Complete(); err != nil {
				return err
			}
			if err := o.Validate(args); err != nil {
				return err
			}
			if err := o.RunWardleServer(stopCh); err != nil {
				return err
			}
			return nil
		},
	}

	flags := cmd.Flags()
	o.RecommendedOptions.AddFlags(flags)
	o.Admission.AddFlags(flags)

	return cmd
}

func (o WardleServerOptions) Validate(args []string) error {
	errors := []error{}
	errors = append(errors, o.RecommendedOptions.Validate()...)
	errors = append(errors, o.Admission.Validate()...)
	return utilerrors.NewAggregate(errors)
}

func (o *WardleServerOptions) Complete() error {
	return nil
}

func (o WardleServerOptions) Config() (*apiserver.Config, error) {
	// register admission plugins
	banflunder.Register(o.Admission.Plugins)

	// TODO have a "real" external address
	if err := o.RecommendedOptions.SecureServing.MaybeDefaultWithSelfSignedCerts("localhost", nil, []net.IP{net.ParseIP("127.0.0.1")}); err != nil {
		return nil, fmt.Errorf("error creating self-signed certificates: %v", err)
	}

	serverConfig := genericapiserver.NewRecommendedConfig(apiserver.Codecs)
	if err := o.RecommendedOptions.ApplyTo(serverConfig); err != nil {
		return nil, err
	}

	client, err := clientset.NewForConfig(serverConfig.LoopbackClientConfig)
	if err != nil {
		return nil, err
	}
	informerFactory := informers.NewSharedInformerFactory(client, serverConfig.LoopbackClientConfig.Timeout)
	admissionInitializer, err := wardleinitializer.New(informerFactory)
	if err != nil {
		return nil, err
	}

	if err := o.Admission.ApplyTo(&serverConfig.Config, serverConfig.SharedInformerFactory, nil, nil, serverConfig.ClientConfig, apiserver.Scheme, admissionInitializer); err != nil {
		return nil, err
	}

	config := &apiserver.Config{
		GenericConfig: serverConfig,
		ExtraConfig:   apiserver.ExtraConfig{},
	}
	return config, nil
}

func (o WardleServerOptions) RunWardleServer(stopCh <-chan struct{}) error {
	config, err := o.Config()
	if err != nil {
		return err
	}

	server, err := config.Complete().New()
	if err != nil {
		return err
	}

	server.GenericAPIServer.AddPostStartHook("start-sample-server-informers", func(context genericapiserver.PostStartHookContext) error {
		config.GenericConfig.SharedInformerFactory.Start(context.StopCh)
		return nil
	})

	return server.GenericAPIServer.PrepareRun().Run(stopCh)
}
<commit_msg>update admission webhook to accept client config<commit_after>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package server

import (
	"fmt"
	"io"
	"net"

	"github.com/spf13/cobra"

	utilerrors "k8s.io/apimachinery/pkg/util/errors"
	genericapiserver "k8s.io/apiserver/pkg/server"
	genericoptions "k8s.io/apiserver/pkg/server/options"
	"k8s.io/sample-apiserver/pkg/admission/plugin/banflunder"
	"k8s.io/sample-apiserver/pkg/admission/wardleinitializer"
	"k8s.io/sample-apiserver/pkg/apis/wardle/v1alpha1"
	"k8s.io/sample-apiserver/pkg/apiserver"
	clientset "k8s.io/sample-apiserver/pkg/client/clientset/internalversion"
	informers "k8s.io/sample-apiserver/pkg/client/informers/internalversion"
)

const defaultEtcdPathPrefix = "/registry/wardle.kubernetes.io"

type WardleServerOptions struct {
	RecommendedOptions *genericoptions.RecommendedOptions
	Admission          *genericoptions.AdmissionOptions

	StdOut io.Writer
	StdErr io.Writer
}

func NewWardleServerOptions(out, errOut io.Writer) *WardleServerOptions {
	o := &WardleServerOptions{
		RecommendedOptions: genericoptions.NewRecommendedOptions(defaultEtcdPathPrefix, apiserver.Codecs.LegacyCodec(v1alpha1.SchemeGroupVersion)),
		Admission:          genericoptions.NewAdmissionOptions(),

		StdOut: out,
		StdErr: errOut,
	}

	return o
}

// NewCommandStartMaster provides a CLI handler for 'start master' command
func NewCommandStartWardleServer(out, errOut io.Writer, stopCh <-chan struct{}) *cobra.Command {
	o := NewWardleServerOptions(out, errOut)

	cmd := &cobra.Command{
		Short: "Launch a wardle API server",
		Long:  "Launch a wardle API server",
		RunE: func(c *cobra.Command, args []string) error {
			if err := o.Complete(); err != nil {
				return err
			}
			if err := o.Validate(args); err != nil {
				return err
			}
			if err := o.RunWardleServer(stopCh); err != nil {
				return err
			}
			return nil
		},
	}

	flags := cmd.Flags()
	o.RecommendedOptions.AddFlags(flags)
	o.Admission.AddFlags(flags)

	return cmd
}

func (o WardleServerOptions) Validate(args []string) error {
	errors := []error{}
	errors = append(errors, o.RecommendedOptions.Validate()...)
	errors = append(errors, o.Admission.Validate()...)
	return utilerrors.NewAggregate(errors)
}

func (o *WardleServerOptions) Complete() error {
	return nil
}

func (o WardleServerOptions) Config() (*apiserver.Config, error) {
	// register admission plugins
	banflunder.Register(o.Admission.Plugins)

	// TODO have a "real" external address
	if err := o.RecommendedOptions.SecureServing.MaybeDefaultWithSelfSignedCerts("localhost", nil, []net.IP{net.ParseIP("127.0.0.1")}); err != nil {
		return nil, fmt.Errorf("error creating self-signed certificates: %v", err)
	}

	serverConfig := genericapiserver.NewRecommendedConfig(apiserver.Codecs)
	if err := o.RecommendedOptions.ApplyTo(serverConfig); err != nil {
		return nil, err
	}

	client, err := clientset.NewForConfig(serverConfig.LoopbackClientConfig)
	if err != nil {
		return nil, err
	}
	informerFactory := informers.NewSharedInformerFactory(client, serverConfig.LoopbackClientConfig.Timeout)
	admissionInitializer, err := wardleinitializer.New(informerFactory)
	if err != nil {
		return nil, err
	}

	if err := o.Admission.ApplyTo(&serverConfig.Config, serverConfig.SharedInformerFactory, serverConfig.ClientConfig, serverConfig.ClientConfig, apiserver.Scheme, admissionInitializer); err != nil {
		return nil, err
	}

	config := &apiserver.Config{
		GenericConfig: serverConfig,
		ExtraConfig:   apiserver.ExtraConfig{},
	}
	return config, nil
}

func (o WardleServerOptions) RunWardleServer(stopCh <-chan struct{}) error {
	config, err := o.Config()
	if err != nil {
		return err
	}

	server, err := config.Complete().New()
	if err != nil {
		return err
	}

	server.GenericAPIServer.AddPostStartHook("start-sample-server-informers", func(context genericapiserver.PostStartHookContext) error {
		config.GenericConfig.SharedInformerFactory.Start(context.StopCh)
		return nil
	})

	return server.GenericAPIServer.PrepareRun().Run(stopCh)
}
<|endoftext|>


<commit_before>package jwt

import (
	"context"
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"errors"
	"io/ioutil"
	"net/http"
	"strings"
	"time"

	"github.com/aukbit/pluto/v6/auth/jws"
)

var (
	privKeyPath = "./keys/auth.rsa"
	pubKeyPath  = "./keys/auth.rsa.pub"
)

var (
	ErrExpiredToken           = errors.New("token has expired")
	ErrInvalidAudience        = errors.New("token has invalid audience")
	ErrInvalidIdentifier      = errors.New("token has invalid identifier")
	ErrPrivateKeyNotAvailable = errors.New("private key not available in context")
	ErrPublicKeyNotAvailable  = errors.New("public key not available in context")
)

// LoadPublicKey loads a public key from PEM encoded data.
func LoadPublicKey(path string) (*rsa.PublicKey, error) {
	if path == "" {
		path = pubKeyPath
	}
	b, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}
	block, _ := pem.Decode(b)
	if block == nil {
		err = errors.New("Invalid PublicKey format")
		return nil, err
	}
	pub, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		return nil, err
	}
	return pub.(*rsa.PublicKey), nil
}

// LoadPrivateKey loads a private key from PEM encoded data.
func LoadPrivateKey(path string) (*rsa.PrivateKey, error) {
	if path == "" {
		path = privKeyPath
	}
	b, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}
	block, _ := pem.Decode(b)
	if block == nil {
		err = errors.New("Invalid PrivateKey format")
		return nil, err
	}
	priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
	if err != nil {
		return nil, err
	}
	return priv, nil
}

type ClaimSet struct {
	Identifier, Audience, Scope, Jti, Principal string
	Expiration                                  int64
}

// NewToken returns a JWT token signed with the given RSA private key.
func NewToken(ctx context.Context, cs *ClaimSet) (string, error) {
	prv, ok := PrivateKeyFromContext(ctx)
	if !ok {
		return "", ErrPrivateKeyNotAvailable
	}
	header := &jws.Header{
		Algorithm: "RS256",
		Typ:       "JWT",
	}
	payload := &jws.ClaimSet{
		Iss:   cs.Identifier,
		Aud:   cs.Audience,
		Scope: cs.Scope,
		Exp:   time.Now().Unix() + cs.Expiration,
		Iat:   time.Now().Unix(),
		Sub:   cs.Jti,
		Prn:   cs.Principal,
	}
	t, err := jws.Encode(header, payload, prv)
	if err != nil {
		return "", err
	}

	return t, nil
}

// Verify tests whether the provided JWT token's signature was produced by the
// private key associated with the supplied public key.
// Also verifies if Token as expired
func Verify(ctx context.Context, token string) error {
	pub, ok := PublicKeyFromContext(ctx)
	if !ok {
		return ErrPublicKeyNotAvailable
	}
	err := jws.Verify(token, pub)
	if err != nil {
		return err
	}
	c, err := jws.Decode(token)
	if err != nil {
		return err
	}
	if time.Now().Unix() > c.Exp {
		return ErrExpiredToken
	}
	return nil
}

// Identifier the "iss" (issuer) claim identifies the principal that issued the JWT.
func Identifier(token string) string {
	c, _ := jws.Decode(token)
	return c.Iss
}

// Scope space-delimited list of the permissions the application requests.
func Scope(token string) string {
	c, _ := jws.Decode(token)
	return c.Scope
}

// Audience The "aud" (audience) claim identifies the audience that the JWT is
// intended for.
func Audience(token string) string {
	c, _ := jws.Decode(token)
	return c.Aud
}

// Principal The "prn" (principal) claim identifies the subject of the JWT.
func Principal(token string) string {
	c, _ := jws.Decode(token)
	return c.Prn
}

// Jti The "jti" (JWT ID) claim provides a unique identifier for the JWT.
func Jti(token string) string {
	c, _ := jws.Decode(token)
	return c.Sub
}

// BearerAuth returns the token provided in the request's
// Authorization header, if the request uses HTTP Bearer Authentication.
func BearerAuth(r *http.Request) (token string, ok bool) {
	auth := r.Header.Get("Authorization")
	if auth == "" {
		return
	}
	return parseBearerAuth(auth)
}

// parseBearerAuth parses an HTTP Bearer Authentication string.
// "Bearer QWxhZGRpbjpvcGVuIHNlc2FtZQ==" returns ("QWxhZGRpbjpvcGVuIHNlc2FtZQ==", true).
func parseBearerAuth(auth string) (token string, ok bool) {
	const prefix = "Bearer "
	if !strings.HasPrefix(auth, prefix) {
		return
	}
	return auth[len(prefix):], true
}
<commit_msg>jwt explicit use keys<commit_after>package jwt

import (
	"context"
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"errors"
	"io/ioutil"
	"net/http"
	"strings"
	"time"

	"github.com/aukbit/pluto/v6/auth/jws"
)

var (
	privKeyPath = "./keys/auth.rsa"
	pubKeyPath  = "./keys/auth.rsa.pub"
)

var (
	ErrExpiredToken           = errors.New("token has expired")
	ErrInvalidAudience        = errors.New("token has invalid audience")
	ErrInvalidIdentifier      = errors.New("token has invalid identifier")
	ErrPrivateKeyNotAvailable = errors.New("private key not available in context")
	ErrPublicKeyNotAvailable  = errors.New("public key not available in context")
)

// LoadPublicKey loads a public key from PEM encoded data.
func LoadPublicKey(path string) (*rsa.PublicKey, error) {
	if path == "" {
		path = pubKeyPath
	}
	b, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}
	block, _ := pem.Decode(b)
	if block == nil {
		err = errors.New("Invalid PublicKey format")
		return nil, err
	}
	pub, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		return nil, err
	}
	return pub.(*rsa.PublicKey), nil
}

// LoadPrivateKey loads a private key from PEM encoded data.
func LoadPrivateKey(path string) (*rsa.PrivateKey, error) {
	if path == "" {
		path = privKeyPath
	}
	b, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}
	block, _ := pem.Decode(b)
	if block == nil {
		err = errors.New("Invalid PrivateKey format")
		return nil, err
	}
	priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
	if err != nil {
		return nil, err
	}
	return priv, nil
}

type ClaimSet struct {
	Identifier, Audience, Scope, Jti, Principal string
	Expiration                                  int64
}

// NewToken returns a JWT token signed with the given RSA private key.
func NewToken(ctx context.Context, cs *ClaimSet) (string, error) {
	prv, ok := PrivateKeyFromContext(ctx)
	if !ok {
		return "", ErrPrivateKeyNotAvailable
	}
	return NewTokenWithPrivateKey(cs, prv)
}

// NewTokenWithPrivateKey returns a JWT token signed with the given RSA private key.
func NewTokenWithPrivateKey(cs *ClaimSet, key *rsa.PrivateKey) (string, error) {
	header := &jws.Header{
		Algorithm: "RS256",
		Typ:       "JWT",
	}
	payload := &jws.ClaimSet{
		Iss:   cs.Identifier,
		Aud:   cs.Audience,
		Scope: cs.Scope,
		Exp:   time.Now().Unix() + cs.Expiration,
		Iat:   time.Now().Unix(),
		Sub:   cs.Jti,
		Prn:   cs.Principal,
	}
	t, err := jws.Encode(header, payload, key)
	if err != nil {
		return "", err
	}

	return t, nil
}

// Verify tests whether the provided JWT token's signature was produced by the
// private key associated with the supplied public key.
// Also verifies if Token as expired
func Verify(ctx context.Context, token string) error {
	pub, ok := PublicKeyFromContext(ctx)
	if !ok {
		return ErrPublicKeyNotAvailable
	}
	return VerifyWithPublicKey(token, pub)
}

// VerifyWithPublicKey tests whether the provided JWT token's signature was produced by the
// private key associated with the supplied public key.
// Also verifies if Token as expired
func VerifyWithPublicKey(token string, key *rsa.PublicKey) error {
	err := jws.Verify(token, key)
	if err != nil {
		return err
	}
	c, err := jws.Decode(token)
	if err != nil {
		return err
	}
	if time.Now().Unix() > c.Exp {
		return ErrExpiredToken
	}
	return nil
}

// Identifier the "iss" (issuer) claim identifies the principal that issued the JWT.
func Identifier(token string) string {
	c, _ := jws.Decode(token)
	return c.Iss
}

// Scope space-delimited list of the permissions the application requests.
func Scope(token string) string {
	c, _ := jws.Decode(token)
	return c.Scope
}

// Audience The "aud" (audience) claim identifies the audience that the JWT is
// intended for.
func Audience(token string) string {
	c, _ := jws.Decode(token)
	return c.Aud
}

// Principal The "prn" (principal) claim identifies the subject of the JWT.
func Principal(token string) string {
	c, _ := jws.Decode(token)
	return c.Prn
}

// Jti The "jti" (JWT ID) claim provides a unique identifier for the JWT.
func Jti(token string) string {
	c, _ := jws.Decode(token)
	return c.Sub
}

// BearerAuth returns the token provided in the request's
// Authorization header, if the request uses HTTP Bearer Authentication.
func BearerAuth(r *http.Request) (token string, ok bool) {
	auth := r.Header.Get("Authorization")
	if auth == "" {
		return
	}
	return parseBearerAuth(auth)
}

// parseBearerAuth parses an HTTP Bearer Authentication string.
// "Bearer QWxhZGRpbjpvcGVuIHNlc2FtZQ==" returns ("QWxhZGRpbjpvcGVuIHNlc2FtZQ==", true).
func parseBearerAuth(auth string) (token string, ok bool) {
	const prefix = "Bearer "
	if !strings.HasPrefix(auth, prefix) {
		return
	}
	return auth[len(prefix):], true
}
<|endoftext|>


<commit_before>package handler

import (
	"encoding/json"
	"net/http"

	"github.com/skygeario/skygear-server/pkg/core/handler/context"
	nextSkyerr "github.com/skygeario/skygear-server/pkg/core/skyerr"
	"github.com/skygeario/skygear-server/pkg/server/skyerr"
)

type APIHandler interface {
	DecodeRequest(request *http.Request) (RequestPayload, error)
	Handle(requestPayload interface{}, ctx context.AuthContext) (interface{}, error)
}

type APIResponse struct {
	Result interface{}  `json:"result,omitempty"`
	Err    skyerr.Error `json:"error,omitempty"`
}

func APIHandlerToHandler(apiHandler APIHandler) Handler {
	return HandlerFunc(func(rw http.ResponseWriter, r *http.Request, ctx context.AuthContext) {
		httpStatus := http.StatusOK
		response := APIResponse{}
		encoder := json.NewEncoder(rw)

		defer func() {
			if response.Err != nil {
				httpStatus = nextSkyerr.ErrorDefaultStatusCode(response.Err)
			}

			rw.Header().Set("Content-Type", "application/json")
			rw.WriteHeader(httpStatus)
			encoder.Encode(response)
		}()

		payload, err := apiHandler.DecodeRequest(r)
		if err != nil {
			response.Err = skyerr.MakeError(err)
			return
		}

		if err := payload.Validate(); err != nil {
			response.Err = skyerr.MakeError(err)
			return
		}

		responsePayload, err := apiHandler.Handle(payload, ctx)
		if err == nil {
			response.Result = responsePayload
		} else {
			response.Err = skyerr.MakeError(err)
		}
	})
}
<commit_msg>Remove defer function call that write to response<commit_after>package handler

import (
	"encoding/json"
	"net/http"

	"github.com/skygeario/skygear-server/pkg/core/handler/context"
	nextSkyerr "github.com/skygeario/skygear-server/pkg/core/skyerr"
	"github.com/skygeario/skygear-server/pkg/server/skyerr"
)

type APIHandler interface {
	DecodeRequest(request *http.Request) (RequestPayload, error)
	Handle(requestPayload interface{}, ctx context.AuthContext) (interface{}, error)
}

type APIResponse struct {
	Result interface{}  `json:"result,omitempty"`
	Err    skyerr.Error `json:"error,omitempty"`
}

func APIHandlerToHandler(apiHandler APIHandler) Handler {
	handleAPICall := func(r *http.Request, ctx context.AuthContext) (response APIResponse) {
		payload, err := apiHandler.DecodeRequest(r)
		if err != nil {
			response.Err = skyerr.MakeError(err)
			return
		}

		if err := payload.Validate(); err != nil {
			response.Err = skyerr.MakeError(err)
			return
		}

		responsePayload, err := apiHandler.Handle(payload, ctx)
		if err == nil {
			response.Result = responsePayload
		} else {
			response.Err = skyerr.MakeError(err)
		}

		return
	}

	return HandlerFunc(func(rw http.ResponseWriter, r *http.Request, ctx context.AuthContext) {
		response := handleAPICall(r, ctx)
		writeResponse(rw, response)
	})
}

func writeResponse(rw http.ResponseWriter, response APIResponse) {
	httpStatus := http.StatusOK
	encoder := json.NewEncoder(rw)

	if response.Err != nil {
		httpStatus = nextSkyerr.ErrorDefaultStatusCode(response.Err)
	}

	rw.Header().Set("Content-Type", "application/json")
	rw.WriteHeader(httpStatus)
	encoder.Encode(response)
}
<|endoftext|>


<commit_before>package dbg

import (
	"fmt"
	"io/ioutil"
	"os"
	"runtime"
	"runtime/pprof"
	"time"

	"github.com/sirupsen/logrus"
)

const (
	path     = "/var/cores/"
	fnameFmt = "2006-01-02T15:04:05.999999-0700MST"
)

// GetTimeStamp returns 'readable' timestamp with no spaces 'YYYYMMDDHHMMSS'
func GetTimeStamp() string {
	tnow := time.Now()
	return fmt.Sprintf("%d%02d%02d%02d%02d%02d",
		tnow.Year(), tnow.Month(), tnow.Day(),
		tnow.Hour(), tnow.Minute(), tnow.Second())
}

// GetHostNamePrefix return '<hostname>' to use as prefix.
func GetHostNamePrefix() string {
	hname, _ := os.Hostname()
	return hname
}

// DumpGoMemoryTrace output memory profile to logs.
func DumpGoMemoryTrace() {
	m := &runtime.MemStats{}
	runtime.ReadMemStats(m)
	res := fmt.Sprintf("%#v", m)
	logrus.Infof("==== Dumping Memory Profile ===")
	logrus.Infof(res)
}

// DumpGoProfile output goroutines to file.
func DumpGoProfile() error {
	trace := make([]byte, 5120*1024)
	len := runtime.Stack(trace, true)
	return ioutil.WriteFile(path+GetHostNamePrefix()+"-"+GetTimeStamp()+".stack", trace[:len], 0644)
}

func DumpHeap() {
	f, err := os.Create(path + GetHostNamePrefix() + "-" + GetTimeStamp() + ".heap")
	if err != nil {
		logrus.Errorf("could not create memory profile: %v", err)
		return
	}
	defer f.Close()
	if err := pprof.WriteHeapProfile(f); err != nil {
		logrus.Errorf("could not write memory profile: %v", err)
	}
}
<commit_msg>PWX-23061: Add new funcs to allow for dumping profiles to specific fi… (#2002)<commit_after>package dbg

import (
	"fmt"
	"io/ioutil"
	"os"
	"runtime"
	"runtime/pprof"
	"time"

	"github.com/sirupsen/logrus"
)

const (
	// Path defailt location for debug/profile output
	Path     = "/var/cores/"
	fnameFmt = "2006-01-02T15:04:05.999999-0700MST"
)

// GetTimeStamp returns 'readable' timestamp with no spaces 'YYYYMMDDHHMMSS'
func GetTimeStamp() string {
	tnow := time.Now()
	return fmt.Sprintf("%d%02d%02d%02d%02d%02d",
		tnow.Year(), tnow.Month(), tnow.Day(),
		tnow.Hour(), tnow.Minute(), tnow.Second())
}

// GetHostNamePrefix return '<hostname>' to use as prefix.
func GetHostNamePrefix() string {
	hname, _ := os.Hostname()
	return hname
}

// GoProfileDump output goroutines to file.
func DumpGoProfileTo(fname string) error {
	trace := make([]byte, 5120*1024)
	len := runtime.Stack(trace, true)
	return ioutil.WriteFile(fname, trace[:len], 0644)
}

// HeapDump output heap to filename.
func HeapDumpTo(fname string) {
	f, err := os.Create(fname)
	if err != nil {
		logrus.Errorf("could not create memory profile: %v", err)
		return
	}
	defer f.Close()
	if err := pprof.WriteHeapProfile(f); err != nil {
		logrus.Errorf("could not write memory profile: %v", err)
	}
}

// DumpGoMemoryTrace output memory profile to logs.
func DumpGoMemoryTrace() {
	m := &runtime.MemStats{}
	runtime.ReadMemStats(m)
	res := fmt.Sprintf("%#v", m)
	logrus.Infof("==== Dumping Memory Profile ===")
	logrus.Infof(res)
}

// DumpGoProfile output goroutines to file.
func DumpGoProfile() error {
	return DumpGoProfileTo(Path + GetHostNamePrefix() + "-" + GetTimeStamp() + ".stack")
}

// DumpHeap output heap to file.
func DumpHeap() {
	HeapDumpTo(Path + GetHostNamePrefix() + "-" + GetTimeStamp() + ".heap")
}
<|endoftext|>


<commit_before>package vizzini_test

import (
	. "github.com/pivotal-cf-experimental/vizzini/matchers"

	"github.com/cloudfoundry-incubator/receptor"
	"github.com/cloudfoundry-incubator/runtime-schema/models"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("Users", func() {
	var task receptor.TaskCreateRequest

	Context("{DOCKER} with an existing 'alice' user in the rootfs", func() {
		BeforeEach(func() {
			task = TaskWithGuid(guid)

			// TODO: REMOVE once #95582718 delivered, so that garden-linux in diego-release supports user namespaces correctly
			task.Privileged = true

			task.RootFS = "docker:///cloudfoundry/busybox-alice"
			task.Action = &models.RunAction{
				Path: "sh",
				Args: []string{"-c", "whoami > /tmp/output"},
				User: "alice",
			}
			task.ResultFile = "/tmp/output"

			Ω(client.CreateTask(task)).Should(Succeed())
		})

		It("runs an action as alice", func() {
			Eventually(TaskGetter(guid)).Should(HaveTaskState(receptor.TaskStateRunning))
			Eventually(TaskGetter(guid), 10).Should(HaveTaskState(receptor.TaskStateCompleted))

			task, err := client.GetTask(guid)
			Ω(err).ShouldNot(HaveOccurred())

			Ω(task.Failed).Should(BeFalse())
			Ω(task.Result).Should(ContainSubstring("alice"))

			Ω(client.DeleteTask(guid)).Should(Succeed())
		})
	})
})
<commit_msg>Allow docker-based container in user test more time to start running<commit_after>package vizzini_test

import (
	. "github.com/pivotal-cf-experimental/vizzini/matchers"

	"github.com/cloudfoundry-incubator/receptor"
	"github.com/cloudfoundry-incubator/runtime-schema/models"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("Users", func() {
	var task receptor.TaskCreateRequest

	Context("{DOCKER} with an existing 'alice' user in the rootfs", func() {
		BeforeEach(func() {
			task = TaskWithGuid(guid)

			// TODO: REMOVE once #95582718 delivered, so that garden-linux in diego-release supports user namespaces correctly
			task.Privileged = true

			task.RootFS = "docker:///cloudfoundry/busybox-alice"
			task.Action = &models.RunAction{
				Path: "sh",
				Args: []string{"-c", "whoami > /tmp/output"},
				User: "alice",
			}
			task.ResultFile = "/tmp/output"

			Ω(client.CreateTask(task)).Should(Succeed())
		})

		It("runs an action as alice", func() {
			Eventually(TaskGetter(guid), 120).Should(HaveTaskState(receptor.TaskStateRunning))
			Eventually(TaskGetter(guid), 10).Should(HaveTaskState(receptor.TaskStateCompleted))

			task, err := client.GetTask(guid)
			Ω(err).ShouldNot(HaveOccurred())

			Ω(task.Failed).Should(BeFalse())
			Ω(task.Result).Should(ContainSubstring("alice"))

			Ω(client.DeleteTask(guid)).Should(Succeed())
		})
	})
})
<|endoftext|>


<commit_before>package main

import (
	"net/http"
	"strings"
)

var (
	// Whitelist crawlers here
	crawlerPatterns = [...]string{
		"Googlebot",
		"bingbot",
		"MSNbot",
		"facebookexternalhit",
		"PlurkBot",
		"Twitterbot",
		"CloudFlare-AlwaysOnline",
	}
)

func isCrawlerUserAgent(r *http.Request) bool {
	ua := r.UserAgent()

	for _, pattern := range crawlerPatterns {
		if strings.Contains(ua, pattern) {
			return true
		}
	}

	return false
}
<commit_msg>Add Google+ snippet fetcher to crawler list<commit_after>package main

import (
	"net/http"
	"strings"
)

var (
	// Whitelist crawlers here
	crawlerPatterns = [...]string{
		"Google (+https://developers.google.com/+/web/snippet/)",
		"Googlebot",
		"bingbot",
		"MSNbot",
		"facebookexternalhit",
		"PlurkBot",
		"Twitterbot",
		"CloudFlare-AlwaysOnline",
	}
)

func isCrawlerUserAgent(r *http.Request) bool {
	ua := r.UserAgent()

	for _, pattern := range crawlerPatterns {
		if strings.Contains(ua, pattern) {
			return true
		}
	}

	return false
}
<|endoftext|>


<commit_before>// Copyright 2015 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package mgrconfig

import (
	"encoding/json"
)

type Config struct {
	// Instance name (used for identification and as GCE instance prefix).
	Name string `json:"name"`
	// Target OS/arch, e.g. "linux/arm64" or "linux/amd64/386" (amd64 OS with 386 test process).
	RawTarget string `json:"target"`
	// URL that will display information about the running syz-manager process (e.g. "localhost:50000").
	HTTP string `json:"http"`
	// TCP address to serve RPC for fuzzer processes (optional).
	RPC string `json:"rpc,omitempty"`
	// Location of a working directory for the syz-manager process. Outputs here include:
	// - <workdir>/crashes/*: crash output files
	// - <workdir>/corpus.db: corpus with interesting programs
	// - <workdir>/instance-x: per VM instance temporary files
	Workdir string `json:"workdir"`
	// Refers to a directory. Optional.
	// Each VM will get a recursive copy of the files that are present in workdir_template.
	// VM config can then use these private copies as needed. The copy directory
	// can be referenced with "{{TEMPLATE}}" string. This is different from using
	// the files directly in that each instance will get own clean, private,
	// scratch copy of the files. Currently supported only for qemu_args argument
	// of qemu VM type. Use example:
	// Create a template dir with necessary files:
	// $ mkdir /mytemplatedir
	// $ truncate -s 64K /mytemplatedir/fd
	// Then specify the dir in the manager config:
	//	"workdir_template": "/mytemplatedir"
	// Then use these files in VM config:
	//	"qemu_args": "-fda {{TEMPLATE}}/fd"
	WorkdirTemplate string `json:"workdir_template"`
	// Directory with kernel object files (e.g. `vmlinux` for linux)
	// (used for report symbolization, coverage reports and in tree modules finding, optional).
	KernelObj string `json:"kernel_obj"`
	// Directories with out-of-free kernel module object files (optional).
	// KernelObj is also scanned for in-tree kernel modules and does not need to be duplicated here.
	// Note: KASLR needs to be disabled and modules need to be pre-loaded at fixed addressses by init process.
	// Note: the modules need to be unstripped and contain debug info.
	ModuleObj []string `json:"module_obj"`
	// Kernel source directory (if not set defaults to KernelObj).
	KernelSrc string `json:"kernel_src,omitempty"`
	// Location of the driectory where the kernel was built (if not set defaults to KernelSrc)
	KernelBuildSrc string `json:"kernel_build_src"`
	// Kernel subsystem with paths to each subsystem
	//	"kernel_subsystem": [
	//		{ "name": "sound", "path": ["sound", "techpack/audio"]},
	//		{ "name": "mydriver": "path": ["mydriver_path"]}
	//	]
	KernelSubsystem []Subsystem `json:"kernel_subsystem,omitempty"`
	// Arbitrary optional tag that is saved along with crash reports (e.g. branch/commit).
	Tag string `json:"tag,omitempty"`
	// Location of the disk image file.
	Image string `json:"image,omitempty"`
	// Location (on the host machine) of a root SSH identity to use for communicating with
	// the virtual machine (may be empty for some VM types).
	SSHKey string `json:"sshkey,omitempty"`
	// SSH user ("root" by default).
	SSHUser string `json:"ssh_user,omitempty"`

	HubClient string `json:"hub_client,omitempty"`
	HubAddr   string `json:"hub_addr,omitempty"`
	HubKey    string `json:"hub_key,omitempty"`
	// Hub input domain identifier (optional).
	// The domain is used to avoid duplicate work (input minimization, smashing)
	// across multiple managers testing similar kernels and connected to the same hub.
	// If two managers are in the same domain, they will not do input minimization after each other.
	// If additionally they are in the same smashing sub-domain, they will also not do smashing
	// after each other.
	// By default (empty domain) all managers testing the same OS are placed into the same domain,
	// this is a reasonable setting if managers test roughly the same kernel. In this case they
	// will not do minimization nor smashing after each other.
	// The setting can be either a single identifier (e.g. "foo") which will affect both minimization
	// and smashing; or two identifiers separated with '/' (e.g. "foo/bar"), in this case the first
	// identifier affects minimization and both affect smashing.
	// For example, if managers test different Linux kernel versions with different tools,
	// a reasonable use of domains on these managers can be:
	//  - "upstream/kasan"
	//  - "upstream/kmsan"
	//  - "upstream/kcsan"
	//  - "5.4/kasan"
	//  - "5.4/kcsan"
	//  - "4.19/kasan"
	HubDomain string `json:"hub_domain,omitempty"`

	// List of email addresses to receive notifications when bugs are encountered for the first time (optional).
	// Mailx is the only supported mailer. Please set it up prior to using this function.
	EmailAddrs []string `json:"email_addrs,omitempty"`

	DashboardClient string `json:"dashboard_client,omitempty"`
	DashboardAddr   string `json:"dashboard_addr,omitempty"`
	DashboardKey    string `json:"dashboard_key,omitempty"`

	// Location of the syzkaller checkout, syz-manager will look
	// for binaries in bin subdir (does not have to be syzkaller checkout as
	// long as it preserves `bin` dir structure)
	Syzkaller string `json:"syzkaller"`

	// Number of parallel test processes inside of each VM.
	// Allowed values are 1-32, recommended range is ~4-8, default value is 6.
	// It should be chosen to saturate CPU inside of the VM and maximize number of test executions,
	// but to not oversubscribe CPU and memory too severe to not cause OOMs and false hangs/stalls.
	Procs int `json:"procs"`

	// Maximum number of logs to store per crash (default: 100).
	MaxCrashLogs int `json:"max_crash_logs"`

	// Type of sandbox to use during fuzzing:
	// "none": don't do anything special beyond resource sandboxing, default
	// "setuid": impersonate into user nobody (65534). Supported only for some OSes.
	// "namespace": create a new namespace for fuzzer using CLONE_NEWNS/CLONE_NEWNET/CLONE_NEWPID/etc,
	//	requires building kernel with CONFIG_NAMESPACES, CONFIG_UTS_NS, CONFIG_USER_NS,
	//	CONFIG_PID_NS and CONFIG_NET_NS. Supported only for some OSes.
	// "android": (Android) Emulate permissions of an untrusted app.
	Sandbox string `json:"sandbox"`

	// Use KCOV coverage (default: true).
	Cover bool `json:"cover"`
	// Use coverage filter. Supported types of filter:
	// "files": support specifying kernel source files, support regular expression.
	// eg. "files": ["^net/core/tcp.c$", "^net/sctp/", "tcp"].
	// "functions": support specifying kernel functions, support regular expression.
	// eg. "functions": ["^foo$", "^bar", "baz"].
	// "pcs": specify raw PC table files name.
	// Each line of the file should be: "64-bit-pc:32-bit-weight\n".
	// eg. "0xffffffff81000000:0x10\n"
	CovFilter covFilterCfg `json:"cover_filter"`

	// Reproduce, localize and minimize crashers (default: true).
	Reproduce bool `json:"reproduce"`

	// List of syscalls to test (optional). For example:
	//	"enable_syscalls": [ "mmap", "openat$ashmem", "ioctl$ASHMEM*" ]
	EnabledSyscalls []string `json:"enable_syscalls,omitempty"`
	// List of system calls that should be treated as disabled (optional).
	DisabledSyscalls []string `json:"disable_syscalls,omitempty"`
	// List of regexps for known bugs.
	// Don't save reports matching these regexps, but reboot VM after them,
	// matched against whole report output.
	Suppressions []string `json:"suppressions,omitempty"`
	// Completely ignore reports matching these regexps (don't save nor reboot),
	// must match the first line of crash message.
	Ignores []string `json:"ignores,omitempty"`

	// Type of virtual machine to use, e.g. "qemu", "gce", "android", "isolated", etc.
	Type string `json:"type"`
	// VM-type-specific parameters.
	// Parameters for concrete types are in Config type in vm/TYPE/TYPE.go, e.g. vm/qemu/qemu.go.
	VM json.RawMessage `json:"vm"`

	// Implementation details beyond this point. Filled after parsing.
	Derived `json:"-"`
}

type Subsystem struct {
	Name  string   `json:"name"`
	Paths []string `json:"path"`
}

type covFilterCfg struct {
	Files     []string `json:"files"`
	Functions []string `json:"functions"`
	RawPCs    []string `json:"pcs"`
}
<commit_msg>pkg/mgrconfig: add more omitempty<commit_after>// Copyright 2015 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package mgrconfig

import (
	"encoding/json"
)

type Config struct {
	// Instance name (used for identification and as GCE instance prefix).
	Name string `json:"name"`
	// Target OS/arch, e.g. "linux/arm64" or "linux/amd64/386" (amd64 OS with 386 test process).
	RawTarget string `json:"target"`
	// URL that will display information about the running syz-manager process (e.g. "localhost:50000").
	HTTP string `json:"http"`
	// TCP address to serve RPC for fuzzer processes (optional).
	RPC string `json:"rpc,omitempty"`
	// Location of a working directory for the syz-manager process. Outputs here include:
	// - <workdir>/crashes/*: crash output files
	// - <workdir>/corpus.db: corpus with interesting programs
	// - <workdir>/instance-x: per VM instance temporary files
	Workdir string `json:"workdir"`
	// Refers to a directory. Optional.
	// Each VM will get a recursive copy of the files that are present in workdir_template.
	// VM config can then use these private copies as needed. The copy directory
	// can be referenced with "{{TEMPLATE}}" string. This is different from using
	// the files directly in that each instance will get own clean, private,
	// scratch copy of the files. Currently supported only for qemu_args argument
	// of qemu VM type. Use example:
	// Create a template dir with necessary files:
	// $ mkdir /mytemplatedir
	// $ truncate -s 64K /mytemplatedir/fd
	// Then specify the dir in the manager config:
	//	"workdir_template": "/mytemplatedir"
	// Then use these files in VM config:
	//	"qemu_args": "-fda {{TEMPLATE}}/fd"
	WorkdirTemplate string `json:"workdir_template,omitempty"`
	// Directory with kernel object files (e.g. `vmlinux` for linux)
	// (used for report symbolization, coverage reports and in tree modules finding, optional).
	KernelObj string `json:"kernel_obj"`
	// Directories with out-of-free kernel module object files (optional).
	// KernelObj is also scanned for in-tree kernel modules and does not need to be duplicated here.
	// Note: KASLR needs to be disabled and modules need to be pre-loaded at fixed addressses by init process.
	// Note: the modules need to be unstripped and contain debug info.
	ModuleObj []string `json:"module_obj,omitempty"`
	// Kernel source directory (if not set defaults to KernelObj).
	KernelSrc string `json:"kernel_src,omitempty"`
	// Location of the driectory where the kernel was built (if not set defaults to KernelSrc)
	KernelBuildSrc string `json:"kernel_build_src,omitempty"`
	// Kernel subsystem with paths to each subsystem
	//	"kernel_subsystem": [
	//		{ "name": "sound", "path": ["sound", "techpack/audio"]},
	//		{ "name": "mydriver": "path": ["mydriver_path"]}
	//	]
	KernelSubsystem []Subsystem `json:"kernel_subsystem,omitempty"`
	// Arbitrary optional tag that is saved along with crash reports (e.g. branch/commit).
	Tag string `json:"tag,omitempty"`
	// Location of the disk image file.
	Image string `json:"image,omitempty"`
	// Location (on the host machine) of a root SSH identity to use for communicating with
	// the virtual machine (may be empty for some VM types).
	SSHKey string `json:"sshkey,omitempty"`
	// SSH user ("root" by default).
	SSHUser string `json:"ssh_user,omitempty"`

	HubClient string `json:"hub_client,omitempty"`
	HubAddr   string `json:"hub_addr,omitempty"`
	HubKey    string `json:"hub_key,omitempty"`
	// Hub input domain identifier (optional).
	// The domain is used to avoid duplicate work (input minimization, smashing)
	// across multiple managers testing similar kernels and connected to the same hub.
	// If two managers are in the same domain, they will not do input minimization after each other.
	// If additionally they are in the same smashing sub-domain, they will also not do smashing
	// after each other.
	// By default (empty domain) all managers testing the same OS are placed into the same domain,
	// this is a reasonable setting if managers test roughly the same kernel. In this case they
	// will not do minimization nor smashing after each other.
	// The setting can be either a single identifier (e.g. "foo") which will affect both minimization
	// and smashing; or two identifiers separated with '/' (e.g. "foo/bar"), in this case the first
	// identifier affects minimization and both affect smashing.
	// For example, if managers test different Linux kernel versions with different tools,
	// a reasonable use of domains on these managers can be:
	//  - "upstream/kasan"
	//  - "upstream/kmsan"
	//  - "upstream/kcsan"
	//  - "5.4/kasan"
	//  - "5.4/kcsan"
	//  - "4.19/kasan"
	HubDomain string `json:"hub_domain,omitempty"`

	// List of email addresses to receive notifications when bugs are encountered for the first time (optional).
	// Mailx is the only supported mailer. Please set it up prior to using this function.
	EmailAddrs []string `json:"email_addrs,omitempty"`

	DashboardClient string `json:"dashboard_client,omitempty"`
	DashboardAddr   string `json:"dashboard_addr,omitempty"`
	DashboardKey    string `json:"dashboard_key,omitempty"`

	// Location of the syzkaller checkout, syz-manager will look
	// for binaries in bin subdir (does not have to be syzkaller checkout as
	// long as it preserves `bin` dir structure)
	Syzkaller string `json:"syzkaller"`

	// Number of parallel test processes inside of each VM.
	// Allowed values are 1-32, recommended range is ~4-8, default value is 6.
	// It should be chosen to saturate CPU inside of the VM and maximize number of test executions,
	// but to not oversubscribe CPU and memory too severe to not cause OOMs and false hangs/stalls.
	Procs int `json:"procs"`

	// Maximum number of logs to store per crash (default: 100).
	MaxCrashLogs int `json:"max_crash_logs"`

	// Type of sandbox to use during fuzzing:
	// "none": don't do anything special beyond resource sandboxing, default
	// "setuid": impersonate into user nobody (65534). Supported only for some OSes.
	// "namespace": create a new namespace for fuzzer using CLONE_NEWNS/CLONE_NEWNET/CLONE_NEWPID/etc,
	//	requires building kernel with CONFIG_NAMESPACES, CONFIG_UTS_NS, CONFIG_USER_NS,
	//	CONFIG_PID_NS and CONFIG_NET_NS. Supported only for some OSes.
	// "android": (Android) Emulate permissions of an untrusted app.
	Sandbox string `json:"sandbox"`

	// Use KCOV coverage (default: true).
	Cover bool `json:"cover"`
	// Use coverage filter. Supported types of filter:
	// "files": support specifying kernel source files, support regular expression.
	// eg. "files": ["^net/core/tcp.c$", "^net/sctp/", "tcp"].
	// "functions": support specifying kernel functions, support regular expression.
	// eg. "functions": ["^foo$", "^bar", "baz"].
	// "pcs": specify raw PC table files name.
	// Each line of the file should be: "64-bit-pc:32-bit-weight\n".
	// eg. "0xffffffff81000000:0x10\n"
	CovFilter covFilterCfg `json:"cover_filter,omitempty"`

	// Reproduce, localize and minimize crashers (default: true).
	Reproduce bool `json:"reproduce"`

	// List of syscalls to test (optional). For example:
	//	"enable_syscalls": [ "mmap", "openat$ashmem", "ioctl$ASHMEM*" ]
	EnabledSyscalls []string `json:"enable_syscalls,omitempty"`
	// List of system calls that should be treated as disabled (optional).
	DisabledSyscalls []string `json:"disable_syscalls,omitempty"`
	// List of regexps for known bugs.
	// Don't save reports matching these regexps, but reboot VM after them,
	// matched against whole report output.
	Suppressions []string `json:"suppressions,omitempty"`
	// Completely ignore reports matching these regexps (don't save nor reboot),
	// must match the first line of crash message.
	Ignores []string `json:"ignores,omitempty"`

	// Type of virtual machine to use, e.g. "qemu", "gce", "android", "isolated", etc.
	Type string `json:"type"`
	// VM-type-specific parameters.
	// Parameters for concrete types are in Config type in vm/TYPE/TYPE.go, e.g. vm/qemu/qemu.go.
	VM json.RawMessage `json:"vm"`

	// Implementation details beyond this point. Filled after parsing.
	Derived `json:"-"`
}

type Subsystem struct {
	Name  string   `json:"name"`
	Paths []string `json:"path"`
}

type covFilterCfg struct {
	Files     []string `json:"files,omitempty"`
	Functions []string `json:"functions,omitempty"`
	RawPCs    []string `json:"pcs,omitempty"`
}
<|endoftext|>


<commit_before>package proxy

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"strings"
	"time"

	jwt "github.com/dgrijalva/jwt-go"
	"github.com/fagongzi/gateway/pkg/filter"
	"github.com/fagongzi/util/hack"
	"github.com/garyburd/redigo/redis"
	"github.com/valyala/fasthttp"
)

const (
	// besides checking token is legitimate or not, it checks whether token exists in redis
	actionTokenInRedis string = "token_in_redis"
	// besides checking token is legitimate or not, it checks whether token exists in redis and the value is the csrfToken value
	actionTokenAndCSRFInRedis string = "token_and_csrf_in_redis"
	// update token's TTL
	actionRenewByRaw string = "renew_by_raw"
	// update token's TTL and in the same time put new token in redis, previous token invalid
	actionRenewByRedis string = "renew_by_redis"
	// fetch fields from token and put them in header which is redirected to a backend server who is unbeknownst to JWT
	actionFetchToHeader string = "fetch_to_header"
	actionFetchToCookie string = "fetch_to_cookie"
	ctxRenewTokenAttr   string = "__jwt_renew_token__"
	jwtClaimsFieldExp   string = "exp"
)

var (
	errJWTMissing  = errors.New("missing jwt token")
	errJWTInvalid  = errors.New("invalid jwt token")
	errCSRFMissing = errors.New("missing csrf token")
	errCSRFInvalid = errors.New("invalid csrf token")
)

type tokenGetter func(filter.Context) (string, error)
type action func(map[string]interface{}, string, jwt.MapClaims, filter.Context) (bool, error)

// JWTCfg cfg
type JWTCfg struct {
	Secret               string   `json:"secret"`
	Method               string   `json:"method"`
	TokenLookup          string   `json:"tokenLookup"`
	CSRFHeaderName       string   `json:"csrfHeaderName"`
	AuthSchema           string   `json:"authSchema"`
	RenewTokenHeaderName string   `json:"renewTokenHeaderName,omitempty"`
	Redis                *Redis   `json:"redis,omitempty"`
	Actions              []Action `json:"actions,omitempty"`
}

// Action action
type Action struct {
	Method string                 `json:"method"`
	Params map[string]interface{} `json:"params"`
}

// Redis redis
type Redis struct {
	Addr        string `json:"addr"`
	MaxActive   int    `json:"maxActive"`
	MaxIdle     int    `json:"maxIdle"`
	IdleTimeout int    `json:"idleTimeout"`
}

// JWTFilter filter
type JWTFilter struct {
	filter.BaseFilter

	cfg              *JWTCfg
	secretBytes      []byte
	getter           tokenGetter
	csrfGetter       tokenGetter
	redisPool        *redis.Pool
	leaseTTLDuration time.Duration
	signing          *jwt.SigningMethodHMAC
	actions          []action
	actionArgs       []map[string]interface{}
}

func newJWTFilter(file string) (filter.Filter, error) {
	f := &JWTFilter{}

	err := f.parseCfg(file)
	if err != nil {
		return nil, err
	}

	err = f.initSigningMethod()
	if err != nil {
		return nil, err
	}

	err = f.initActions()
	if err != nil {
		return nil, err
	}

	f.initRedisPool()
	f.initTokenLookup()
	return f, nil
}

// Name name
func (f *JWTFilter) Name() string {
	return FilterJWT
}

// Pre execute before proxy
func (f *JWTFilter) Pre(c filter.Context) (statusCode int, err error) {
	if strings.ToUpper(c.API().AuthFilter) != f.Name() {
		return f.BaseFilter.Pre(c)
	}

	token, err := f.getter(c)
	if err != nil {
		return fasthttp.StatusForbidden, err
	}

	claims, err := f.parseJWTToken(token)
	if err != nil {
		return fasthttp.StatusForbidden, err
	}

	for idx, act := range f.actions {
		ok, err := act(f.actionArgs[idx], token, claims, c)
		if err != nil {
			return fasthttp.StatusInternalServerError, err
		}

		if !ok {
			return fasthttp.StatusForbidden, nil
		}
	}

	return f.BaseFilter.Pre(c)
}

// Post execute after proxy
func (f *JWTFilter) Post(c filter.Context) (statusCode int, err error) {
	if value := c.GetAttr(ctxRenewTokenAttr); value != nil {
		c.Response().Header.Add(f.cfg.RenewTokenHeaderName, value.(string))
	}

	return f.BaseFilter.Post(c)
}

func (f *JWTFilter) parseCfg(file string) error {
	data, err := ioutil.ReadFile(file)
	if err != nil {
		return err
	}

	cfg := &JWTCfg{}
	err = json.Unmarshal(data, cfg)
	if err != nil {
		return err
	}

	f.cfg = cfg
	f.secretBytes = []byte(f.cfg.Secret)
	return nil
}

func (f *JWTFilter) initRedisPool() {
	if f.cfg.Redis != nil {
		f.redisPool = &redis.Pool{
			MaxActive:   f.cfg.Redis.MaxActive,
			MaxIdle:     f.cfg.Redis.MaxIdle,
			IdleTimeout: time.Second * time.Duration(f.cfg.Redis.IdleTimeout),
			Dial: func() (redis.Conn, error) {
				return redis.Dial("tcp",
					f.cfg.Redis.Addr,
					redis.DialWriteTimeout(time.Second*10))
			},
		}
	}
}

func (f *JWTFilter) initSigningMethod() error {
	if f.cfg.Method == "HS256" {
		f.signing = jwt.SigningMethodHS256
	} else if f.cfg.Method == "HS384" {
		f.signing = jwt.SigningMethodHS384
	} else if f.cfg.Method == "HS512" {
		f.signing = jwt.SigningMethodHS512
	} else {
		return fmt.Errorf("unsupported method: %s", f.cfg.Method)
	}

	return nil
}

func (f *JWTFilter) initTokenLookup() {
	parts := strings.Split(f.cfg.TokenLookup, ":")
	f.getter = jwtFromHeader(parts[1], f.cfg.AuthSchema)
	switch parts[0] {
	case "query":
		f.getter = jwtFromQuery(parts[1])
	case "cookie":
		f.getter = jwtFromCookie(parts[1])
	}
	f.csrfGetter = jwtFromHeader(f.cfg.CSRFHeaderName, "")
}

func (f *JWTFilter) initActions() error {
	for _, c := range f.cfg.Actions {
		f.actionArgs = append(f.actionArgs, c.Params)

		switch c.Method {
		case actionTokenInRedis:
			f.actions = append(f.actions, f.tokenInRedisAction)
		case actionTokenAndCSRFInRedis:
			f.actions = append(f.actions, f.actionTokenAndCSRFInRedis)
		case actionRenewByRaw:
			f.actions = append(f.actions, f.renewByRawAction)
		case actionRenewByRedis:
			f.actions = append(f.actions, f.renewByRedisAction)
		case actionFetchToHeader:
			f.actions = append(f.actions, f.fetchToHeader)
		case actionFetchToCookie:
			f.actions = append(f.actions, f.fetchToCookie)
		default:
			return fmt.Errorf("not support action method: %s", c.Method)
		}
	}

	return nil
}

func (f *JWTFilter) parseJWTToken(tokenString string) (jwt.MapClaims, error) {
	token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
		}

		return f.secretBytes, nil
	})

	if err != nil {
		return nil, err
	}

	if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
		return claims, nil
	}

	return nil, fmt.Errorf("error jwt token")
}

func (f *JWTFilter) renewToken(claims jwt.MapClaims) (string, error) {
	token := jwt.NewWithClaims(f.signing, claims)
	return token.SignedString(f.secretBytes)
}

func (f *JWTFilter) getRedis() redis.Conn {
	return f.redisPool.Get()
}

func (f *JWTFilter) renewByRawAction(args map[string]interface{}, token string, claims jwt.MapClaims, c filter.Context) (bool, error) {
	if _, ok := claims[jwtClaimsFieldExp]; ok {
		claims[jwtClaimsFieldExp] = time.Now().Add(time.Second * time.Duration(args["ttl"].(float64))).Unix()
		newToken, err := f.renewToken(claims)
		if err != nil {
			return false, err
		}
		c.SetAttr(ctxRenewTokenAttr, newToken)
		return true, nil
	}

	return true, nil
}

func (f *JWTFilter) renewByRedisAction(args map[string]interface{}, token string, claims jwt.MapClaims, c filter.Context) (bool, error) {
	if f.cfg.Redis == nil {
		return false, fmt.Errorf("redis not setting")
	}

	var buf bytes.Buffer
	buf.WriteString(args["prefix"].(string))
	buf.WriteString(token)
	key := hack.SliceToString(buf.Bytes())

	conn := f.getRedis()
	value, err := redis.Int(conn.Do("TTL", key))
	if err != nil {
		conn.Close()
		return false, err
	}

	// key not exists or ttl is 0
	if value == -2 || value == 0 {
		conn.Close()
		return false, nil
	}

	// no ttl
	if value == -1 {
		conn.Close()
		return true, nil
	}

	_, err = conn.Do("SETEX", key, int(args["ttl"].(float64)), token)
	if err != nil {
		conn.Close()
		return false, err
	}

	conn.Close()
	return true, nil
}

func (f *JWTFilter) tokenInRedisAction(args map[string]interface{}, token string, claims jwt.MapClaims, c filter.Context) (bool, error) {
	if f.cfg.Redis == nil {
		return false, fmt.Errorf("redis not setting")
	}

	var buf bytes.Buffer
	buf.WriteString(args["prefix"].(string))
	buf.WriteString(token)
	key := hack.SliceToString(buf.Bytes())

	conn := f.getRedis()
	value, err := redis.Bool(conn.Do("EXISTS", key))
	conn.Close()
	return value, err
}

func (f *JWTFilter) actionTokenAndCSRFInRedis(args map[string]interface{}, token string, claims jwt.MapClaims, c filter.Context) (bool, error) {
	if f.cfg.Redis == nil {
		return false, fmt.Errorf("redis not setting")
	}

	var buf bytes.Buffer
	buf.WriteString(args["prefix"].(string))
	buf.WriteString(token)
	key := hack.SliceToString(buf.Bytes())

	conn := f.getRedis()
	value, err := redis.String(conn.Do("GET", key))
	conn.Close()

	if err != nil {
		return false, err
	}
	csrfToken, err := f.csrfGetter(c)
	if err != nil {
		if err == errJWTMissing {
			err = errCSRFMissing
		}
		return false, err
	}
	if value != csrfToken {
		return false, errCSRFInvalid
	}
	return true, err
}

func (f *JWTFilter) fetchToHeader(args map[string]interface{}, token string, claims jwt.MapClaims, c filter.Context) (bool, error) {
	var buf bytes.Buffer
	prefix := args["prefix"].(string)
	for _, fd := range args["fields"].([]interface{}) {
		field := fd.(string)
		buf.WriteString(prefix)
		buf.WriteString(field)
		c.ForwardRequest().Header.Add(buf.String(), fmt.Sprintf("%v", claims[field]))
		buf.Reset()
	}

	return true, nil
}

func (f *JWTFilter) fetchToCookie(args map[string]interface{}, token string, claims jwt.MapClaims, c filter.Context) (bool, error) {
	var buf bytes.Buffer
	prefix := args["prefix"].(string)
	for _, fd := range args["fields"].([]interface{}) {
		field := fd.(string)
		buf.WriteString(prefix)
		buf.WriteString(field)
		c.ForwardRequest().Header.SetCookie(buf.String(), fmt.Sprintf("%v", claims[field]))
		buf.Reset()
	}

	return true, nil
}

func jwtFromQuery(param string) tokenGetter {
	return func(c filter.Context) (string, error) {
		token := string(c.OriginRequest().Request.URI().QueryArgs().Peek(param))
		if token == "" {
			return "", errJWTMissing
		}
		return token, nil
	}
}

func jwtFromCookie(name string) tokenGetter {
	return func(c filter.Context) (string, error) {
		value := string(c.OriginRequest().Request.Header.Cookie(name))
		if len(value) == 0 {
			return "", errJWTMissing
		}
		return value, nil
	}
}

func jwtFromHeader(header string, authScheme string) tokenGetter {
	return func(c filter.Context) (string, error) {
		auth := string(c.OriginRequest().Request.Header.Peek(header))
		l := len(authScheme)
		if len(auth) > l+1 && auth[:l] == authScheme {
			return auth[l+1:], nil
		}
		return "", errJWTMissing
	}
}
<commit_msg>EnHanced jwt filter,add custom errors map to httpStatusCode<commit_after>package proxy

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"strings"
	"time"

	jwt "github.com/dgrijalva/jwt-go"
	"github.com/fagongzi/gateway/pkg/filter"
	"github.com/fagongzi/util/hack"
	"github.com/garyburd/redigo/redis"
	"github.com/valyala/fasthttp"
)

const (
	// besides checking token is legitimate or not, it checks whether token exists in redis
	actionTokenInRedis string = "token_in_redis"
	// besides checking token is legitimate or not, it checks whether token exists in redis and the value is the csrfToken value
	actionTokenAndCSRFInRedis string = "token_and_csrf_in_redis"
	// update token's TTL
	actionRenewByRaw string = "renew_by_raw"
	// update token's TTL and in the same time put new token in redis, previous token invalid
	actionRenewByRedis string = "renew_by_redis"
	// fetch fields from token and put them in header which is redirected to a backend server who is unbeknownst to JWT
	actionFetchToHeader string = "fetch_to_header"
	actionFetchToCookie string = "fetch_to_cookie"
	ctxRenewTokenAttr   string = "__jwt_renew_token__"
	jwtClaimsFieldExp   string = "exp"
)

var (
	errJWTMissing  = errors.New("missing jwt token")
	errJWTInvalid  = errors.New("invalid jwt token")
	errCSRFMissing = errors.New("missing csrf token")
	errCSRFInvalid = errors.New("invalid csrf token")

	//handler custom error to statusCode
	customErrMaps = map[string]int{
		errJWTMissing.Error():  fasthttp.StatusUnauthorized,
		errJWTInvalid.Error():  fasthttp.StatusUnauthorized,
		errCSRFMissing.Error(): fasthttp.StatusForbidden,
		errCSRFInvalid.Error(): fasthttp.StatusForbidden,
		redis.ErrNil.Error():   fasthttp.StatusForbidden, //redis nil err
	}
)

type tokenGetter func(filter.Context) (string, error)
type action func(map[string]interface{}, string, jwt.MapClaims, filter.Context) (bool, error)

// JWTCfg cfg
type JWTCfg struct {
	Secret               string   `json:"secret"`
	Method               string   `json:"method"`
	TokenLookup          string   `json:"tokenLookup"`
	CSRFHeaderName       string   `json:"csrfHeaderName"`
	AuthSchema           string   `json:"authSchema"`
	RenewTokenHeaderName string   `json:"renewTokenHeaderName,omitempty"`
	Redis                *Redis   `json:"redis,omitempty"`
	Actions              []Action `json:"actions,omitempty"`
}

// Action action
type Action struct {
	Method string                 `json:"method"`
	Params map[string]interface{} `json:"params"`
}

// Redis redis
type Redis struct {
	Addr        string `json:"addr"`
	MaxActive   int    `json:"maxActive"`
	MaxIdle     int    `json:"maxIdle"`
	IdleTimeout int    `json:"idleTimeout"`
}

// JWTFilter filter
type JWTFilter struct {
	filter.BaseFilter

	cfg              *JWTCfg
	secretBytes      []byte
	getter           tokenGetter
	csrfGetter       tokenGetter
	redisPool        *redis.Pool
	leaseTTLDuration time.Duration
	signing          *jwt.SigningMethodHMAC
	actions          []action
	actionArgs       []map[string]interface{}
}

func newJWTFilter(file string) (filter.Filter, error) {
	f := &JWTFilter{}

	err := f.parseCfg(file)
	if err != nil {
		return nil, err
	}

	err = f.initSigningMethod()
	if err != nil {
		return nil, err
	}

	err = f.initActions()
	if err != nil {
		return nil, err
	}

	f.initRedisPool()
	f.initTokenLookup()
	return f, nil
}

// Name name
func (f *JWTFilter) Name() string {
	return FilterJWT
}

// Pre execute before proxy
func (f *JWTFilter) Pre(c filter.Context) (statusCode int, err error) {
	if strings.ToUpper(c.API().AuthFilter) != f.Name() {
		return f.BaseFilter.Pre(c)
	}

	token, err := f.getter(c)
	if err != nil {
		return fasthttp.StatusForbidden, err
	}

	claims, err := f.parseJWTToken(token)
	if err != nil {
		return fasthttp.StatusForbidden, err
	}

	for idx, act := range f.actions {
		ok, err := act(f.actionArgs[idx], token, claims, c)
		if err != nil {
			if code, custom := customErrMaps[err.Error()]; custom {
				return code, err
			}
			return fasthttp.StatusInternalServerError, err
		}

		if !ok {
			return fasthttp.StatusForbidden, nil //Note: if return err is nil, the httpStatusCode will be 200 OK
		}
	}

	return f.BaseFilter.Pre(c)
}

// Post execute after proxy
func (f *JWTFilter) Post(c filter.Context) (statusCode int, err error) {
	if value := c.GetAttr(ctxRenewTokenAttr); value != nil {
		c.Response().Header.Add(f.cfg.RenewTokenHeaderName, value.(string))
	}

	return f.BaseFilter.Post(c)
}

func (f *JWTFilter) parseCfg(file string) error {
	data, err := ioutil.ReadFile(file)
	if err != nil {
		return err
	}

	cfg := &JWTCfg{}
	err = json.Unmarshal(data, cfg)
	if err != nil {
		return err
	}

	f.cfg = cfg
	f.secretBytes = []byte(f.cfg.Secret)
	return nil
}

func (f *JWTFilter) initRedisPool() {
	if f.cfg.Redis != nil {
		f.redisPool = &redis.Pool{
			MaxActive:   f.cfg.Redis.MaxActive,
			MaxIdle:     f.cfg.Redis.MaxIdle,
			IdleTimeout: time.Second * time.Duration(f.cfg.Redis.IdleTimeout),
			Dial: func() (redis.Conn, error) {
				return redis.Dial("tcp",
					f.cfg.Redis.Addr,
					redis.DialWriteTimeout(time.Second*10))
			},
		}
	}
}

func (f *JWTFilter) initSigningMethod() error {
	if f.cfg.Method == "HS256" {
		f.signing = jwt.SigningMethodHS256
	} else if f.cfg.Method == "HS384" {
		f.signing = jwt.SigningMethodHS384
	} else if f.cfg.Method == "HS512" {
		f.signing = jwt.SigningMethodHS512
	} else {
		return fmt.Errorf("unsupported method: %s", f.cfg.Method)
	}

	return nil
}

func (f *JWTFilter) initTokenLookup() {
	parts := strings.Split(f.cfg.TokenLookup, ":")
	f.getter = jwtFromHeader(parts[1], f.cfg.AuthSchema)
	switch parts[0] {
	case "query":
		f.getter = jwtFromQuery(parts[1])
	case "cookie":
		f.getter = jwtFromCookie(parts[1])
	}
	f.csrfGetter = jwtFromHeader(f.cfg.CSRFHeaderName, "")
}

func (f *JWTFilter) initActions() error {
	for _, c := range f.cfg.Actions {
		f.actionArgs = append(f.actionArgs, c.Params)

		switch c.Method {
		case actionTokenInRedis:
			f.actions = append(f.actions, f.tokenInRedisAction)
		case actionTokenAndCSRFInRedis:
			f.actions = append(f.actions, f.actionTokenAndCSRFInRedis)
		case actionRenewByRaw:
			f.actions = append(f.actions, f.renewByRawAction)
		case actionRenewByRedis:
			f.actions = append(f.actions, f.renewByRedisAction)
		case actionFetchToHeader:
			f.actions = append(f.actions, f.fetchToHeader)
		case actionFetchToCookie:
			f.actions = append(f.actions, f.fetchToCookie)
		default:
			return fmt.Errorf("not support action method: %s", c.Method)
		}
	}

	return nil
}

func (f *JWTFilter) parseJWTToken(tokenString string) (jwt.MapClaims, error) {
	token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
		}

		return f.secretBytes, nil
	})

	if err != nil {
		return nil, err
	}

	if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
		return claims, nil
	}

	return nil, fmt.Errorf("error jwt token")
}

func (f *JWTFilter) renewToken(claims jwt.MapClaims) (string, error) {
	token := jwt.NewWithClaims(f.signing, claims)
	return token.SignedString(f.secretBytes)
}

func (f *JWTFilter) getRedis() redis.Conn {
	return f.redisPool.Get()
}

func (f *JWTFilter) renewByRawAction(args map[string]interface{}, token string, claims jwt.MapClaims, c filter.Context) (bool, error) {
	if _, ok := claims[jwtClaimsFieldExp]; ok {
		claims[jwtClaimsFieldExp] = time.Now().Add(time.Second * time.Duration(args["ttl"].(float64))).Unix()
		newToken, err := f.renewToken(claims)
		if err != nil {
			return false, err
		}
		c.SetAttr(ctxRenewTokenAttr, newToken)
		return true, nil
	}

	return true, nil
}

func (f *JWTFilter) renewByRedisAction(args map[string]interface{}, token string, claims jwt.MapClaims, c filter.Context) (bool, error) {
	if f.cfg.Redis == nil {
		return false, fmt.Errorf("redis not setting")
	}

	var buf bytes.Buffer
	buf.WriteString(args["prefix"].(string))
	buf.WriteString(token)
	key := hack.SliceToString(buf.Bytes())

	conn := f.getRedis()
	value, err := redis.Int(conn.Do("TTL", key))
	if err != nil {
		conn.Close()
		return false, err
	}

	// key not exists or ttl is 0
	if value == -2 || value == 0 {
		conn.Close()
		return false, nil
	}

	// no ttl
	if value == -1 {
		conn.Close()
		return true, nil
	}

	_, err = conn.Do("SETEX", key, int(args["ttl"].(float64)), token)
	if err != nil {
		conn.Close()
		return false, err
	}

	conn.Close()
	return true, nil
}

func (f *JWTFilter) tokenInRedisAction(args map[string]interface{}, token string, claims jwt.MapClaims, c filter.Context) (bool, error) {
	if f.cfg.Redis == nil {
		return false, fmt.Errorf("redis not setting")
	}

	var buf bytes.Buffer
	buf.WriteString(args["prefix"].(string))
	buf.WriteString(token)
	key := hack.SliceToString(buf.Bytes())

	conn := f.getRedis()
	value, err := redis.Bool(conn.Do("EXISTS", key))
	conn.Close()
	return value, err
}

func (f *JWTFilter) actionTokenAndCSRFInRedis(args map[string]interface{}, token string, claims jwt.MapClaims, c filter.Context) (bool, error) {
	if f.cfg.Redis == nil {
		return false, fmt.Errorf("redis not setting")
	}

	var buf bytes.Buffer
	buf.WriteString(args["prefix"].(string))
	buf.WriteString(token)
	key := hack.SliceToString(buf.Bytes())

	conn := f.getRedis()
	value, err := redis.String(conn.Do("GET", key))
	conn.Close()

	if err != nil {
		return false, err
	}
	csrfToken, err := f.csrfGetter(c)
	if err != nil {
		if err == errJWTMissing {
			err = errCSRFMissing
		}
		return false, err
	}
	if value != csrfToken {
		return false, errCSRFInvalid
	}
	return true, err
}

func (f *JWTFilter) fetchToHeader(args map[string]interface{}, token string, claims jwt.MapClaims, c filter.Context) (bool, error) {
	var buf bytes.Buffer
	prefix := args["prefix"].(string)
	for _, fd := range args["fields"].([]interface{}) {
		field := fd.(string)
		buf.WriteString(prefix)
		buf.WriteString(field)
		c.ForwardRequest().Header.Add(buf.String(), fmt.Sprintf("%v", claims[field]))
		buf.Reset()
	}

	return true, nil
}

func (f *JWTFilter) fetchToCookie(args map[string]interface{}, token string, claims jwt.MapClaims, c filter.Context) (bool, error) {
	var buf bytes.Buffer
	prefix := args["prefix"].(string)
	for _, fd := range args["fields"].([]interface{}) {
		field := fd.(string)
		buf.WriteString(prefix)
		buf.WriteString(field)
		c.ForwardRequest().Header.SetCookie(buf.String(), fmt.Sprintf("%v", claims[field]))
		buf.Reset()
	}

	return true, nil
}

func jwtFromQuery(param string) tokenGetter {
	return func(c filter.Context) (string, error) {
		token := string(c.OriginRequest().Request.URI().QueryArgs().Peek(param))
		if token == "" {
			return "", errJWTMissing
		}
		return token, nil
	}
}

func jwtFromCookie(name string) tokenGetter {
	return func(c filter.Context) (string, error) {
		value := string(c.OriginRequest().Request.Header.Cookie(name))
		if len(value) == 0 {
			return "", errJWTMissing
		}
		return value, nil
	}
}

func jwtFromHeader(header string, authScheme string) tokenGetter {
	return func(c filter.Context) (string, error) {
		auth := string(c.OriginRequest().Request.Header.Peek(header))
		l := len(authScheme)
		if len(auth) > l+1 && auth[:l] == authScheme {
			return auth[l+1:], nil
		}
		return "", errJWTMissing
	}
}
<|endoftext|>


<commit_before>// Copyright 2017 Authors of Cilium
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build !privileged_tests

package proxy

import (
	"fmt"
	"net"
	"strconv"
	"sync"
	"testing"
	"time"

	"github.com/cilium/cilium/common/addressing"
	"github.com/cilium/cilium/pkg/endpoint"
	"github.com/cilium/cilium/pkg/endpointmanager"
	"github.com/cilium/cilium/pkg/identity"
	"github.com/cilium/cilium/pkg/logging/logfields"
	"github.com/cilium/cilium/pkg/policy"
	"github.com/cilium/cilium/pkg/policy/api"
	"github.com/cilium/cilium/pkg/proxy/logger"

	"github.com/optiopay/kafka"
	"github.com/optiopay/kafka/proto"
	"github.com/sirupsen/logrus"

	. "gopkg.in/check.v1"
)

// Hook up gocheck into the "go test" runner.
func Test(t *testing.T) {
	TestingT(t)
}

type proxyTestSuite struct{}

var _ = Suite(&proxyTestSuite{})

var (
	localEndpointMock logger.EndpointUpdater = &proxyUpdaterMock{
		id:       1000,
		ipv4:     "10.0.0.1",
		ipv6:     "f00d::1",
		labels:   []string{"id.foo", "id.bar"},
		identity: identity.NumericIdentity(256),
	}
)

type DummyRuleCacheOwner struct{}

func (d *DummyRuleCacheOwner) ClearPolicyConsumers(id uint16) *sync.WaitGroup {
	return &sync.WaitGroup{}
}

var ruleCacheOwner *DummyRuleCacheOwner

// newTestBrokerConf returns BrokerConf with default configuration adjusted for
// tests
func newTestBrokerConf(clientID string) kafka.BrokerConf {
	conf := kafka.NewBrokerConf(clientID)
	conf.DialTimeout = 400 * time.Millisecond
	conf.LeaderRetryLimit = 10
	conf.LeaderRetryWait = 2 * time.Millisecond
	return conf
}

type loggerMap struct{}

func fields(args ...interface{}) logrus.Fields {
	fields := logrus.Fields{}
	for i := 0; i+1 < len(args); i += 2 {
		fields[args[i].(string)] = args[i+1]
	}
	return fields
}

func (loggerMap) Debug(msg string, args ...interface{}) { log.WithFields(fields(args...)).Debug(msg) }
func (loggerMap) Info(msg string, args ...interface{})  { log.WithFields(fields(args...)).Info(msg) }
func (loggerMap) Warn(msg string, args ...interface{})  { log.WithFields(fields(args...)).Warn(msg) }
func (loggerMap) Error(msg string, args ...interface{}) { log.WithFields(fields(args...)).Error(msg) }

var (
	proxyAddress = "127.0.0.1"
)

type metadataTester struct {
	host               string
	port               uint16
	topics             map[string]bool
	allowCreate        bool
	numGeneralFetches  int
	numSpecificFetches int
}

func newMetadataHandler(srv *Server, allowCreate bool, proxyPort uint16) *metadataTester {
	tester := &metadataTester{
		host:        proxyAddress,
		port:        proxyPort,
		allowCreate: allowCreate,
		topics:      make(map[string]bool),
	}
	tester.topics["allowedTopic"] = true
	tester.topics["disallowedTopic"] = true
	return tester
}

func (m *metadataTester) NumGeneralFetches() int {
	return m.numGeneralFetches
}

func (m *metadataTester) NumSpecificFetches() int {
	return m.numSpecificFetches
}

func (m *metadataTester) Handler() RequestHandler {
	return func(request Serializable) Serializable {
		req := request.(*proto.MetadataReq)

		if len(req.Topics) == 0 {
			m.numGeneralFetches++
		} else {
			m.numSpecificFetches++
		}

		resp := &proto.MetadataResp{
			CorrelationID: req.CorrelationID,
			Brokers: []proto.MetadataRespBroker{
				{NodeID: 1, Host: m.host, Port: int32(m.port)},
			},
			Topics: []proto.MetadataRespTopic{},
		}

		wantsTopic := make(map[string]bool)
		for _, topic := range req.Topics {
			if m.allowCreate {
				m.topics[topic] = true
			}
			wantsTopic[topic] = true
		}

		for topic := range m.topics {
			// Return either all topics or only topics that they explicitly requested
			_, explicitTopic := wantsTopic[topic]
			if len(req.Topics) > 0 && !explicitTopic {
				continue
			}

			resp.Topics = append(resp.Topics, proto.MetadataRespTopic{
				Name: topic,
				Partitions: []proto.MetadataRespPartition{
					{
						ID:       0,
						Leader:   1,
						Replicas: []int32{1},
						Isrs:     []int32{1},
					},
					{
						ID:       1,
						Leader:   1,
						Replicas: []int32{1},
						Isrs:     []int32{1},
					},
				},
			})
		}
		return resp
	}
}

func (k *proxyTestSuite) TestKafkaRedirect(c *C) {
	// this isn't thread safe but there is no function to get it
	// SetLevel is atomic, however.
	oldLevel := log.Level
	defer log.Logger.SetLevel(oldLevel)
	log.Logger.SetLevel(logrus.DebugLevel)

	server := NewServer()
	server.Start()
	defer server.Close()

	log.WithFields(logrus.Fields{
		"address": server.Address(),
	}).Debug("Started kafka server")

	pp := getProxyPort(policy.ParserTypeKafka, true)
	c.Assert(pp.configured, Equals, false)
	var err error
	pp.proxyPort, err = allocatePort(pp.proxyPort, 10000, 20000)
	c.Assert(err, IsNil)
	c.Assert(pp.proxyPort, Not(Equals), 0)
	pp.reservePort()
	c.Assert(pp.configured, Equals, true)

	proxyAddress := fmt.Sprintf("%s:%d", proxyAddress, uint16(pp.proxyPort))

	kafkaRule1 := api.PortRuleKafka{APIKey: "metadata", APIVersion: "0"}
	c.Assert(kafkaRule1.Sanitize(), IsNil)

	kafkaRule2 := api.PortRuleKafka{APIKey: "produce", APIVersion: "0", Topic: "allowedTopic"}
	c.Assert(kafkaRule2.Sanitize(), IsNil)

	// Insert a mock EP to the endpointmanager so that DefaultEndpointInfoRegistry may find
	// the EP ID by the IP.
	ep := endpoint.NewEndpointWithState(uint16(localEndpointMock.GetID()), endpoint.StateReady)
	ipv4, err := addressing.NewCiliumIPv4("127.0.0.1")
	c.Assert(err, IsNil)
	ep.IPv4 = ipv4
	ep.UpdateLogger(nil)
	endpointmanager.Insert(ep)
	defer endpointmanager.Remove(ep, ruleCacheOwner)

	_, dstPortStr, err := net.SplitHostPort(server.Address())
	c.Assert(err, IsNil)
	portInt, err := strconv.Atoi(dstPortStr)
	c.Assert(err, IsNil)
	r := newRedirect(localEndpointMock, pp, uint16(portInt))

	r.rules = policy.L7DataMap{
		api.WildcardEndpointSelector: api.L7Rules{
			Kafka: []api.PortRuleKafka{kafkaRule1, kafkaRule2},
		},
	}

	redir, err := createKafkaRedirect(r, kafkaConfiguration{
		lookupSrcID: func(mapname, remoteAddr, localAddr string, ingress bool) (uint32, error) {
			return uint32(1000), nil
		},
		// Disable use of SO_MARK, IP_TRANSPARENT for tests
		testMode: true,
	}, DefaultEndpointInfoRegistry)
	c.Assert(err, IsNil)
	defer redir.Close(nil)

	log.WithFields(logrus.Fields{
		"address": proxyAddress,
	}).Debug("Started kafka proxy")

	server.Handle(MetadataRequest, newMetadataHandler(server, false, r.listener.proxyPort).Handler())

	broker, err := kafka.Dial([]string{proxyAddress}, newTestBrokerConf("tester"))
	if err != nil {
		c.Fatalf("cannot create broker: %s", err)
	}

	// setup producer
	prodConf := kafka.NewProducerConf()
	prodConf.RetryWait = time.Millisecond
	prodConf.Logger = loggerMap{}
	producer := broker.Producer(prodConf)
	messages := []*proto.Message{
		{Value: []byte("first")},
		{Value: []byte("second")},
	}

	// Start handling allowedTopic produce requests
	server.Handle(ProduceRequest, func(request Serializable) Serializable {
		req := request.(*proto.ProduceReq)
		log.WithField(logfields.Request, logfields.Repr(req)).Debug("Handling req")
		return &proto.ProduceResp{
			CorrelationID: req.CorrelationID,
			Topics: []proto.ProduceRespTopic{
				{
					Name: req.Topics[0].Name,
					Partitions: []proto.ProduceRespPartition{
						{
							ID:     0,
							Offset: 5,
						},
					},
				},
			},
		}
	})

	// send a Produce request for an allowed topic
	offset, err := producer.Produce("allowedTopic", 0, messages...)
	c.Assert(err, IsNil)
	c.Assert(offset, Equals, int64(5))

	// send a Produce request for disallowed topic
	_, err = producer.Produce("disallowedTopic", 0, messages...)
	c.Assert(err, Equals, proto.ErrTopicAuthorizationFailed)

	log.Debug("Testing done, closing listen socket")
	finalize, _ := redir.Close(nil)
	finalize()

	// In order to see in the logs that the connections get closed after the
	// 1-minute timeout, uncomment this line:
	// time.Sleep(2 * time.Minute)
}
<commit_msg>proxy: fix unit test breakage<commit_after>// Copyright 2017 Authors of Cilium
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build !privileged_tests

package proxy

import (
	"fmt"
	"net"
	"strconv"
	"testing"
	"time"

	"github.com/cilium/cilium/common/addressing"
	"github.com/cilium/cilium/pkg/endpoint"
	"github.com/cilium/cilium/pkg/endpointmanager"
	"github.com/cilium/cilium/pkg/identity"
	"github.com/cilium/cilium/pkg/logging/logfields"
	"github.com/cilium/cilium/pkg/policy"
	"github.com/cilium/cilium/pkg/policy/api"
	"github.com/cilium/cilium/pkg/proxy/logger"

	"github.com/optiopay/kafka"
	"github.com/optiopay/kafka/proto"
	"github.com/sirupsen/logrus"

	. "gopkg.in/check.v1"
)

// Hook up gocheck into the "go test" runner.
func Test(t *testing.T) {
	TestingT(t)
}

type proxyTestSuite struct{}

var _ = Suite(&proxyTestSuite{})

var (
	localEndpointMock logger.EndpointUpdater = &proxyUpdaterMock{
		id:       1000,
		ipv4:     "10.0.0.1",
		ipv6:     "f00d::1",
		labels:   []string{"id.foo", "id.bar"},
		identity: identity.NumericIdentity(256),
	}
)

// newTestBrokerConf returns BrokerConf with default configuration adjusted for
// tests
func newTestBrokerConf(clientID string) kafka.BrokerConf {
	conf := kafka.NewBrokerConf(clientID)
	conf.DialTimeout = 400 * time.Millisecond
	conf.LeaderRetryLimit = 10
	conf.LeaderRetryWait = 2 * time.Millisecond
	return conf
}

type loggerMap struct{}

func fields(args ...interface{}) logrus.Fields {
	fields := logrus.Fields{}
	for i := 0; i+1 < len(args); i += 2 {
		fields[args[i].(string)] = args[i+1]
	}
	return fields
}

func (loggerMap) Debug(msg string, args ...interface{}) { log.WithFields(fields(args...)).Debug(msg) }
func (loggerMap) Info(msg string, args ...interface{})  { log.WithFields(fields(args...)).Info(msg) }
func (loggerMap) Warn(msg string, args ...interface{})  { log.WithFields(fields(args...)).Warn(msg) }
func (loggerMap) Error(msg string, args ...interface{}) { log.WithFields(fields(args...)).Error(msg) }

var (
	proxyAddress = "127.0.0.1"
)

type metadataTester struct {
	host               string
	port               uint16
	topics             map[string]bool
	allowCreate        bool
	numGeneralFetches  int
	numSpecificFetches int
}

func newMetadataHandler(srv *Server, allowCreate bool, proxyPort uint16) *metadataTester {
	tester := &metadataTester{
		host:        proxyAddress,
		port:        proxyPort,
		allowCreate: allowCreate,
		topics:      make(map[string]bool),
	}
	tester.topics["allowedTopic"] = true
	tester.topics["disallowedTopic"] = true
	return tester
}

func (m *metadataTester) NumGeneralFetches() int {
	return m.numGeneralFetches
}

func (m *metadataTester) NumSpecificFetches() int {
	return m.numSpecificFetches
}

func (m *metadataTester) Handler() RequestHandler {
	return func(request Serializable) Serializable {
		req := request.(*proto.MetadataReq)

		if len(req.Topics) == 0 {
			m.numGeneralFetches++
		} else {
			m.numSpecificFetches++
		}

		resp := &proto.MetadataResp{
			CorrelationID: req.CorrelationID,
			Brokers: []proto.MetadataRespBroker{
				{NodeID: 1, Host: m.host, Port: int32(m.port)},
			},
			Topics: []proto.MetadataRespTopic{},
		}

		wantsTopic := make(map[string]bool)
		for _, topic := range req.Topics {
			if m.allowCreate {
				m.topics[topic] = true
			}
			wantsTopic[topic] = true
		}

		for topic := range m.topics {
			// Return either all topics or only topics that they explicitly requested
			_, explicitTopic := wantsTopic[topic]
			if len(req.Topics) > 0 && !explicitTopic {
				continue
			}

			resp.Topics = append(resp.Topics, proto.MetadataRespTopic{
				Name: topic,
				Partitions: []proto.MetadataRespPartition{
					{
						ID:       0,
						Leader:   1,
						Replicas: []int32{1},
						Isrs:     []int32{1},
					},
					{
						ID:       1,
						Leader:   1,
						Replicas: []int32{1},
						Isrs:     []int32{1},
					},
				},
			})
		}
		return resp
	}
}

func (k *proxyTestSuite) TestKafkaRedirect(c *C) {
	// this isn't thread safe but there is no function to get it
	// SetLevel is atomic, however.
	oldLevel := log.Level
	defer log.Logger.SetLevel(oldLevel)
	log.Logger.SetLevel(logrus.DebugLevel)

	server := NewServer()
	server.Start()
	defer server.Close()

	log.WithFields(logrus.Fields{
		"address": server.Address(),
	}).Debug("Started kafka server")

	pp := getProxyPort(policy.ParserTypeKafka, true)
	c.Assert(pp.configured, Equals, false)
	var err error
	pp.proxyPort, err = allocatePort(pp.proxyPort, 10000, 20000)
	c.Assert(err, IsNil)
	c.Assert(pp.proxyPort, Not(Equals), 0)
	pp.reservePort()
	c.Assert(pp.configured, Equals, true)

	proxyAddress := fmt.Sprintf("%s:%d", proxyAddress, uint16(pp.proxyPort))

	kafkaRule1 := api.PortRuleKafka{APIKey: "metadata", APIVersion: "0"}
	c.Assert(kafkaRule1.Sanitize(), IsNil)

	kafkaRule2 := api.PortRuleKafka{APIKey: "produce", APIVersion: "0", Topic: "allowedTopic"}
	c.Assert(kafkaRule2.Sanitize(), IsNil)

	// Insert a mock EP to the endpointmanager so that DefaultEndpointInfoRegistry may find
	// the EP ID by the IP.
	ep := endpoint.NewEndpointWithState(uint16(localEndpointMock.GetID()), endpoint.StateReady)
	ipv4, err := addressing.NewCiliumIPv4("127.0.0.1")
	c.Assert(err, IsNil)
	ep.IPv4 = ipv4
	ep.UpdateLogger(nil)
	endpointmanager.Insert(ep)
	defer endpointmanager.Remove(ep)

	_, dstPortStr, err := net.SplitHostPort(server.Address())
	c.Assert(err, IsNil)
	portInt, err := strconv.Atoi(dstPortStr)
	c.Assert(err, IsNil)
	r := newRedirect(localEndpointMock, pp, uint16(portInt))

	r.rules = policy.L7DataMap{
		api.WildcardEndpointSelector: api.L7Rules{
			Kafka: []api.PortRuleKafka{kafkaRule1, kafkaRule2},
		},
	}

	redir, err := createKafkaRedirect(r, kafkaConfiguration{
		lookupSrcID: func(mapname, remoteAddr, localAddr string, ingress bool) (uint32, error) {
			return uint32(1000), nil
		},
		// Disable use of SO_MARK, IP_TRANSPARENT for tests
		testMode: true,
	}, DefaultEndpointInfoRegistry)
	c.Assert(err, IsNil)
	defer redir.Close(nil)

	log.WithFields(logrus.Fields{
		"address": proxyAddress,
	}).Debug("Started kafka proxy")

	server.Handle(MetadataRequest, newMetadataHandler(server, false, r.listener.proxyPort).Handler())

	broker, err := kafka.Dial([]string{proxyAddress}, newTestBrokerConf("tester"))
	if err != nil {
		c.Fatalf("cannot create broker: %s", err)
	}

	// setup producer
	prodConf := kafka.NewProducerConf()
	prodConf.RetryWait = time.Millisecond
	prodConf.Logger = loggerMap{}
	producer := broker.Producer(prodConf)
	messages := []*proto.Message{
		{Value: []byte("first")},
		{Value: []byte("second")},
	}

	// Start handling allowedTopic produce requests
	server.Handle(ProduceRequest, func(request Serializable) Serializable {
		req := request.(*proto.ProduceReq)
		log.WithField(logfields.Request, logfields.Repr(req)).Debug("Handling req")
		return &proto.ProduceResp{
			CorrelationID: req.CorrelationID,
			Topics: []proto.ProduceRespTopic{
				{
					Name: req.Topics[0].Name,
					Partitions: []proto.ProduceRespPartition{
						{
							ID:     0,
							Offset: 5,
						},
					},
				},
			},
		}
	})

	// send a Produce request for an allowed topic
	offset, err := producer.Produce("allowedTopic", 0, messages...)
	c.Assert(err, IsNil)
	c.Assert(offset, Equals, int64(5))

	// send a Produce request for disallowed topic
	_, err = producer.Produce("disallowedTopic", 0, messages...)
	c.Assert(err, Equals, proto.ErrTopicAuthorizationFailed)

	log.Debug("Testing done, closing listen socket")
	finalize, _ := redir.Close(nil)
	finalize()

	// In order to see in the logs that the connections get closed after the
	// 1-minute timeout, uncomment this line:
	// time.Sleep(2 * time.Minute)
}
<|endoftext|>


<commit_before>// Copyright 2018 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package runtest

import (
	"flag"
	"os"
	"path/filepath"
	"runtime"
	"testing"

	"github.com/google/syzkaller/pkg/csource"
	"github.com/google/syzkaller/pkg/host"
	"github.com/google/syzkaller/prog"
	"github.com/google/syzkaller/sys/targets"
	_ "github.com/google/syzkaller/sys/test/gen" // pull in the test target
)

// Can be used as:
// go test -v -run=Test/64_fork ./pkg/runtest -filter=nonfailing
// to select a subset of tests to run.
var flagFilter = flag.String("filter", "", "prefix to match test file names")

func Test(t *testing.T) {
	switch runtime.GOOS {
	case "openbsd":
		t.Skipf("broken on %v", runtime.GOOS)
	}
	// Test only one target in short mode (each takes 5+ seconds to run).
	shortTarget := targets.Get("test", "64")
	for _, sysTarget := range targets.List["test"] {
		if testing.Short() && sysTarget != shortTarget {
			continue
		}
		sysTarget1 := targets.Get(sysTarget.OS, sysTarget.Arch)
		t.Run(sysTarget1.Arch, func(t *testing.T) {
			t.Parallel()
			test(t, sysTarget1)
		})
	}
}

func test(t *testing.T, sysTarget *targets.Target) {
	target, err := prog.GetTarget(sysTarget.OS, sysTarget.Arch)
	if err != nil {
		t.Fatal(err)
	}
	executor, err := csource.BuildFile(target, filepath.FromSlash("../../executor/executor.cc"))
	if err != nil {
		t.Fatal(err)
	}
	defer os.Remove(executor)
	features, err := host.Check(target)
	if err != nil {
		t.Fatalf("failed to detect host features: %v", err)
	}
	calls, _, err := host.DetectSupportedSyscalls(target, "none")
	if err != nil {
		t.Fatalf("failed to detect supported syscalls: %v", err)
	}
	enabledCalls := map[string]map[*prog.Syscall]bool{
		"":     calls,
		"none": calls,
	}
	featureFlags, err := csource.ParseFeaturesFlags("none", "none", true)
	if err != nil {
		t.Fatal(err)
	}
	if err := host.Setup(target, features, featureFlags, executor); err != nil {
		t.Fatal(err)
	}
	requests := make(chan *RunRequest, 2*runtime.GOMAXPROCS(0))
	go func() {
		for req := range requests {
			RunTest(req, executor)
			close(req.Done)
		}
	}()
	ctx := &Context{
		Dir:          filepath.Join("..", "..", "sys", target.OS, "test"),
		Target:       target,
		Tests:        *flagFilter,
		Features:     features,
		EnabledCalls: enabledCalls,
		Requests:     requests,
		LogFunc: func(text string) {
			t.Helper()
			t.Logf(text)
		},
		Retries: 7, // empirical number that seem to reduce flakes to zero
		Verbose: true,
	}
	if err := ctx.Run(); err != nil {
		t.Fatal(err)
	}
}
<commit_msg>pkg/runtest: disable 32-bit tests on FreeBSD for now<commit_after>// Copyright 2018 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package runtest

import (
	"flag"
	"os"
	"path/filepath"
	"runtime"
	"testing"

	"github.com/google/syzkaller/pkg/csource"
	"github.com/google/syzkaller/pkg/host"
	"github.com/google/syzkaller/prog"
	"github.com/google/syzkaller/sys/targets"
	_ "github.com/google/syzkaller/sys/test/gen" // pull in the test target
)

// Can be used as:
// go test -v -run=Test/64_fork ./pkg/runtest -filter=nonfailing
// to select a subset of tests to run.
var flagFilter = flag.String("filter", "", "prefix to match test file names")

func Test(t *testing.T) {
	switch runtime.GOOS {
	case "openbsd":
		t.Skipf("broken on %v", runtime.GOOS)
	}
	// Test only one target in short mode (each takes 5+ seconds to run).
	shortTarget := targets.Get("test", "64")
	for _, sysTarget := range targets.List["test"] {
		if testing.Short() && sysTarget != shortTarget {
			continue
		}
		sysTarget1 := targets.Get(sysTarget.OS, sysTarget.Arch)
		if runtime.GOOS == "freebsd" && sysTarget1.PtrSize == 4 {
			// The default DataOffset collides with a runtime mapping
			// on FreeBSD.
			continue
		}
		t.Run(sysTarget1.Arch, func(t *testing.T) {
			t.Parallel()
			test(t, sysTarget1)
		})
	}
}

func test(t *testing.T, sysTarget *targets.Target) {
	target, err := prog.GetTarget(sysTarget.OS, sysTarget.Arch)
	if err != nil {
		t.Fatal(err)
	}
	executor, err := csource.BuildFile(target, filepath.FromSlash("../../executor/executor.cc"))
	if err != nil {
		t.Fatal(err)
	}
	defer os.Remove(executor)
	features, err := host.Check(target)
	if err != nil {
		t.Fatalf("failed to detect host features: %v", err)
	}
	calls, _, err := host.DetectSupportedSyscalls(target, "none")
	if err != nil {
		t.Fatalf("failed to detect supported syscalls: %v", err)
	}
	enabledCalls := map[string]map[*prog.Syscall]bool{
		"":     calls,
		"none": calls,
	}
	featureFlags, err := csource.ParseFeaturesFlags("none", "none", true)
	if err != nil {
		t.Fatal(err)
	}
	if err := host.Setup(target, features, featureFlags, executor); err != nil {
		t.Fatal(err)
	}
	requests := make(chan *RunRequest, 2*runtime.GOMAXPROCS(0))
	go func() {
		for req := range requests {
			RunTest(req, executor)
			close(req.Done)
		}
	}()
	ctx := &Context{
		Dir:          filepath.Join("..", "..", "sys", target.OS, "test"),
		Target:       target,
		Tests:        *flagFilter,
		Features:     features,
		EnabledCalls: enabledCalls,
		Requests:     requests,
		LogFunc: func(text string) {
			t.Helper()
			t.Logf(text)
		},
		Retries: 7, // empirical number that seem to reduce flakes to zero
		Verbose: true,
	}
	if err := ctx.Run(); err != nil {
		t.Fatal(err)
	}
}
<|endoftext|>