<commit_before>package exec

import (
    "github.com/luopengift/golibs/logger"
    "errors"
    "bytes"
    "fmt"
    "os/exec"
    "strings"
    "time"
)

func Cmd(name string,arg ...string) *exec.Cmd {
    return exec.Command(name, arg...)
}

func CmdOut(name string, arg ...string) (string, error) {
    cmd := exec.Command(name, arg...)
    ret,err := cmd.CombinedOutput()
    return string(ret), err
}

func CmdOutBytes(name string, arg ...string) ([]byte, error) {
    cmd := exec.Command(name, arg...)
    var out bytes.Buffer
    cmd.Stdout = &out
    err := cmd.Run()
    return out.Bytes(), err
}

func CmdOutNoLn(name string, arg ...string) (out string, err error) {
    out, err = CmdOut(name, arg...)
    if err != nil {
        return
    }

    return strings.TrimSpace(string(out)), nil
}

func CmdRunWithTimeout(command string, timeout int64) ([]byte, error) {
    cmd := Cmd("/bin/bash","-c",command)
    done := make(chan error)
    var stdout,stderr bytes.Buffer
    cmd.Stdout, cmd.Stderr = &stdout, &stderr

    cmd.Start()
    go func() {
        done <- cmd.Wait()
    }()

    select {
    case <-time.After(time.Duration(timeout) * time.Second):

        go func() {
            result := <-done // allow goroutine to exit
            logger.Info("%v",result)
        }()

        err := cmd.Process.Kill()   // timeout
        if err != nil {
            logger.Error("failed to kill: %s, error: %s", cmd.Path, err)
            return stderr.Bytes(),err
        }
        return stderr.Bytes(),
            errors.New(fmt.Sprintf("exit status 62:TIMEOUT %d,Process %s has been killed",timeout,strings.Join(cmd.Args, " ")))
    case err := <-done:
        //fmt.Println(string(stdout.Bytes()),string(stderr.Bytes()),err)
        if err != nil {
            return stderr.Bytes(), err
        }
        return stdout.Bytes(), err
    }
}
<commit_msg>Update cmd.go<commit_after>package exec

import (
    "bytes"
    "fmt"
    "os/exec"
    "strings"
    "time"
)

func Cmd(name string, arg ...string) *exec.Cmd {
    return exec.Command(name, arg...)
}

func CmdOut(name string, arg ...string) ([]byte, error) {
    cmd := exec.Command(name, arg...)
    out, err := cmd.CombinedOutput()
    if err != nil {
        return nil, fmt.Errorf(err.Error() + ":" + string(out))
    }
    return out, nil
}

func CmdOutWithTimeout(command string, timeout int) ([]byte, error) {
    cmd := exec.Command("/bin/bash", "-c", command)
    done := make(chan error)
    var stdout, stderr bytes.Buffer
    cmd.Stdout, cmd.Stderr = &stdout, &stderr

    cmd.Start()
    go func() {
        done <- cmd.Wait()
    }()

    select {
    case <-time.After(time.Duration(timeout) * time.Second):
        err := cmd.Process.Kill() // timeout
        if err != nil {
            return stdout.Bytes(), fmt.Errorf(stderr.String() + err.Error())
        }
        return stdout.Bytes(), fmt.Errorf(stderr.String() + `TIMEOUT %d,Process "%s" has been killed`, timeout, strings.Join(cmd.Args, " "))
    case err := <-done:
        if err != nil {
            return nil, fmt.Errorf(err.Error() + ":" + stderr.String())
        }
        return stdout.Bytes(), err
    }
}
<|endoftext|>


<commit_before>package server

import (
	"context"
	"database/sql"
	"fmt"
	"github.com/gofrs/uuid"
	"github.com/golang/protobuf/jsonpb"
	"github.com/golang/protobuf/proto"
	"github.com/golang/protobuf/ptypes/empty"
	"github.com/heroiclabs/nakama-common/api"
	"github.com/heroiclabs/nakama/v2/console"
	"go.uber.org/zap"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"reflect"
	"sort"
	"strings"
)

type MethodName string

type rpcReflectCache struct {
	endpoints map[MethodName]*methodReflection
	rpcs map[MethodName]*console.ApiEndpointDescriptor
}

type methodReflection struct {
	method              reflect.Method
	request             reflect.Type
	requestBodyTemplate string
	response            reflect.Type
}

func (s *ConsoleServer) CallRpcEndpoint(ctx context.Context, in *console.CallApiEndpointRequest) (*console.CallApiEndpointResponse, error) {
	callCtx, err := s.extractApiCallContext(ctx, in, true)
	if err != nil {
		return nil, err
	}

	out, err := s.api.RpcFunc(callCtx, &api.Rpc{Id: in.Method, Payload: in.Body})
	if err != nil {
		return &console.CallApiEndpointResponse{
			Body:         "",
			ErrorMessage: err.Error(),
		}, nil
	}

	return &console.CallApiEndpointResponse{
		Body:         out.Payload,
		ErrorMessage: "",
	}, nil

}

func (s *ConsoleServer) CallApiEndpoint(ctx context.Context, in *console.CallApiEndpointRequest) (*console.CallApiEndpointResponse, error) {
	r, ok := s.rpcMethodCache.endpoints[MethodName(in.Method)]
	if !ok {
		return nil, fmt.Errorf("API method doesn't exist: %s", in.Method)
	}
	callCtx, err := s.extractApiCallContext(ctx, in, false)
	if err != nil {
		return nil, err
	}

	args := make([]reflect.Value, r.method.Type.NumIn())
	args[0] = reflect.ValueOf(s.api)
	args[1] = reflect.ValueOf(callCtx)

	if r.method.Type.In(2) == reflect.TypeOf(&empty.Empty{}) {
		if in.Body != "" {
			s.logger.Error("Body passed to an api call that doesn't accept any.", zap.String("method", in.Method))
			return nil, status.Error(codes.InvalidArgument, "Api method doesn't accept a request body.")
		}
		args[2] = reflect.ValueOf(&empty.Empty{})
	} else {
		request := reflect.New(r.request).Interface().(proto.Message)
		err = jsonpb.UnmarshalString(in.Body, request)
		if err != nil {
			s.logger.Error("Error parsing method request body.", zap.String("method", in.Method), zap.Error(err))
			return nil, status.Error(codes.InvalidArgument, "Error parsing method request body.")
		}
		args[2] = reflect.ValueOf(request)
	}
	out := r.method.Func.Call(args)
	cval := out[0].Interface()
	cerr := out[1].Interface()
	if cerr != nil {
		return &console.CallApiEndpointResponse{
			Body:         "",
			ErrorMessage: cerr.(error).Error(),
		}, nil
	} else {
		var j string
		if cval != nil {
			m := new(jsonpb.Marshaler)
			j, err = m.MarshalToString(cval.(proto.Message))
			if err != nil {
				s.logger.Error("Error serializing method response body.", zap.String("method", in.Method), zap.Error(err))
				return nil, status.Error(codes.Internal, "Error serializing method response body.")
			}
		}
		return &console.CallApiEndpointResponse{
			Body: j,
		}, nil
	}

}

func (s *ConsoleServer) extractApiCallContext(ctx context.Context, in *console.CallApiEndpointRequest, userIdOptional bool) (context.Context, error) {
	var callCtx context.Context
	if strings.HasPrefix(in.Method, "Authenticate") {
		callCtx = ctx
	} else if in.UserId == "" {
		if !userIdOptional {
			s.logger.Error("Error calling a built-in RPC function without a user_id.", zap.String("method", in.Method))
			return nil, status.Error(codes.InvalidArgument, "Built-in RPC functions require a user_id.")
		} else {
			callCtx = ctx
		}
	} else {
		row := s.db.QueryRowContext(ctx, "SELECT username FROM users WHERE id = $1", in.UserId)
		var dbUsername string
		userUUID, err := uuid.FromString(in.UserId)
		if err != nil {
			s.logger.Error("Invalid user uuid.", zap.String("method", in.Method))
			return nil, status.Error(codes.InvalidArgument, "Invalid user uuid.")
		}
		err = row.Scan(&dbUsername)
		if err != nil {
			if err == sql.ErrNoRows {
				s.logger.Error("User id not found.", zap.String("method", in.Method))
				return nil, status.Error(codes.InvalidArgument, "User id not found.")
			}
			s.logger.Error("Error looking up user account.", zap.String("method", in.Method), zap.Error(err))
			return nil, status.Error(codes.Internal, "Error looking up user account.")
		}
		callCtx = context.WithValue(context.WithValue(ctx, ctxUserIDKey{}, userUUID), ctxUsernameKey{}, dbUsername)
	}
	return callCtx, nil

}

func (s *ConsoleServer) ListApiEndpoints(ctx context.Context, _ *empty.Empty) (*console.ApiEndpointList, error) {

	endpointNames := make([]string, 0)
	for name := range s.rpcMethodCache.endpoints {
		endpointNames = append(endpointNames, string(name))
	}
	sort.Strings(endpointNames)
	var endpoints []*console.ApiEndpointDescriptor
	for _, name := range endpointNames {
		endpoint := s.rpcMethodCache.endpoints[MethodName(name)]
		endpoints = append(endpoints, &console.ApiEndpointDescriptor{
			Method: name,
			BodyTemplate: endpoint.requestBodyTemplate,
		})
	}

	rpcs := make([]string, 0)
	for name := range s.rpcMethodCache.rpcs {
		rpcs = append(rpcs, string(name))
	}
	sort.Strings(rpcs)
	var rpcEndpoints []*console.ApiEndpointDescriptor
	for _, name := range rpcs {
		endpoint := s.rpcMethodCache.rpcs[MethodName(name)]
		rpcEndpoints = append(rpcEndpoints, &console.ApiEndpointDescriptor{
			Method: name,
			BodyTemplate: endpoint.BodyTemplate,
		})
	}
	return &console.ApiEndpointList{
		Endpoints:    endpoints,
		RpcEndpoints: rpcEndpoints,
	}, nil

}

func (s *ConsoleServer) initRpcMethodCache() error {
	endpoints := make(map[MethodName]*methodReflection)
	apiType := reflect.TypeOf(s.api)
	for i := 0; i < apiType.NumMethod(); i++ {
		method := apiType.Method(i)
		if method.Type.NumIn() != 3 || method.Type.NumOut() != 2 {
			continue
		}
		if method.Name == "Healthcheck" {
			continue
		}
		if method.Name == "RpcFunc" {
			continue
		}
		request := method.Type.In(2)
		if request.Kind() == reflect.Ptr {
			request = request.Elem()
		}

		bodyTemplate, err := reflectProtoMessageAsJsonTemplate(request)
		if err != nil {
			return err
		}
		endpoints[MethodName(method.Name)] = &methodReflection{
			method:              method,
			request:             request,
			requestBodyTemplate: bodyTemplate,
			response: 					 method.Type.In(0),
		}

	}

	rpcs := make(map[MethodName]*console.ApiEndpointDescriptor)
	for _, rpc := range s.runtimeInfo.JavaScriptRpcFunctions {
		rpcs[MethodName(rpc)] = &console.ApiEndpointDescriptor{Method: rpc}
	}
	for _, rpc := range s.runtimeInfo.LuaRpcFunctions {
		rpcs[MethodName(rpc)] = &console.ApiEndpointDescriptor{Method: rpc}
	}
	for _, rpc := range s.runtimeInfo.GoRpcFunctions {
		rpcs[MethodName(rpc)] = &console.ApiEndpointDescriptor{Method: rpc}
	}

	s.rpcMethodCache = &rpcReflectCache{
		endpoints: endpoints,
		rpcs:      rpcs,
	}
	return nil
}

func reflectProtoMessageAsJsonTemplate(s reflect.Type) (string, error) {

	var populate func(m reflect.Value) reflect.Value
	populate = func(m reflect.Value) reflect.Value {
		switch m.Kind() {
		case reflect.Ptr:
			if m.IsNil() {
				m.Set(reflect.New(m.Type().Elem()))
			}
			populate(m.Elem())
		case reflect.Slice:
			m.Set(reflect.Append(m, populate(reflect.New(m.Type().Elem()).Elem())))
		case reflect.Map:
			if m.IsNil() {
				m.Set(reflect.MakeMap(m.Type()))
			}
			key := populate(reflect.New(m.Type().Key()).Elem())
			value := populate(reflect.New(m.Type().Elem()).Elem())
			m.SetMapIndex(key, value)
		case reflect.String:
			m.Set(reflect.ValueOf("<string>"))
		case reflect.Bool:
			m.Set(reflect.ValueOf(false))
		case reflect.Int:
			m.Set(reflect.ValueOf(0))
		case reflect.Int8:
			m.Set(reflect.ValueOf(int8(0)))
		case reflect.Int16:
			m.Set(reflect.ValueOf(int16(0)))
		case reflect.Int32:
			m.Set(reflect.ValueOf(int32(0)))
		case reflect.Int64:
			m.Set(reflect.ValueOf(int64(0)))
		case reflect.Uint:
			m.Set(reflect.ValueOf(uint(0)))
		case reflect.Uint8:
			m.Set(reflect.ValueOf(uint8(0)))
		case reflect.Uint16:
			m.Set(reflect.ValueOf(uint16(0)))
		case reflect.Uint32:
			m.Set(reflect.ValueOf(uint32(0)))
		case reflect.Uint64:
			m.Set(reflect.ValueOf(uint64(0)))
		case reflect.Float32:
			m.Set(reflect.ValueOf(float32(0)))
		case reflect.Float64:
			m.Set(reflect.ValueOf(float64(0)))
		case reflect.Struct:
			for i := 0; i < m.NumField(); i++ {
				field := m.Field(i)
				fieldName := m.Type().Field(i).Name
				if fieldName[0] == strings.ToLower(fieldName)[0] {
					continue
				}
				populate(field)
			}
		}
		return m
	}
	i := populate(reflect.New(s)).Interface().(proto.Message)
	m := jsonpb.Marshaler{OrigName: false, EnumsAsInts: true, EmitDefaults: true}
	j, err := m.MarshalToString(i)
	if err != nil {
		return "", err
	}
	return j, nil
}
<commit_msg>Add extra values to API explorer request contexts.<commit_after>package server

