<commit_before>package tokenbucket

import (
	"time"
)

type TokenBucket struct {
	time int64  // Unix timestamp in nanoseconds indicating the last time that tokens were added to the bucket
	rate uint64 // The rate at which tokens are added to the bucket measured in tokens per second
	fill uint64 // The number of tokens currently in the bucket
	size uint64 // The capacity of the bucket measured in tokens
}

func New(rate uint64, size uint64) *TokenBucket {
	bucket := new(TokenBucket)
	bucket.time = time.Now().UnixNano()
	bucket.rate = rate
	bucket.fill = 0
	bucket.size = size
	return bucket
}

func (tb *TokenBucket) Remove(tokens uint64) uint64 {
	// A remove is a request that blocks until are tokens to remove are available
	rv := tb.Request(tokens)

	if rv < tokens {
		if tb.rate > 0 {
			deadline := time.Unix(0, int64(tokens - rv) * int64(time.Second) / int64(tb.rate) + int64(tb.time))
			duration := time.Until(deadline)

			timer := time.NewTimer(duration)
			<-timer.C

			rv = rv + tb.Request(tokens - rv)
		}
	}

	return rv
}

func (tb *TokenBucket) Request(tokens uint64) uint64 {
	var rv uint64 = 0

	if tb != nil {
		if tb.rate <= 0 {
			if tb.size < tokens {
				rv = tb.size
			} else {
				rv = tokens
			}
		} else if tb.fill >= tokens {
			tb.fill = tb.fill - tokens
			rv = tokens
		} else {
			now := time.Now().UnixNano()
			newTokens := tb.rate * uint64(now-tb.time) / uint64(time.Second)

			if newTokens > 0 {
				tb.fill = tb.fill + newTokens
				tb.time = now
			}

			if tb.fill > tb.size {
				tb.fill = tb.size
			}

			if tb.fill >= tokens {
				tb.fill = tb.fill - tokens
				rv = tokens
			} else {
				rv = tb.fill
				tb.fill = 0
			}
		}
	}

	return rv
}

func (tb *TokenBucket) Return(tokens uint64) uint64 {
	var rv uint64 = 0

	if tb != nil && tb.fill < tb.size {
		tb.fill = tb.fill + tokens

		if tb.fill > tb.size {
			rv = tokens - (tb.fill - tb.size)
			tb.fill = tb.size
		} else {
			rv = tokens
		}
	}

	return rv
}
<commit_msg>Use more efficient timer calls when waiting for tokens<commit_after>package tokenbucket

import (
	"time"
)

type TokenBucket struct {
	fill  uint64 // The number of tokens currently in the bucket
	rate  uint64 // The rate at which tokens are added to the bucket measured in tokens per second
	size  uint64 // The capacity of the bucket measured in tokens
	time  int64  // Unix timestamp in nanoseconds indicating the last time that tokens were added to the bucket
	wait *time.Timer
}

func New(rate uint64, size uint64) *TokenBucket {
	if size == 0 {
		panic("size: must be a non-zero value")
	}

	bucket := new(TokenBucket)
	bucket.time = time.Now().UnixNano()
	bucket.rate = rate
	bucket.fill = 0
	bucket.size = size
	bucket.wait = time.NewTimer(0 * time.Second)

	<-bucket.wait.C
	bucket.wait.Stop()

	return bucket
}

func (tb *TokenBucket) Remove(tokens uint64) uint64 {
	// A remove is a request that blocks until are tokens to remove are available
	rv := tb.Request(tokens)

	if rv < tokens {
		if tb.rate > 0 {
			deadline := time.Unix(0, int64(tokens - rv) * int64(time.Second) / int64(tb.rate) + int64(tb.time))
			duration := time.Until(deadline)

			tb.wait.Reset(duration)
			<-tb.wait.C
			tb.wait.Stop()

			rv = rv + tb.Request(tokens - rv)
		}
	}

	return rv
}

func (tb *TokenBucket) Request(tokens uint64) uint64 {
	var rv uint64 = 0

	if tb != nil {
		if tb.rate <= 0 {
			if tb.size < tokens {
				rv = tb.size
			} else {
				rv = tokens
			}
		} else if tb.fill >= tokens {
			tb.fill = tb.fill - tokens
			rv = tokens
		} else {
			now := time.Now().UnixNano()
			newTokens := tb.rate * uint64(now-tb.time) / uint64(time.Second)

			if newTokens > 0 {
				tb.fill = tb.fill + newTokens
				tb.time = now
			}

			if tb.fill > tb.size {
				tb.fill = tb.size
			}

			if tb.fill >= tokens {
				tb.fill = tb.fill - tokens
				rv = tokens
			} else {
				rv = tb.fill
				tb.fill = 0
			}
		}
	}

	return rv
}

func (tb *TokenBucket) Return(tokens uint64) uint64 {
	var rv uint64 = 0

	if tb != nil && tb.fill < tb.size {
		tb.fill = tb.fill + tokens

		if tb.fill > tb.size {
			rv = tokens - (tb.fill - tb.size)
			tb.fill = tb.size
		} else {
			rv = tokens
		}
	}

	return rv
}
<|endoftext|>


<commit_before>// Copyright 2017 TiKV Project 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 main

import (
	"context"
	"flag"
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"os"
	"os/signal"
	"sync"
	"syscall"
	"time"

	"github.com/pingcap/errors"
	"github.com/pingcap/log"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	pd "github.com/tikv/pd/client"
	"go.uber.org/zap"
)

var (
	pdAddrs      = flag.String("pd", "127.0.0.1:2379", "pd address")
	clientNumber = flag.Int("client", 1, "the number of pd clients involved in each benchmark")
	concurrency  = flag.Int("c", 1000, "concurrency")
	count        = flag.Int("count", 1, "the count number that the test will run")
	duration     = flag.Duration("duration", 60*time.Second, "how many seconds the test will last")
	dcLocation   = flag.String("dc", "global", "which dc-location this bench will request")
	verbose      = flag.Bool("v", false, "output statistics info every interval and output metrics info at the end")
	interval     = flag.Duration("interval", time.Second, "interval to output the statistics")
	caPath       = flag.String("cacert", "", "path of file that contains list of trusted SSL CAs")
	certPath     = flag.String("cert", "", "path of file that contains X509 certificate in PEM format")
	keyPath      = flag.String("key", "", "path of file that contains X509 key in PEM format")
	wg           sync.WaitGroup
)

var promServer *httptest.Server

func collectMetrics(server *httptest.Server) string {
	time.Sleep(1100 * time.Millisecond)
	res, _ := http.Get(server.URL)
	body, _ := io.ReadAll(res.Body)
	res.Body.Close()
	return string(body)
}

func main() {
	flag.Parse()
	ctx, cancel := context.WithCancel(context.Background())

	sc := make(chan os.Signal, 1)
	signal.Notify(sc,
		syscall.SIGHUP,
		syscall.SIGINT,
		syscall.SIGTERM,
		syscall.SIGQUIT)
	go func() {
		<-sc
		cancel()
	}()

	for i := 0; i < *count; i++ {
		fmt.Printf("\nStart benchmark #%d, duration: %+vs\n", i, (*duration).Seconds())
		bench(ctx)
	}
}

func bench(mainCtx context.Context) {
	promServer = httptest.NewServer(promhttp.Handler())

	// Initialize all clients
	fmt.Printf("Create %d client(s) for benchmark\n", *clientNumber)
	pdClients := make([]pd.Client, *clientNumber)
	for idx := range pdClients {
		pdCli, err := pd.NewClient([]string{*pdAddrs}, pd.SecurityOption{
			CAPath:   *caPath,
			CertPath: *certPath,
			KeyPath:  *keyPath,
		})
		if err != nil {
			log.Fatal(fmt.Sprintf("create pd client #%d failed: %v", idx, err))
		}
		pdClients[idx] = pdCli
	}

	ctx, cancel := context.WithCancel(mainCtx)
	// To avoid the first time high latency.
	for idx, pdCli := range pdClients {
		_, _, err := pdCli.GetLocalTS(ctx, *dcLocation)
		if err != nil {
			log.Fatal("get first time tso failed", zap.Int("client-number", idx), zap.Error(err))
		}
	}

	durCh := make(chan time.Duration, 2*(*concurrency)*(*clientNumber))

	wg.Add((*concurrency) * (*clientNumber))
	for _, pdCli := range pdClients {
		for i := 0; i < *concurrency; i++ {
			go reqWorker(ctx, pdCli, durCh)
		}
	}

	wg.Add(1)
	go showStats(ctx, durCh)

	timer := time.NewTimer(*duration)
	defer timer.Stop()

	select {
	case <-ctx.Done():
	case <-timer.C:
	}
	cancel()

	wg.Wait()

	for _, pdCli := range pdClients {
		pdCli.Close()
	}
}

func showStats(ctx context.Context, durCh chan time.Duration) {
	defer wg.Done()

	statCtx, cancel := context.WithCancel(ctx)
	defer cancel()

	ticker := time.NewTicker(*interval)

	s := newStats()
	total := newStats()

	for {
		select {
		case <-ticker.C:
			// runtime.GC()
			if *verbose {
				fmt.Println(s.Counter())
			}
			total.merge(s)
			s = newStats()
		case d := <-durCh:
			s.update(d)
		case <-statCtx.Done():
			fmt.Println("\nTotal:")
			fmt.Println(total.Counter())
			fmt.Println(total.Percentage())
			if *verbose {
				fmt.Println(collectMetrics(promServer))
			}
			return
		}
	}
}

const (
	twoDur          = time.Millisecond * 2
	fiveDur         = time.Millisecond * 5
	tenDur          = time.Millisecond * 10
	thirtyDur       = time.Millisecond * 30
	fiftyDur        = time.Millisecond * 50
	oneHundredDur   = time.Millisecond * 100
	twoHundredDur   = time.Millisecond * 200
	fourHundredDur  = time.Millisecond * 400
	eightHundredDur = time.Millisecond * 800
	oneThousandDur  = time.Millisecond * 1000
)

type stats struct {
	maxDur          time.Duration
	minDur          time.Duration
	totalDur        time.Duration
	count           int
	submilliCnt     int
	milliCnt        int
	twoMilliCnt     int
	fiveMilliCnt    int
	tenMSCnt        int
	thirtyCnt       int
	fiftyCnt        int
	oneHundredCnt   int
	twoHundredCnt   int
	fourHundredCnt  int
	eightHundredCnt int
	oneThousandCnt  int
}

func newStats() *stats {
	return &stats{
		minDur: time.Hour,
		maxDur: 0,
	}
}

func (s *stats) update(dur time.Duration) {
	s.count++
	s.totalDur += dur

	if dur > s.maxDur {
		s.maxDur = dur
	}
	if dur < s.minDur {
		s.minDur = dur
	}

	if dur > oneThousandDur {
		s.oneThousandCnt++
		return
	}

	if dur > eightHundredDur {
		s.eightHundredCnt++
		return
	}

	if dur > fourHundredDur {
		s.fourHundredCnt++
		return
	}

	if dur > twoHundredDur {
		s.twoHundredCnt++
		return
	}

	if dur > oneHundredDur {
		s.oneHundredCnt++
		return
	}

	if dur > fiftyDur {
		s.fiftyCnt++
		return
	}

	if dur > thirtyDur {
		s.thirtyCnt++
		return
	}

	if dur > tenDur {
		s.tenMSCnt++
		return
	}

	if dur > fiveDur {
		s.fiveMilliCnt++
		return
	}

	if dur > twoDur {
		s.twoMilliCnt++
		return
	}

	if dur > time.Millisecond {
		s.milliCnt++
		return
	}

	s.submilliCnt++
}

func (s *stats) merge(other *stats) {
	if s.maxDur < other.maxDur {
		s.maxDur = other.maxDur
	}
	if s.minDur > other.minDur {
		s.minDur = other.minDur
	}

	s.count += other.count
	s.totalDur += other.totalDur
	s.submilliCnt += other.submilliCnt
	s.milliCnt += other.milliCnt
	s.twoMilliCnt += other.twoMilliCnt
	s.fiveMilliCnt += other.fiveMilliCnt
	s.tenMSCnt += other.tenMSCnt
	s.thirtyCnt += other.thirtyCnt
	s.fiftyCnt += other.fiftyCnt
	s.oneHundredCnt += other.oneHundredCnt
	s.twoHundredCnt += other.twoHundredCnt
	s.fourHundredCnt += other.fourHundredCnt
	s.eightHundredCnt += other.eightHundredCnt
	s.oneThousandCnt += other.oneThousandCnt
}

func (s *stats) Counter() string {
	return fmt.Sprintf(
		"count: %d, max: %.4fms, min: %.4fms, avg: %.4fms\n<1ms: %d, >1ms: %d, >2ms: %d, >5ms: %d, >10ms: %d, >30ms: %d, >50ms: %d, >100ms: %d, >200ms: %d, >400ms: %d, >800ms: %d, >1s: %d",
		s.count, float64(s.maxDur.Nanoseconds())/float64(time.Millisecond), float64(s.minDur.Nanoseconds())/float64(time.Millisecond), float64(s.totalDur.Nanoseconds())/float64(s.count)/float64(time.Millisecond),
		s.submilliCnt, s.milliCnt, s.twoMilliCnt, s.fiveMilliCnt, s.tenMSCnt, s.thirtyCnt, s.fiftyCnt, s.oneHundredCnt, s.twoHundredCnt, s.fourHundredCnt,
		s.eightHundredCnt, s.oneThousandCnt)
}

func (s *stats) Percentage() string {
	return fmt.Sprintf(
		"count: %d, <1ms: %2.2f%%, >1ms: %2.2f%%, >2ms: %2.2f%%, >5ms: %2.2f%%, >10ms: %2.2f%%, >30ms: %2.2f%%, >50ms: %2.2f%%, >100ms: %2.2f%%, >200ms: %2.2f%%, >400ms: %2.2f%%, >800ms: %2.2f%%, >1s: %2.2f%%", s.count,
		s.calculate(s.submilliCnt), s.calculate(s.milliCnt), s.calculate(s.twoMilliCnt), s.calculate(s.fiveMilliCnt), s.calculate(s.tenMSCnt), s.calculate(s.thirtyCnt), s.calculate(s.fiftyCnt),
		s.calculate(s.oneHundredCnt), s.calculate(s.twoHundredCnt), s.calculate(s.fourHundredCnt), s.calculate(s.eightHundredCnt), s.calculate(s.oneThousandCnt))
}

func (s *stats) calculate(count int) float64 {
	return float64(count) * 100 / float64(s.count)
}

func reqWorker(ctx context.Context, pdCli pd.Client, durCh chan time.Duration) {
	defer wg.Done()

	reqCtx, cancel := context.WithCancel(ctx)
	defer cancel()

	for {
		start := time.Now()
		_, _, err := pdCli.GetLocalTS(reqCtx, *dcLocation)
		if errors.Cause(err) == context.Canceled {
			return
		}

		if err != nil {
			log.Fatal(fmt.Sprintf("%v", err))
		}
		dur := time.Since(start)

		select {
		case <-reqCtx.Done():
			return
		case durCh <- dur:
		}
	}
}
<commit_msg>tools: fix cannot kill bench process when using the wrong address (#4065)<commit_after>// Copyright 2017 TiKV Project 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 main

import (
	"context"
	"flag"
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"os"
	"os/signal"
	"sync"
	"syscall"
	"time"

	"github.com/pingcap/errors"
	"github.com/pingcap/log"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	pd "github.com/tikv/pd/client"
	"go.uber.org/zap"
)

var (
	pdAddrs      = flag.String("pd", "127.0.0.1:2379", "pd address")
	clientNumber = flag.Int("client", 1, "the number of pd clients involved in each benchmark")
	concurrency  = flag.Int("c", 1000, "concurrency")
	count        = flag.Int("count", 1, "the count number that the test will run")
	duration     = flag.Duration("duration", 60*time.Second, "how many seconds the test will last")
	dcLocation   = flag.String("dc", "global", "which dc-location this bench will request")
	verbose      = flag.Bool("v", false, "output statistics info every interval and output metrics info at the end")
	interval     = flag.Duration("interval", time.Second, "interval to output the statistics")
	caPath       = flag.String("cacert", "", "path of file that contains list of trusted SSL CAs")
	certPath     = flag.String("cert", "", "path of file that contains X509 certificate in PEM format")
	keyPath      = flag.String("key", "", "path of file that contains X509 key in PEM format")
	wg           sync.WaitGroup
)

var promServer *httptest.Server

func collectMetrics(server *httptest.Server) string {
	time.Sleep(1100 * time.Millisecond)
	res, _ := http.Get(server.URL)
	body, _ := io.ReadAll(res.Body)
	res.Body.Close()
	return string(body)
}

func main() {
	flag.Parse()
	ctx, cancel := context.WithCancel(context.Background())

	sc := make(chan os.Signal, 1)
	signal.Notify(sc,
		syscall.SIGHUP,
		syscall.SIGINT,
		syscall.SIGTERM,
		syscall.SIGQUIT)
	go func() {
		<-sc
		cancel()
	}()

	for i := 0; i < *count; i++ {
		fmt.Printf("\nStart benchmark #%d, duration: %+vs\n", i, (*duration).Seconds())
		bench(ctx)
	}
}

func bench(mainCtx context.Context) {
	promServer = httptest.NewServer(promhttp.Handler())

	// Initialize all clients
	fmt.Printf("Create %d client(s) for benchmark\n", *clientNumber)
	pdClients := make([]pd.Client, *clientNumber)
	for idx := range pdClients {
		pdCli, err := pd.NewClientWithContext(mainCtx, []string{*pdAddrs}, pd.SecurityOption{
			CAPath:   *caPath,
			CertPath: *certPath,
			KeyPath:  *keyPath,
		})
		if err != nil {
			log.Fatal(fmt.Sprintf("create pd client #%d failed: %v", idx, err))
		}
		pdClients[idx] = pdCli
	}

	ctx, cancel := context.WithCancel(mainCtx)
	// To avoid the first time high latency.
	for idx, pdCli := range pdClients {
		_, _, err := pdCli.GetLocalTS(ctx, *dcLocation)
		if err != nil {
			log.Fatal("get first time tso failed", zap.Int("client-number", idx), zap.Error(err))
		}
	}

	durCh := make(chan time.Duration, 2*(*concurrency)*(*clientNumber))

	wg.Add((*concurrency) * (*clientNumber))
	for _, pdCli := range pdClients {
		for i := 0; i < *concurrency; i++ {
			go reqWorker(ctx, pdCli, durCh)
		}
	}

	wg.Add(1)
	go showStats(ctx, durCh)

	timer := time.NewTimer(*duration)
	defer timer.Stop()

	select {
	case <-ctx.Done():
	case <-timer.C:
	}
	cancel()

	wg.Wait()

	for _, pdCli := range pdClients {
		pdCli.Close()
	}
}

func showStats(ctx context.Context, durCh chan time.Duration) {
	defer wg.Done()

	statCtx, cancel := context.WithCancel(ctx)
	defer cancel()

	ticker := time.NewTicker(*interval)

	s := newStats()
	total := newStats()

	for {
		select {
		case <-ticker.C:
			// runtime.GC()
			if *verbose {
				fmt.Println(s.Counter())
			}
			total.merge(s)
			s = newStats()
		case d := <-durCh:
			s.update(d)
		case <-statCtx.Done():
			fmt.Println("\nTotal:")
			fmt.Println(total.Counter())
			fmt.Println(total.Percentage())
			if *verbose {
				fmt.Println(collectMetrics(promServer))
			}
			return
		}
	}
}

const (
	twoDur          = time.Millisecond * 2
	fiveDur         = time.Millisecond * 5
	tenDur          = time.Millisecond * 10
	thirtyDur       = time.Millisecond * 30
	fiftyDur        = time.Millisecond * 50
	oneHundredDur   = time.Millisecond * 100
	twoHundredDur   = time.Millisecond * 200
	fourHundredDur  = time.Millisecond * 400
	eightHundredDur = time.Millisecond * 800
	oneThousandDur  = time.Millisecond * 1000
)

type stats struct {
	maxDur          time.Duration
	minDur          time.Duration
	totalDur        time.Duration
	count           int
	submilliCnt     int
	milliCnt        int
	twoMilliCnt     int
	fiveMilliCnt    int
	tenMSCnt        int
	thirtyCnt       int
	fiftyCnt        int
	oneHundredCnt   int
	twoHundredCnt   int
	fourHundredCnt  int
	eightHundredCnt int
	oneThousandCnt  int
}

func newStats() *stats {
	return &stats{
		minDur: time.Hour,
		maxDur: 0,
	}
}

func (s *stats) update(dur time.Duration) {
	s.count++
	s.totalDur += dur

	if dur > s.maxDur {
		s.maxDur = dur
	}
	if dur < s.minDur {
		s.minDur = dur
	}

	if dur > oneThousandDur {
		s.oneThousandCnt++
		return
	}

	if dur > eightHundredDur {
		s.eightHundredCnt++
		return
	}

	if dur > fourHundredDur {
		s.fourHundredCnt++
		return
	}

	if dur > twoHundredDur {
		s.twoHundredCnt++
		return
	}

	if dur > oneHundredDur {
		s.oneHundredCnt++
		return
	}

	if dur > fiftyDur {
		s.fiftyCnt++
		return
	}

	if dur > thirtyDur {
		s.thirtyCnt++
		return
	}

	if dur > tenDur {
		s.tenMSCnt++
		return
	}

	if dur > fiveDur {
		s.fiveMilliCnt++
		return
	}

	if dur > twoDur {
		s.twoMilliCnt++
		return
	}

	if dur > time.Millisecond {
		s.milliCnt++
		return
	}

	s.submilliCnt++
}

func (s *stats) merge(other *stats) {
	if s.maxDur < other.maxDur {
		s.maxDur = other.maxDur
	}
	if s.minDur > other.minDur {
		s.minDur = other.minDur
	}

	s.count += other.count
	s.totalDur += other.totalDur
	s.submilliCnt += other.submilliCnt
	s.milliCnt += other.milliCnt
	s.twoMilliCnt += other.twoMilliCnt
	s.fiveMilliCnt += other.fiveMilliCnt
	s.tenMSCnt += other.tenMSCnt
	s.thirtyCnt += other.thirtyCnt
	s.fiftyCnt += other.fiftyCnt
	s.oneHundredCnt += other.oneHundredCnt
	s.twoHundredCnt += other.twoHundredCnt
	s.fourHundredCnt += other.fourHundredCnt
	s.eightHundredCnt += other.eightHundredCnt
	s.oneThousandCnt += other.oneThousandCnt
}

func (s *stats) Counter() string {
	return fmt.Sprintf(
		"count: %d, max: %.4fms, min: %.4fms, avg: %.4fms\n<1ms: %d, >1ms: %d, >2ms: %d, >5ms: %d, >10ms: %d, >30ms: %d, >50ms: %d, >100ms: %d, >200ms: %d, >400ms: %d, >800ms: %d, >1s: %d",
		s.count, float64(s.maxDur.Nanoseconds())/float64(time.Millisecond), float64(s.minDur.Nanoseconds())/float64(time.Millisecond), float64(s.totalDur.Nanoseconds())/float64(s.count)/float64(time.Millisecond),
		s.submilliCnt, s.milliCnt, s.twoMilliCnt, s.fiveMilliCnt, s.tenMSCnt, s.thirtyCnt, s.fiftyCnt, s.oneHundredCnt, s.twoHundredCnt, s.fourHundredCnt,
		s.eightHundredCnt, s.oneThousandCnt)
}

func (s *stats) Percentage() string {
	return fmt.Sprintf(
		"count: %d, <1ms: %2.2f%%, >1ms: %2.2f%%, >2ms: %2.2f%%, >5ms: %2.2f%%, >10ms: %2.2f%%, >30ms: %2.2f%%, >50ms: %2.2f%%, >100ms: %2.2f%%, >200ms: %2.2f%%, >400ms: %2.2f%%, >800ms: %2.2f%%, >1s: %2.2f%%", s.count,
		s.calculate(s.submilliCnt), s.calculate(s.milliCnt), s.calculate(s.twoMilliCnt), s.calculate(s.fiveMilliCnt), s.calculate(s.tenMSCnt), s.calculate(s.thirtyCnt), s.calculate(s.fiftyCnt),
		s.calculate(s.oneHundredCnt), s.calculate(s.twoHundredCnt), s.calculate(s.fourHundredCnt), s.calculate(s.eightHundredCnt), s.calculate(s.oneThousandCnt))
}

func (s *stats) calculate(count int) float64 {
	return float64(count) * 100 / float64(s.count)
}

func reqWorker(ctx context.Context, pdCli pd.Client, durCh chan time.Duration) {
	defer wg.Done()

	reqCtx, cancel := context.WithCancel(ctx)
	defer cancel()

	for {
		start := time.Now()
		_, _, err := pdCli.GetLocalTS(reqCtx, *dcLocation)
		if errors.Cause(err) == context.Canceled {
			return
		}

		if err != nil {
			log.Fatal(fmt.Sprintf("%v", err))
		}
		dur := time.Since(start)

		select {
		case <-reqCtx.Done():
			return
		case durCh <- dur:
		}
	}
}
<|endoftext|>


<commit_before>package container

import (
	"bytes"
	"encoding/json"
	"errors"

	"h12.me/gspec/util"
)

func (c *Container) ip() (string, error) {
	type networkSettings struct {
		IPAddress string
	}
	type container struct {
		NetworkSettings networkSettings
	}
	var cs []container
	out := util.Command("docker", "inspect", c.ID).Output()
	if err := json.NewDecoder(bytes.NewReader(out)).Decode(&cs); err != nil {
		return "", err
	}
	if len(cs) == 0 {
		return "", errors.New("no output from docker inspect")
	}
	if ip := cs[0].NetworkSettings.IPAddress; ip != "" {
		return ip, nil
	}
	return "", errors.New("could not find an IP. Not running?")
}

func initDocker() error {
	if !util.CmdExists("docker") {
		return errors.New("docker not installed")
	}
	return nil
}
<commit_msg>use local address<commit_after>package container

import (
	"bytes"
	"encoding/json"
	"errors"

	"h12.me/gspec/util"
)

func (c *Container) ip() (string, error) {
	return "127.0.0.1", nil
	/*
		type networkSettings struct {
			IPAddress string
		}
		type container struct {
			NetworkSettings networkSettings
		}
		var cs []container
		out := util.Command("docker", "inspect", c.ID).Output()
		if err := json.NewDecoder(bytes.NewReader(out)).Decode(&cs); err != nil {
			return "", err
		}
		if len(cs) == 0 {
			return "", errors.New("no output from docker inspect")
		}
		if ip := cs[0].NetworkSettings.IPAddress; ip != "" {
			return ip, nil
		}
		return "", errors.New("could not find an IP. Not running?")
	*/
}

func initDocker() error {
	if !util.CmdExists("docker") {
		return errors.New("docker not installed")
	}
	return nil
}
<|endoftext|>


<commit_before>// TrackBuffer accumulates ADSB messages, grouped by aircraft, and flushes out
// bundles of them.
package trackbuffer

import (
	"sort"
	"time"
	"github.com/skypies/adsb"
)

// A slice of ADSB messages that share the same IcaoId
type Track struct {
	Messages  []*adsb.CompositeMsg
}

type TrackBuffer struct {
	MaxAge      time.Duration // Flush any track with data older than this
	Tracks      map[adsb.IcaoId]*Track
	lastFlush   time.Time
}

func NewTrackBuffer() *TrackBuffer {
	tb := TrackBuffer{
		MaxAge: time.Second*30,
		Tracks: make(map[adsb.IcaoId]*Track),
		lastFlush: time.Now(),
	}
	return &tb
}

func (t *Track)Age() time.Duration {
	if len(t.Messages)==0 { return time.Duration(time.Hour * 24) }
	return time.Since(t.Messages[0].GeneratedTimestampUTC)
}

func (tb *TrackBuffer)AddTrack(icao adsb.IcaoId) {
	track := Track{
		Messages: []*adsb.CompositeMsg{},
	}
	tb.Tracks[icao] = &track
}

func (tb *TrackBuffer)RemoveTracks(icaos []adsb.IcaoId) []*Track{
	removed := []*Track{}
	for _,icao := range icaos {
		removed = append(removed, tb.Tracks[icao])
		delete(tb.Tracks, icao)
	}
	return removed
}

func (tb *TrackBuffer)AddMessage(m *adsb.CompositeMsg) {
	if _,exists := tb.Tracks[m.Icao24]; exists == false {
		tb.AddTrack(m.Icao24)
	}
	track := tb.Tracks[m.Icao24]
	track.Messages = append(track.Messages, m)
}

// Flushing should be automatic and internal, not explicit like this.
func (tb *TrackBuffer)Flush(flushChan chan<- []*adsb.CompositeMsg) {
	// When we get late or out-of-order delivery, the timestamps in the messages will be so
	// old that they will trigger immediate flushing every time. This causes so many DB writes
	// that the system can't keep up, so we never get back to useful buffering. Put a mild rate
	// limiter in here.
	if time.Since(tb.lastFlush) < time.Second {
		return
	} else {
		tb.lastFlush = time.Now()
	}

	toRemove := []adsb.IcaoId{}

	for id,_ := range tb.Tracks {
		if tb.Tracks[id].Age() > tb.MaxAge {
			toRemove = append(toRemove, id)
		}
	}

	for _,t := range tb.RemoveTracks(toRemove) {
		sort.Sort(adsb.CompositeMsgPtrByTimeAsc(t.Messages))
		flushChan <- t.Messages
	}
}
<commit_msg>Finer grained metrics<commit_after>// TrackBuffer accumulates ADSB messages, grouped by aircraft, and flushes out
// bundles of them.
package trackbuffer

import (
	"sort"
	"time"
	"github.com/skypies/adsb"
)

// A slice of ADSB messages that share the same IcaoId
type Track struct {
	Messages  []*adsb.CompositeMsg
}

type TrackBuffer struct {
	MaxAge      time.Duration // Flush any track with data older than this
	Tracks      map[adsb.IcaoId]*Track
	lastFlush   time.Time
}

func NewTrackBuffer() *TrackBuffer {
	tb := TrackBuffer{
		MaxAge: time.Second*30,
		Tracks: make(map[adsb.IcaoId]*Track),
		lastFlush: time.Now(),
	}
	return &tb
}

func (t *Track)Age() time.Duration {
	if len(t.Messages)==0 { return time.Duration(time.Hour * 24) }
	return time.Since(t.Messages[0].GeneratedTimestampUTC)
}

func (tb *TrackBuffer)AddTrack(icao adsb.IcaoId) {
	track := Track{
		Messages: []*adsb.CompositeMsg{},
	}
	tb.Tracks[icao] = &track
}

func (tb *TrackBuffer)RemoveTracks(icaos []adsb.IcaoId) []*Track{
	removed := []*Track{}
	for _,icao := range icaos {
		removed = append(removed, tb.Tracks[icao])
		delete(tb.Tracks, icao)
	}
	return removed
}

func (tb *TrackBuffer)Size() int64 {
	i := 0
	for _,t := range tb.Tracks {
		i += len(t.Messages)
	}
	return int64(i)
}

func (tb *TrackBuffer)AddMessage(m *adsb.CompositeMsg) {
	if _,exists := tb.Tracks[m.Icao24]; exists == false {
		tb.AddTrack(m.Icao24)
	}
	track := tb.Tracks[m.Icao24]
	track.Messages = append(track.Messages, m)
}

// Flushing should be automatic and internal, not explicit like this.
func (tb *TrackBuffer)Flush(flushChan chan<- []*adsb.CompositeMsg) {
	// When we get late or out-of-order delivery, the timestamps in the messages will be so
	// old that they will trigger immediate flushing every time. This causes so many DB writes
	// that the system can't keep up, so we never get back to useful buffering. Put a mild rate
	// limiter in here.
	if time.Since(tb.lastFlush) < time.Second {
		return
	} else {
		tb.lastFlush = time.Now()
	}

	toRemove := []adsb.IcaoId{}

	for id,_ := range tb.Tracks {
		if tb.Tracks[id].Age() > tb.MaxAge {
			toRemove = append(toRemove, id)
		}
	}

	for _,t := range tb.RemoveTracks(toRemove) {
		sort.Sort(adsb.CompositeMsgPtrByTimeAsc(t.Messages))
		flushChan <- t.Messages
	}
}
<|endoftext|>


<commit_before>package sdl

// #include <SDL2/SDL_events.h>
import "C"
import "unsafe"
import "reflect"
import "fmt"

const (
	FIRSTEVENT			= 0
	QUIT				= 0x100
	APP_TERMINATING			= 0x101
	APP_LOWMEMORY			= 0x102
	APP_WILLENTERBACKGROUND		= 0x103
	APP_DIDENTERBACKGROUND		= 0x104
	APP_WILLENTERFOREGROUND		= 0x105
	APP_DIDENTERFOREGROUND		= 0x106

	/* Window events */
	WINDOWEVENT			= 0x200
	SYSWMEVENT			= 0x201

	/* Keyboard events */
	KEYDOWN				= 0x300
	KEYUP				= 0x301
	TEXTEDITING			= 0x302
	TEXTINPUT			= 0x303

	/* Mouse events */
	MOUSEMOTION			= 0x400
	MOUSEBUTTONDOWN			= 0x401
	MOUSEBUTTONUP			= 0x402
	MOUSEWHEEL			= 0x403

	/* Joystick events */
	JOYAXISMOTION			= 0x600
	JOYBALLMOTION			= 0x601
	JOYHATMOTION			= 0x602
	JOYBUTTONDOWN			= 0x603
	JOYBUTTONUP			= 0x604
	JOYDEVICEADDED			= 0x605
	JOYDEVICEREMOVED		= 0x606

	/* Game controller events */
	CONTROLLERAXISMOTION		= 0x650
	CONTROLLERBUTTONDOWN		= 0x651
	CONTROLLERBUTTONUP		= 0x652
	CONTROLLERDEVICEADDED		= 0x653
	CONTROLLERDEVICEREMOVED		= 0x654
	CONTROLLERDEVICEREMAPPED	= 0x655

	/* Touch events */
	FINGERDOWN			= 0x700
	FINGERUP			= 0x701
	FINGERMOTION			= 0x702

	/* Gesture events */
	DOLLARGESTURE			= 0x800
	DOLLARRECORD			= 0x801
	MULTIGESTURE			= 0x802

	/* Clipboard events */
	CLIPBOARDUPDATE			= 0x900

	/* Drag and drop events */
	DROPFILE			= 0x1000

	USEREVENT			= 0x8000
	LASTEVENT			= 0xFFFF
)

const (
	ADDEVENT	= iota
	PEEKEVENT
	GETEVENT
)

const (
	QUERY		= -1
	IGNORE		= 0
	DISABLE		= 0
	ENABLE		= 1
)

type Event interface {}

type CEvent struct {
	Type uint32
	padding1 [52]byte
}

type Scancode uint32

type CommonEvent struct {
	Type uint32
	Timestamp uint32
}

type WindowEvent struct {
	Type uint32
	Timestamp uint32
	WindowID uint32
	Event uint8
	padding1 uint8
	padding2 uint8
	padding3 uint8
	Data1 int32
	Data2 int32
}

type KeyDownEvent struct {
	Type uint32
	Timestamp uint32
	WindowID uint32
	State uint8
	Repeat uint8
	padding1 uint8
	padding2 uint8
	Keysym Keysym
}

type KeyUpEvent struct {
	Type uint32
	Timestamp uint32
	WindowID uint32
	State uint8
	Repeat uint8
	padding1 uint8
	padding2 uint8
	Keysym Keysym
}

type TextEditingEvent struct {
	Type uint32
	Timestamp uint32
	WindowID uint32
	Text [C.SDL_TEXTINPUTEVENT_TEXT_SIZE]byte;
	Start int32
	Length int32
}

type TextInputEvent struct {
	Type uint32
	Timestamp uint32
	WindowID uint32
	Text [C.SDL_TEXTINPUTEVENT_TEXT_SIZE]byte;
}

type MouseMotionEvent struct {
	Type uint32
	Timestamp uint32
	WindowID uint32
	Which uint32
	State uint32
	X int32
	Y int32
	XRel int32
	YRel int32
}

type MouseButtonEvent struct {
	Type uint32
	Timestamp uint32
	WindowID uint32
	Which uint32
	Button uint8
	State uint8
	padding1 uint8
	padding2 uint8
	X int32
	Y int32
}

type MouseWheelEvent struct {
	Type uint32
	Timestamp uint32
	WindowID uint32
	Which uint32
	X int32
	Y int32
}

type JoyAxisEvent struct {
	Type uint32
	Timestamp uint32
	Which JoystickID
	Axis uint8
	padding1 uint8
	padding2 uint8
	padding3 uint8
	Value int16
	padding4 uint16
}

type JoyBallEvent struct {
	Type uint32
	Timestamp uint32
	Which JoystickID
	Ball uint8
	padding1 uint8
	padding2 uint8
	padding3 uint8
	XRel int16
	YRel int16
}

type JoyHatEvent struct {
	Type uint32
	Timestamp uint32
	Which JoystickID
	Hat uint8
	Value uint8
	padding1 uint8
	padding2 uint8
}

type JoyButtonEvent struct {
	Type uint32
	Timestamp uint32
	Which JoystickID
	Button uint8
	State uint8
	padding1 uint8
	padding2 uint8
}

type JoyDeviceEvent struct {
	Type uint32
	Timestamp uint32
	Which JoystickID
}

type ControllerAxisEvent struct {
	Type uint32
	Timestamp uint32
	Which JoystickID
	Axis uint8
	padding1 uint8
	padding2 uint8
	padding3 uint8
	Value int16
	padding4 uint16
}

type ControllerButtonEvent struct {
	Type uint32
	Timestamp uint32
	Which JoystickID
	Button uint8
	State uint8
	padding1 uint8
	padding2 uint8
}

type ControllerDeviceEvent struct {
	Type uint32
	Timestamp uint32
	Which JoystickID
}

type TouchFingerEvent struct {
	Type uint32
	Timestamp uint32
	TouchID TouchID
	FingerID FingerID
	X float32
	Y float32
	DX float32
	DY float32
	Pressure float32
}

type MultiGestureEvent struct {
	Type uint32
	Timestamp uint32
	TouchId TouchID
	DTheta float32
	DDist float32
	X float32
	Y float32
	NumFingers uint16
	padding uint16
}

type DollarGestureEvent struct {
	Type uint32
	Timestamp uint32
	TouchID TouchID
	GestureID GestureID
	NumFingers uint32
	Error float32
	X float32
	Y float32
}

type DropEvent struct {
	Type uint32
	Timestamp uint32
	file unsafe.Pointer
}

type QuitEvent struct {
	Type uint32
	Timestamp uint32
}

type OSEvent struct {
	Type uint32
	Timestamp uint32
}

type ClipboardEvent struct {
	Type uint32
	Timestamp uint32
}

type UserEvent struct {
	Type uint32
	Timestamp uint32
	WindowID uint32
	Code int32
	Data1 unsafe.Pointer
	Data2 unsafe.Pointer
}

type SysWMEvent struct {
	Type uint32
	Timestamp uint32
	msg unsafe.Pointer
}

type EventFilter C.SDL_EventFilter

func PumpEvents() {
	C.SDL_PumpEvents()
}

func PeepEvents(events []Event, numevents int, action, minType, maxType uint32) int {
	_events := (*C.SDL_Event) (unsafe.Pointer(cEvent(&events[0])))
	_numevents := (C.int) (numevents)
	_action := (C.SDL_eventaction) (action)
	_mintype := (C.Uint32) (minType)
	_maxtype := (C.Uint32) (maxType)
	return (int) (C.SDL_PeepEvents(_events, _numevents, _action, _mintype, _maxtype))
}

func HasEvent(type_ uint32) bool {
	_type := (C.Uint32) (type_)
	return C.SDL_HasEvent(_type) != 0
}

func HasEvents(minType, maxType uint32) bool {
	_minType := (C.Uint32) (minType)
	_maxType := (C.Uint32) (maxType)
	return C.SDL_HasEvents(_minType, _maxType) != 0
}

func FlushEvent(type_ uint32) {
	_type := (C.Uint32) (type_)
	C.SDL_FlushEvent(_type)
}

func FlushEvents(minType, maxType uint32) {
	_minType := (C.Uint32) (minType)
	_maxType := (C.Uint32) (maxType)
	C.SDL_FlushEvents(_minType, _maxType)
}

func PollEvent() Event {
	var cevent C.SDL_Event
	ret := C.SDL_PollEvent(&cevent)
	if ret == 0 {
		return nil
	}
	return goEvent((*CEvent)(unsafe.Pointer(&cevent)))
}

func goEvent(cevent *CEvent) Event {
	switch cevent.Type {
	case WINDOWEVENT:
		return (*WindowEvent) (unsafe.Pointer(cevent))
	case SYSWMEVENT:
		return (*SysWMEvent) (unsafe.Pointer(cevent))
	case KEYDOWN:
		return (*KeyDownEvent) (unsafe.Pointer(cevent))
	case KEYUP:
		return (*KeyUpEvent) (unsafe.Pointer(cevent))
	case TEXTEDITING:
		return (*TextEditingEvent) (unsafe.Pointer(cevent))
	case TEXTINPUT:
		return (*TextInputEvent) (unsafe.Pointer(cevent))
	case MOUSEMOTION:
		return (*MouseMotionEvent) (unsafe.Pointer(cevent))
	case MOUSEBUTTONDOWN, MOUSEBUTTONUP:
		return (*MouseButtonEvent) (unsafe.Pointer(cevent))
	case MOUSEWHEEL:
		return (*MouseWheelEvent) (unsafe.Pointer(cevent))
	case JOYAXISMOTION:
		return (*JoyAxisEvent) (unsafe.Pointer(cevent))
	case JOYBALLMOTION:
		return (*JoyBallEvent) (unsafe.Pointer(cevent))
	case JOYHATMOTION:
		return (*JoyHatEvent) (unsafe.Pointer(cevent))
	case JOYBUTTONDOWN, JOYBUTTONUP:
		return (*JoyButtonEvent) (unsafe.Pointer(cevent))
	case JOYDEVICEADDED, JOYDEVICEREMOVED:
		return (*JoyDeviceEvent) (unsafe.Pointer(cevent))
	case CONTROLLERAXISMOTION:
		return (*ControllerAxisEvent) (unsafe.Pointer(cevent))
	case CONTROLLERBUTTONDOWN, CONTROLLERBUTTONUP:
		return (*ControllerButtonEvent) (unsafe.Pointer(cevent))
	case CONTROLLERDEVICEADDED, CONTROLLERDEVICEREMOVED, CONTROLLERDEVICEREMAPPED:
		return (*ControllerDeviceEvent) (unsafe.Pointer(cevent))
	case FINGERDOWN, FINGERUP, FINGERMOTION:
		return (*TouchFingerEvent) (unsafe.Pointer(cevent))
	case DOLLARGESTURE, DOLLARRECORD:
		return (*DollarGestureEvent) (unsafe.Pointer(cevent))
	case MULTIGESTURE:
		return (*MultiGestureEvent) (unsafe.Pointer(cevent))
	case DROPFILE:
		return (*DropEvent) (unsafe.Pointer(cevent))
	case QUIT:
		return (*QuitEvent) (unsafe.Pointer(cevent))
	case USEREVENT:
		return (*UserEvent) (unsafe.Pointer(cevent))
	case CLIPBOARDUPDATE:
		return (*ClipboardEvent) (unsafe.Pointer(cevent))
	}

	panic(fmt.Errorf("Unknown event type: %v", cevent.Type))
}

func cEvent(event Event) *CEvent {
	evv := reflect.ValueOf(event)
	return (*CEvent) (unsafe.Pointer(evv.UnsafeAddr()))
}

func WaitEventTimeout(event *Event, timeout int) bool {
	var cevent CEvent
	_event := (*C.SDL_Event) (unsafe.Pointer(&cevent))
	_timeout := (C.int) (timeout)
	ok := (int) (C.SDL_WaitEventTimeout(_event, _timeout))
	if ok == 0 {
		return false
	}
	*event = goEvent(&cevent)
	return true
}

func WaitEvent(event *Event) bool {
	var cevent CEvent
	_event := (*C.SDL_Event) (unsafe.Pointer(&cevent))
	ok := (int) (C.SDL_WaitEvent(_event))
	if ok == 0 {
		return false
	}
	*event = goEvent(&cevent)
	return true
}

func PushEvent(event Event) int {
	_event := (*C.SDL_Event) (unsafe.Pointer(cEvent(&event)))
	return (int) (C.SDL_PushEvent(_event))
}

/* TODO: implement SDL_EventFilter functions */

func EventState(type_ uint32, state int) uint8 {
	_type := (C.Uint32) (type_)
	_state := (C.int) (state)
	return (uint8) (C.SDL_EventState(_type, _state))
}

func GetEventState(type_ uint32) uint8 {
	_type := (C.Uint32) (type_)
	return (uint8) (C.SDL_EventState(_type, QUERY))
}

func RegisterEvents(numevents int) uint32 {
	_numevents := (C.int) (numevents)
	return (uint32) (C.SDL_RegisterEvents(_numevents))
}
<commit_msg>Fixed cEvent() and PushEvent()<commit_after>package sdl

// #include <SDL2/SDL_events.h>
import "C"
import "unsafe"
import "reflect"
import "fmt"

const (
	FIRSTEVENT			= 0
	QUIT				= 0x100
	APP_TERMINATING			= 0x101
	APP_LOWMEMORY			= 0x102
	APP_WILLENTERBACKGROUND		= 0x103
	APP_DIDENTERBACKGROUND		= 0x104
	APP_WILLENTERFOREGROUND		= 0x105
	APP_DIDENTERFOREGROUND		= 0x106

	/* Window events */
	WINDOWEVENT			= 0x200
	SYSWMEVENT			= 0x201

	/* Keyboard events */
	KEYDOWN				= 0x300
	KEYUP				= 0x301
	TEXTEDITING			= 0x302
	TEXTINPUT			= 0x303

	/* Mouse events */
	MOUSEMOTION			= 0x400
	MOUSEBUTTONDOWN			= 0x401
	MOUSEBUTTONUP			= 0x402
	MOUSEWHEEL			= 0x403

	/* Joystick events */
	JOYAXISMOTION			= 0x600
	JOYBALLMOTION			= 0x601
	JOYHATMOTION			= 0x602
	JOYBUTTONDOWN			= 0x603
	JOYBUTTONUP			= 0x604
	JOYDEVICEADDED			= 0x605
	JOYDEVICEREMOVED		= 0x606

	/* Game controller events */
	CONTROLLERAXISMOTION		= 0x650
	CONTROLLERBUTTONDOWN		= 0x651
	CONTROLLERBUTTONUP		= 0x652
	CONTROLLERDEVICEADDED		= 0x653
	CONTROLLERDEVICEREMOVED		= 0x654
	CONTROLLERDEVICEREMAPPED	= 0x655

	/* Touch events */
	FINGERDOWN			= 0x700
	FINGERUP			= 0x701
	FINGERMOTION			= 0x702

	/* Gesture events */
	DOLLARGESTURE			= 0x800
	DOLLARRECORD			= 0x801
	MULTIGESTURE			= 0x802

	/* Clipboard events */
	CLIPBOARDUPDATE			= 0x900

	/* Drag and drop events */
	DROPFILE			= 0x1000

	USEREVENT			= 0x8000
	LASTEVENT			= 0xFFFF
)

const (
	ADDEVENT	= iota
	PEEKEVENT
	GETEVENT
)

const (
	QUERY		= -1
	IGNORE		= 0
	DISABLE		= 0
	ENABLE		= 1
)

type Event interface {}

type CEvent struct {
	Type uint32
	padding1 [52]byte
}

type Scancode uint32

type CommonEvent struct {
	Type uint32
	Timestamp uint32
}

type WindowEvent struct {
	Type uint32
	Timestamp uint32
	WindowID uint32
	Event uint8
	padding1 uint8
	padding2 uint8
	padding3 uint8
	Data1 int32
	Data2 int32
}

type KeyDownEvent struct {
	Type uint32
	Timestamp uint32
	WindowID uint32
	State uint8
	Repeat uint8
	padding1 uint8
	padding2 uint8
	Keysym Keysym
}

type KeyUpEvent struct {
	Type uint32
	Timestamp uint32
	WindowID uint32
	State uint8
	Repeat uint8
	padding1 uint8
	padding2 uint8
	Keysym Keysym
}

type TextEditingEvent struct {
	Type uint32
	Timestamp uint32
	WindowID uint32
	Text [C.SDL_TEXTINPUTEVENT_TEXT_SIZE]byte;
	Start int32
	Length int32
}

type TextInputEvent struct {
	Type uint32
	Timestamp uint32
	WindowID uint32
	Text [C.SDL_TEXTINPUTEVENT_TEXT_SIZE]byte;
}

type MouseMotionEvent struct {
	Type uint32
	Timestamp uint32
	WindowID uint32
	Which uint32
	State uint32
	X int32
	Y int32
	XRel int32
	YRel int32
}

type MouseButtonEvent struct {
	Type uint32
	Timestamp uint32
	WindowID uint32
	Which uint32
	Button uint8
	State uint8
	padding1 uint8
	padding2 uint8
	X int32
	Y int32
}

type MouseWheelEvent struct {
	Type uint32
	Timestamp uint32
	WindowID uint32
	Which uint32
	X int32
	Y int32
}

type JoyAxisEvent struct {
	Type uint32
	Timestamp uint32
	Which JoystickID
	Axis uint8
	padding1 uint8
	padding2 uint8
	padding3 uint8
	Value int16
	padding4 uint16
}

type JoyBallEvent struct {
	Type uint32
	Timestamp uint32
	Which JoystickID
	Ball uint8
	padding1 uint8
	padding2 uint8
	padding3 uint8
	XRel int16
	YRel int16
}

type JoyHatEvent struct {
	Type uint32
	Timestamp uint32
	Which JoystickID
	Hat uint8
	Value uint8
	padding1 uint8
	padding2 uint8
}

type JoyButtonEvent struct {
	Type uint32
	Timestamp uint32
	Which JoystickID
	Button uint8
	State uint8
	padding1 uint8
	padding2 uint8
}

type JoyDeviceEvent struct {
	Type uint32
	Timestamp uint32
	Which JoystickID
}

type ControllerAxisEvent struct {
	Type uint32
	Timestamp uint32
	Which JoystickID
	Axis uint8
	padding1 uint8
	padding2 uint8
	padding3 uint8
	Value int16
	padding4 uint16
}

type ControllerButtonEvent struct {
	Type uint32
	Timestamp uint32
	Which JoystickID
	Button uint8
	State uint8
	padding1 uint8
	padding2 uint8
}

type ControllerDeviceEvent struct {
	Type uint32
	Timestamp uint32
	Which JoystickID
}

type TouchFingerEvent struct {
	Type uint32
	Timestamp uint32
	TouchID TouchID
	FingerID FingerID
	X float32
	Y float32
	DX float32
	DY float32
	Pressure float32
}

type MultiGestureEvent struct {
	Type uint32
	Timestamp uint32
	TouchId TouchID
	DTheta float32
	DDist float32
	X float32
	Y float32
	NumFingers uint16
	padding uint16
}

type DollarGestureEvent struct {
	Type uint32
	Timestamp uint32
	TouchID TouchID
	GestureID GestureID
	NumFingers uint32
	Error float32
	X float32
	Y float32
}

type DropEvent struct {
	Type uint32
	Timestamp uint32
	file unsafe.Pointer
}

type QuitEvent struct {
	Type uint32
	Timestamp uint32
}

type OSEvent struct {
	Type uint32
	Timestamp uint32
}

type ClipboardEvent struct {
	Type uint32
	Timestamp uint32
}

type UserEvent struct {
	Type uint32
	Timestamp uint32
	WindowID uint32
	Code int32
	Data1 unsafe.Pointer
	Data2 unsafe.Pointer
}

type SysWMEvent struct {
	Type uint32
	Timestamp uint32
	msg unsafe.Pointer
}

type EventFilter C.SDL_EventFilter

func PumpEvents() {
	C.SDL_PumpEvents()
}

func PeepEvents(events []Event, numevents int, action, minType, maxType uint32) int {
	_events := (*C.SDL_Event) (unsafe.Pointer(cEvent(&events[0])))
	_numevents := (C.int) (numevents)
	_action := (C.SDL_eventaction) (action)
	_mintype := (C.Uint32) (minType)
	_maxtype := (C.Uint32) (maxType)
	return (int) (C.SDL_PeepEvents(_events, _numevents, _action, _mintype, _maxtype))
}

func HasEvent(type_ uint32) bool {
	_type := (C.Uint32) (type_)
	return C.SDL_HasEvent(_type) != 0
}

func HasEvents(minType, maxType uint32) bool {
	_minType := (C.Uint32) (minType)
	_maxType := (C.Uint32) (maxType)
	return C.SDL_HasEvents(_minType, _maxType) != 0
}

func FlushEvent(type_ uint32) {
	_type := (C.Uint32) (type_)
	C.SDL_FlushEvent(_type)
}

func FlushEvents(minType, maxType uint32) {
	_minType := (C.Uint32) (minType)
	_maxType := (C.Uint32) (maxType)
	C.SDL_FlushEvents(_minType, _maxType)
}

func PollEvent() Event {
	var cevent C.SDL_Event
	ret := C.SDL_PollEvent(&cevent)
	if ret == 0 {
		return nil
	}
	return goEvent((*CEvent)(unsafe.Pointer(&cevent)))
}

func goEvent(cevent *CEvent) Event {
	switch cevent.Type {
	case WINDOWEVENT:
		return (*WindowEvent) (unsafe.Pointer(cevent))
	case SYSWMEVENT:
		return (*SysWMEvent) (unsafe.Pointer(cevent))
	case KEYDOWN:
		return (*KeyDownEvent) (unsafe.Pointer(cevent))
	case KEYUP:
		return (*KeyUpEvent) (unsafe.Pointer(cevent))
	case TEXTEDITING:
		return (*TextEditingEvent) (unsafe.Pointer(cevent))
	case TEXTINPUT:
		return (*TextInputEvent) (unsafe.Pointer(cevent))
	case MOUSEMOTION:
		return (*MouseMotionEvent) (unsafe.Pointer(cevent))
	case MOUSEBUTTONDOWN, MOUSEBUTTONUP:
		return (*MouseButtonEvent) (unsafe.Pointer(cevent))
	case MOUSEWHEEL:
		return (*MouseWheelEvent) (unsafe.Pointer(cevent))
	case JOYAXISMOTION:
		return (*JoyAxisEvent) (unsafe.Pointer(cevent))
	case JOYBALLMOTION:
		return (*JoyBallEvent) (unsafe.Pointer(cevent))
	case JOYHATMOTION:
		return (*JoyHatEvent) (unsafe.Pointer(cevent))
	case JOYBUTTONDOWN, JOYBUTTONUP:
		return (*JoyButtonEvent) (unsafe.Pointer(cevent))
	case JOYDEVICEADDED, JOYDEVICEREMOVED:
		return (*JoyDeviceEvent) (unsafe.Pointer(cevent))
	case CONTROLLERAXISMOTION:
		return (*ControllerAxisEvent) (unsafe.Pointer(cevent))
	case CONTROLLERBUTTONDOWN, CONTROLLERBUTTONUP:
		return (*ControllerButtonEvent) (unsafe.Pointer(cevent))
	case CONTROLLERDEVICEADDED, CONTROLLERDEVICEREMOVED, CONTROLLERDEVICEREMAPPED:
		return (*ControllerDeviceEvent) (unsafe.Pointer(cevent))
	case FINGERDOWN, FINGERUP, FINGERMOTION:
		return (*TouchFingerEvent) (unsafe.Pointer(cevent))
	case DOLLARGESTURE, DOLLARRECORD:
		return (*DollarGestureEvent) (unsafe.Pointer(cevent))
	case MULTIGESTURE:
		return (*MultiGestureEvent) (unsafe.Pointer(cevent))
	case DROPFILE:
		return (*DropEvent) (unsafe.Pointer(cevent))
	case QUIT:
		return (*QuitEvent) (unsafe.Pointer(cevent))
	case USEREVENT:
		return (*UserEvent) (unsafe.Pointer(cevent))
	case CLIPBOARDUPDATE:
		return (*ClipboardEvent) (unsafe.Pointer(cevent))
	}

	panic(fmt.Errorf("Unknown event type: %v", cevent.Type))
}

func cEvent(event Event) *CEvent {
	evv := reflect.ValueOf(event)
	p := evv.Elem()
	return (*CEvent) (unsafe.Pointer(p.UnsafeAddr()))
}

func WaitEventTimeout(event *Event, timeout int) bool {
	var cevent CEvent
	_event := (*C.SDL_Event) (unsafe.Pointer(&cevent))
	_timeout := (C.int) (timeout)
	ok := (int) (C.SDL_WaitEventTimeout(_event, _timeout))
	if ok == 0 {
		return false
	}
	*event = goEvent(&cevent)
	return true
}

func WaitEvent(event *Event) bool {
	var cevent CEvent
	_event := (*C.SDL_Event) (unsafe.Pointer(&cevent))
	ok := (int) (C.SDL_WaitEvent(_event))
	if ok == 0 {
		return false
	}
	*event = goEvent(&cevent)
	return true
}

func PushEvent(event Event) int {
	_event := (*C.SDL_Event) (unsafe.Pointer(cEvent(event)))
	return (int) (C.SDL_PushEvent(_event))
}

/* TODO: implement SDL_EventFilter functions */

func EventState(type_ uint32, state int) uint8 {
	_type := (C.Uint32) (type_)
	_state := (C.int) (state)
	return (uint8) (C.SDL_EventState(_type, _state))
}

func GetEventState(type_ uint32) uint8 {
	_type := (C.Uint32) (type_)
	return (uint8) (C.SDL_EventState(_type, QUERY))
}

func RegisterEvents(numevents int) uint32 {
	_numevents := (C.int) (numevents)
	return (uint32) (C.SDL_RegisterEvents(_numevents))
}
<|endoftext|>


<commit_before>package domainsocket

import (
	"context"
	"net"
	"os"
	"syscall"
)

type Listener struct {
	ln           net.Listener
	listenerChan <-chan net.Conn
	ctx          context.Context
	path         string
	lockfile     os.File
}

func ListenDS(ctx context.Context, path string) (*Listener, error) {

	vln := &Listener{path: path}
	return vln, nil
}

func (ls *Listener) Down() error {
	err := ls.ln.Close()
	if err != nil {
		newError(err).AtDebug().WriteToLog()
	}
	return err
}

//Setup systen level Listener
func (ls *Listener) LowerUP() error {

	if isUnixDomainSocketFileSystemBased(ls.path) && !___DEBUG_IGNORE_FLOCK {

	}

	addr := new(net.UnixAddr)
	addr.Name = ls.path
	addr.Net = "unix"
	li, err := net.ListenUnix("unix", addr)
	if err != nil {
		return err
	}

}

func isUnixDomainSocketFileSystemBased(path string) bool {
	//No Branching
	return path[0] != 0
}

func AcquireLock(lockfilepath string) (*os.File, error) {
	f, err := os.Create(lockfilepath)
	if err != nil {
		newError(err).AtDebug().WriteToLog()
		return f, err
	}
	err = syscall.Flock(int(f.Fd()), syscall.LOCK_EX)
	if err != nil {
		newError(err).AtDebug().WriteToLog()
		err = f.Close()
		if err != nil {
			if ___DEBUG_PANIC_WHEN_ENCOUNTED_IMPOSSIBLE_ERROR {
				panic(err)
			}
			newError(err).AtDebug().WriteToLog()
		}
		return nil, err
	}
}

//DEBUG CONSTS
const ___DEBUG_IGNORE_FLOCK = false
const ___DEBUG_PANIC_WHEN_ERROR_UNPROPAGATEABLE = false
const ___DEBUG_PANIC_WHEN_ENCOUNTED_IMPOSSIBLE_ERROR = false
<commit_msg>finish transport listener<commit_after>package domainsocket

import (
	"context"
	"net"
	"os"
	"syscall"
	"time"

	"v2ray.com/core/common/bitmask"
)

type Listener struct {
	ln           net.Listener
	listenerChan chan<- net.Conn
	ctx          context.Context
	path         string
	lockfile     *os.File
	state        bitmask.Byte
	cancal       func()
}

const (
	STATE_UNDEFINED   = 0
	STATE_INITIALIZED = 1 << iota
	STATE_LOWERUP     = 1 << iota
	STATE_UP          = 1 << iota
	STATE_TAINT       = 1 << iota
)

func ListenDS(ctx context.Context, path string) (*Listener, error) {

	vln := &Listener{path: path, state: STATE_INITIALIZED, ctx: ctx}
	return vln, nil
}

func (ls *Listener) Down() error {
	var err error
	if !ls.state.Has(STATE_LOWERUP | STATE_UP) {
		err = newError(ls.state).Base(newError("Invalid State:Down"))
		if ___DEBUG_PANIC_WHEN_ENCOUNTED_IMPOSSIBLE_ERROR {
			panic(err)
		}
		return err
	}

	ls.cancal()
	closeerr := ls.ln.Close()
	var lockerr error
	if isUnixDomainSocketFileSystemBased(ls.path) {
		lockerr = giveupLock(ls.lockfile)
	}
	if closeerr != nil && lockerr != nil {
		if ___DEBUG_PANIC_WHEN_ERROR_UNPROPAGATEABLE {
			panic(closeerr.Error() + lockerr.Error())
		}
	}

	if closeerr != nil {
		return newError("Cannot Close Unix domain socket listener").Base(closeerr)
	}
	if lockerr != nil {
		return newError("Cannot release lock for Unix domain socket listener").Base(lockerr)
	}
	ls.state.Clear(STATE_LOWERUP | STATE_UP)
	return nil
}

//LowerUP Setup systen level Listener
func (ls *Listener) LowerUP() error {
	var err error

	if !ls.state.Has(STATE_INITIALIZED) || ls.state.Has(STATE_LOWERUP) {
		err = newError(ls.state).Base(newError("Invalid State:LowerUP"))
		if ___DEBUG_PANIC_WHEN_ENCOUNTED_IMPOSSIBLE_ERROR {
			panic(err)
		}
		return err
	}

	if isUnixDomainSocketFileSystemBased(ls.path) && !___DEBUG_IGNORE_FLOCK {
		ls.lockfile, err = acquireLock(ls.path + ".lock")
		if err != nil {
			newError(err).AtDebug().WriteToLog()
			return newError("Unable to acquire lock for filesystem based unix domain socket").Base(err)
		}
	}

	err = cleansePath(ls.path)
	if err != nil {
		return newError("Unable to cleanse path for the creation of unix domain socket").Base(err)
	}

	addr := new(net.UnixAddr)
	addr.Name = ls.path
	addr.Net = "unix"
	li, err := net.ListenUnix("unix", addr)
	ls.ln = li
	if err != nil {
		return newError("Unable to listen unix domain socket").Base(err)
	}

	ls.state.Set(STATE_LOWERUP)

	return nil
}

func (ls *Listener) UP(listener chan<- net.Conn, allowkick bool) error {
	var err error
	if !ls.state.Has(STATE_INITIALIZED|STATE_LOWERUP) || (ls.state.Has(STATE_UP) && !allowkick) {
		err = newError(ls.state).Base(newError("Invalid State:UP"))
		if ___DEBUG_PANIC_WHEN_ENCOUNTED_IMPOSSIBLE_ERROR {
			panic(err)
		}
		return err
	}
	ls.listenerChan = listener
	if ls.state.Has(STATE_UP) {
		cctx, cancel := context.WithCancel(ls.ctx)
		ls.cancal = cancel
		go ls.uploop(cctx)
	}
	return nil
}

func (ls *Listener) uploop(cctx context.Context) {
	var lasterror error
	errortolerance := 5
	for {
		if cctx.Err() != nil {
			return
		}
		conn, err := ls.ln.Accept()

		if err != nil {
			newError("Cannot Accept socket from listener").Base(err).AtDebug().WriteToLog()
			if err == lasterror {
				errortolerance--
				if errortolerance == 0 {
					newError("unix domain socket melt down as the error is repeating").Base(err).AtError().WriteToLog()
					ls.cancal()
				}
				newError("unix domain socket listener is throttling accept as the error is repeating").Base(err).AtError().WriteToLog()
				time.Sleep(time.Second * 5)
			}
			lasterror = err
		}

		ls.listenerChan <- conn
	}
}

func isUnixDomainSocketFileSystemBased(path string) bool {
	//No Branching
	return path[0] != 0
}

func acquireLock(lockfilepath string) (*os.File, error) {
	f, err := os.Create(lockfilepath)
	if err != nil {
		newError(err).AtDebug().WriteToLog()
		return f, err
	}
	err = syscall.Flock(int(f.Fd()), syscall.LOCK_EX)
	if err != nil {
		newError(err).AtDebug().WriteToLog()
		err = f.Close()
		if err != nil {
			if ___DEBUG_PANIC_WHEN_ENCOUNTED_IMPOSSIBLE_ERROR {
				panic(err)
			}
			newError(err).AtDebug().WriteToLog()
		}
		return nil, err
	}
	return nil, err
}

func giveupLock(locker *os.File) error {
	err := syscall.Flock(int(locker.Fd()), syscall.LOCK_UN)
	if err != nil {
		closeerr := locker.Close()
		if err != nil {
			if ___DEBUG_PANIC_WHEN_ERROR_UNPROPAGATEABLE {
				panic(closeerr)
			}
			newError(closeerr).AtDebug().WriteToLog()
		}
		newError(err).AtDebug().WriteToLog()
		return err
	}
	closeerr := locker.Close()
	if closeerr != nil {
		newError(closeerr).AtDebug().WriteToLog()
		return closeerr
	}
	return closeerr
}

func cleansePath(path string) error {
	_, err := os.Stat(path)
	if err == os.ErrNotExist {
		return nil
	}
	err = os.Remove(path)
	return err
}

//DEBUG CONSTS
const ___DEBUG_IGNORE_FLOCK = false
const ___DEBUG_PANIC_WHEN_ERROR_UNPROPAGATEABLE = false
const ___DEBUG_PANIC_WHEN_ENCOUNTED_IMPOSSIBLE_ERROR = false
<|endoftext|>


<commit_before>package session

import (
	"testing"
	"time"

	"github.com/skygeario/skygear-server/pkg/core/config"
	. "github.com/smartystreets/goconvey/convey"
)

func TestComputeSessionExpiry(t *testing.T) {
	Convey("computeSessionExpiry", t, func() {
		session := &Session{
			ID:         "session-id",
			CreatedAt:  time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
			AccessedAt: time.Date(2020, 1, 1, 0, 0, 25, 0, time.UTC),
		}

		Convey("idle timeout is disabled", func() {
			expiry := computeSessionStorageExpiry(session, config.APIClientConfiguration{
				RefreshTokenLifetime:      120,
				SessionIdleTimeoutEnabled: false,
				SessionIdleTimeout:        30,
			})
			So(expiry, ShouldResemble, time.Date(2020, 1, 1, 0, 2, 0, 0, time.UTC))
		})

		Convey("idle timeout is enabled", func() {
			expiry := computeSessionStorageExpiry(session, config.APIClientConfiguration{
				RefreshTokenLifetime:      120,
				SessionIdleTimeoutEnabled: true,
				SessionIdleTimeout:        30,
			})
			So(expiry, ShouldResemble, time.Date(2020, 1, 1, 0, 0, 55, 0, time.UTC))
		})
	})
}
func TestCheckSessionExpired(t *testing.T) {
	Convey("checkSessionExpired", t, func() {
		session := &Session{
			ID:         "session-id",
			CreatedAt:  time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
			AccessedAt: time.Date(2020, 1, 1, 0, 0, 25, 0, time.UTC),
		}
		var cfg config.APIClientConfiguration
		check := func(mins, secs int) bool {
			return !checkSessionExpired(session, time.Date(2020, 1, 1, 0, mins, secs, 0, time.UTC), cfg)
		}

		Convey("check session lifetime", func() {
			cfg = config.APIClientConfiguration{
				RefreshTokenLifetime:      120,
				SessionIdleTimeoutEnabled: false,
				SessionIdleTimeout:        30,
			}

			So(check(0, 0), ShouldBeTrue)
			So(check(0, 56), ShouldBeTrue)
			So(check(2, 0), ShouldBeTrue)
			So(check(2, 1), ShouldBeFalse)
		})

		Convey("check idle timeout", func() {
			cfg = config.APIClientConfiguration{
				RefreshTokenLifetime:      120,
				SessionIdleTimeoutEnabled: true,
				SessionIdleTimeout:        30,
			}

			So(check(0, 0), ShouldBeTrue)
			So(check(0, 55), ShouldBeTrue)
			So(check(0, 56), ShouldBeFalse)
			So(check(2, 1), ShouldBeFalse)
		})
	})
}

/*
func TestCheckSessionExpired(t *testing.T) {
	Convey("checkSessionExpired", t, func() {
		session := &auth.Session{
			ID:                   "session-id",
			ClientID:             "web-app",
			UserID:               "user-id",
			PrincipalID:          "principal-id",
			CreatedAt:            time.Date(2006, 1, 1, 0, 0, 0, 0, gotime.UTC),
			AccessedAt:           time.Date(2006, 1, 1, 0, 25, 0, 0, gotime.UTC),
			AccessTokenHash:      "access-token-hash",
			RefreshTokenHash:     "refresh-token-hash",
			AccessTokenCreatedAt: time.Date(2006, 1, 1, 0, 20, 0, 0, gotime.UTC),
		}
		config := config.APIClientConfiguration{
			Name:                 "Web App",
			APIKey:               "api_key",
			SessionTransport:     config.SessionTransportTypeHeader,
			AccessTokenLifetime:  1800,
			SessionIdleTimeout:   600,
			RefreshTokenLifetime: 86400,
		}

		doCheckSessionExpired := func(mins int, kind auth.SessionTokenKind) bool {
			return checkSessionExpired(session, time.Date(2006, 1, 1, 0, mins, 0, 0, gotime.UTC), config, kind)
		}

		Convey("should treat refresh tokens as expired if disabled", func() {
			So(doCheckSessionExpired(0, auth.SessionTokenKindRefreshToken), ShouldBeFalse)
			config.RefreshTokenDisabled = true
			So(doCheckSessionExpired(0, auth.SessionTokenKindRefreshToken), ShouldBeTrue)
		})

		Convey("should check refresh token lifetime expiry", func() {
			So(doCheckSessionExpired(1440, auth.SessionTokenKindRefreshToken), ShouldBeFalse)
			So(doCheckSessionExpired(1441, auth.SessionTokenKindRefreshToken), ShouldBeTrue)
		})

		Convey("should check refresh token idle expiry", func() {
			config.SessionIdleTimeoutEnabled = true
			So(doCheckSessionExpired(30, auth.SessionTokenKindRefreshToken), ShouldBeFalse)
			So(doCheckSessionExpired(31, auth.SessionTokenKindRefreshToken), ShouldBeTrue)
		})

		Convey("should check access token expiry", func() {
			So(doCheckSessionExpired(50, auth.SessionTokenKindAccessToken), ShouldBeFalse)
			So(doCheckSessionExpired(51, auth.SessionTokenKindAccessToken), ShouldBeTrue)
		})

		Convey("should check access token idle expiry", func() {
			config.SessionIdleTimeoutEnabled = true
			So(doCheckSessionExpired(30, auth.SessionTokenKindAccessToken), ShouldBeFalse)
			So(doCheckSessionExpired(31, auth.SessionTokenKindAccessToken), ShouldBeTrue)

			config.RefreshTokenDisabled = true
			So(doCheckSessionExpired(35, auth.SessionTokenKindAccessToken), ShouldBeFalse)
			So(doCheckSessionExpired(36, auth.SessionTokenKindAccessToken), ShouldBeTrue)
		})

		Convey("should treat access token as expired if refresh token is expired", func() {
			So(doCheckSessionExpired(25, auth.SessionTokenKindAccessToken), ShouldBeFalse)
			So(doCheckSessionExpired(26, auth.SessionTokenKindAccessToken), ShouldBeFalse)
			So(doCheckSessionExpired(25, auth.SessionTokenKindRefreshToken), ShouldBeFalse)
			So(doCheckSessionExpired(26, auth.SessionTokenKindRefreshToken), ShouldBeFalse)
			config.RefreshTokenLifetime = 25 * 60
			So(doCheckSessionExpired(25, auth.SessionTokenKindAccessToken), ShouldBeFalse)
			So(doCheckSessionExpired(26, auth.SessionTokenKindAccessToken), ShouldBeTrue)
			So(doCheckSessionExpired(25, auth.SessionTokenKindRefreshToken), ShouldBeFalse)
			So(doCheckSessionExpired(26, auth.SessionTokenKindRefreshToken), ShouldBeTrue)
		})
	})
}
*/
<commit_msg>Cleanup commented code<commit_after>package session

import (
	"testing"
	"time"

	"github.com/skygeario/skygear-server/pkg/core/config"
	. "github.com/smartystreets/goconvey/convey"
)

func TestComputeSessionExpiry(t *testing.T) {
	Convey("computeSessionExpiry", t, func() {
		session := &Session{
			ID:         "session-id",
			CreatedAt:  time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
			AccessedAt: time.Date(2020, 1, 1, 0, 0, 25, 0, time.UTC),
		}

		Convey("idle timeout is disabled", func() {
			expiry := computeSessionStorageExpiry(session, config.APIClientConfiguration{
				RefreshTokenLifetime:      120,
				SessionIdleTimeoutEnabled: false,
				SessionIdleTimeout:        30,
			})
			So(expiry, ShouldResemble, time.Date(2020, 1, 1, 0, 2, 0, 0, time.UTC))
		})

		Convey("idle timeout is enabled", func() {
			expiry := computeSessionStorageExpiry(session, config.APIClientConfiguration{
				RefreshTokenLifetime:      120,
				SessionIdleTimeoutEnabled: true,
				SessionIdleTimeout:        30,
			})
			So(expiry, ShouldResemble, time.Date(2020, 1, 1, 0, 0, 55, 0, time.UTC))
		})
	})
}
func TestCheckSessionExpired(t *testing.T) {
	Convey("checkSessionExpired", t, func() {
		session := &Session{
			ID:         "session-id",
			CreatedAt:  time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
			AccessedAt: time.Date(2020, 1, 1, 0, 0, 25, 0, time.UTC),
		}
		var cfg config.APIClientConfiguration
		check := func(mins, secs int) bool {
			return !checkSessionExpired(session, time.Date(2020, 1, 1, 0, mins, secs, 0, time.UTC), cfg)
		}

		Convey("check session lifetime", func() {
			cfg = config.APIClientConfiguration{
				RefreshTokenLifetime:      120,
				SessionIdleTimeoutEnabled: false,
				SessionIdleTimeout:        30,
			}

			So(check(0, 0), ShouldBeTrue)
			So(check(0, 56), ShouldBeTrue)
			So(check(2, 0), ShouldBeTrue)
			So(check(2, 1), ShouldBeFalse)
		})

		Convey("check idle timeout", func() {
			cfg = config.APIClientConfiguration{
				RefreshTokenLifetime:      120,
				SessionIdleTimeoutEnabled: true,
				SessionIdleTimeout:        30,
			}

			So(check(0, 0), ShouldBeTrue)
			So(check(0, 55), ShouldBeTrue)
			So(check(0, 56), ShouldBeFalse)
			So(check(2, 1), ShouldBeFalse)
		})
	})
}
<|endoftext|>


<commit_before>package openshift_kube_apiserver

import (
	"k8s.io/klog"

	"k8s.io/apiserver/pkg/admission"
	genericapiserver "k8s.io/apiserver/pkg/server"
	"k8s.io/kube-aggregator/pkg/apiserver"
	"k8s.io/kubernetes/cmd/kube-apiserver/app"
	"k8s.io/kubernetes/pkg/capabilities"
	kubelettypes "k8s.io/kubernetes/pkg/kubelet/types"
	"k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy"

	kubecontrolplanev1 "github.com/openshift/api/kubecontrolplane/v1"

	"github.com/openshift/origin/pkg/admission/customresourcevalidation/customresourcevalidationregistration"
	"github.com/openshift/origin/pkg/cmd/openshift-kube-apiserver/kubeadmission"
	"github.com/openshift/origin/pkg/cmd/openshift-kube-apiserver/openshiftkubeapiserver"

	"k8s.io/kubernetes/pkg/kubeapiserver/options"

	// for metrics
	_ "k8s.io/kubernetes/pkg/client/metrics/prometheus"
)

func RunOpenShiftKubeAPIServerServer(kubeAPIServerConfig *kubecontrolplanev1.KubeAPIServerConfig, stopCh <-chan struct{}) error {
	// This allows to move cluster resource quota to CRD
	apiserver.AddAlwaysLocalDelegateForPrefix("/apis/quota.openshift.io/v1/clusterresourcequotas")

	// Allow privileged containers
	capabilities.Initialize(capabilities.Capabilities{
		AllowPrivileged: true,
		PrivilegedSources: capabilities.PrivilegedSources{
			HostNetworkSources: []string{kubelettypes.ApiserverSource, kubelettypes.FileSource},
			HostPIDSources:     []string{kubelettypes.ApiserverSource, kubelettypes.FileSource},
			HostIPCSources:     []string{kubelettypes.ApiserverSource, kubelettypes.FileSource},
		},
	})

	bootstrappolicy.ClusterRoles = bootstrappolicy.OpenshiftClusterRoles
	bootstrappolicy.ClusterRoleBindings = bootstrappolicy.OpenshiftClusterRoleBindings

	options.AllOrderedPlugins = kubeadmission.NewOrderedKubeAdmissionPlugins(options.AllOrderedPlugins)

	kubeRegisterAdmission := options.RegisterAllAdmissionPlugins
	options.RegisterAllAdmissionPlugins = func(plugins *admission.Plugins) {
		kubeRegisterAdmission(plugins)
		kubeadmission.RegisterOpenshiftKubeAdmissionPlugins(plugins)
		customresourcevalidationregistration.RegisterCustomResourceValidation(plugins)
	}
	options.DefaultOffAdmissionPlugins = kubeadmission.NewDefaultOffPluginsFunc(options.DefaultOffAdmissionPlugins())

	configPatchFn, serverPatchContext := openshiftkubeapiserver.NewOpenShiftKubeAPIServerConfigPatch(genericapiserver.NewEmptyDelegate(), kubeAPIServerConfig)
	app.OpenShiftKubeAPIServerConfigPatch = configPatchFn
	app.OpenShiftKubeAPIServerServerPatch = serverPatchContext.PatchServer

	cmd := app.NewAPIServerCommand(stopCh)
	args, err := openshiftkubeapiserver.ConfigToFlags(kubeAPIServerConfig)
	if err != nil {
		return err
	}
	if err := cmd.ParseFlags(args); err != nil {
		return err
	}
	klog.Infof("`kube-apiserver %v`", args)
	if err := cmd.RunE(cmd, nil); err != nil {
		return err
	}

	return nil
}
<commit_msg>don't fight with CRDs for SCC and RBR<commit_after>package openshift_kube_apiserver

import (
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apiserver/pkg/admission"
	genericapiserver "k8s.io/apiserver/pkg/server"
	"k8s.io/klog"
	"k8s.io/kube-aggregator/pkg/apiserver"
	"k8s.io/kubernetes/cmd/kube-apiserver/app"
	"k8s.io/kubernetes/pkg/capabilities"
	"k8s.io/kubernetes/pkg/kubeapiserver/options"
	kubelettypes "k8s.io/kubernetes/pkg/kubelet/types"
	"k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy"

	kubecontrolplanev1 "github.com/openshift/api/kubecontrolplane/v1"
	"github.com/openshift/origin/pkg/admission/customresourcevalidation/customresourcevalidationregistration"
	"github.com/openshift/origin/pkg/cmd/openshift-kube-apiserver/kubeadmission"
	"github.com/openshift/origin/pkg/cmd/openshift-kube-apiserver/openshiftkubeapiserver"

	// for metrics
	_ "k8s.io/kubernetes/pkg/client/metrics/prometheus"
)

func RunOpenShiftKubeAPIServerServer(kubeAPIServerConfig *kubecontrolplanev1.KubeAPIServerConfig, stopCh <-chan struct{}) error {
	// This allows to move cluster resource quota to CRD
	apiserver.AddAlwaysLocalDelegateForPrefix("/apis/quota.openshift.io/v1/clusterresourcequotas")

	// This allows the CRD registration to avoid fighting with the APIService from the operator
	apiserver.AddOverlappingGroupVersion(schema.GroupVersion{Group: "authorization.openshift.io", Version: "v1"})
	apiserver.AddOverlappingGroupVersion(schema.GroupVersion{Group: "security.openshift.io", Version: "v1"})

	// Allow privileged containers
	capabilities.Initialize(capabilities.Capabilities{
		AllowPrivileged: true,
		PrivilegedSources: capabilities.PrivilegedSources{
			HostNetworkSources: []string{kubelettypes.ApiserverSource, kubelettypes.FileSource},
			HostPIDSources:     []string{kubelettypes.ApiserverSource, kubelettypes.FileSource},
			HostIPCSources:     []string{kubelettypes.ApiserverSource, kubelettypes.FileSource},
		},
	})

	bootstrappolicy.ClusterRoles = bootstrappolicy.OpenshiftClusterRoles
	bootstrappolicy.ClusterRoleBindings = bootstrappolicy.OpenshiftClusterRoleBindings

	options.AllOrderedPlugins = kubeadmission.NewOrderedKubeAdmissionPlugins(options.AllOrderedPlugins)

	kubeRegisterAdmission := options.RegisterAllAdmissionPlugins
	options.RegisterAllAdmissionPlugins = func(plugins *admission.Plugins) {
		kubeRegisterAdmission(plugins)
		kubeadmission.RegisterOpenshiftKubeAdmissionPlugins(plugins)
		customresourcevalidationregistration.RegisterCustomResourceValidation(plugins)
	}
	options.DefaultOffAdmissionPlugins = kubeadmission.NewDefaultOffPluginsFunc(options.DefaultOffAdmissionPlugins())

	configPatchFn, serverPatchContext := openshiftkubeapiserver.NewOpenShiftKubeAPIServerConfigPatch(genericapiserver.NewEmptyDelegate(), kubeAPIServerConfig)
	app.OpenShiftKubeAPIServerConfigPatch = configPatchFn
	app.OpenShiftKubeAPIServerServerPatch = serverPatchContext.PatchServer

	cmd := app.NewAPIServerCommand(stopCh)
	args, err := openshiftkubeapiserver.ConfigToFlags(kubeAPIServerConfig)
	if err != nil {
		return err
	}
	if err := cmd.ParseFlags(args); err != nil {
		return err
	}
	klog.Infof("`kube-apiserver %v`", args)
	if err := cmd.RunE(cmd, nil); err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package action

import (
	"github.com/Aptomi/aptomi/pkg/slinga/object"
	"strings"
)

// Metadata is an object metadata for all state update actions
type Metadata struct {
	Key      string
	Kind     string
	Revision object.Generation
}

// NewMetadata creates new Metadata
func NewMetadata(revision object.Generation, kind string, keys ...string) *Metadata {
	keysStr := strings.Join(keys, object.KeySeparator)
	return &Metadata{
		Key:      strings.Join([]string{revision.String(), kind, keysStr}, object.KeySeparator),
		Kind:     kind,
		Revision: revision,
	}
}

// GetKey returns an object key
func (meta *Metadata) GetKey() string {
	return meta.Key
}

// GetNamespace returns a namespace for an action (it's always a system namespace)
func (meta *Metadata) GetNamespace() string {
	return object.SystemNS
}

// GetKind returns an object kind
func (meta *Metadata) GetKind() string {
	return meta.Kind
}

// GetGeneration returns a generation for action (it's always zero as actions are not versioned)
func (meta *Metadata) GetGeneration() object.Generation {
	// we aren't storing action versions
	return 0
}

// SetGeneration for an action (not needed)
func (meta *Metadata) SetGeneration(generation object.Generation) {
	panic("Action is not a versioned object")
}
<commit_msg>Fix action meta<commit_after>package action

import (
	"github.com/Aptomi/aptomi/pkg/slinga/object"
	"strings"
)

// Metadata is an object metadata for all state update actions
type Metadata struct {
	Kind     string
	Name     string
	Revision object.Generation
}

// NewMetadata creates new Metadata
func NewMetadata(revision object.Generation, kind string, keys ...string) *Metadata {
	return &Metadata{
		Kind:     kind,
		Name:     strings.Join(keys, object.KeySeparator),
		Revision: revision,
	}
}

// GetKey returns an object key
func (meta *Metadata) GetKey() string {
	return strings.Join([]string{meta.Revision.String(), meta.Kind, meta.Name}, object.KeySeparator)
}

// GetNamespace returns a namespace for an action (it's always a system namespace)
func (meta *Metadata) GetNamespace() string {
	return object.SystemNS
}

// GetKind returns an object kind
func (meta *Metadata) GetKind() string {
	return meta.Kind
}

// GetGeneration returns a generation for action (it's always zero as actions are not versioned)
func (meta *Metadata) GetGeneration() object.Generation {
	// we aren't storing action versions
	return 0
}

// SetGeneration for an action (not needed)
func (meta *Metadata) SetGeneration(generation object.Generation) {
	panic("Action is not a versioned object")
}
<|endoftext|>


<commit_before>package server

import (
	"encoding/json"
	"errors"
	log "github.com/Sirupsen/logrus"
	"github.com/gorilla/mux"
	"github.com/gorilla/schema"
	"github.com/oxfeeefeee/appgo"
	"github.com/oxfeeefeee/appgo/auth"
	"net/http"
	"reflect"
)

const (
	UserIdFieldName      = "UserId__"
	AdminUserIdFieldName = "AdminUserId__"
	ResIdFieldName       = "ResourceId__"
	ContentFieldName     = "Content__"
)

var decoder = schema.NewDecoder()

type httpFunc struct {
	requireAuth    bool
	requireAdmin   bool
	hasResId       bool
	hasContent     bool
	dummyInput     bool
	allowAnonymous bool
	inputType      reflect.Type
	contentType    reflect.Type
	funcValue      reflect.Value
}

type handler struct {
	path     string
	funcs    map[string]*httpFunc
	supports []string
	ts       TokenStore
}

func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	f := h.funcs[r.Method]
	var input reflect.Value
	if f.dummyInput {
		input = reflect.ValueOf((*appgo.DummyInput)(nil))
	} else {
		input = reflect.New(f.inputType)
		if err := decoder.Decode(input.Interface(), r.URL.Query()); err != nil {
			renderError(w, appgo.NewApiErr(appgo.ECodeBadRequest, err.Error()))
			return
		}
	}
	if f.requireAuth {
		user, _ := h.authByHeader(r)
		s := input.Elem()
		field := s.FieldByName(UserIdFieldName)
		if user == 0 {
			if f.allowAnonymous {
				field.SetInt(appgo.AnonymousId)
			} else {
				renderError(w, appgo.NewApiErrWithCode(appgo.ECodeUnauthorized))
				return
			}
		}
		field.SetInt(int64(user))
	} else if f.requireAdmin {
		user, role := h.authByHeader(r)
		s := input.Elem()
		f := s.FieldByName(AdminUserIdFieldName)
		if user == 0 || role != appgo.RoleWebAdmin {
			renderError(w, appgo.NewApiErrWithCode(appgo.ECodeUnauthorized))
			return
		}
		f.SetInt(int64(user))
	}
	if f.hasResId {
		vars := mux.Vars(r)
		id := appgo.IdFromStr(vars["id"])
		if id == 0 {
			renderError(w, appgo.NewApiErrWithCode(appgo.ECodeNotFound))
			return
		}
		s := input.Elem()
		f := s.FieldByName(ResIdFieldName)
		f.SetInt(int64(id))
	}
	if f.hasContent {
		content := reflect.New(f.contentType.Elem())
		if err := json.NewDecoder(r.Body).Decode(content.Interface()); err != nil {
			renderError(w, appgo.NewApiErr(appgo.ECodeBadRequest, err.Error()))
			return
		}
		s := input.Elem()
		f := s.FieldByName(ContentFieldName)
		f.Set(content)
	}
	argsIn := []reflect.Value{input}
	returns := f.funcValue.Call(argsIn)
	if len(returns) == 0 || len(returns) > 2 {
		renderError(w, appgo.NewApiErr(appgo.ECodeInternal, "Bad api-func format"))
		return
	}
	// Either returns (reply, error) or returns (error)
	var retErr reflect.Value
	if len(returns) == 1 {
		retErr = returns[0]
	} else {
		retErr = returns[1]
	}
	// First check is err is nil
	if retErr.IsNil() {
		if len(returns) == 2 {
			renderJSON(w, returns[0].Interface())
		} else { // Empty return
			renderJSON(w, map[string]string{})
		}
	} else {
		if aerr, ok := retErr.Interface().(*appgo.ApiError); !ok {
			aerr = appgo.NewApiErr(appgo.ECodeInternal, "Bad api-func format")
		} else {
			renderError(w, aerr)
		}
	}
}

func (h *handler) authByHeader(r *http.Request) (appgo.Id, appgo.Role) {
	token := auth.Token(r.Header.Get(appgo.CustomTokenHeaderName))
	user, role := token.Validate()
	if user == 0 {
		return 0, 0
	}
	if !h.ts.Validate(token) {
		return 0, 0
	}
	return user, role
}

func newHandler(funcSet interface{}, ts TokenStore) *handler {
	funcs := make(map[string]*httpFunc)
	// Let if panic if funSet's type is not right
	path := ""
	t := reflect.TypeOf(funcSet).Elem()
	if field, ok := t.FieldByName("META"); !ok {
		log.Panicln("Bad API path")
	} else if p := field.Tag.Get("path"); p == "" {
		log.Panicln("Empty API path")
	} else {
		path = p
	}

	structVal := reflect.Indirect(reflect.ValueOf(funcSet))
	methods := []string{"GET", "POST", "PUT", "DELETE"}
	supports := make([]string, 0, 4)
	for _, m := range methods {
		if fun, err := newHttpFunc(structVal, m); err != nil {
			log.Panicln(err)
		} else if fun != nil {
			funcs[m] = fun
			supports = append(supports, m)
		}
	}
	if len(supports) == 0 {
		log.Panicln("API supports no HTTP method")
	}
	return &handler{path, funcs, supports, ts}
}

func newHttpFunc(structVal reflect.Value, fieldName string) (*httpFunc, error) {
	fieldVal := structVal.MethodByName(fieldName)
	if !fieldVal.IsValid() {
		return nil, nil
	}
	ftype := fieldVal.Type()
	inNum := ftype.NumIn()
	if inNum != 1 {
		return nil, errors.New("API func needs to have exact 1 parameter")
	}
	inputType := ftype.In(0)
	dummyInput := false
	if inputType.Kind() != reflect.Ptr {
		return nil, errors.New("API func's parameter needs to be a pointer")
	}
	if inputType == reflect.TypeOf((*appgo.DummyInput)(nil)) {
		dummyInput = true
	}
	inputType = inputType.Elem()
	requireAuth := false
	allowAnonymous := false
	if fromIdField, ok := inputType.FieldByName(UserIdFieldName); ok {
		requireAuth = true
		if fromIdField.Type.Kind() != reflect.Int64 {
			return nil, errors.New("API func's 2nd parameter needs to be Int64")
		}
		aa := fromIdField.Tag.Get("allowAnonymous")
		allowAnonymous = (aa == "true")
	}
	requireAdmin := false
	if fromIdType, ok := inputType.FieldByName(AdminUserIdFieldName); ok {
		requireAdmin = true
		if fromIdType.Type.Kind() != reflect.Int64 {
			return nil, errors.New("API func's 2nd parameter needs to be Int64")
		}
	}
	hasResId := false
	if resIdType, ok := inputType.FieldByName(ResIdFieldName); ok {
		hasResId = true
		if resIdType.Type.Kind() != reflect.Int64 {
			return nil, errors.New("ResId needs to be Int64")
		}
	}
	hasContent := false
	var contentType reflect.Type
	if ctype, ok := inputType.FieldByName(ContentFieldName); ok {
		hasContent = true
		contentType = ctype.Type
		if ctype.Type.Kind() != reflect.Ptr {
			return nil, errors.New("Content needs to be a pointer")
		}
	}
	return &httpFunc{requireAuth, requireAdmin, hasResId, hasContent,
		dummyInput, allowAnonymous, inputType, contentType, fieldVal}, nil
}
<commit_msg>Add error messages<commit_after>package server

import (
	"encoding/json"
	"errors"
	log "github.com/Sirupsen/logrus"
	"github.com/gorilla/mux"
	"github.com/gorilla/schema"
	"github.com/oxfeeefeee/appgo"
	"github.com/oxfeeefeee/appgo/auth"
	"net/http"
	"reflect"
)

const (
	UserIdFieldName      = "UserId__"
	AdminUserIdFieldName = "AdminUserId__"
	ResIdFieldName       = "ResourceId__"
	ContentFieldName     = "Content__"
)

var decoder = schema.NewDecoder()

type httpFunc struct {
	requireAuth    bool
	requireAdmin   bool
	hasResId       bool
	hasContent     bool
	dummyInput     bool
	allowAnonymous bool
	inputType      reflect.Type
	contentType    reflect.Type
	funcValue      reflect.Value
}

type handler struct {
	path     string
	funcs    map[string]*httpFunc
	supports []string
	ts       TokenStore
}

func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	f := h.funcs[r.Method]
	var input reflect.Value
	if f.dummyInput {
		input = reflect.ValueOf((*appgo.DummyInput)(nil))
	} else {
		input = reflect.New(f.inputType)
		if err := decoder.Decode(input.Interface(), r.URL.Query()); err != nil {
			renderError(w, appgo.NewApiErr(appgo.ECodeBadRequest, err.Error()))
			return
		}
	}
	if f.requireAuth {
		user, _ := h.authByHeader(r)
		s := input.Elem()
		field := s.FieldByName(UserIdFieldName)
		if user == 0 {
			if f.allowAnonymous {
				field.SetInt(appgo.AnonymousId)
			} else {
				renderError(w, appgo.NewApiErr(
					appgo.ECodeUnauthorized,
					"either remove UserId__ in your input define, or add allowAnonymous tag",
				))
				return
			}
		}
		field.SetInt(int64(user))
	} else if f.requireAdmin {
		user, role := h.authByHeader(r)
		s := input.Elem()
		f := s.FieldByName(AdminUserIdFieldName)
		if user == 0 || role != appgo.RoleWebAdmin {
			renderError(w, appgo.NewApiErr(
				appgo.ECodeUnauthorized,
				"admin role required, you could remove AdminUserId__ in your input define"))
			return
		}
		f.SetInt(int64(user))
	}
	if f.hasResId {
		vars := mux.Vars(r)
		id := appgo.IdFromStr(vars["id"])
		if id == 0 {
			renderError(w, appgo.NewApiErr(
				appgo.ECodeNotFound,
				"ResourceId ('{id}' in url) required, you could remove ResourceId__ in your input define"))
			return
		}
		s := input.Elem()
		f := s.FieldByName(ResIdFieldName)
		f.SetInt(int64(id))
	}
	if f.hasContent {
		content := reflect.New(f.contentType.Elem())
		if err := json.NewDecoder(r.Body).Decode(content.Interface()); err != nil {
			renderError(w, appgo.NewApiErr(appgo.ECodeBadRequest, err.Error()))
			return
		}
		s := input.Elem()
		f := s.FieldByName(ContentFieldName)
		f.Set(content)
	}
	argsIn := []reflect.Value{input}
	returns := f.funcValue.Call(argsIn)
	if len(returns) == 0 || len(returns) > 2 {
		renderError(w, appgo.NewApiErr(appgo.ECodeInternal, "Bad api-func format"))
		return
	}
	// Either returns (reply, error) or returns (error)
	var retErr reflect.Value
	if len(returns) == 1 {
		retErr = returns[0]
	} else {
		retErr = returns[1]
	}
	// First check is err is nil
	if retErr.IsNil() {
		if len(returns) == 2 {
			renderJSON(w, returns[0].Interface())
		} else { // Empty return
			renderJSON(w, map[string]string{})
		}
	} else {
		if aerr, ok := retErr.Interface().(*appgo.ApiError); !ok {
			aerr = appgo.NewApiErr(appgo.ECodeInternal, "Bad api-func format")
		} else {
			renderError(w, aerr)
		}
	}
}