import (
	"context"
	"database/sql"
	"fmt"
	"github.com/gofrs/uuid"
	"github.com/golang/protobuf/jsonpb"
	"github.com/golang/protobuf/proto"
	"github.com/golang/protobuf/ptypes/empty"
	"github.com/heroiclabs/nakama-common/api"
	"github.com/heroiclabs/nakama/v2/console"
	"go.uber.org/zap"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"reflect"
	"sort"
	"strings"
	"time"
)

type MethodName string

type rpcReflectCache struct {
	endpoints map[MethodName]*methodReflection
	rpcs      map[MethodName]*console.ApiEndpointDescriptor
}

type methodReflection struct {
	method              reflect.Method
	request             reflect.Type
	requestBodyTemplate string
	response            reflect.Type
}

func (s *ConsoleServer) CallRpcEndpoint(ctx context.Context, in *console.CallApiEndpointRequest) (*console.CallApiEndpointResponse, error) {
	callCtx, err := s.extractApiCallContext(ctx, in, true)
	if err != nil {
		return nil, err
	}

	out, err := s.api.RpcFunc(callCtx, &api.Rpc{Id: in.Method, Payload: in.Body})
	if err != nil {
		return &console.CallApiEndpointResponse{
			Body:         "",
			ErrorMessage: err.Error(),
		}, nil
	}

	return &console.CallApiEndpointResponse{
		Body:         out.Payload,
		ErrorMessage: "",
	}, nil

}

func (s *ConsoleServer) CallApiEndpoint(ctx context.Context, in *console.CallApiEndpointRequest) (*console.CallApiEndpointResponse, error) {
	r, ok := s.rpcMethodCache.endpoints[MethodName(in.Method)]
	if !ok {
		return nil, fmt.Errorf("API method doesn't exist: %s", in.Method)
	}
	callCtx, err := s.extractApiCallContext(ctx, in, false)
	if err != nil {
		return nil, err
	}

	args := make([]reflect.Value, r.method.Type.NumIn())
	args[0] = reflect.ValueOf(s.api)
	args[1] = reflect.ValueOf(callCtx)

	if r.method.Type.In(2) == reflect.TypeOf(&empty.Empty{}) {
		if in.Body != "" {
			s.logger.Error("Body passed to an api call that doesn't accept any.", zap.String("method", in.Method))
			return nil, status.Error(codes.InvalidArgument, "Api method doesn't accept a request body.")
		}
		args[2] = reflect.ValueOf(&empty.Empty{})
	} else {
		request := reflect.New(r.request).Interface().(proto.Message)
		err = jsonpb.UnmarshalString(in.Body, request)
		if err != nil {
			s.logger.Error("Error parsing method request body.", zap.String("method", in.Method), zap.Error(err))
			return nil, status.Error(codes.InvalidArgument, "Error parsing method request body.")
		}
		args[2] = reflect.ValueOf(request)
	}
	out := r.method.Func.Call(args)
	cval := out[0].Interface()
	cerr := out[1].Interface()
	if cerr != nil {
		return &console.CallApiEndpointResponse{
			Body:         "",
			ErrorMessage: cerr.(error).Error(),
		}, nil
	} else {
		var j string
		if cval != nil {
			m := new(jsonpb.Marshaler)
			j, err = m.MarshalToString(cval.(proto.Message))
			if err != nil {
				s.logger.Error("Error serializing method response body.", zap.String("method", in.Method), zap.Error(err))
				return nil, status.Error(codes.Internal, "Error serializing method response body.")
			}
		}
		return &console.CallApiEndpointResponse{
			Body: j,
		}, nil
	}

}

func (s *ConsoleServer) extractApiCallContext(ctx context.Context, in *console.CallApiEndpointRequest, userIdOptional bool) (context.Context, error) {
	var callCtx context.Context
	if strings.HasPrefix(in.Method, "Authenticate") {
		callCtx = ctx
	} else if in.UserId == "" {
		if !userIdOptional {
			s.logger.Error("Error calling a built-in RPC function without a user_id.", zap.String("method", in.Method))
			return nil, status.Error(codes.InvalidArgument, "Built-in RPC functions require a user_id.")
		} else {
			callCtx = ctx
		}
	} else {
		row := s.db.QueryRowContext(ctx, "SELECT username FROM users WHERE id = $1", in.UserId)
		var dbUsername string
		userUUID, err := uuid.FromString(in.UserId)
		if err != nil {
			s.logger.Error("Invalid user uuid.", zap.String("method", in.Method))
			return nil, status.Error(codes.InvalidArgument, "Invalid user uuid.")
		}
		err = row.Scan(&dbUsername)
		if err != nil {
			if err == sql.ErrNoRows {
				s.logger.Error("User id not found.", zap.String("method", in.Method))
				return nil, status.Error(codes.InvalidArgument, "User id not found.")
			}
			s.logger.Error("Error looking up user account.", zap.String("method", in.Method), zap.Error(err))
			return nil, status.Error(codes.Internal, "Error looking up user account.")
		}
		callCtx = context.WithValue(ctx, ctxUserIDKey{}, userUUID)
		callCtx = context.WithValue(callCtx, ctxUsernameKey{}, dbUsername)
		callCtx = context.WithValue(callCtx, ctxVarsKey{}, map[string]string{})
		callCtx = context.WithValue(callCtx, ctxExpiryKey{}, time.Now().Add(24*time.Hour).Unix())
	}
	return callCtx, nil
}

func (s *ConsoleServer) ListApiEndpoints(ctx context.Context, _ *empty.Empty) (*console.ApiEndpointList, error) {

	endpointNames := make([]string, 0)
	for name := range s.rpcMethodCache.endpoints {
		endpointNames = append(endpointNames, string(name))
	}
	sort.Strings(endpointNames)
	var endpoints []*console.ApiEndpointDescriptor
	for _, name := range endpointNames {
		endpoint := s.rpcMethodCache.endpoints[MethodName(name)]
		endpoints = append(endpoints, &console.ApiEndpointDescriptor{
			Method:       name,
			BodyTemplate: endpoint.requestBodyTemplate,
		})
	}

	rpcs := make([]string, 0)
	for name := range s.rpcMethodCache.rpcs {
		rpcs = append(rpcs, string(name))
	}
	sort.Strings(rpcs)
	var rpcEndpoints []*console.ApiEndpointDescriptor
	for _, name := range rpcs {
		endpoint := s.rpcMethodCache.rpcs[MethodName(name)]
		rpcEndpoints = append(rpcEndpoints, &console.ApiEndpointDescriptor{
			Method:       name,
			BodyTemplate: endpoint.BodyTemplate,
		})
	}
	return &console.ApiEndpointList{
		Endpoints:    endpoints,
		RpcEndpoints: rpcEndpoints,
	}, nil

}

func (s *ConsoleServer) initRpcMethodCache() error {
	endpoints := make(map[MethodName]*methodReflection)
	apiType := reflect.TypeOf(s.api)
	for i := 0; i < apiType.NumMethod(); i++ {
		method := apiType.Method(i)
		if method.Type.NumIn() != 3 || method.Type.NumOut() != 2 {
			continue
		}
		if method.Name == "Healthcheck" {
			continue
		}
		if method.Name == "RpcFunc" {
			continue
		}
		request := method.Type.In(2)
		if request.Kind() == reflect.Ptr {
			request = request.Elem()
		}

		bodyTemplate, err := reflectProtoMessageAsJsonTemplate(request)
		if err != nil {
			return err
		}
		endpoints[MethodName(method.Name)] = &methodReflection{
			method:              method,
			request:             request,
			requestBodyTemplate: bodyTemplate,
			response:            method.Type.In(0),
		}

	}

	rpcs := make(map[MethodName]*console.ApiEndpointDescriptor)
	for _, rpc := range s.runtimeInfo.JavaScriptRpcFunctions {
		rpcs[MethodName(rpc)] = &console.ApiEndpointDescriptor{Method: rpc}
	}
	for _, rpc := range s.runtimeInfo.LuaRpcFunctions {
		rpcs[MethodName(rpc)] = &console.ApiEndpointDescriptor{Method: rpc}
	}
	for _, rpc := range s.runtimeInfo.GoRpcFunctions {
		rpcs[MethodName(rpc)] = &console.ApiEndpointDescriptor{Method: rpc}
	}

	s.rpcMethodCache = &rpcReflectCache{
		endpoints: endpoints,
		rpcs:      rpcs,
	}
	return nil
}

func reflectProtoMessageAsJsonTemplate(s reflect.Type) (string, error) {

	var populate func(m reflect.Value) reflect.Value
	populate = func(m reflect.Value) reflect.Value {
		switch m.Kind() {
		case reflect.Ptr:
			if m.IsNil() {
				m.Set(reflect.New(m.Type().Elem()))
			}
			populate(m.Elem())
		case reflect.Slice:
			m.Set(reflect.Append(m, populate(reflect.New(m.Type().Elem()).Elem())))
		case reflect.Map:
			if m.IsNil() {
				m.Set(reflect.MakeMap(m.Type()))
			}
			key := populate(reflect.New(m.Type().Key()).Elem())
			value := populate(reflect.New(m.Type().Elem()).Elem())
			m.SetMapIndex(key, value)
		case reflect.String:
			m.Set(reflect.ValueOf("<string>"))
		case reflect.Bool:
			m.Set(reflect.ValueOf(false))
		case reflect.Int:
			m.Set(reflect.ValueOf(0))
		case reflect.Int8:
			m.Set(reflect.ValueOf(int8(0)))
		case reflect.Int16:
			m.Set(reflect.ValueOf(int16(0)))
		case reflect.Int32:
			m.Set(reflect.ValueOf(int32(0)))
		case reflect.Int64:
			m.Set(reflect.ValueOf(int64(0)))
		case reflect.Uint:
			m.Set(reflect.ValueOf(uint(0)))
		case reflect.Uint8:
			m.Set(reflect.ValueOf(uint8(0)))
		case reflect.Uint16:
			m.Set(reflect.ValueOf(uint16(0)))
		case reflect.Uint32:
			m.Set(reflect.ValueOf(uint32(0)))
		case reflect.Uint64:
			m.Set(reflect.ValueOf(uint64(0)))
		case reflect.Float32:
			m.Set(reflect.ValueOf(float32(0)))
		case reflect.Float64:
			m.Set(reflect.ValueOf(float64(0)))
		case reflect.Struct:
			for i := 0; i < m.NumField(); i++ {
				field := m.Field(i)
				fieldName := m.Type().Field(i).Name
				if fieldName[0] == strings.ToLower(fieldName)[0] {
					continue
				}
				populate(field)
			}
		}
		return m
	}
	i := populate(reflect.New(s)).Interface().(proto.Message)
	m := jsonpb.Marshaler{OrigName: false, EnumsAsInts: true, EmitDefaults: true}
	j, err := m.MarshalToString(i)
	if err != nil {
		return "", err
	}
	return j, nil
}
<|endoftext|>


<commit_before>package revtesthelpers

import "gobot.io/x/gobot"

type I2cTestAdaptor struct {
	name         string
	I2cReadImpl  func(i int, l int) ([]byte, error)
	I2cWriteImpl func(int, []byte) error
	I2cStartImpl func() error
}

func (t *I2cTestAdaptor) I2cStart(int) (err error) {
	return t.I2cStartImpl()
}
func (t *I2cTestAdaptor) I2cRead(i int, l int) (data []byte, err error) {
	return t.I2cReadImpl(i, l)
}
func (t *I2cTestAdaptor) I2cWrite(i int, b []byte) (err error) {
	return t.I2cWriteImpl(i, b)
}
func (t *I2cTestAdaptor) Name() string           { return t.name }
func (t *I2cTestAdaptor) SetName(n string)       { t.name = n }
func (t *I2cTestAdaptor) Connect() (errs error)  { return }
func (t *I2cTestAdaptor) Finalize() (errs error) { return }