func (h *handler) authByHeader(r *http.Request) (appgo.Id, appgo.Role) {
	token := auth.Token(r.Header.Get(appgo.CustomTokenHeaderName))
	user, role := token.Validate()
	if user == 0 {
		return 0, 0
	}
	if !h.ts.Validate(token) {
		return 0, 0
	}
	return user, role
}

func newHandler(funcSet interface{}, ts TokenStore) *handler {
	funcs := make(map[string]*httpFunc)
	// Let if panic if funSet's type is not right
	path := ""
	t := reflect.TypeOf(funcSet).Elem()
	if field, ok := t.FieldByName("META"); !ok {
		log.Panicln("Bad API path")
	} else if p := field.Tag.Get("path"); p == "" {
		log.Panicln("Empty API path")
	} else {
		path = p
	}

	structVal := reflect.Indirect(reflect.ValueOf(funcSet))
	methods := []string{"GET", "POST", "PUT", "DELETE"}
	supports := make([]string, 0, 4)
	for _, m := range methods {
		if fun, err := newHttpFunc(structVal, m); err != nil {
			log.Panicln(err)
		} else if fun != nil {
			funcs[m] = fun
			supports = append(supports, m)
		}
	}
	if len(supports) == 0 {
		log.Panicln("API supports no HTTP method")
	}
	return &handler{path, funcs, supports, ts}
}

func newHttpFunc(structVal reflect.Value, fieldName string) (*httpFunc, error) {
	fieldVal := structVal.MethodByName(fieldName)
	if !fieldVal.IsValid() {
		return nil, nil
	}
	ftype := fieldVal.Type()
	inNum := ftype.NumIn()
	if inNum != 1 {
		return nil, errors.New("API func needs to have exact 1 parameter")
	}
	inputType := ftype.In(0)
	dummyInput := false
	if inputType.Kind() != reflect.Ptr {
		return nil, errors.New("API func's parameter needs to be a pointer")
	}
	if inputType == reflect.TypeOf((*appgo.DummyInput)(nil)) {
		dummyInput = true
	}
	inputType = inputType.Elem()
	requireAuth := false
	allowAnonymous := false
	if fromIdField, ok := inputType.FieldByName(UserIdFieldName); ok {
		requireAuth = true
		if fromIdField.Type.Kind() != reflect.Int64 {
			return nil, errors.New("API func's 2nd parameter needs to be Int64")
		}
		aa := fromIdField.Tag.Get("allowAnonymous")
		allowAnonymous = (aa == "true")
	}
	requireAdmin := false
	if fromIdType, ok := inputType.FieldByName(AdminUserIdFieldName); ok {
		requireAdmin = true
		if fromIdType.Type.Kind() != reflect.Int64 {
			return nil, errors.New("API func's 2nd parameter needs to be Int64")
		}
	}
	hasResId := false
	if resIdType, ok := inputType.FieldByName(ResIdFieldName); ok {
		hasResId = true
		if resIdType.Type.Kind() != reflect.Int64 {
			return nil, errors.New("ResId needs to be Int64")
		}
	}
	hasContent := false
	var contentType reflect.Type
	if ctype, ok := inputType.FieldByName(ContentFieldName); ok {
		hasContent = true
		contentType = ctype.Type
		if ctype.Type.Kind() != reflect.Ptr {
			return nil, errors.New("Content needs to be a pointer")
		}
	}
	return &httpFunc{requireAuth, requireAdmin, hasResId, hasContent,
		dummyInput, allowAnonymous, inputType, contentType, fieldVal}, nil
}
<|endoftext|>