func NewI2cTestAdaptor(name string) *I2cTestAdaptor {
	return &I2cTestAdaptor{
		name: name,
		I2cReadImpl: func(i int, l int) ([]byte, error) {
			b := make([]byte, l, l)
			b[1] = 0x15
			return b, nil
		},
		I2cWriteImpl: func(i int, b []byte) error {
			return nil
		},
		I2cStartImpl: func() error {
			return nil
		},
	}
}

type FakeRevDriver struct {
	name              string
	connection        gobot.Connection
	SetMotorAImpl     func(float32) error
	SetMotorBImpl     func(float32) error
	StartImpl         func() []error
	StopAllMotorsImpl func() error
	HaltImpl          func() error
	ResetEPOImpl      func() error
	GetEPOImpl        func() (bool, error)
}

func NewFakeRevDriver() *FakeRevDriver {
	return &FakeRevDriver{
		name:       "FakeRevDriver",
		connection: newI2cTestAdaptor("I2CTest"),
		SetMotorAImpl: func(power float32) error {
			return nil
		},
		SetMotorBImpl: func(power float32) error {
			return nil
		},
		StartImpl: func() []error {
			return nil
		},
		StopAllMotorsImpl: func() error {
			return nil
		},
		HaltImpl: func() error {
			return nil
		},
		GetEPOImpl: func() (bool, error) {
			return true, nil
		},
		ResetEPOImpl: func() error {
			return nil
		},
	}
}

func (b *FakeRevDriver) SetMotorA(power float32) error {
	return b.SetMotorAImpl(power)
}

func (b *FakeRevDriver) SetMotorB(power float32) error {
	return b.SetMotorBImpl(power)
}

func (b *FakeRevDriver) Start() []error {
	return b.StartImpl()
}

func (b *FakeRevDriver) Halt() error {
	return b.HaltImpl()
}

func (b *FakeRevDriver) Name() string {
	return b.name
}

func (b *FakeRevDriver) Connection() gobot.Connection {
	return b.connection
}

func (b *FakeRevDriver) ResetEPO() error {
	return b.ResetEPOImpl()
}
func (b *FakeRevDriver) GetEPO() (bool, error) {
	return b.GetEPOImpl()
}

func (b *FakeRevDriver) StopAllMotors() error {
	return b.StopAllMotorsImpl()
}

type i2cTestAdaptor struct {
	name         string
	I2cReadImpl  func(i int, l int) ([]byte, error)
	I2cWriteImpl func(int, []byte) error
	I2cStartImpl func() error
}

func (t *i2cTestAdaptor) I2cStart(int) (err error) {
	return t.I2cStartImpl()
}
func (t *i2cTestAdaptor) I2cRead(i int, l int) (data []byte, err error) {
	return t.I2cReadImpl(i, l)
}
func (t *i2cTestAdaptor) I2cWrite(i int, b []byte) (err error) {
	return t.I2cWriteImpl(i, b)
}
func (t *i2cTestAdaptor) Name() string           { return t.name }
func (t *i2cTestAdaptor) SetName(n string)       { t.name = n }
func (t *i2cTestAdaptor) Connect() (errs error)  { return }
func (t *i2cTestAdaptor) Finalize() (errs error) { return }

func newI2cTestAdaptor(name string) *i2cTestAdaptor {
	return &i2cTestAdaptor{
		name: name,
		I2cReadImpl: func(i int, l int) ([]byte, error) {
			b := make([]byte, l, l)
			b[1] = 0x15
			return b, nil
		},
		I2cWriteImpl: func(i int, b []byte) error {
			return nil
		},
		I2cStartImpl: func() error {
			return nil
		},
	}
}
<commit_msg>Fixed tests<commit_after>package revtesthelpers

import "gobot.io/x/gobot"

type I2cTestAdaptor struct {
	name         string
	I2cReadImpl  func(i int, l int) ([]byte, error)
	I2cWriteImpl func(int, []byte) error
	I2cStartImpl func() error
}

func (t *I2cTestAdaptor) I2cStart(int) (err error) {
	return t.I2cStartImpl()
}
func (t *I2cTestAdaptor) I2cRead(i int, l int) (data []byte, err error) {
	return t.I2cReadImpl(i, l)
}
func (t *I2cTestAdaptor) I2cWrite(i int, b []byte) (err error) {
	return t.I2cWriteImpl(i, b)
}
func (t *I2cTestAdaptor) Name() string           { return t.name }
func (t *I2cTestAdaptor) SetName(n string)       { t.name = n }
func (t *I2cTestAdaptor) Connect() (errs error)  { return }
func (t *I2cTestAdaptor) Finalize() (errs error) { return }

func NewI2cTestAdaptor(name string) *I2cTestAdaptor {
	return &I2cTestAdaptor{
		name: name,
		I2cReadImpl: func(i int, l int) ([]byte, error) {
			b := make([]byte, l, l)
			b[1] = 0x15
			return b, nil
		},
		I2cWriteImpl: func(i int, b []byte) error {
			return nil
		},
		I2cStartImpl: func() error {
			return nil
		},
	}
}

type FakeRevDriver struct {
	name              string
	connection        gobot.Connection
	SetMotorAImpl     func(float32) error
	SetMotorBImpl     func(float32) error
	StartImpl         func() error
	StopAllMotorsImpl func() error
	HaltImpl          func() error
	ResetEPOImpl      func() error
	GetEPOImpl        func() (bool, error)
}

func NewFakeRevDriver() *FakeRevDriver {
	return &FakeRevDriver{
		name:       "FakeRevDriver",
		connection: newI2cTestAdaptor("I2CTest"),
		SetMotorAImpl: func(power float32) error {
			return nil
		},
		SetMotorBImpl: func(power float32) error {
			return nil
		},
		StartImpl: func() error {
			return nil
		},
		StopAllMotorsImpl: func() error {
			return nil
		},
		HaltImpl: func() error {
			return nil
		},
		GetEPOImpl: func() (bool, error) {
			return true, nil
		},
		ResetEPOImpl: func() error {
			return nil
		},
	}
}

func (b *FakeRevDriver) SetMotorA(power float32) error {
	return b.SetMotorAImpl(power)
}

func (b *FakeRevDriver) SetMotorB(power float32) error {
	return b.SetMotorBImpl(power)
}

func (b *FakeRevDriver) Start() error {
	return b.StartImpl()
}

func (b *FakeRevDriver) Halt() error {
	return b.HaltImpl()
}

func (b *FakeRevDriver) Name() string {
	return b.name
}

func (b *FakeRevDriver) Connection() gobot.Connection {
	return b.connection
}

func (b *FakeRevDriver) ResetEPO() error {
	return b.ResetEPOImpl()
}
func (b *FakeRevDriver) GetEPO() (bool, error) {
	return b.GetEPOImpl()
}

func (b *FakeRevDriver) StopAllMotors() error {
	return b.StopAllMotorsImpl()
}

type i2cTestAdaptor struct {
	name         string
	I2cReadImpl  func(i int, l int) ([]byte, error)
	I2cWriteImpl func(int, []byte) error
	I2cStartImpl func() error
}

func (t *i2cTestAdaptor) I2cStart(int) (err error) {
	return t.I2cStartImpl()
}
func (t *i2cTestAdaptor) I2cRead(i int, l int) (data []byte, err error) {
	return t.I2cReadImpl(i, l)
}
func (t *i2cTestAdaptor) I2cWrite(i int, b []byte) (err error) {
	return t.I2cWriteImpl(i, b)
}
func (t *i2cTestAdaptor) Name() string           { return t.name }
func (t *i2cTestAdaptor) SetName(n string)       { t.name = n }
func (t *i2cTestAdaptor) Connect() (errs error)  { return }
func (t *i2cTestAdaptor) Finalize() (errs error) { return }

func newI2cTestAdaptor(name string) *i2cTestAdaptor {
	return &i2cTestAdaptor{
		name: name,
		I2cReadImpl: func(i int, l int) ([]byte, error) {
			b := make([]byte, l, l)
			b[1] = 0x15
			return b, nil
		},
		I2cWriteImpl: func(i int, b []byte) error {
			return nil
		},
		I2cStartImpl: func() error {
			return nil
		},
	}
}
<|endoftext|>


<commit_before>package log

import (
	"fmt"
	"github.com/viant/assertly"
	"github.com/viant/toolbox"
	"github.com/viant/toolbox/url"
	"regexp"
)

//AssertRequest represents a log assert request
type AssertRequest struct {
	LogWaitTimeMs       int
	LogWaitRetryCount   int
	Description         string
	DescriptionTemplate string
	Expect              []*TypedRecord `required:"true" description:"expected log data"`
}

//Init converts yaml kv pairs to a map if applicable
func (r *AssertRequest) Init() error {
	if len(r.Expect) == 0 {
		return nil
	}
	for _, expecRecords := range r.Expect {
		if len(expecRecords.Records) == 0 {
			continue
		}
		for i, record := range expecRecords.Records {
			if toolbox.IsSlice(record) {
				if aMap, err := toolbox.ToMap(record); err == nil {
					expecRecords.Records[i] = aMap
				}
			}
		}
	}
	if r.DescriptionTemplate == "" {
		r.DescriptionTemplate = "Log Validation: $logType"
	}
	if r.LogWaitTimeMs == 0 {
		r.LogWaitTimeMs = 500
	}
	if r.LogWaitRetryCount == 0 {
		r.LogWaitRetryCount = 3
	}
	return nil
}

//Validate check if request is valid
func (r *AssertRequest) Validate() error {
	if len(r.Expect) == 0 {
		return nil
	}
	for i, expecRecords := range r.Expect {
		if expecRecords.Type == "" {
			return fmt.Errorf("Expect[%d].Type was empty", i)
		}
	}
	return nil
}

//TypedRecord represents an expected log record.
type TypedRecord struct {
	TagID   string `description:"neatly tag id for matching validation summary"`
	Type    string `required:"true" description:"log type register with listener"`
	Records []interface{}
}

//AssertResponse represents a log assert response
type AssertResponse struct {
	Validations []*assertly.Validation
}

//Assertion returns description with validation slice
func (r *AssertResponse) Assertion() []*assertly.Validation {
	return r.Validations
}

//Type represents  a log type
type Type struct {
	Name         string `required:"true" description:"log type name"`
	Format       string `description:"log format"`
	Mask         string `description:"expected log file mast"`
	Exclusion    string `description:"if specified, exclusion fragment can not match log record"`
	Inclusion    string `description:"if specified, inclusion fragment must match log record"`
	IndexRegExpr string `description:"provide expression for indexing log messages, in this case position based logging will not apply"` //provide expression for indexing log message, in this case position based logging will not apply
	indexExpr    *regexp.Regexp
	UDF          string `description:"registered user defined function to transform content file before applying validation"`
	Debug        bool   `description:"if set, every record appended to validation queue will be listed"`
}

//ListenRequest represents listen for a logs request.
type ListenRequest struct {
	FrequencyMs int
	Source      *url.Resource `required:"true" description:"log location"`
	Types       []*Type       `required:"true" description:"log types"`
}

//ListenResponse represents a log validation listen response.
type ListenResponse struct {
	Meta TypesMeta
}

//UseIndex returns true if index can be used.
func (t *Type) UseIndex() bool {
	return t.IndexRegExpr != ""
}

//GetIndexExpr returns index expression.
func (t *Type) GetIndexExpr() (*regexp.Regexp, error) {
	if t.indexExpr != nil {
		return t.indexExpr, nil
	}
	var err error
	t.indexExpr, err = regexp.Compile(t.IndexRegExpr)
	return t.indexExpr, err
}

//ResetRequest represents a log reset request
type ResetRequest struct {
	LogTypes []string `required:"true" description:"log types to reset"`
}

//ResetResponse represents a log reset response
type ResetResponse struct {
	LogFiles []string
}
<commit_msg>updated description template<commit_after>package log

import (
	"fmt"
	"github.com/viant/assertly"
	"github.com/viant/toolbox"
	"github.com/viant/toolbox/url"
	"regexp"
)

//AssertRequest represents a log assert request
type AssertRequest struct {
	LogWaitTimeMs       int
	LogWaitRetryCount   int
	Description         string
	DescriptionTemplate string
	Expect              []*TypedRecord `required:"true" description:"expected log data"`
}

//Init converts yaml kv pairs to a map if applicable
func (r *AssertRequest) Init() error {
	if len(r.Expect) == 0 {
		return nil
	}
	for _, expecRecords := range r.Expect {
		if len(expecRecords.Records) == 0 {
			continue
		}
		for i, record := range expecRecords.Records {
			if toolbox.IsSlice(record) {
				if aMap, err := toolbox.ToMap(record); err == nil {
					expecRecords.Records[i] = aMap
				}
			}
		}
	}
	if r.DescriptionTemplate == "" {
		r.DescriptionTemplate = "$tagID: log $logType"
	}
	if r.LogWaitTimeMs == 0 {
		r.LogWaitTimeMs = 500
	}
	if r.LogWaitRetryCount == 0 {
		r.LogWaitRetryCount = 3
	}
	return nil
}

//Validate check if request is valid
func (r *AssertRequest) Validate() error {
	if len(r.Expect) == 0 {
		return nil
	}
	for i, expecRecords := range r.Expect {
		if expecRecords.Type == "" {
			return fmt.Errorf("Expect[%d].Type was empty", i)
		}
	}
	return nil
}