<commit_before>package graylog

import (
	"bytes"
	"compress/zlib"
	"encoding/json"
	"io"
	"net"
	"sync"
	"testing"

	"github.com/influxdata/telegraf/testutil"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestWriteDefault(t *testing.T) {
	scenarioUDP(t, "127.0.0.1:12201")
}

func TestWriteUDP(t *testing.T) {
	scenarioUDP(t, "udp://127.0.0.1:12201")
}

func TestWriteTCP(t *testing.T) {
	scenarioTCP(t, "tcp://127.0.0.1:12201")
}

func scenarioUDP(t *testing.T, server string) {
	var wg sync.WaitGroup
	var wg2 sync.WaitGroup
	wg.Add(1)
	wg2.Add(1)
	go UDPServer(t, &wg, &wg2)
	wg2.Wait()

	i := Graylog{
		Servers: []string{server},
	}
	err := i.Connect()
	require.NoError(t, err)

	metrics := testutil.MockMetrics()

	// UDP scenario:
	// 4 messages are send

	err = i.Write(metrics)
	require.NoError(t, err)
	err = i.Write(metrics)
	require.NoError(t, err)
	err = i.Write(metrics)
	require.NoError(t, err)
	err = i.Write(metrics)
	require.NoError(t, err)

	wg.Wait()
	i.Close()
}

func scenarioTCP(t *testing.T, server string) {
	var wg sync.WaitGroup
	var wg2 sync.WaitGroup
	var wg3 sync.WaitGroup
	wg.Add(1)
	wg2.Add(1)
	wg3.Add(1)
	go TCPServer(t, &wg, &wg2, &wg3)
	wg2.Wait()

	i := Graylog{
		Servers: []string{server},
	}
	err := i.Connect()
	require.NoError(t, err)

	metrics := testutil.MockMetrics()

	// TCP scenario:
	// 4 messages are send
	// -> connection gets broken after the 2nd message (server closes connection)
	// -> the 3rd write ends with error
	// -> in the 4th write connection is restored and write is successful

	err = i.Write(metrics)
	require.NoError(t, err)
	err = i.Write(metrics)
	require.NoError(t, err)
	wg3.Wait()
	err = i.Write(metrics)
	require.Error(t, err)
	err = i.Write(metrics)
	require.NoError(t, err)

	wg.Wait()
	i.Close()
}

type GelfObject map[string]interface{}

func UDPServer(t *testing.T, wg *sync.WaitGroup, wg2 *sync.WaitGroup) {
	serverAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:12201")
	require.NoError(t, err)
	udpServer, err := net.ListenUDP("udp", serverAddr)
	require.NoError(t, err)
	defer udpServer.Close()
	defer wg.Done()

	bufR := make([]byte, 1024)
	wg2.Done()

	recv := func() {
		n, _, err := udpServer.ReadFromUDP(bufR)
		require.NoError(t, err)

		b := bytes.NewReader(bufR[0:n])
		r, _ := zlib.NewReader(b)

		bufW := bytes.NewBuffer(nil)
		_, _ = io.Copy(bufW, r)
		_ = r.Close()

		var obj GelfObject
		_ = json.Unmarshal(bufW.Bytes(), &obj)
		require.NoError(t, err)
		assert.Equal(t, obj["_value"], float64(1))
	}

	// in UDP scenario all 4 messages are received

	recv()
	recv()
	recv()
	recv()
}

func TCPServer(t *testing.T, wg *sync.WaitGroup, wg2 *sync.WaitGroup, wg3 *sync.WaitGroup) {
	serverAddr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:12201")
	require.NoError(t, err)
	tcpServer, err := net.ListenTCP("tcp", serverAddr)
	require.NoError(t, err)
	defer tcpServer.Close()
	defer wg.Done()

	bufR := make([]byte, 1)
	bufW := bytes.NewBuffer(nil)
	wg2.Done()

	accept := func() *net.TCPConn {
		conn, err := tcpServer.AcceptTCP()
		require.NoError(t, err)
		_ = conn.SetLinger(0)
		return conn
	}
	conn := accept()
	defer conn.Close()

	recv := func() {
		bufW.Reset()
		for {
			n, err := conn.Read(bufR)
			require.NoError(t, err)
			if n > 0 {
				if bufR[0] == 0 { // message delimiter found
					break
				}
				_, _ = bufW.Write(bufR)
			}
		}

		var obj GelfObject
		err = json.Unmarshal(bufW.Bytes(), &obj)
		require.NoError(t, err)
		assert.Equal(t, obj["_value"], float64(1))
	}

	// in TCP scenario only 3 messages are received (1st, 2dn and 4th) due to connection break after the 2nd

	recv()
	recv()
	_ = conn.Close()
	wg3.Done()
	conn = accept()
	defer conn.Close()
	recv()
}
<commit_msg>fix: mute graylog UDP/TCP tests by marking them as integration (#9881)<commit_after>package graylog

import (
	"bytes"
	"compress/zlib"
	"encoding/json"
	"io"
	"net"
	"sync"
	"testing"

	"github.com/influxdata/telegraf/testutil"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestWriteDefault(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping test in short mode.")
	}

	scenarioUDP(t, "127.0.0.1:12201")
}

func TestWriteUDP(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping test in short mode.")
	}

	scenarioUDP(t, "udp://127.0.0.1:12201")
}

func TestWriteTCP(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping test in short mode.")
	}

	scenarioTCP(t, "tcp://127.0.0.1:12201")
}

func scenarioUDP(t *testing.T, server string) {
	var wg sync.WaitGroup
	var wg2 sync.WaitGroup
	wg.Add(1)
	wg2.Add(1)
	go UDPServer(t, &wg, &wg2)
	wg2.Wait()

	i := Graylog{
		Servers: []string{server},
	}
	err := i.Connect()
	require.NoError(t, err)

	metrics := testutil.MockMetrics()

	// UDP scenario:
	// 4 messages are send

	err = i.Write(metrics)
	require.NoError(t, err)
	err = i.Write(metrics)
	require.NoError(t, err)
	err = i.Write(metrics)
	require.NoError(t, err)
	err = i.Write(metrics)
	require.NoError(t, err)

	wg.Wait()
	i.Close()
}

func scenarioTCP(t *testing.T, server string) {
	var wg sync.WaitGroup
	var wg2 sync.WaitGroup
	var wg3 sync.WaitGroup
	wg.Add(1)
	wg2.Add(1)
	wg3.Add(1)
	go TCPServer(t, &wg, &wg2, &wg3)
	wg2.Wait()

	i := Graylog{
		Servers: []string{server},
	}
	err := i.Connect()
	require.NoError(t, err)

	metrics := testutil.MockMetrics()

	// TCP scenario:
	// 4 messages are send
	// -> connection gets broken after the 2nd message (server closes connection)
	// -> the 3rd write ends with error
	// -> in the 4th write connection is restored and write is successful

	err = i.Write(metrics)
	require.NoError(t, err)
	err = i.Write(metrics)
	require.NoError(t, err)
	wg3.Wait()
	err = i.Write(metrics)
	require.Error(t, err)
	err = i.Write(metrics)
	require.NoError(t, err)

	wg.Wait()
	i.Close()
}