//TypedRecord represents an expected log record.
type TypedRecord struct {
	TagID   string `description:"neatly tag id for matching validation summary"`
	Type    string `required:"true" description:"log type register with listener"`
	Records []interface{}
}

//AssertResponse represents a log assert response
type AssertResponse struct {
	Validations []*assertly.Validation
}

//Assertion returns description with validation slice
func (r *AssertResponse) Assertion() []*assertly.Validation {
	return r.Validations
}

//Type represents  a log type
type Type struct {
	Name         string `required:"true" description:"log type name"`
	Format       string `description:"log format"`
	Mask         string `description:"expected log file mast"`
	Exclusion    string `description:"if specified, exclusion fragment can not match log record"`
	Inclusion    string `description:"if specified, inclusion fragment must match log record"`
	IndexRegExpr string `description:"provide expression for indexing log messages, in this case position based logging will not apply"` //provide expression for indexing log message, in this case position based logging will not apply
	indexExpr    *regexp.Regexp
	UDF          string `description:"registered user defined function to transform content file before applying validation"`
	Debug        bool   `description:"if set, every record appended to validation queue will be listed"`
}

//ListenRequest represents listen for a logs request.
type ListenRequest struct {
	FrequencyMs int
	Source      *url.Resource `required:"true" description:"log location"`
	Types       []*Type       `required:"true" description:"log types"`
}

//ListenResponse represents a log validation listen response.
type ListenResponse struct {
	Meta TypesMeta
}

//UseIndex returns true if index can be used.
func (t *Type) UseIndex() bool {
	return t.IndexRegExpr != ""
}

//GetIndexExpr returns index expression.
func (t *Type) GetIndexExpr() (*regexp.Regexp, error) {
	if t.indexExpr != nil {
		return t.indexExpr, nil
	}
	var err error
	t.indexExpr, err = regexp.Compile(t.IndexRegExpr)
	return t.indexExpr, err
}

//ResetRequest represents a log reset request
type ResetRequest struct {
	LogTypes []string `required:"true" description:"log types to reset"`
}

//ResetResponse represents a log reset response
type ResetResponse struct {
	LogFiles []string
}
<|endoftext|>


<commit_before>// Copyright 2015 Martin Gallagher. All rights reserved.
// Use of this source code is governed by the Apache License,
// Version 2.0 that can be found in the LICENSE file.

package router

import (
	"net/http"
	"testing"
)

const (
	longStatic = "/static/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u"
	shortParam = "/schemas/test/archives/2015/02/12"
	longParam  = "/nofunc/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u"
)

func TestRuntimeRouter(t *testing.T) {
	r := &Router{}
	r.AddValidator(":year", IsYear)
	r.AddValidator(":month", IsMonth)
	r.AddValidator(":day", IsDay)

	if err := r.Add("GET", "schemas/:schema/archives/:year/:month/:day", exampleHandler); err != nil {
		t.Fatal(err)
	}

	req, err := http.NewRequest("GET", shortParam, nil)

	if err != nil {
		t.Fatal(err)
	} else if _, p, err := r.Get(req); err != nil {
		t.Fatal(err)
	} else if p.Get("year") != "2015" {
		t.Fatal("unexpected value")
	}
}

func TestRouter(t *testing.T) {
	req, err := http.NewRequest("GET", shortParam, nil)

	if err != nil {
		t.Fatal(err)
	}

	h, p, err := routes.Get(req)

	if err != nil {
		t.Fatal(err)
	} else if len(p) == 0 {
		t.Fatal("empty params")
	} else if h == nil {
		t.Fatal("nil handler")
	}

	t.Logf("%#v", p)
}

func TestRouterLongSimple(t *testing.T) {
	req, err := http.NewRequest("GET", longParam, nil)

	if err != nil {
		t.Fatal(err)
	}

	h, p, err := routes.Get(req)

	if err != nil {
		t.Fatal(err)
	} else if len(p) == 0 || p.Get("g") == "" {
		t.Fatal("empty params")
	} else if h == nil {
		t.Fatal("nil handler")
	}

	t.Logf("%#v", p)
}

func TestRouterLongStatic(t *testing.T) {
	req, err := http.NewRequest("GET", longStatic, nil)

	if err != nil {
		t.Fatal(err)
	} else if h, _, err := routes.Get(req); err != nil {
		t.Fatal(err)
	} else if h == nil {
		t.Fatal("nil handler")
	}
}

func BenchmarkRouterLongSimple(b *testing.B) {
	req, err := http.NewRequest("GET", longParam, nil)

	if err != nil {
		b.Fatal(err)
	}

	b.ReportAllocs()

	for i := 0; i < b.N; i++ {
		routes.Get(req)
	}
}

func BenchmarkRouterLongStatic(b *testing.B) {
	req, err := http.NewRequest("GET", longStatic, nil)

	if err != nil {
		b.Fatal(err)
	}

	b.ReportAllocs()

	for i := 0; i < b.N; i++ {
		routes.Get(req)
	}
}

func BenchmarkRouterMultiParam(b *testing.B) {
	req, err := http.NewRequest("GET", shortParam, nil)

	if err != nil {
		b.Fatal(err)
	}

	b.ReportAllocs()

	for i := 0; i < b.N; i++ {
		routes.Get(req)
	}
}
<commit_msg>Test route prefixed with forward slash.<commit_after>// Copyright 2015 Martin Gallagher. All rights reserved.
// Use of this source code is governed by the Apache License,
// Version 2.0 that can be found in the LICENSE file.

package router

import (
	"net/http"
	"testing"
)

const (
	longStatic = "/static/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u"
	shortParam = "/schemas/test/archives/2015/02/12"
	longParam  = "/nofunc/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u"
)

func TestRuntimeRouter(t *testing.T) {
	r := &Router{}
	r.AddValidator(":year", IsYear)
	r.AddValidator(":month", IsMonth)
	r.AddValidator(":day", IsDay)

	if err := r.Add("GET", "/schemas/:schema/archives/:year/:month/:day", exampleHandler); err != nil {
		t.Fatal(err)
	}

	req, err := http.NewRequest("GET", shortParam, nil)

	if err != nil {
		t.Fatal(err)
	} else if _, p, err := r.Get(req); err != nil {
		t.Fatal(err)
	} else if p.Get("year") != "2015" {
		t.Fatal("unexpected value")
	}
}

func TestRouter(t *testing.T) {
	req, err := http.NewRequest("GET", shortParam, nil)

	if err != nil {
		t.Fatal(err)
	}

	h, p, err := routes.Get(req)

	if err != nil {
		t.Fatal(err)
	} else if len(p) == 0 {
		t.Fatal("empty params")
	} else if h == nil {
		t.Fatal("nil handler")
	}

	t.Logf("%#v", p)
}

func TestRouterLongSimple(t *testing.T) {
	req, err := http.NewRequest("GET", longParam, nil)

	if err != nil {
		t.Fatal(err)
	}

	h, p, err := routes.Get(req)

	if err != nil {
		t.Fatal(err)
	} else if len(p) == 0 || p.Get("g") == "" {
		t.Fatal("empty params")
	} else if h == nil {
		t.Fatal("nil handler")
	}

	t.Logf("%#v", p)
}

func TestRouterLongStatic(t *testing.T) {
	req, err := http.NewRequest("GET", longStatic, nil)

	if err != nil {
		t.Fatal(err)
	} else if h, _, err := routes.Get(req); err != nil {
		t.Fatal(err)
	} else if h == nil {
		t.Fatal("nil handler")
	}
}

func BenchmarkRouterLongSimple(b *testing.B) {
	req, err := http.NewRequest("GET", longParam, nil)

	if err != nil {
		b.Fatal(err)
	}

	b.ReportAllocs()

	for i := 0; i < b.N; i++ {
		routes.Get(req)
	}
}

func BenchmarkRouterLongStatic(b *testing.B) {
	req, err := http.NewRequest("GET", longStatic, nil)

	if err != nil {
		b.Fatal(err)
	}

	b.ReportAllocs()

	for i := 0; i < b.N; i++ {
		routes.Get(req)
	}
}

func BenchmarkRouterMultiParam(b *testing.B) {
	req, err := http.NewRequest("GET", shortParam, nil)

	if err != nil {
		b.Fatal(err)
	}

	b.ReportAllocs()

	for i := 0; i < b.N; i++ {
		routes.Get(req)
	}
}
<|endoftext|>


<commit_before>package api

import (
	"fmt"
	"github.com/julienschmidt/httprouter"
	"net/http"
)

func (api *coreAPI) handleActualStateReset(writer http.ResponseWriter, request *http.Request, params httprouter.Params) {
	err := api.store.ResetActualState()
	if err != nil {
		panic(fmt.Sprintf("error while resetting actual state: %s", err))
	}

	api.handleRevisionGet(writer, request, params)
}
<commit_msg>Trigger enforcement after state reset<commit_after>package api

import (
	"fmt"
	"github.com/julienschmidt/httprouter"
	"net/http"
)

func (api *coreAPI) handleActualStateReset(writer http.ResponseWriter, request *http.Request, params httprouter.Params) {
	err := api.store.ResetActualState()
	if err != nil {
		panic(fmt.Sprintf("error while resetting actual state: %s", err))
	}

	// signal to the channel that policy has changed, that will trigger the enforcement right away
	api.policyChanged <- true

	api.handleRevisionGet(writer, request, params)
}
<|endoftext|>