type GelfObject map[string]interface{}

func UDPServer(t *testing.T, wg *sync.WaitGroup, wg2 *sync.WaitGroup) {
	serverAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:12201")
	require.NoError(t, err)
	udpServer, err := net.ListenUDP("udp", serverAddr)
	require.NoError(t, err)
	defer udpServer.Close()
	defer wg.Done()

	bufR := make([]byte, 1024)
	wg2.Done()

	recv := func() {
		n, _, err := udpServer.ReadFromUDP(bufR)
		require.NoError(t, err)

		b := bytes.NewReader(bufR[0:n])
		r, _ := zlib.NewReader(b)

		bufW := bytes.NewBuffer(nil)
		_, _ = io.Copy(bufW, r)
		_ = r.Close()

		var obj GelfObject
		_ = json.Unmarshal(bufW.Bytes(), &obj)
		require.NoError(t, err)
		assert.Equal(t, obj["_value"], float64(1))
	}

	// in UDP scenario all 4 messages are received

	recv()
	recv()
	recv()
	recv()
}

func TCPServer(t *testing.T, wg *sync.WaitGroup, wg2 *sync.WaitGroup, wg3 *sync.WaitGroup) {
	serverAddr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:12201")
	require.NoError(t, err)
	tcpServer, err := net.ListenTCP("tcp", serverAddr)
	require.NoError(t, err)
	defer tcpServer.Close()
	defer wg.Done()

	bufR := make([]byte, 1)
	bufW := bytes.NewBuffer(nil)
	wg2.Done()

	accept := func() *net.TCPConn {
		conn, err := tcpServer.AcceptTCP()
		require.NoError(t, err)
		_ = conn.SetLinger(0)
		return conn
	}
	conn := accept()
	defer conn.Close()

	recv := func() {
		bufW.Reset()
		for {
			n, err := conn.Read(bufR)
			require.NoError(t, err)
			if n > 0 {
				if bufR[0] == 0 { // message delimiter found
					break
				}
				_, _ = bufW.Write(bufR)
			}
		}

		var obj GelfObject
		err = json.Unmarshal(bufW.Bytes(), &obj)
		require.NoError(t, err)
		assert.Equal(t, obj["_value"], float64(1))
	}

	// in TCP scenario only 3 messages are received (1st, 2dn and 4th) due to connection break after the 2nd

	recv()
	recv()
	_ = conn.Close()
	wg3.Done()
	conn = accept()
	defer conn.Close()
	recv()
}
<|endoftext|>