<commit_before>/*
Copyright 2014 The Kubernetes Authors All rights reserved.

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 apiserver

import (
	"bytes"
	"fmt"
	"net/http"
	"reflect"
	"time"

	"k8s.io/kubernetes/pkg/api/errors"
	"k8s.io/kubernetes/pkg/httplog"
	"k8s.io/kubernetes/pkg/runtime"
	"k8s.io/kubernetes/pkg/runtime/serializer/streaming"
	utilruntime "k8s.io/kubernetes/pkg/util/runtime"
	"k8s.io/kubernetes/pkg/util/wsstream"
	"k8s.io/kubernetes/pkg/watch"
	"k8s.io/kubernetes/pkg/watch/versioned"

	"github.com/emicklei/go-restful"
	"golang.org/x/net/websocket"
)

// nothing will ever be sent down this channel
var neverExitWatch <-chan time.Time = make(chan time.Time)

// timeoutFactory abstracts watch timeout logic for testing
type timeoutFactory interface {
	TimeoutCh() (<-chan time.Time, func() bool)
}

// realTimeoutFactory implements timeoutFactory
type realTimeoutFactory struct {
	timeout time.Duration
}

// TimeoutChan returns a channel which will receive something when the watch times out,
// and a cleanup function to call when this happens.
func (w *realTimeoutFactory) TimeoutCh() (<-chan time.Time, func() bool) {
	if w.timeout == 0 {
		return neverExitWatch, func() bool { return false }
	}
	t := time.NewTimer(w.timeout)
	return t.C, t.Stop
}

// serveWatch handles serving requests to the server
// TODO: the functionality in this method and in WatchServer.Serve is not cleanly decoupled.
func serveWatch(watcher watch.Interface, scope RequestScope, req *restful.Request, res *restful.Response, timeout time.Duration) {
	// negotiate for the stream serializer
	serializer, err := negotiateOutputStreamSerializer(req.Request, scope.Serializer)
	if err != nil {
		scope.err(err, res.ResponseWriter, req.Request)
		return
	}
	if serializer.Framer == nil {
		scope.err(fmt.Errorf("no framer defined for %q available for embedded encoding", serializer.MediaType), res.ResponseWriter, req.Request)
		return
	}
	encoder := scope.Serializer.EncoderForVersion(serializer.Serializer, scope.Kind.GroupVersion())

	useTextFraming := serializer.EncodesAsText

	// find the embedded serializer matching the media type
	embeddedEncoder := scope.Serializer.EncoderForVersion(serializer.Embedded.Serializer, scope.Kind.GroupVersion())

	server := &WatchServer{
		watching: watcher,
		scope:    scope,

		useTextFraming:  useTextFraming,
		mediaType:       serializer.MediaType,
		framer:          serializer.Framer,
		encoder:         encoder,
		embeddedEncoder: embeddedEncoder,
		fixup: func(obj runtime.Object) {
			if err := setSelfLink(obj, req, scope.Namer); err != nil {
				utilruntime.HandleError(fmt.Errorf("failed to set link for object %v: %v", reflect.TypeOf(obj), err))
			}
		},

		t: &realTimeoutFactory{timeout},
	}

	server.ServeHTTP(res.ResponseWriter, req.Request)
}

// WatchServer serves a watch.Interface over a websocket or vanilla HTTP.
type WatchServer struct {
	watching watch.Interface
	scope    RequestScope

	// true if websocket messages should use text framing (as opposed to binary framing)
	useTextFraming bool
	// the media type this watch is being served with
	mediaType string
	// used to frame the watch stream
	framer runtime.Framer
	// used to encode the watch stream event itself
	encoder runtime.Encoder
	// used to encode the nested object in the watch stream
	embeddedEncoder runtime.Encoder
	fixup           func(runtime.Object)

	t timeoutFactory
}

// Serve serves a series of encoded events via HTTP with Transfer-Encoding: chunked
// or over a websocket connection.
func (s *WatchServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	w = httplog.Unlogged(w)

	if wsstream.IsWebSocketRequest(req) {
		w.Header().Set("Content-Type", s.mediaType)
		websocket.Handler(s.HandleWS).ServeHTTP(w, req)
		return
	}

	cn, ok := w.(http.CloseNotifier)
	if !ok {
		err := fmt.Errorf("unable to start watch - can't get http.CloseNotifier: %#v", w)
		utilruntime.HandleError(err)
		s.scope.err(errors.NewInternalError(err), w, req)
		return
	}
	flusher, ok := w.(http.Flusher)
	if !ok {
		err := fmt.Errorf("unable to start watch - can't get http.Flusher: %#v", w)
		utilruntime.HandleError(err)
		s.scope.err(errors.NewInternalError(err), w, req)
		return
	}

	framer := s.framer.NewFrameWriter(w)
	if framer == nil {
		// programmer error
		err := fmt.Errorf("no stream framing support is available for media type %q", s.mediaType)
		utilruntime.HandleError(err)
		s.scope.err(errors.NewBadRequest(err.Error()), w, req)
		return
	}
	e := streaming.NewEncoder(framer, s.encoder)

	// ensure the connection times out
	timeoutCh, cleanup := s.t.TimeoutCh()
	defer cleanup()
	defer s.watching.Stop()

	// begin the stream
	w.Header().Set("Content-Type", s.mediaType)
	w.Header().Set("Transfer-Encoding", "chunked")
	w.WriteHeader(http.StatusOK)
	flusher.Flush()

	buf := &bytes.Buffer{}
	for {
		select {
		case <-cn.CloseNotify():
			return
		case <-timeoutCh:
			return
		case event, ok := <-s.watching.ResultChan():
			if !ok {
				// End of results.
				return
			}
			obj := event.Object
			s.fixup(obj)
			if err := s.embeddedEncoder.EncodeToStream(obj, buf); err != nil {
				// unexpected error
				utilruntime.HandleError(fmt.Errorf("unable to encode watch object: %v", err))
				return
			}
			event.Object = &runtime.Unknown{
				Raw: buf.Bytes(),
				// ContentType is not required here because we are defaulting to the serializer
				// type
			}
			if err := e.Encode((*versioned.InternalEvent)(&event)); err != nil {
				utilruntime.HandleError(fmt.Errorf("unable to encode watch object: %v (%#v)", err, e))
				// client disconnect.
				return
			}
			flusher.Flush()

			buf.Reset()
		}
	}
}

// HandleWS implements a websocket handler.
func (s *WatchServer) HandleWS(ws *websocket.Conn) {
	defer ws.Close()
	done := make(chan struct{})
	go wsstream.IgnoreReceives(ws, 0)
	buf := &bytes.Buffer{}
	streamBuf := &bytes.Buffer{}
	for {
		select {
		case <-done:
			s.watching.Stop()
			return
		case event, ok := <-s.watching.ResultChan():
			if !ok {
				// End of results.
				return
			}
			obj := event.Object
			s.fixup(obj)
			if err := s.embeddedEncoder.EncodeToStream(obj, buf); err != nil {
				// unexpected error
				utilruntime.HandleError(fmt.Errorf("unable to encode watch object: %v", err))
				return
			}
			event.Object = &runtime.Unknown{
				Raw: buf.Bytes(),
				// ContentType is not required here because we are defaulting to the serializer
				// type
			}
			// the internal event will be versioned by the encoder
			internalEvent := versioned.InternalEvent(event)
			if err := s.encoder.EncodeToStream(&internalEvent, streamBuf); err != nil {
				// encoding error
				utilruntime.HandleError(fmt.Errorf("unable to encode event: %v", err))
				s.watching.Stop()
				return
			}
			if s.useTextFraming {
				if err := websocket.Message.Send(ws, streamBuf.String()); err != nil {
					// Client disconnect.
					s.watching.Stop()
					return
				}
			} else {
				if err := websocket.Message.Send(ws, streamBuf.Bytes()); err != nil {
					// Client disconnect.
					s.watching.Stop()
					return
				}
			}
			buf.Reset()
			streamBuf.Reset()
		}
	}
}
<commit_msg>Delay flush if the watch queue has pending items<commit_after>/*
Copyright 2014 The Kubernetes Authors All rights reserved.

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 apiserver

import (
	"bytes"
	"fmt"
	"net/http"
	"reflect"
	"time"

	"k8s.io/kubernetes/pkg/api/errors"
	"k8s.io/kubernetes/pkg/httplog"
	"k8s.io/kubernetes/pkg/runtime"
	"k8s.io/kubernetes/pkg/runtime/serializer/streaming"
	utilruntime "k8s.io/kubernetes/pkg/util/runtime"
	"k8s.io/kubernetes/pkg/util/wsstream"
	"k8s.io/kubernetes/pkg/watch"
	"k8s.io/kubernetes/pkg/watch/versioned"

	"github.com/emicklei/go-restful"
	"golang.org/x/net/websocket"
)

// nothing will ever be sent down this channel
var neverExitWatch <-chan time.Time = make(chan time.Time)

// timeoutFactory abstracts watch timeout logic for testing
type timeoutFactory interface {
	TimeoutCh() (<-chan time.Time, func() bool)
}

// realTimeoutFactory implements timeoutFactory
type realTimeoutFactory struct {
	timeout time.Duration
}

// TimeoutChan returns a channel which will receive something when the watch times out,
// and a cleanup function to call when this happens.
func (w *realTimeoutFactory) TimeoutCh() (<-chan time.Time, func() bool) {
	if w.timeout == 0 {
		return neverExitWatch, func() bool { return false }
	}
	t := time.NewTimer(w.timeout)
	return t.C, t.Stop
}

// serveWatch handles serving requests to the server
// TODO: the functionality in this method and in WatchServer.Serve is not cleanly decoupled.
func serveWatch(watcher watch.Interface, scope RequestScope, req *restful.Request, res *restful.Response, timeout time.Duration) {
	// negotiate for the stream serializer
	serializer, err := negotiateOutputStreamSerializer(req.Request, scope.Serializer)
	if err != nil {
		scope.err(err, res.ResponseWriter, req.Request)
		return
	}
	if serializer.Framer == nil {
		scope.err(fmt.Errorf("no framer defined for %q available for embedded encoding", serializer.MediaType), res.ResponseWriter, req.Request)
		return
	}
	encoder := scope.Serializer.EncoderForVersion(serializer.Serializer, scope.Kind.GroupVersion())

	useTextFraming := serializer.EncodesAsText

	// find the embedded serializer matching the media type
	embeddedEncoder := scope.Serializer.EncoderForVersion(serializer.Embedded.Serializer, scope.Kind.GroupVersion())

	server := &WatchServer{
		watching: watcher,
		scope:    scope,

		useTextFraming:  useTextFraming,
		mediaType:       serializer.MediaType,
		framer:          serializer.Framer,
		encoder:         encoder,
		embeddedEncoder: embeddedEncoder,
		fixup: func(obj runtime.Object) {
			if err := setSelfLink(obj, req, scope.Namer); err != nil {
				utilruntime.HandleError(fmt.Errorf("failed to set link for object %v: %v", reflect.TypeOf(obj), err))
			}
		},

		t: &realTimeoutFactory{timeout},
	}

	server.ServeHTTP(res.ResponseWriter, req.Request)
}

// WatchServer serves a watch.Interface over a websocket or vanilla HTTP.
type WatchServer struct {
	watching watch.Interface
	scope    RequestScope

	// true if websocket messages should use text framing (as opposed to binary framing)
	useTextFraming bool
	// the media type this watch is being served with
	mediaType string
	// used to frame the watch stream
	framer runtime.Framer
	// used to encode the watch stream event itself
	encoder runtime.Encoder
	// used to encode the nested object in the watch stream
	embeddedEncoder runtime.Encoder
	fixup           func(runtime.Object)

	t timeoutFactory
}

// Serve serves a series of encoded events via HTTP with Transfer-Encoding: chunked
// or over a websocket connection.
func (s *WatchServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	w = httplog.Unlogged(w)

	if wsstream.IsWebSocketRequest(req) {
		w.Header().Set("Content-Type", s.mediaType)
		websocket.Handler(s.HandleWS).ServeHTTP(w, req)
		return
	}

	cn, ok := w.(http.CloseNotifier)
	if !ok {
		err := fmt.Errorf("unable to start watch - can't get http.CloseNotifier: %#v", w)
		utilruntime.HandleError(err)
		s.scope.err(errors.NewInternalError(err), w, req)
		return
	}
	flusher, ok := w.(http.Flusher)
	if !ok {
		err := fmt.Errorf("unable to start watch - can't get http.Flusher: %#v", w)
		utilruntime.HandleError(err)
		s.scope.err(errors.NewInternalError(err), w, req)
		return
	}

	framer := s.framer.NewFrameWriter(w)
	if framer == nil {
		// programmer error
		err := fmt.Errorf("no stream framing support is available for media type %q", s.mediaType)
		utilruntime.HandleError(err)
		s.scope.err(errors.NewBadRequest(err.Error()), w, req)
		return
	}
	e := streaming.NewEncoder(framer, s.encoder)

	// ensure the connection times out
	timeoutCh, cleanup := s.t.TimeoutCh()
	defer cleanup()
	defer s.watching.Stop()

	// begin the stream
	w.Header().Set("Content-Type", s.mediaType)
	w.Header().Set("Transfer-Encoding", "chunked")
	w.WriteHeader(http.StatusOK)
	flusher.Flush()

	buf := &bytes.Buffer{}
	ch := s.watching.ResultChan()
	for {
		select {
		case <-cn.CloseNotify():
			return
		case <-timeoutCh:
			return
		case event, ok := <-ch:
			if !ok {
				// End of results.
				return
			}
			obj := event.Object
			s.fixup(obj)
			if err := s.embeddedEncoder.EncodeToStream(obj, buf); err != nil {
				// unexpected error
				utilruntime.HandleError(fmt.Errorf("unable to encode watch object: %v", err))
				return
			}
			event.Object = &runtime.Unknown{
				Raw: buf.Bytes(),
				// ContentType is not required here because we are defaulting to the serializer
				// type
			}
			if err := e.Encode((*versioned.InternalEvent)(&event)); err != nil {
				utilruntime.HandleError(fmt.Errorf("unable to encode watch object: %v (%#v)", err, e))
				// client disconnect.
				return
			}
			if len(ch) == 0 {
				flusher.Flush()
			}

			buf.Reset()
		}
	}
}

// HandleWS implements a websocket handler.
func (s *WatchServer) HandleWS(ws *websocket.Conn) {
	defer ws.Close()
	done := make(chan struct{})
	go wsstream.IgnoreReceives(ws, 0)
	buf := &bytes.Buffer{}
	streamBuf := &bytes.Buffer{}
	for {
		select {
		case <-done:
			s.watching.Stop()
			return
		case event, ok := <-s.watching.ResultChan():
			if !ok {
				// End of results.
				return
			}
			obj := event.Object
			s.fixup(obj)
			if err := s.embeddedEncoder.EncodeToStream(obj, buf); err != nil {
				// unexpected error
				utilruntime.HandleError(fmt.Errorf("unable to encode watch object: %v", err))
				return
			}
			event.Object = &runtime.Unknown{
				Raw: buf.Bytes(),
				// ContentType is not required here because we are defaulting to the serializer
				// type
			}
			// the internal event will be versioned by the encoder
			internalEvent := versioned.InternalEvent(event)
			if err := s.encoder.EncodeToStream(&internalEvent, streamBuf); err != nil {
				// encoding error
				utilruntime.HandleError(fmt.Errorf("unable to encode event: %v", err))
				s.watching.Stop()
				return
			}
			if s.useTextFraming {
				if err := websocket.Message.Send(ws, streamBuf.String()); err != nil {
					// Client disconnect.
					s.watching.Stop()
					return
				}
			} else {
				if err := websocket.Message.Send(ws, streamBuf.Bytes()); err != nil {
					// Client disconnect.
					s.watching.Stop()
					return
				}
			}
			buf.Reset()
			streamBuf.Reset()
		}
	}
}
<|endoftext|>


<commit_before>// Package zip implements the Archive interface providing zip archiving
// and compression.
package zip

import (
	"archive/zip"
	"io"
	"os"
)

// Archive zip struct
type Archive struct {
	z *zip.Writer
}

// Close all closeables
func (a Archive) Close() error {
	return a.z.Close()
}

// New zip archive
func New(target io.Writer) Archive {
	return Archive{
		z: zip.NewWriter(target),
	}
}

// Add a file to the zip archive
func (a Archive) Add(name, path string) (err error) {
	file, err := os.Open(path)
	if err != nil {
		return
	}
	defer file.Close() // nolint: errcheck
	info, err := file.Stat()
	if err != nil {
		return
	}
	if info.IsDir() {
		return
	}
	header, err := zip.FileInfoHeader(info)
	if err != nil {
		return err
	}
	header.Name = name
	w, err := a.z.CreateHeader(header)
	if err != nil {
		return err
	}
	_, err = io.Copy(w, file)
	return err
}
<commit_msg>fix: Zip files were no longer being compressed (#748)<commit_after>// Package zip implements the Archive interface providing zip archiving
// and compression.
package zip

import (
	"archive/zip"
	"io"
	"os"
)

// Archive zip struct
type Archive struct {
	z *zip.Writer
}

// Close all closeables
func (a Archive) Close() error {
	return a.z.Close()
}

// New zip archive
func New(target io.Writer) Archive {
	return Archive{
		z: zip.NewWriter(target),
	}
}

// Add a file to the zip archive
func (a Archive) Add(name, path string) (err error) {
	file, err := os.Open(path)
	if err != nil {
		return
	}
	defer file.Close() // nolint: errcheck
	info, err := file.Stat()
	if err != nil {
		return
	}
	if info.IsDir() {
		return
	}
	header, err := zip.FileInfoHeader(info)
	if err != nil {
		return err
	}
	header.Name = name
	header.Method = zip.Deflate
	w, err := a.z.CreateHeader(header)
	if err != nil {
		return err
	}
	_, err = io.Copy(w, file)
	return err
}
<|endoftext|>


<commit_before>package connpool

import (
	"context"
	"io"
	"net"
	"sync"
	"sync/atomic"
	"time"

	"github.com/datawire/dlib/dlog"
	"github.com/telepresenceio/telepresence/v2/pkg/ipproto"
)

// The idleDuration controls how long a dialer for a specific proto+from-to address combination remains alive without
// reading or writing any messages. The dialer is normally closed by one of the peers.
const tcpConnTTL = 2 * time.Hour // Default tcp_keepalive_time on Linux
const udpConnTTL = 1 * time.Minute
const partlyClosedDuration = 5 * time.Second
const dialTimeout = 30 * time.Second

// handlerBufferSize is the total number of messages that a handler can have in its buffer before it
// starts blocking on the HandleMessage function (and hence, generate backpressure on the tunnel)
const handlerBufferSize = 100

const (
	notConnected = int32(iota)
	halfConnected
	connected
	disconnecting
)

// The dialer takes care of dispatching messages between gRPC and UDP connections
type dialer struct {
	id        ConnID
	release   func()
	tunnel    Tunnel
	incoming  chan Message
	conn      net.Conn
	idleTimer *time.Timer
	idleLock  sync.Mutex
	ttl       int64
	connected int32
}

// NewDialer creates a new handler that dispatches messages in both directions between the given gRPC server
// and the destination identified by the given connID.
//
// The handler remains active until it's been idle for idleDuration, at which time it will automatically close
// and call the release function it got from the connpool.Pool to ensure that it gets properly released.
func NewDialer(connID ConnID, tunnel Tunnel, release func()) Handler {
	ttl := tcpConnTTL
	if connID.Protocol() == ipproto.UDP {
		ttl = udpConnTTL
	}
	return &dialer{
		id:        connID,
		tunnel:    tunnel,
		release:   release,
		incoming:  make(chan Message, handlerBufferSize),
		connected: notConnected,
		ttl:       int64(ttl),
	}
}

// HandlerFromConn is like NewHandler but initializes the handler with an already existing connection.
func HandlerFromConn(connID ConnID, tunnel Tunnel, release func(), conn net.Conn) Handler {
	ttl := tcpConnTTL
	if connID.Protocol() == ipproto.UDP {
		ttl = udpConnTTL
	}
	return &dialer{
		id:        connID,
		tunnel:    tunnel,
		release:   release,
		incoming:  make(chan Message, handlerBufferSize),
		connected: halfConnected,
		conn:      conn,
		ttl:       int64(ttl),
	}
}

func (h *dialer) Start(ctx context.Context) {
	// Set up the idle timer to close and release this handler when it's been idle for a while.
	h.idleTimer = time.NewTimer(h.getTTL())

	switch h.connected {
	case notConnected:
		if h.id.Protocol() == ipproto.UDP {
			h.open(ctx)
		}
	case halfConnected:
		// Connection is created by listener on this side. Establish other
		// side using a control message and start the loops
		h.connected = connected
		h.sendTCD(ctx, Connect)
	}

	// Start writeLoop so that initial control packages can be handled
	go h.writeLoop(ctx)
}

func (h *dialer) getTTL() time.Duration {
	return time.Duration(atomic.LoadInt64(&h.ttl))
}

func (h *dialer) open(ctx context.Context) ControlCode {
	if !atomic.CompareAndSwapInt32(&h.connected, notConnected, connected) {
		// already connected
		return ConnectOK
	}
	dlog.Debugf(ctx, "   CONN %s, dialing", h.id)
	dialer := net.Dialer{Timeout: dialTimeout}
	conn, err := dialer.DialContext(ctx, h.id.ProtocolString(), h.id.DestinationAddr().String())
	if err != nil {
		dlog.Errorf(ctx, "%s: failed to establish connection: %v", h.id, err)
		return ConnectReject
	}
	h.conn = conn
	dlog.Debugf(ctx, "   CONN %s, dial answered", h.id)
	go h.readLoop(ctx)
	return ConnectOK
}

func (h *dialer) handleControl(ctx context.Context, cm Control) {
	dlog.Debugf(ctx, "<- GRPC %s", cm)
	switch cm.Code() {
	case Connect:
		h.sendTCD(ctx, h.open(ctx))
	case ConnectOK:
		go h.readLoop(ctx)
	case ReadClosed, WriteClosed, Disconnect: // Peer requests a disconnect. No more messages will arrive
		h.Close(ctx)
		h.sendTCD(ctx, DisconnectOK)
	case DisconnectOK: // Peer responded to our disconnect or wants to hard-close. No more messages will arrive
		h.Close(ctx)
	case ConnectReject:
		h.Close(ctx)
	case KeepAlive:
		h.resetIdle()
	default:
		dlog.Errorf(ctx, "%s: unhandled connection control message: %s", h.id, cm)
	}
}

// HandleMessage sends a package to the underlying TCP/UDP connection
func (h *dialer) HandleMessage(ctx context.Context, dg Message) {
	select {
	case <-ctx.Done():
	case h.incoming <- dg:
	}
}

// Close will close the underlying TCP/UDP connection
func (h *dialer) Close(_ context.Context) {
	if atomic.CompareAndSwapInt32(&h.connected, connected, notConnected) ||
		atomic.CompareAndSwapInt32(&h.connected, disconnecting, notConnected) {
		h.drop()
	}
}

func (h *dialer) drop() {
	h.release()
	if h.conn != nil {
		_ = h.conn.Close()
	}
}

func (h *dialer) sendTCD(ctx context.Context, code ControlCode) {
	ctrl := NewControl(h.id, code, nil)
	dlog.Debugf(ctx, "-> GRPC %s", ctrl)
	err := h.tunnel.Send(ctx, ctrl)
	if err != nil {
		dlog.Errorf(ctx, "failed to send control message: %v", err)
	}
}

func (h *dialer) startDisconnect(ctx context.Context, sendOK bool) {
	if atomic.CompareAndSwapInt32(&h.connected, connected, disconnecting) {
		dlog.Debugf(ctx, "-- GRPC %s disconnecting", h.id)
		atomic.StoreInt64(&h.ttl, int64(partlyClosedDuration))
		if sendOK && h.id.Protocol() == ipproto.TCP {
			h.sendTCD(ctx, Disconnect)
		}
	}
}

func (h *dialer) readLoop(ctx context.Context) {
	b := make([]byte, 0x10000)

	stateCheck := func() bool {
		state := atomic.LoadInt32(&h.connected)
		if state == disconnecting || state == notConnected {
			return false
		}
		if ctx.Err() != nil {
			// Hard close of peer. We don't want any more data
			h.sendTCD(ctx, DisconnectOK)
			h.Close(ctx)
			return false
		}
		return true
	}

	for {
		n, err := h.conn.Read(b)
		if !stateCheck() {
			return
		}

		if err != nil {
			h.startDisconnect(ctx, true)
			if err != io.EOF {
				dlog.Errorf(ctx, "!! CONN %s, conn read: %v", h.id, err)
			}
			return
		}
		if !h.resetIdle() {
			// Hard close of peer. We don't want any more data
			h.sendTCD(ctx, DisconnectOK)
			h.Close(ctx)
			return
		}
		if n > 0 {
			dlog.Debugf(ctx, "<- CONN %s, len %d", h.id, n)
			err = h.tunnel.Send(ctx, NewMessage(h.id, b[:n]))
			if err != nil {
				if ctx.Err() == nil {
					dlog.Errorf(ctx, "!! GRPC %s, send: %v", h.id, err)
				}
				h.startDisconnect(ctx, false)
				return
			}
			dlog.Debugf(ctx, "-> GRPC %s, len %d", h.id, n)
		}
	}
}

func (h *dialer) writeLoop(ctx context.Context) {
	for {
		select {
		case <-ctx.Done():
			return
		case <-h.idleTimer.C:
			return
		case dg := <-h.incoming:
			if dg == nil {
				// h.incoming was closed by the reader and is now drained.
				h.startDisconnect(ctx, true)
				return
			}
			if ctrl, ok := dg.(Control); ok {
				h.handleControl(ctx, ctrl)
				continue
			}
			if !h.resetIdle() {
				// Hard close of peer. We don't want any more data
				h.sendTCD(ctx, DisconnectOK)
				h.Close(ctx)
				return
			}
			state := atomic.LoadInt32(&h.connected)
			if state == notConnected { // state == disconnecting will still be able to write
				return
			}
			payload := dg.Payload()
			pn := len(payload)
			dlog.Debugf(ctx, "<- GRPC %s, len %d", h.id, pn)
			for n := 0; n < pn; {
				wn, err := h.conn.Write(payload[n:])
				if err != nil {
					h.startDisconnect(ctx, true)
					dlog.Errorf(ctx, "!! CONN %s, write: %v", h.id, err)
					return
				}
				dlog.Debugf(ctx, "-> CONN %s, len %d", h.id, wn)
				n += wn
			}
		}
	}
}

func (h *dialer) resetIdle() bool {
	h.idleLock.Lock()
	stopped := h.idleTimer.Stop()
	if stopped {
		h.idleTimer.Reset(h.getTTL())
	}
	h.idleLock.Unlock()
	return stopped
}
<commit_msg>Send DISCONNECT_OK before closing the connection.<commit_after>package connpool

import (
	"context"
	"io"
	"net"
	"sync"
	"sync/atomic"
	"time"

	"github.com/datawire/dlib/dlog"
	"github.com/telepresenceio/telepresence/v2/pkg/ipproto"
)

// The idleDuration controls how long a dialer for a specific proto+from-to address combination remains alive without
// reading or writing any messages. The dialer is normally closed by one of the peers.
const tcpConnTTL = 2 * time.Hour // Default tcp_keepalive_time on Linux
const udpConnTTL = 1 * time.Minute
const partlyClosedDuration = 5 * time.Second
const dialTimeout = 30 * time.Second

// handlerBufferSize is the total number of messages that a handler can have in its buffer before it
// starts blocking on the HandleMessage function (and hence, generate backpressure on the tunnel)
const handlerBufferSize = 100

const (
	notConnected = int32(iota)
	halfConnected
	connected
	disconnecting
)

// The dialer takes care of dispatching messages between gRPC and UDP connections
type dialer struct {
	id        ConnID
	release   func()
	tunnel    Tunnel
	incoming  chan Message
	conn      net.Conn
	idleTimer *time.Timer
	idleLock  sync.Mutex
	ttl       int64
	connected int32
}

// NewDialer creates a new handler that dispatches messages in both directions between the given gRPC server
// and the destination identified by the given connID.
//
// The handler remains active until it's been idle for idleDuration, at which time it will automatically close
// and call the release function it got from the connpool.Pool to ensure that it gets properly released.
func NewDialer(connID ConnID, tunnel Tunnel, release func()) Handler {
	ttl := tcpConnTTL
	if connID.Protocol() == ipproto.UDP {
		ttl = udpConnTTL
	}
	return &dialer{
		id:        connID,
		tunnel:    tunnel,
		release:   release,
		incoming:  make(chan Message, handlerBufferSize),
		connected: notConnected,
		ttl:       int64(ttl),
	}
}

// HandlerFromConn is like NewHandler but initializes the handler with an already existing connection.
func HandlerFromConn(connID ConnID, tunnel Tunnel, release func(), conn net.Conn) Handler {
	ttl := tcpConnTTL
	if connID.Protocol() == ipproto.UDP {
		ttl = udpConnTTL
	}
	return &dialer{
		id:        connID,
		tunnel:    tunnel,
		release:   release,
		incoming:  make(chan Message, handlerBufferSize),
		connected: halfConnected,
		conn:      conn,
		ttl:       int64(ttl),
	}
}

func (h *dialer) Start(ctx context.Context) {
	// Set up the idle timer to close and release this handler when it's been idle for a while.
	h.idleTimer = time.NewTimer(h.getTTL())

	switch h.connected {
	case notConnected:
		if h.id.Protocol() == ipproto.UDP {
			h.open(ctx)
		}
	case halfConnected:
		// Connection is created by listener on this side. Establish other
		// side using a control message and start the loops
		h.connected = connected
		h.sendTCD(ctx, Connect)
	}

	// Start writeLoop so that initial control packages can be handled
	go h.writeLoop(ctx)
}

func (h *dialer) getTTL() time.Duration {
	return time.Duration(atomic.LoadInt64(&h.ttl))
}

func (h *dialer) open(ctx context.Context) ControlCode {
	if !atomic.CompareAndSwapInt32(&h.connected, notConnected, connected) {
		// already connected
		return ConnectOK
	}
	dlog.Debugf(ctx, "   CONN %s, dialing", h.id)
	dialer := net.Dialer{Timeout: dialTimeout}
	conn, err := dialer.DialContext(ctx, h.id.ProtocolString(), h.id.DestinationAddr().String())
	if err != nil {
		dlog.Errorf(ctx, "%s: failed to establish connection: %v", h.id, err)
		return ConnectReject
	}
	h.conn = conn
	dlog.Debugf(ctx, "   CONN %s, dial answered", h.id)
	go h.readLoop(ctx)
	return ConnectOK
}

func (h *dialer) handleControl(ctx context.Context, cm Control) {
	dlog.Debugf(ctx, "<- GRPC %s", cm)
	switch cm.Code() {
	case Connect:
		h.sendTCD(ctx, h.open(ctx))
	case ConnectOK:
		go h.readLoop(ctx)
	case ReadClosed, WriteClosed, Disconnect: // Peer requests a disconnect. No more messages will arrive
		h.sendTCD(ctx, DisconnectOK)
		h.Close(ctx)
	case DisconnectOK: // Peer responded to our disconnect or wants to hard-close. No more messages will arrive
		h.Close(ctx)
	case ConnectReject:
		h.Close(ctx)
	case KeepAlive:
		h.resetIdle()
	default:
		dlog.Errorf(ctx, "%s: unhandled connection control message: %s", h.id, cm)
	}
}

// HandleMessage sends a package to the underlying TCP/UDP connection
func (h *dialer) HandleMessage(ctx context.Context, dg Message) {
	select {
	case <-ctx.Done():
	case h.incoming <- dg:
	}
}

// Close will close the underlying TCP/UDP connection
func (h *dialer) Close(_ context.Context) {
	if atomic.CompareAndSwapInt32(&h.connected, connected, notConnected) ||
		atomic.CompareAndSwapInt32(&h.connected, disconnecting, notConnected) {
		h.drop()
	}
}

func (h *dialer) drop() {
	h.release()
	if h.conn != nil {
		_ = h.conn.Close()
	}
}

func (h *dialer) sendTCD(ctx context.Context, code ControlCode) {
	ctrl := NewControl(h.id, code, nil)
	dlog.Debugf(ctx, "-> GRPC %s", ctrl)
	err := h.tunnel.Send(ctx, ctrl)
	if err != nil {
		dlog.Errorf(ctx, "failed to send control message: %v", err)
	}
}

func (h *dialer) startDisconnect(ctx context.Context, sendOK bool) {
	if atomic.CompareAndSwapInt32(&h.connected, connected, disconnecting) {
		dlog.Debugf(ctx, "-- GRPC %s disconnecting", h.id)
		atomic.StoreInt64(&h.ttl, int64(partlyClosedDuration))
		if sendOK && h.id.Protocol() == ipproto.TCP {
			h.sendTCD(ctx, Disconnect)
		}
	}
}

func (h *dialer) readLoop(ctx context.Context) {
	b := make([]byte, 0x10000)

	stateCheck := func() bool {
		state := atomic.LoadInt32(&h.connected)
		if state == disconnecting || state == notConnected {
			return false
		}
		if ctx.Err() != nil {
			// Hard close of peer. We don't want any more data
			h.sendTCD(ctx, DisconnectOK)
			h.Close(ctx)
			return false
		}
		return true
	}

	for {
		n, err := h.conn.Read(b)
		if !stateCheck() {
			return
		}

		if err != nil {
			h.startDisconnect(ctx, true)
			if err != io.EOF {
				dlog.Errorf(ctx, "!! CONN %s, conn read: %v", h.id, err)
			}
			return
		}
		if !h.resetIdle() {
			// Hard close of peer. We don't want any more data
			h.sendTCD(ctx, DisconnectOK)
			h.Close(ctx)
			return
		}
		if n > 0 {
			dlog.Debugf(ctx, "<- CONN %s, len %d", h.id, n)
			err = h.tunnel.Send(ctx, NewMessage(h.id, b[:n]))
			if err != nil {
				if ctx.Err() == nil {
					dlog.Errorf(ctx, "!! GRPC %s, send: %v", h.id, err)
				}
				h.startDisconnect(ctx, false)
				return
			}
			dlog.Debugf(ctx, "-> GRPC %s, len %d", h.id, n)
		}
	}
}

func (h *dialer) writeLoop(ctx context.Context) {
	for {
		select {
		case <-ctx.Done():
			return
		case <-h.idleTimer.C:
			return
		case dg := <-h.incoming:
			if dg == nil {
				// h.incoming was closed by the reader and is now drained.
				h.startDisconnect(ctx, true)
				return
			}
			if ctrl, ok := dg.(Control); ok {
				h.handleControl(ctx, ctrl)
				continue
			}
			if !h.resetIdle() {
				// Hard close of peer. We don't want any more data
				h.sendTCD(ctx, DisconnectOK)
				h.Close(ctx)
				return
			}
			state := atomic.LoadInt32(&h.connected)
			if state == notConnected { // state == disconnecting will still be able to write
				return
			}
			payload := dg.Payload()
			pn := len(payload)
			dlog.Debugf(ctx, "<- GRPC %s, len %d", h.id, pn)
			for n := 0; n < pn; {
				wn, err := h.conn.Write(payload[n:])
				if err != nil {
					h.startDisconnect(ctx, true)
					dlog.Errorf(ctx, "!! CONN %s, write: %v", h.id, err)
					return
				}
				dlog.Debugf(ctx, "-> CONN %s, len %d", h.id, wn)
				n += wn
			}
		}
	}
}

func (h *dialer) resetIdle() bool {
	h.idleLock.Lock()
	stopped := h.idleTimer.Stop()
	if stopped {
		h.idleTimer.Reset(h.getTTL())
	}
	h.idleLock.Unlock()
	return stopped
}
<|endoftext|>


<commit_before>package haproxy

import (
	"fmt"
	"strings"

	"github.com/dynport/urknall"
	"github.com/dynport/urknall/cmd"
)

type Package struct {
	Version string `urknall:"default=1.4.24"`
}

func New(version string) *Package {
	return &Package{
		Version: version,
	}
}

func (p *Package) url() string {
	return "http://haproxy.1wt.eu/download/1.4/src/haproxy-1.4.24.tar.gz"
}

func (p *Package) minorVersion() string {
	parts := strings.Split(p.Version, ".")
	if len(parts) == 3 {
		return strings.Join(parts[0:2], ".")
	}
	panic(fmt.Sprintf("unable to extract minor version from %q", p.Version))
}

func (p *Package) InstallPath() string {
	return "/opt/haproxy-" + p.Version
}

func (p *Package) Package(r *urknall.Runlist) {
	r.Add(
		cmd.InstallPackages("curl", "build-essential", "libpcre3-dev"),
		cmd.Mkdir("/opt/src/", "root", 0755),
		cmd.DownloadAndExtract(p.url(), "/opt/src/"),
		cmd.Mkdir("{{ .InstallPath }}/sbin", "root", 0755),
		"cd /opt/src/haproxy-{{ .Version }} && make TARGET=linux25 USER_STATIC_PCRE=1 && cp ./haproxy {{ .InstallPath }}/sbin/",
		cmd.WriteFile("/etc/init/haproxy.conf", initScript, "root", 0755),
	)
}

const initScript = `description "Properly handle haproxy"

start on (filesystem and net-device-up IFACE=lo)

env PID_PATH=/var/run/haproxy.pid
env BIN_PATH={{ .InstallPath }}/sbin/haproxy

script
exec /bin/bash <<EOF
  $BIN_PATH -f /etc/haproxy.cfg -D -p $PID_PATH

  trap "$BIN_PATH -f /etc/haproxy.cfg -p $PID_PATH -sf \\\$(cat $PID_PATH)" SIGHUP
  trap "kill -TERM \\\$(cat $PID_PATH) && exit 0" SIGTERM SIGINT

  while true; do # Iterate to keep job running.
    sleep 1 # Don't sleep to long as signals will not be handled during sleep.
  done
EOF
end script`
<commit_msg>fixes to haproxy packages<commit_after>package haproxy

import (
	"fmt"
	"strings"

	"github.com/dynport/urknall"
	"github.com/dynport/urknall/cmd"
)

type Package struct {
	Version string `urknall:"default=1.4.24"`
}

func New(version string) *Package {
	return &Package{
		Version: version,
	}
}

func (p *Package) url() string {
	return "http://haproxy.1wt.eu/download/1.4/src/haproxy-" + p.Version + ".tar.gz"
}

func (p *Package) minorVersion() string {
	parts := strings.Split(p.Version, ".")
	if len(parts) == 3 {
		return strings.Join(parts[0:2], ".")
	}
	panic(fmt.Sprintf("unable to extract minor version from %q", p.Version))
}

func (p *Package) InstallPath() string {
	return "/opt/haproxy-" + p.Version
}

func (p *Package) Package(r *urknall.Runlist) {
	r.Add(
		cmd.InstallPackages("curl", "build-essential", "libpcre3-dev"),
		cmd.Mkdir("/opt/src/", "root", 0755),
		cmd.DownloadAndExtract(p.url(), "/opt/src/"),
		cmd.Mkdir("{{ .InstallPath }}/sbin", "root", 0755),
		"cd /opt/src/haproxy-{{ .Version }} && make TARGET=linux25 USER_STATIC_PCRE=1 && cp ./haproxy {{ .InstallPath }}/sbin/",
		cmd.WriteFile("/etc/init/haproxy.conf", initScript, "root", 0755),
	)
}

const initScript = `description "Properly handle haproxy"

start on (filesystem and net-device-up IFACE=lo)

env PID_PATH=/var/run/haproxy.pid
env BIN_PATH={{ .InstallPath }}/sbin/haproxy

script
exec /bin/bash <<EOF
  $BIN_PATH -f /etc/haproxy.cfg -D -p $PID_PATH

  trap "$BIN_PATH -f /etc/haproxy.cfg -p $PID_PATH -D -sf \$(cat $PID_PATH)" SIGHUP
  trap "kill -TERM \$(cat $PID_PATH) && exit 0" SIGTERM SIGINT

  while true; do # Iterate to keep job running.
    sleep 1 # Don't sleep to long as signals will not be handled during sleep.
  done
EOF
end script`
<|endoftext|>


<commit_before>// Copyright 2015 CNI 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 ipam

import (
	"fmt"
	"net"
	"os"

	"github.com/containernetworking/cni/pkg/types/current"
	"github.com/containernetworking/plugins/pkg/ip"
	"github.com/containernetworking/plugins/pkg/utils/sysctl"

	"github.com/vishvananda/netlink"
)

const (
	DisableIPv6SysctlTemplate = "net.ipv6.conf.%s.disable_ipv6"
)

// ConfigureIface takes the result of IPAM plugin and
// applies to the ifName interface
func ConfigureIface(ifName string, res *current.Result) error {
	if len(res.Interfaces) == 0 {
		return fmt.Errorf("no interfaces to configure")
	}

	link, err := netlink.LinkByName(ifName)
	if err != nil {
		return fmt.Errorf("failed to lookup %q: %v", ifName, err)
	}

	if err := netlink.LinkSetUp(link); err != nil {
		return fmt.Errorf("failed to set %q UP: %v", ifName, err)
	}

	var v4gw, v6gw net.IP
	var has_enabled_ipv6 bool = false
	for _, ipc := range res.IPs {
		if ipc.Interface == nil {
			continue
		}
		intIdx := *ipc.Interface
		if intIdx < 0 || intIdx >= len(res.Interfaces) || res.Interfaces[intIdx].Name != ifName {
			// IP address is for a different interface
			return fmt.Errorf("failed to add IP addr %v to %q: invalid interface index", ipc, ifName)
		}

		// Make sure sysctl "disable_ipv6" is 0 if we are about to add
		// an IPv6 address to the interface
		if !has_enabled_ipv6 && ipc.Version == "6" {
			// Enabled IPv6 for loopback "lo" and the interface
			// being configured
			for _, iface := range [2]string{"lo", ifName} {
				ipv6SysctlValueName := fmt.Sprintf(DisableIPv6SysctlTemplate, iface)

				// Read current sysctl value
				value, err := sysctl.Sysctl(ipv6SysctlValueName)
				if err != nil || value == "0" {
					// FIXME: log warning if unable to read sysctl value
					continue
				}

				// Write sysctl to enable IPv6
				_, err = sysctl.Sysctl(ipv6SysctlValueName, "0")
				if err != nil {
					return fmt.Errorf("failed to enable IPv6 for interface %q (%s=%s): %v", iface, ipv6SysctlValueName, value, err)
				}
			}
			has_enabled_ipv6 = true
		}

		addr := &netlink.Addr{IPNet: &ipc.Address, Label: ""}
		if err = netlink.AddrAdd(link, addr); err != nil {
			return fmt.Errorf("failed to add IP addr %v to %q: %v", ipc, ifName, err)
		}

		gwIsV4 := ipc.Gateway.To4() != nil
		if gwIsV4 && v4gw == nil {
			v4gw = ipc.Gateway
		} else if !gwIsV4 && v6gw == nil {
			v6gw = ipc.Gateway
		}
	}

	if v6gw != nil {
		ip.SettleAddresses(ifName, 10)
	}

	for _, r := range res.Routes {
		routeIsV4 := r.Dst.IP.To4() != nil
		gw := r.GW
		if gw == nil {
			if routeIsV4 && v4gw != nil {
				gw = v4gw
			} else if !routeIsV4 && v6gw != nil {
				gw = v6gw
			}
		}
		if err = ip.AddRoute(&r.Dst, gw, link); err != nil {
			// we skip over duplicate routes as we assume the first one wins
			if !os.IsExist(err) {
				return fmt.Errorf("failed to add route '%v via %v dev %v': %v", r.Dst, gw, ifName, err)
			}
		}
	}

	return nil
}
<commit_msg>pkg/ipam: use slash as sysctl separator so interface name can have dot<commit_after>// Copyright 2015 CNI 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 ipam

import (
	"fmt"
	"net"
	"os"

	"github.com/containernetworking/cni/pkg/types/current"
	"github.com/containernetworking/plugins/pkg/ip"
	"github.com/containernetworking/plugins/pkg/utils/sysctl"

	"github.com/vishvananda/netlink"
)

const (
	// Note: use slash as separator so we can have dots in interface name (VLANs)
	DisableIPv6SysctlTemplate = "net/ipv6/conf/%s/disable_ipv6"
)

// ConfigureIface takes the result of IPAM plugin and
// applies to the ifName interface
func ConfigureIface(ifName string, res *current.Result) error {
	if len(res.Interfaces) == 0 {
		return fmt.Errorf("no interfaces to configure")
	}

	link, err := netlink.LinkByName(ifName)
	if err != nil {
		return fmt.Errorf("failed to lookup %q: %v", ifName, err)
	}

	if err := netlink.LinkSetUp(link); err != nil {
		return fmt.Errorf("failed to set %q UP: %v", ifName, err)
	}

	var v4gw, v6gw net.IP
	var has_enabled_ipv6 bool = false
	for _, ipc := range res.IPs {
		if ipc.Interface == nil {
			continue
		}
		intIdx := *ipc.Interface
		if intIdx < 0 || intIdx >= len(res.Interfaces) || res.Interfaces[intIdx].Name != ifName {
			// IP address is for a different interface
			return fmt.Errorf("failed to add IP addr %v to %q: invalid interface index", ipc, ifName)
		}

		// Make sure sysctl "disable_ipv6" is 0 if we are about to add
		// an IPv6 address to the interface
		if !has_enabled_ipv6 && ipc.Version == "6" {
			// Enabled IPv6 for loopback "lo" and the interface
			// being configured
			for _, iface := range [2]string{"lo", ifName} {
				ipv6SysctlValueName := fmt.Sprintf(DisableIPv6SysctlTemplate, iface)

				// Read current sysctl value
				value, err := sysctl.Sysctl(ipv6SysctlValueName)
				if err != nil {
					fmt.Fprintf(os.Stderr, "ipam_linux: failed to read sysctl %q: %v\n", ipv6SysctlValueName, err)
					continue
				}
				if value == "0" {
					continue
				}

				// Write sysctl to enable IPv6
				_, err = sysctl.Sysctl(ipv6SysctlValueName, "0")
				if err != nil {
					return fmt.Errorf("failed to enable IPv6 for interface %q (%s=%s): %v", iface, ipv6SysctlValueName, value, err)
				}
			}
			has_enabled_ipv6 = true
		}

		addr := &netlink.Addr{IPNet: &ipc.Address, Label: ""}
		if err = netlink.AddrAdd(link, addr); err != nil {
			return fmt.Errorf("failed to add IP addr %v to %q: %v", ipc, ifName, err)
		}

		gwIsV4 := ipc.Gateway.To4() != nil
		if gwIsV4 && v4gw == nil {
			v4gw = ipc.Gateway
		} else if !gwIsV4 && v6gw == nil {
			v6gw = ipc.Gateway
		}
	}

	if v6gw != nil {
		ip.SettleAddresses(ifName, 10)
	}

	for _, r := range res.Routes {
		routeIsV4 := r.Dst.IP.To4() != nil
		gw := r.GW
		if gw == nil {
			if routeIsV4 && v4gw != nil {
				gw = v4gw
			} else if !routeIsV4 && v6gw != nil {
				gw = v6gw
			}
		}
		if err = ip.AddRoute(&r.Dst, gw, link); err != nil {
			// we skip over duplicate routes as we assume the first one wins
			if !os.IsExist(err) {
				return fmt.Errorf("failed to add route '%v via %v dev %v': %v", r.Dst, gw, ifName, err)
			}
		}
	}

	return nil
}
<|endoftext|>


<commit_before>// +build lockdebug

// Copyright 2017-2018 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.

package lock

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"runtime/debug"
	"time"

	"github.com/cilium/cilium/pkg/logging"
	"github.com/cilium/cilium/pkg/logging/logfields"

	"github.com/sasha-s/go-deadlock"
	"github.com/sirupsen/logrus"
)

const (
	// selfishThresholdSec is the number of seconds that should be used when
	// detecting if a lock was held for more than the specified time.
	selfishThresholdSec = 0.1

	// Waiting for a lock for longer than DeadlockTimeout is considered a deadlock.
	// Ignored is DeadlockTimeout <= 0.
	deadLockTimeout = 310 * time.Second
)

var (
	log = logging.DefaultLogger.WithField(logfields.LogSubsys, "lock-lib")

	// selfishThresholdMsg is the message that will be printed when a lock was
	// held for more than selfishThresholdSec.
	selfishThresholdMsg = fmt.Sprintf("Goroutine took lock for more than %.2f seconds", selfishThresholdSec)
)

func init() {
	deadlock.Opts.DeadlockTimeout = deadLockTimeout
}

type internalRWMutex struct {
	deadlock.RWMutex
	time.Time
}

func (i *internalRWMutex) Lock() {
	i.RWMutex.Lock()
	i.Time = time.Now()
}

func (i *internalRWMutex) Unlock() {
	if sec := time.Since(i.Time).Seconds(); sec >= selfishThresholdSec {
		printStackTo(sec, debug.Stack(), os.Stderr)
	}
	i.RWMutex.Unlock()
}

func (i *internalRWMutex) UnlockIgnoreTime() {
	i.RWMutex.Unlock()
}

func (i *internalRWMutex) RLock() {
	i.RWMutex.Lock()
	i.Time = time.Now()
}

func (i *internalRWMutex) RUnlock() {
	if sec := time.Since(i.Time).Seconds(); sec >= selfishThresholdSec {
		printStackTo(sec, debug.Stack(), os.Stderr)
	}
	i.RWMutex.Unlock()
}

func (i *internalRWMutex) RUnlockIgnoreTime() {
	i.RWMutex.Unlock()
}

type internalMutex struct {
	deadlock.Mutex
	time.Time
}

func (i *internalMutex) Lock() {
	i.Mutex.Lock()
	i.Time = time.Now()
}

func (i *internalMutex) Unlock() {
	if sec := time.Since(i.Time).Seconds(); sec >= selfishThresholdSec {
		printStackTo(sec, debug.Stack(), os.Stderr)
	}
	i.Mutex.Unlock()
}

func (i *internalMutex) UnlockIgnoreTime() {
	i.Mutex.Unlock()
}

func printStackTo(sec float64, stack []byte, writer io.Writer) {
	goRoutineNumber := []byte("0")
	newLines := 0

	if bytes.Equal([]byte("goroutine"), stack[:len("goroutine")]) {
		newLines = bytes.Count(stack, []byte{'\n'})
		goroutineLine := bytes.IndexRune(stack, '[')
		goRoutineNumber = stack[:goroutineLine]
	}

	log.WithFields(logrus.Fields{
		"seconds":   sec,
		"goroutine": string(goRoutineNumber[len("goroutine") : len(goRoutineNumber)-1]),
	}).Debug(selfishThresholdMsg)

	// A stack trace is usually in the following format:
	// goroutine 1432 [running]:
	// runtime/debug.Stack(0xc424c4a370, 0xc421f7f750, 0x1)
	//   /usr/local/go/src/runtime/debug/stack.go:24 +0xa7
	//   ...
	// To know which trace belongs to which go routine we will append the
	// go routine number to every line of the stack trace.
	writer.Write(bytes.Replace(
		stack,
		[]byte{'\n'},
		append([]byte{'\n'}, goRoutineNumber...),
		// Don't replace the last '\n'
		newLines-1),
	)
}
<commit_msg>pkg/lock: make time of internalRWMutex private<commit_after>// +build lockdebug

// Copyright 2017-2018 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.

package lock

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"runtime/debug"
	"time"

	"github.com/cilium/cilium/pkg/logging"
	"github.com/cilium/cilium/pkg/logging/logfields"

	"github.com/sasha-s/go-deadlock"
	"github.com/sirupsen/logrus"
)

const (
	// selfishThresholdSec is the number of seconds that should be used when
	// detecting if a lock was held for more than the specified time.
	selfishThresholdSec = 0.1

	// Waiting for a lock for longer than DeadlockTimeout is considered a deadlock.
	// Ignored is DeadlockTimeout <= 0.
	deadLockTimeout = 310 * time.Second
)

var (
	log = logging.DefaultLogger.WithField(logfields.LogSubsys, "lock-lib")

	// selfishThresholdMsg is the message that will be printed when a lock was
	// held for more than selfishThresholdSec.
	selfishThresholdMsg = fmt.Sprintf("Goroutine took lock for more than %.2f seconds", selfishThresholdSec)
)

func init() {
	deadlock.Opts.DeadlockTimeout = deadLockTimeout
}

type internalRWMutex struct {
	deadlock.RWMutex
	t time.Time
}

func (i *internalRWMutex) Lock() {
	i.RWMutex.Lock()
	i.t = time.Now()
}

func (i *internalRWMutex) Unlock() {
	if sec := time.Since(i.t).Seconds(); sec >= selfishThresholdSec {
		printStackTo(sec, debug.Stack(), os.Stderr)
	}
	i.RWMutex.Unlock()
}

func (i *internalRWMutex) UnlockIgnoreTime() {
	i.RWMutex.Unlock()
}

func (i *internalRWMutex) RLock() {
	i.RWMutex.Lock()
	i.t = time.Now()
}

func (i *internalRWMutex) RUnlock() {
	if sec := time.Since(i.t).Seconds(); sec >= selfishThresholdSec {
		printStackTo(sec, debug.Stack(), os.Stderr)
	}
	i.RWMutex.Unlock()
}

func (i *internalRWMutex) RUnlockIgnoreTime() {
	i.RWMutex.Unlock()
}

type internalMutex struct {
	deadlock.Mutex
	time.Time
}

func (i *internalMutex) Lock() {
	i.Mutex.Lock()
	i.Time = time.Now()
}

func (i *internalMutex) Unlock() {
	if sec := time.Since(i.Time).Seconds(); sec >= selfishThresholdSec {
		printStackTo(sec, debug.Stack(), os.Stderr)
	}
	i.Mutex.Unlock()
}

func (i *internalMutex) UnlockIgnoreTime() {
	i.Mutex.Unlock()
}

func printStackTo(sec float64, stack []byte, writer io.Writer) {
	goRoutineNumber := []byte("0")
	newLines := 0

	if bytes.Equal([]byte("goroutine"), stack[:len("goroutine")]) {
		newLines = bytes.Count(stack, []byte{'\n'})
		goroutineLine := bytes.IndexRune(stack, '[')
		goRoutineNumber = stack[:goroutineLine]
	}

	log.WithFields(logrus.Fields{
		"seconds":   sec,
		"goroutine": string(goRoutineNumber[len("goroutine") : len(goRoutineNumber)-1]),
	}).Debug(selfishThresholdMsg)

	// A stack trace is usually in the following format:
	// goroutine 1432 [running]:
	// runtime/debug.Stack(0xc424c4a370, 0xc421f7f750, 0x1)
	//   /usr/local/go/src/runtime/debug/stack.go:24 +0xa7
	//   ...
	// To know which trace belongs to which go routine we will append the
	// go routine number to every line of the stack trace.
	writer.Write(bytes.Replace(
		stack,
		[]byte{'\n'},
		append([]byte{'\n'}, goRoutineNumber...),
		// Don't replace the last '\n'
		newLines-1),
	)
}
<|endoftext|>