<commit_before>// Downloads torrents from the command-line.
package main

import (
	"errors"
	"expvar"
	"fmt"
	"io"
	stdLog "log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"sync"
	"syscall"
	"time"

	"github.com/anacrolix/args"
	"github.com/anacrolix/envpprof"
	"github.com/anacrolix/log"
	"github.com/anacrolix/missinggo/v2"
	"github.com/anacrolix/tagflag"
	"github.com/anacrolix/torrent/bencode"
	"github.com/anacrolix/torrent/version"
	"github.com/davecgh/go-spew/spew"
	"github.com/dustin/go-humanize"
	"golang.org/x/time/rate"

	"github.com/anacrolix/torrent"
	"github.com/anacrolix/torrent/iplist"
	"github.com/anacrolix/torrent/metainfo"
	"github.com/anacrolix/torrent/storage"
)

func torrentBar(t *torrent.Torrent, pieceStates bool) {
	go func() {
		start := time.Now()
		if t.Info() == nil {
			fmt.Printf("%v: getting torrent info for %q\n", time.Since(start), t.Name())
			<-t.GotInfo()
		}
		lastStats := t.Stats()
		var lastLine string
		for range time.Tick(time.Second) {
			var completedPieces, partialPieces int
			psrs := t.PieceStateRuns()
			for _, r := range psrs {
				if r.Complete {
					completedPieces += r.Length
				}
				if r.Partial {
					partialPieces += r.Length
				}
			}
			stats := t.Stats()
			line := fmt.Sprintf(
				"%v: downloading %q: %s/%s, %d/%d pieces completed (%d partial): %v/s\n",
				time.Since(start),
				t.Name(),
				humanize.Bytes(uint64(t.BytesCompleted())),
				humanize.Bytes(uint64(t.Length())),
				completedPieces,
				t.NumPieces(),
				partialPieces,
				humanize.Bytes(uint64(stats.BytesReadUsefulData.Int64()-lastStats.BytesReadUsefulData.Int64())),
			)
			if line != lastLine {
				lastLine = line
				os.Stdout.WriteString(line)
			}
			if pieceStates {
				fmt.Println(psrs)
			}
			lastStats = stats
		}
	}()
}

type stringAddr string

func (stringAddr) Network() string   { return "" }
func (me stringAddr) String() string { return string(me) }

func resolveTestPeers(addrs []string) (ret []torrent.PeerInfo) {
	for _, ta := range addrs {
		ret = append(ret, torrent.PeerInfo{
			Addr: stringAddr(ta),
		})
	}
	return
}

func addTorrents(client *torrent.Client, flags downloadFlags) error {
	testPeers := resolveTestPeers(flags.TestPeer)
	for _, arg := range flags.Torrent {
		t, err := func() (*torrent.Torrent, error) {
			if strings.HasPrefix(arg, "magnet:") {
				t, err := client.AddMagnet(arg)
				if err != nil {
					return nil, fmt.Errorf("error adding magnet: %w", err)
				}
				return t, nil
			} else if strings.HasPrefix(arg, "http://") || strings.HasPrefix(arg, "https://") {
				response, err := http.Get(arg)
				if err != nil {
					return nil, fmt.Errorf("Error downloading torrent file: %s", err)
				}

				metaInfo, err := metainfo.Load(response.Body)
				defer response.Body.Close()
				if err != nil {
					return nil, fmt.Errorf("error loading torrent file %q: %s\n", arg, err)
				}
				t, err := client.AddTorrent(metaInfo)
				if err != nil {
					return nil, fmt.Errorf("adding torrent: %w", err)
				}
				return t, nil
			} else if strings.HasPrefix(arg, "infohash:") {
				t, _ := client.AddTorrentInfoHash(metainfo.NewHashFromHex(strings.TrimPrefix(arg, "infohash:")))
				return t, nil
			} else {
				metaInfo, err := metainfo.LoadFromFile(arg)
				if err != nil {
					return nil, fmt.Errorf("error loading torrent file %q: %s\n", arg, err)
				}
				t, err := client.AddTorrent(metaInfo)
				if err != nil {
					return nil, fmt.Errorf("adding torrent: %w", err)
				}
				return t, nil
			}
		}()
		if err != nil {
			return fmt.Errorf("adding torrent for %q: %w", arg, err)
		}
		if flags.Progress {
			torrentBar(t, flags.PieceStates)
		}
		t.AddPeers(testPeers)
		go func() {
			<-t.GotInfo()
			if len(flags.File) == 0 {
				t.DownloadAll()
			} else {
				for _, f := range t.Files() {
					for _, fileArg := range flags.File {
						if f.DisplayPath() == fileArg {
							f.Download()
						}
					}
				}
			}
		}()
	}
	return nil
}

type downloadFlags struct {
	Debug bool
	DownloadCmd
}

type DownloadCmd struct {
	Mmap               bool           `help:"memory-map torrent data"`
	TestPeer           []string       `help:"addresses of some starting peers"`
	Seed               bool           `help:"seed after download is complete"`
	Addr               string         `help:"network listen addr"`
	MaxUnverifiedBytes tagflag.Bytes  `help:"maximum number bytes to have pending verification"`
	UploadRate         *tagflag.Bytes `help:"max piece bytes to send per second"`
	DownloadRate       *tagflag.Bytes `help:"max bytes per second down from peers"`
	PackedBlocklist    string
	PublicIP           net.IP
	Progress           bool `default:"true"`
	PieceStates        bool
	Quiet              bool  `help:"discard client logging"`
	Stats              *bool `help:"print stats at termination"`
	Dht                bool  `default:"true"`

	TcpPeers        bool `default:"true"`
	UtpPeers        bool `default:"true"`
	Webtorrent      bool `default:"true"`
	DisableWebseeds bool

	Ipv4 bool `default:"true"`
	Ipv6 bool `default:"true"`
	Pex  bool `default:"true"`

	File    []string
	Torrent []string `arity:"+" help:"torrent file path or magnet uri" arg:"positional"`
}

func stdoutAndStderrAreSameFile() bool {
	fi1, _ := os.Stdout.Stat()
	fi2, _ := os.Stderr.Stat()
	return os.SameFile(fi1, fi2)
}

func statsEnabled(flags downloadFlags) bool {
	if flags.Stats == nil {
		return flags.Debug
	}
	return *flags.Stats
}

func exitSignalHandlers(notify *missinggo.SynchronizedEvent) {
	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
	for {
		log.Printf("close signal received: %+v", <-c)
		notify.Set()
	}
}

func main() {
	defer envpprof.Stop()
	if err := mainErr(); err != nil {
		log.Printf("error in main: %v", err)
		os.Exit(1)
	}
}

func mainErr() error {
	stdLog.SetFlags(stdLog.Flags() | stdLog.Lshortfile)
	debug := args.Flag(args.FlagOpt{Long: "debug"})
	p := args.ParseMain(
		debug,
		args.Subcommand("metainfo", metainfoCmd),
		args.Subcommand("announce", func(p args.SubCmdCtx) error {
			var cmd AnnounceCmd
			err := p.NewParser().AddParams(
				args.Pos("tracker", &cmd.Tracker),
				args.Pos("infohash", &cmd.InfoHash)).Parse()
			if err != nil {
				return err
			}
			return announceErr(cmd)
		}),
		args.Subcommand("download", func(p args.SubCmdCtx) error {
			var dlf DownloadCmd
			err := p.NewParser().AddParams(
				append(args.FromStruct(&dlf), debug)...,
			).Parse()
			if err != nil {
				return err
			}
			return downloadErr(downloadFlags{
				Debug:       debug.Bool(),
				DownloadCmd: dlf,
			})
		}),
		args.Subcommand(
			"spew-bencoding",
			func(p args.SubCmdCtx) error {
				d := bencode.NewDecoder(os.Stdin)
				for i := 0; ; i++ {
					var v interface{}
					err := d.Decode(&v)
					if err == io.EOF {
						break
					}
					if err != nil {
						return fmt.Errorf("decoding message index %d: %w", i, err)
					}
					spew.Dump(v)
				}
				return nil
			},
			args.Help("reads bencoding from stdin into Go native types and spews the result"),
		),
		args.Subcommand("version", func(p args.SubCmdCtx) error {
			fmt.Printf("HTTP User-Agent: %q\n", version.DefaultHttpUserAgent)
			fmt.Printf("Torrent client version: %q\n", version.DefaultExtendedHandshakeClientVersion)
			fmt.Printf("Torrent version prefix: %q\n", version.DefaultBep20Prefix)
			return nil
		}),
	)
	if p.Err != nil {
		if errors.Is(p.Err, args.ErrHelped) {
			return nil
		}
		return p.Err
	}
	if !p.RanSubCmd {
		p.PrintChoices(os.Stderr)
		args.FatalUsage()
	}
	return nil
}

func downloadErr(flags downloadFlags) error {
	clientConfig := torrent.NewDefaultClientConfig()
	clientConfig.DisableWebseeds = flags.DisableWebseeds
	clientConfig.DisableTCP = !flags.TcpPeers
	clientConfig.DisableUTP = !flags.UtpPeers
	clientConfig.DisableIPv4 = !flags.Ipv4
	clientConfig.DisableIPv6 = !flags.Ipv6
	clientConfig.DisableAcceptRateLimiting = true
	clientConfig.NoDHT = !flags.Dht
	clientConfig.Debug = flags.Debug
	clientConfig.Seed = flags.Seed
	clientConfig.PublicIp4 = flags.PublicIP
	clientConfig.PublicIp6 = flags.PublicIP
	clientConfig.DisablePEX = !flags.Pex
	clientConfig.DisableWebtorrent = !flags.Webtorrent
	if flags.PackedBlocklist != "" {
		blocklist, err := iplist.MMapPackedFile(flags.PackedBlocklist)
		if err != nil {
			return fmt.Errorf("loading blocklist: %v", err)
		}
		defer blocklist.Close()
		clientConfig.IPBlocklist = blocklist
	}
	if flags.Mmap {
		clientConfig.DefaultStorage = storage.NewMMap("")
	}
	if flags.Addr != "" {
		clientConfig.SetListenAddr(flags.Addr)
	}
	if flags.UploadRate != nil {
		clientConfig.UploadRateLimiter = rate.NewLimiter(rate.Limit(*flags.UploadRate), 256<<10)
	}
	if flags.DownloadRate != nil {
		clientConfig.DownloadRateLimiter = rate.NewLimiter(rate.Limit(*flags.DownloadRate), 1<<20)
	}
	if flags.Quiet {
		clientConfig.Logger = log.Discard
	}
	clientConfig.MaxUnverifiedBytes = flags.MaxUnverifiedBytes.Int64()

	var stop missinggo.SynchronizedEvent
	defer func() {
		stop.Set()
	}()

	client, err := torrent.NewClient(clientConfig)
	if err != nil {
		return fmt.Errorf("creating client: %w", err)
	}
	var clientClose sync.Once //In certain situations, close was being called more than once.
	defer clientClose.Do(client.Close)
	go exitSignalHandlers(&stop)
	go func() {
		<-stop.C()
		clientClose.Do(client.Close)
	}()

	// Write status on the root path on the default HTTP muxer. This will be bound to localhost
	// somewhere if GOPPROF is set, thanks to the envpprof import.
	http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		client.WriteStatus(w)
	})
	err = addTorrents(client, flags)
	if err != nil {
		return fmt.Errorf("adding torrents: %w", err)
	}
	defer outputStats(client, flags)
	if client.WaitAll() {
		log.Print("downloaded ALL the torrents")
	} else {
		return errors.New("y u no complete torrents?!")
	}
	if flags.Seed {
		if len(client.Torrents()) == 0 {
			log.Print("no torrents to seed")
		} else {
			outputStats(client, flags)
			<-stop.C()
		}
	}
	spew.Dump(expvar.Get("torrent").(*expvar.Map).Get("chunks received"))
	spew.Dump(client.ConnStats())
	clStats := client.ConnStats()
	sentOverhead := clStats.BytesWritten.Int64() - clStats.BytesWrittenData.Int64()
	log.Printf(
		"client read %v, %v was useful data. sent %v non-data bytes",
		humanize.Bytes(uint64(clStats.BytesRead.Int64())),
		100*float64(clStats.BytesReadUsefulData.Int64())/float64(clStats.BytesRead.Int64()),
		humanize.Bytes(uint64(sentOverhead)))
	return nil
}

func outputStats(cl *torrent.Client, args downloadFlags) {
	if !statsEnabled(args) {
		return
	}
	expvar.Do(func(kv expvar.KeyValue) {
		fmt.Printf("%s: %s\n", kv.Key, kv.Value)
	})
	cl.WriteStatus(os.Stdout)
}
<commit_msg>cmd/torrent: Include download stats on interrupt<commit_after>// Downloads torrents from the command-line.
package main

import (
	"errors"
	"expvar"
	"fmt"
	"io"
	stdLog "log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"sync"
	"syscall"
	"time"

	"github.com/anacrolix/args"
	"github.com/anacrolix/envpprof"
	"github.com/anacrolix/log"
	"github.com/anacrolix/missinggo/v2"
	"github.com/anacrolix/tagflag"
	"github.com/anacrolix/torrent/bencode"
	"github.com/anacrolix/torrent/version"
	"github.com/davecgh/go-spew/spew"
	"github.com/dustin/go-humanize"
	"golang.org/x/time/rate"

	"github.com/anacrolix/torrent"
	"github.com/anacrolix/torrent/iplist"
	"github.com/anacrolix/torrent/metainfo"
	"github.com/anacrolix/torrent/storage"
)

func torrentBar(t *torrent.Torrent, pieceStates bool) {
	go func() {
		start := time.Now()
		if t.Info() == nil {
			fmt.Printf("%v: getting torrent info for %q\n", time.Since(start), t.Name())
			<-t.GotInfo()
		}
		lastStats := t.Stats()
		var lastLine string
		for range time.Tick(time.Second) {
			var completedPieces, partialPieces int
			psrs := t.PieceStateRuns()
			for _, r := range psrs {
				if r.Complete {
					completedPieces += r.Length
				}
				if r.Partial {
					partialPieces += r.Length
				}
			}
			stats := t.Stats()
			line := fmt.Sprintf(
				"%v: downloading %q: %s/%s, %d/%d pieces completed (%d partial): %v/s\n",
				time.Since(start),
				t.Name(),
				humanize.Bytes(uint64(t.BytesCompleted())),
				humanize.Bytes(uint64(t.Length())),
				completedPieces,
				t.NumPieces(),
				partialPieces,
				humanize.Bytes(uint64(stats.BytesReadUsefulData.Int64()-lastStats.BytesReadUsefulData.Int64())),
			)
			if line != lastLine {
				lastLine = line
				os.Stdout.WriteString(line)
			}
			if pieceStates {
				fmt.Println(psrs)
			}
			lastStats = stats
		}
	}()
}

type stringAddr string

func (stringAddr) Network() string   { return "" }
func (me stringAddr) String() string { return string(me) }

func resolveTestPeers(addrs []string) (ret []torrent.PeerInfo) {
	for _, ta := range addrs {
		ret = append(ret, torrent.PeerInfo{
			Addr: stringAddr(ta),
		})
	}
	return
}

func addTorrents(client *torrent.Client, flags downloadFlags) error {
	testPeers := resolveTestPeers(flags.TestPeer)
	for _, arg := range flags.Torrent {
		t, err := func() (*torrent.Torrent, error) {
			if strings.HasPrefix(arg, "magnet:") {
				t, err := client.AddMagnet(arg)
				if err != nil {
					return nil, fmt.Errorf("error adding magnet: %w", err)
				}
				return t, nil
			} else if strings.HasPrefix(arg, "http://") || strings.HasPrefix(arg, "https://") {
				response, err := http.Get(arg)
				if err != nil {
					return nil, fmt.Errorf("Error downloading torrent file: %s", err)
				}

				metaInfo, err := metainfo.Load(response.Body)
				defer response.Body.Close()
				if err != nil {
					return nil, fmt.Errorf("error loading torrent file %q: %s\n", arg, err)
				}
				t, err := client.AddTorrent(metaInfo)
				if err != nil {
					return nil, fmt.Errorf("adding torrent: %w", err)
				}
				return t, nil
			} else if strings.HasPrefix(arg, "infohash:") {
				t, _ := client.AddTorrentInfoHash(metainfo.NewHashFromHex(strings.TrimPrefix(arg, "infohash:")))
				return t, nil
			} else {
				metaInfo, err := metainfo.LoadFromFile(arg)
				if err != nil {
					return nil, fmt.Errorf("error loading torrent file %q: %s\n", arg, err)
				}
				t, err := client.AddTorrent(metaInfo)
				if err != nil {
					return nil, fmt.Errorf("adding torrent: %w", err)
				}
				return t, nil
			}
		}()
		if err != nil {
			return fmt.Errorf("adding torrent for %q: %w", arg, err)
		}
		if flags.Progress {
			torrentBar(t, flags.PieceStates)
		}
		t.AddPeers(testPeers)
		go func() {
			<-t.GotInfo()
			if len(flags.File) == 0 {
				t.DownloadAll()
			} else {
				for _, f := range t.Files() {
					for _, fileArg := range flags.File {
						if f.DisplayPath() == fileArg {
							f.Download()
						}
					}
				}
			}
		}()
	}
	return nil
}

type downloadFlags struct {
	Debug bool
	DownloadCmd
}

type DownloadCmd struct {
	Mmap               bool           `help:"memory-map torrent data"`
	TestPeer           []string       `help:"addresses of some starting peers"`
	Seed               bool           `help:"seed after download is complete"`
	Addr               string         `help:"network listen addr"`
	MaxUnverifiedBytes tagflag.Bytes  `help:"maximum number bytes to have pending verification"`
	UploadRate         *tagflag.Bytes `help:"max piece bytes to send per second"`
	DownloadRate       *tagflag.Bytes `help:"max bytes per second down from peers"`
	PackedBlocklist    string
	PublicIP           net.IP
	Progress           bool `default:"true"`
	PieceStates        bool
	Quiet              bool  `help:"discard client logging"`
	Stats              *bool `help:"print stats at termination"`
	Dht                bool  `default:"true"`

	TcpPeers        bool `default:"true"`
	UtpPeers        bool `default:"true"`
	Webtorrent      bool `default:"true"`
	DisableWebseeds bool

	Ipv4 bool `default:"true"`
	Ipv6 bool `default:"true"`
	Pex  bool `default:"true"`

	File    []string
	Torrent []string `arity:"+" help:"torrent file path or magnet uri" arg:"positional"`
}

func stdoutAndStderrAreSameFile() bool {
	fi1, _ := os.Stdout.Stat()
	fi2, _ := os.Stderr.Stat()
	return os.SameFile(fi1, fi2)
}

func statsEnabled(flags downloadFlags) bool {
	if flags.Stats == nil {
		return flags.Debug
	}
	return *flags.Stats
}

func exitSignalHandlers(notify *missinggo.SynchronizedEvent) {
	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
	for {
		log.Printf("close signal received: %+v", <-c)
		notify.Set()
	}
}

func main() {
	defer envpprof.Stop()
	if err := mainErr(); err != nil {
		log.Printf("error in main: %v", err)
		os.Exit(1)
	}
}

func mainErr() error {
	stdLog.SetFlags(stdLog.Flags() | stdLog.Lshortfile)
	debug := args.Flag(args.FlagOpt{Long: "debug"})
	p := args.ParseMain(
		debug,
		args.Subcommand("metainfo", metainfoCmd),
		args.Subcommand("announce", func(p args.SubCmdCtx) error {
			var cmd AnnounceCmd
			err := p.NewParser().AddParams(
				args.Pos("tracker", &cmd.Tracker),
				args.Pos("infohash", &cmd.InfoHash)).Parse()
			if err != nil {
				return err
			}
			return announceErr(cmd)
		}),
		args.Subcommand("download", func(p args.SubCmdCtx) error {
			var dlf DownloadCmd
			err := p.NewParser().AddParams(
				append(args.FromStruct(&dlf), debug)...,
			).Parse()
			if err != nil {
				return err
			}
			return downloadErr(downloadFlags{
				Debug:       debug.Bool(),
				DownloadCmd: dlf,
			})
		}),
		args.Subcommand(
			"spew-bencoding",
			func(p args.SubCmdCtx) error {
				d := bencode.NewDecoder(os.Stdin)
				for i := 0; ; i++ {
					var v interface{}
					err := d.Decode(&v)
					if err == io.EOF {
						break
					}
					if err != nil {
						return fmt.Errorf("decoding message index %d: %w", i, err)
					}
					spew.Dump(v)
				}
				return nil
			},
			args.Help("reads bencoding from stdin into Go native types and spews the result"),
		),
		args.Subcommand("version", func(p args.SubCmdCtx) error {
			fmt.Printf("HTTP User-Agent: %q\n", version.DefaultHttpUserAgent)
			fmt.Printf("Torrent client version: %q\n", version.DefaultExtendedHandshakeClientVersion)
			fmt.Printf("Torrent version prefix: %q\n", version.DefaultBep20Prefix)
			return nil
		}),
	)
	if p.Err != nil {
		if errors.Is(p.Err, args.ErrHelped) {
			return nil
		}
		return p.Err
	}
	if !p.RanSubCmd {
		p.PrintChoices(os.Stderr)
		args.FatalUsage()
	}
	return nil
}

func downloadErr(flags downloadFlags) error {
	clientConfig := torrent.NewDefaultClientConfig()
	clientConfig.DisableWebseeds = flags.DisableWebseeds
	clientConfig.DisableTCP = !flags.TcpPeers
	clientConfig.DisableUTP = !flags.UtpPeers
	clientConfig.DisableIPv4 = !flags.Ipv4
	clientConfig.DisableIPv6 = !flags.Ipv6
	clientConfig.DisableAcceptRateLimiting = true
	clientConfig.NoDHT = !flags.Dht
	clientConfig.Debug = flags.Debug
	clientConfig.Seed = flags.Seed
	clientConfig.PublicIp4 = flags.PublicIP
	clientConfig.PublicIp6 = flags.PublicIP
	clientConfig.DisablePEX = !flags.Pex
	clientConfig.DisableWebtorrent = !flags.Webtorrent
	if flags.PackedBlocklist != "" {
		blocklist, err := iplist.MMapPackedFile(flags.PackedBlocklist)
		if err != nil {
			return fmt.Errorf("loading blocklist: %v", err)
		}
		defer blocklist.Close()
		clientConfig.IPBlocklist = blocklist
	}
	if flags.Mmap {
		clientConfig.DefaultStorage = storage.NewMMap("")
	}
	if flags.Addr != "" {
		clientConfig.SetListenAddr(flags.Addr)
	}
	if flags.UploadRate != nil {
		clientConfig.UploadRateLimiter = rate.NewLimiter(rate.Limit(*flags.UploadRate), 256<<10)
	}
	if flags.DownloadRate != nil {
		clientConfig.DownloadRateLimiter = rate.NewLimiter(rate.Limit(*flags.DownloadRate), 1<<20)
	}
	if flags.Quiet {
		clientConfig.Logger = log.Discard
	}
	clientConfig.MaxUnverifiedBytes = flags.MaxUnverifiedBytes.Int64()

	var stop missinggo.SynchronizedEvent
	defer func() {
		stop.Set()
	}()

	client, err := torrent.NewClient(clientConfig)
	if err != nil {
		return fmt.Errorf("creating client: %w", err)
	}
	var clientClose sync.Once //In certain situations, close was being called more than once.
	defer clientClose.Do(client.Close)
	go exitSignalHandlers(&stop)
	go func() {
		<-stop.C()
		clientClose.Do(client.Close)
	}()

	// Write status on the root path on the default HTTP muxer. This will be bound to localhost
	// somewhere if GOPPROF is set, thanks to the envpprof import.
	http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		client.WriteStatus(w)
	})
	err = addTorrents(client, flags)
	if err != nil {
		return fmt.Errorf("adding torrents: %w", err)
	}
	defer outputStats(client, flags)
	if client.WaitAll() {
		log.Print("downloaded ALL the torrents")
	} else {
		err = errors.New("y u no complete torrents?!")
	}
	if flags.Seed {
		if len(client.Torrents()) == 0 {
			log.Print("no torrents to seed")
		} else {
			outputStats(client, flags)
			<-stop.C()
		}
	}
	spew.Dump(expvar.Get("torrent").(*expvar.Map).Get("chunks received"))
	spew.Dump(client.ConnStats())
	clStats := client.ConnStats()
	sentOverhead := clStats.BytesWritten.Int64() - clStats.BytesWrittenData.Int64()
	log.Printf(
		"client read %v, %v was useful data. sent %v non-data bytes",
		humanize.Bytes(uint64(clStats.BytesRead.Int64())),
		100*float64(clStats.BytesReadUsefulData.Int64())/float64(clStats.BytesRead.Int64()),
		humanize.Bytes(uint64(sentOverhead)))
	return err
}

func outputStats(cl *torrent.Client, args downloadFlags) {
	if !statsEnabled(args) {
		return
	}
	expvar.Do(func(kv expvar.KeyValue) {
		fmt.Printf("%s: %s\n", kv.Key, kv.Value)
	})
	cl.WriteStatus(os.Stdout)
}
<|endoftext|>


<commit_before>package main

import (
	_ "net/http/pprof"
	"os"

	"github.com/appscode/errors"
	err_logger "github.com/appscode/errors/h/log"
	"github.com/appscode/go/flags"
	"github.com/appscode/log"
	logs "github.com/appscode/log/golog"
	"github.com/appscode/voyager/cmd/voyager/app"
	"github.com/appscode/voyager/cmd/voyager/app/options"
	"github.com/mikespook/golib/signal"
	"github.com/spf13/pflag"
	// Add fake package as a dependency to add this under vendor
	_ "github.com/appscode/k8s-addons/client/clientset/fake"
	_ "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
)

func main() {
	config := options.NewConfig()
	config.AddFlags(pflag.CommandLine)

	flags.InitFlags()
	logs.InitLogs()
	defer logs.FlushLogs()
	errors.Handlers.Add(err_logger.LogHandler{})
	flags.DumpAll()

	if config.ProviderName == "" ||
		config.ClusterName == "" ||
		config.LoadbalancerImageName == "" {
		log.Fatalln("Required flag not provided.")
	}

	log.Infoln("Starting Voyager Controller...")
	go app.Run(config)
	sig := signal.New(nil)
	sig.Bind(os.Interrupt, func() uint { return signal.BreakExit })
	sig.Wait()
}
<commit_msg>cloud-provider & cloud-name is not required for unknown providers. (#65)<commit_after>package main

import (
	_ "net/http/pprof"
	"os"

	"github.com/appscode/errors"
	err_logger "github.com/appscode/errors/h/log"
	"github.com/appscode/go/flags"
	stringz "github.com/appscode/go/strings"
	"github.com/appscode/log"
	logs "github.com/appscode/log/golog"
	"github.com/appscode/voyager/cmd/voyager/app"
	"github.com/appscode/voyager/cmd/voyager/app/options"
	"github.com/mikespook/golib/signal"
	"github.com/spf13/pflag"
	// Add fake package as a dependency to add this under vendor
	_ "github.com/appscode/k8s-addons/client/clientset/fake"
	_ "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
)

func main() {
	config := options.NewConfig()
	config.AddFlags(pflag.CommandLine)

	flags.InitFlags()
	logs.InitLogs()
	defer logs.FlushLogs()
	errors.Handlers.Add(err_logger.LogHandler{})
	flags.DumpAll()

	if config.LoadbalancerImageName == "" {
		log.Fatalln("Missing required flag --haproxy-image")
	}
	if stringz.Contains([]string{"aws", "gce", "gke", "azure"}, config.ProviderName) && config.ClusterName == "" {
		log.Fatalln("--cluster-name flag must be set when --cloud-provider={aws,gce,gke,azure}")
	}

	log.Infoln("Starting Voyager Controller...")
	go app.Run(config)
	sig := signal.New(nil)
	sig.Bind(os.Interrupt, func() uint { return signal.BreakExit })
	sig.Wait()
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/url"
	"sync/atomic"
	"time"

	"github.com/hagen1778/chproxy/config"
	"github.com/hagen1778/chproxy/log"
)

func (s *scope) String() string {
	return fmt.Sprintf("[ Id: %d; User %q(%d) proxying as %q(%d) to %q(%d) ]",
		s.id,
		s.user.name, s.user.runningQueries(),
		s.clusterUser.name, s.clusterUser.runningQueries(),
		s.host.addr.Host, s.host.runningQueries())
}

type scope struct {
	id          uint64
	host        *host
	cluster     *cluster
	user        *user
	clusterUser *clusterUser
}

var scopeId uint64

func newScope(u *user, cu *clusterUser, c *cluster) *scope {
	return &scope{
		id:          atomic.AddUint64(&scopeId, 1),
		host:        c.getHost(),
		cluster:     c,
		user:        u,
		clusterUser: cu,
	}
}

func (s *scope) inc() error {
	s.user.inc()
	s.clusterUser.inc()
	s.host.inc()

	var err error
	if s.user.maxConcurrentQueries > 0 && s.user.runningQueries() > s.user.maxConcurrentQueries {
		err = fmt.Errorf("limits for user %q are exceeded: maxConcurrentQueries limit: %d", s.user.name, s.user.maxConcurrentQueries)
	}

	if s.clusterUser.maxConcurrentQueries > 0 && s.clusterUser.runningQueries() > s.clusterUser.maxConcurrentQueries {
		err = fmt.Errorf("limits for cluster user %q are exceeded: maxConcurrentQueries limit: %d", s.clusterUser.name, s.clusterUser.maxConcurrentQueries)
	}

	if err != nil {
		s.dec()
		return err
	}

	return nil
}

func (s *scope) dec() {
	s.host.dec()
	s.user.dec()
	s.clusterUser.dec()
}

type user struct {
	toUser          string
	toCluster       string
	allowedNetworks config.Networks

	name, password       string
	maxExecutionTime     time.Duration
	maxConcurrentQueries uint32

	queryCounter
}

type clusterUser struct {
	name, password       string
	maxExecutionTime     time.Duration
	maxConcurrentQueries uint32

	queryCounter
}

func (u *clusterUser) timeout() <-chan time.Time {
	if u.maxExecutionTime > 0 {
		return time.After(u.maxExecutionTime)
	}

	return nil
}

type host struct {
	addr *url.URL

	queryCounter
}

type cluster struct {
	nextIdx uint32
	hosts   []*host
	users   map[string]*clusterUser
}

func newCluster(h []*host, cu map[string]*clusterUser) *cluster {
	return &cluster{
		hosts:   h,
		users:   cu,
		nextIdx: uint32(time.Now().UnixNano()),
	}
}

// We don't use query_id because of distributed processing, the query ID is not passed to remote servers
func (c *cluster) killQueries(ua string, elapsed float64) {
	q := fmt.Sprintf("KILL QUERY WHERE http_user_agent = '%s' AND elapsed >= %d", ua, int(elapsed))
	log.Debugf("ExecutionTime exceeded. Going to call query %q for hosts %v", q, c.hosts)
	for _, host := range c.hosts {
		if err := doQuery(q, host.addr.String()); err != nil {
			log.Errorf("error while killing queries older than %.2fs: %s", elapsed, err)
		}
	}
}

// get least loaded + round-robin host from cluster
func (c *cluster) getHost() *host {
	idx := atomic.AddUint32(&c.nextIdx, 1)

	l := uint32(len(c.hosts))
	idx = idx % l
	idle := c.hosts[idx]
	idleN := idle.runningQueries()

	if idleN == 0 {
		return idle
	}

	// round checking of hosts in slice
	// until hits least loaded
	for i := (idx + 1) % l; i != idx; i = (i + 1) % l {
		h := c.hosts[i]
		n := h.runningQueries()
		if n == 0 {
			return h
		}
		if n < idleN {
			idle, idleN = h, n
		}
	}

	return idle
}

type queryCounter struct {
	value uint32
}

func (qc *queryCounter) runningQueries() uint32 {
	return atomic.LoadUint32(&qc.value)
}

func (qc *queryCounter) inc() {
	atomic.AddUint32(&qc.value, 1)
}

func (qc *queryCounter) dec() {
	atomic.AddUint32(&qc.value, ^uint32(0))
}
<commit_msg>fix comment<commit_after>package main

import (
	"fmt"
	"net/url"
	"sync/atomic"
	"time"

	"github.com/hagen1778/chproxy/config"
	"github.com/hagen1778/chproxy/log"
)

func (s *scope) String() string {
	return fmt.Sprintf("[ Id: %d; User %q(%d) proxying as %q(%d) to %q(%d) ]",
		s.id,
		s.user.name, s.user.runningQueries(),
		s.clusterUser.name, s.clusterUser.runningQueries(),
		s.host.addr.Host, s.host.runningQueries())
}

type scope struct {
	id          uint64
	host        *host
	cluster     *cluster
	user        *user
	clusterUser *clusterUser
}

var scopeId uint64

func newScope(u *user, cu *clusterUser, c *cluster) *scope {
	return &scope{
		id:          atomic.AddUint64(&scopeId, 1),
		host:        c.getHost(),
		cluster:     c,
		user:        u,
		clusterUser: cu,
	}
}

func (s *scope) inc() error {
	s.user.inc()
	s.clusterUser.inc()
	s.host.inc()

	var err error
	if s.user.maxConcurrentQueries > 0 && s.user.runningQueries() > s.user.maxConcurrentQueries {
		err = fmt.Errorf("limits for user %q are exceeded: maxConcurrentQueries limit: %d", s.user.name, s.user.maxConcurrentQueries)
	}

	if s.clusterUser.maxConcurrentQueries > 0 && s.clusterUser.runningQueries() > s.clusterUser.maxConcurrentQueries {
		err = fmt.Errorf("limits for cluster user %q are exceeded: maxConcurrentQueries limit: %d", s.clusterUser.name, s.clusterUser.maxConcurrentQueries)
	}

	if err != nil {
		s.dec()
		return err
	}

	return nil
}

func (s *scope) dec() {
	s.host.dec()
	s.user.dec()
	s.clusterUser.dec()
}

type user struct {
	toUser          string
	toCluster       string
	allowedNetworks config.Networks

	name, password       string
	maxExecutionTime     time.Duration
	maxConcurrentQueries uint32

	queryCounter
}

type clusterUser struct {
	name, password       string
	maxExecutionTime     time.Duration
	maxConcurrentQueries uint32

	queryCounter
}

func (u *clusterUser) timeout() <-chan time.Time {
	if u.maxExecutionTime > 0 {
		return time.After(u.maxExecutionTime)
	}

	return nil
}

type host struct {
	addr *url.URL

	queryCounter
}

type cluster struct {
	nextIdx uint32
	hosts   []*host
	users   map[string]*clusterUser
}

func newCluster(h []*host, cu map[string]*clusterUser) *cluster {
	return &cluster{
		hosts:   h,
		users:   cu,
		nextIdx: uint32(time.Now().UnixNano()),
	}
}

// We don't use query_id because of distributed processing, the query ID is not passed to remote servers
func (c *cluster) killQueries(ua string, elapsed float64) {
	q := fmt.Sprintf("KILL QUERY WHERE http_user_agent = '%s' AND elapsed >= %d", ua, int(elapsed))
	log.Debugf("ExecutionTime exceeded. Going to call query %q for hosts %v", q, c.hosts)
	for _, host := range c.hosts {
		if err := doQuery(q, host.addr.String()); err != nil {
			log.Errorf("error while killing queries older than %.2fs: %s", elapsed, err)
		}
	}
}

// get least loaded + round-robin host from cluster
func (c *cluster) getHost() *host {
	idx := atomic.AddUint32(&c.nextIdx, 1)

	l := uint32(len(c.hosts))
	idx = idx % l
	idle := c.hosts[idx]
	idleN := idle.runningQueries()

	if idleN == 0 {
		return idle
	}

	// round hosts checking
	// until the least loaded is found
	for i := (idx + 1) % l; i != idx; i = (i + 1) % l {
		h := c.hosts[i]
		n := h.runningQueries()
		if n == 0 {
			return h
		}
		if n < idleN {
			idle, idleN = h, n
		}
	}

	return idle
}

type queryCounter struct {
	value uint32
}

func (qc *queryCounter) runningQueries() uint32 {
	return atomic.LoadUint32(&qc.value)
}

func (qc *queryCounter) inc() {
	atomic.AddUint32(&qc.value, 1)
}

func (qc *queryCounter) dec() {
	atomic.AddUint32(&qc.value, ^uint32(0))
}
<|endoftext|>


<commit_before>// Copyright 2009 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package eval

import (
	"go/token"
	"log"
)

/*
 * Blocks and scopes
 */

// A definition can be a *Variable, *Constant, or Type.
type Def interface {
	Pos() token.Pos
}

type Variable struct {
	VarPos token.Pos
	// Index of this variable in the Frame structure
	Index int
	// Static type of this variable
	Type Type
	// Value of this variable.  This is only used by Scope.NewFrame;
	// therefore, it is useful for global scopes but cannot be used
	// in function scopes.
	Init Value
}

func (v *Variable) Pos() token.Pos {
	return v.VarPos
}

type Constant struct {
	ConstPos token.Pos
	Type     Type
	Value    Value
}

func (c *Constant) Pos() token.Pos {
	return c.ConstPos
}

type PkgIdent struct {
	PkgPos token.Pos
	path   string // the unique path to that package
	scope  *Scope // the scope holding the package definition(s)
}

func (p *PkgIdent) Pos() token.Pos {
	return p.PkgPos
}

// A block represents a definition block in which a name may not be
// defined more than once.
type block struct {
	// The block enclosing this one, including blocks in other
	// scopes.
	outer *block
	// The nested block currently being compiled, or nil.
	inner *block
	// The Scope containing this block.
	scope *Scope
	// The Variables, Constants, and Types defined in this block.
	defs map[string]Def
	// The index of the first variable defined in this block.
	// This must be greater than the index of any variable defined
	// in any parent of this block within the same Scope at the
	// time this block is entered.
	offset int
	// The number of Variables defined in this block.
	numVars int
	// If global, do not allocate new vars and consts in
	// the frame; assume that the refs will be compiled in
	// using defs[name].Init.
	global bool
}

// A Scope is the compile-time analogue of a Frame, which captures
// some subtree of blocks.
type Scope struct {
	// The root block of this scope.
	*block
	// The maximum number of variables required at any point in
	// this Scope.  This determines the number of slots needed in
	// Frame's created from this Scope at run-time.
	maxVars int
}

// A special scope for the universe, to be able to stash 'packages'
type universeScope struct {
	*Scope
	// a lookup-table for easy retrieval of packages by their 'path'
	pkgs map[string]*Scope
}

func (b *block) enterChild() *block {
	if b.inner != nil && b.inner.scope == b.scope {
		log.Panic("Failed to exit child block before entering another child")
	}
	sub := &block{
		outer:  b,
		scope:  b.scope,
		defs:   make(map[string]Def),
		offset: b.offset + b.numVars,
	}
	b.inner = sub
	return sub
}

func (b *block) exit() {
	if b.outer == nil {
		log.Panic("Cannot exit top-level block")
	}
	if b.outer.scope == b.scope {
		if b.outer.inner != b {
			log.Panic("Already exited block")
		}
		if b.inner != nil && b.inner.scope == b.scope {
			log.Panic("Exit of parent block without exit of child block")
		}
	}
	b.outer.inner = nil
}

func (b *block) ChildScope() *Scope {
	if b.inner != nil && b.inner.scope == b.scope {
		log.Panic("Failed to exit child block before entering a child scope")
	}
	sub := b.enterChild()
	sub.offset = 0
	sub.scope = &Scope{sub, 0}
	return sub.scope
}

func (b *block) DefineVar(name string, pos token.Pos, t Type) (*Variable, Def) {
	if prev, ok := b.defs[name]; ok {
		return nil, prev
	}
	v := b.defineSlot(t, false)
	v.VarPos = pos
	b.defs[name] = v
	return v, nil
}

func (b *block) DefineTemp(t Type) *Variable { return b.defineSlot(t, true) }

func (b *block) defineSlot(t Type, temp bool) *Variable {
	if b.inner != nil && b.inner.scope == b.scope {
		log.Panic("Failed to exit child block before defining variable")
	}
	index := -1
	if !b.global || temp {
		index = b.offset + b.numVars
		b.numVars++
		if index >= b.scope.maxVars {
			b.scope.maxVars = index + 1
		}
	}
	v := &Variable{token.NoPos, index, t, nil}
	return v
}

func (b *block) DefineConst(name string, pos token.Pos, t Type, v Value) (*Constant, Def) {
	if prev, ok := b.defs[name]; ok {
		return nil, prev
	}
	c := &Constant{pos, t, v}
	b.defs[name] = c
	return c, nil
}

func (b *block) DefineType(name string, pos token.Pos, t Type) Type {
	if _, ok := b.defs[name]; ok {
		return nil
	}
	nt := &NamedType{pos, name, nil, true, make(map[string]Method)}
	if t != nil {
		nt.Complete(t)
	}
	b.defs[name] = nt
	return nt
}

func (b *block) DefinePackage(id, path string, pos token.Pos) (*PkgIdent, Def) {
	if prev, ok := b.defs[id]; ok {
		return nil, prev
	}
	p := &PkgIdent{pos, path, universe.pkgs[path]}
	b.defs[id] = p
	return p, nil
}

func (b *block) Lookup(name string) (bl *block, level int, def Def) {
	for b != nil {
		if d, ok := b.defs[name]; ok {
			return b, level, d
		}
		if b.outer != nil && b.scope != b.outer.scope {
			level++
		}
		b = b.outer
	}
	return nil, 0, nil
}

func (s *Scope) NewFrame(outer *Frame) *Frame { return outer.child(s.maxVars) }

/*
 * Frames
 */

type Frame struct {
	Outer *Frame
	Vars  []Value
}

func (f *Frame) Get(level int, index int) Value {
	for ; level > 0; level-- {
		f = f.Outer
	}
	return f.Vars[index]
}

func (f *Frame) child(numVars int) *Frame {
	// TODO(austin) This is probably rather expensive.  All values
	// require heap allocation and zeroing them when we execute a
	// definition typically requires some computation.
	return &Frame{f, make([]Value, numVars)}
}
<commit_msg>block: allow to undefine definitions<commit_after>// Copyright 2009 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package eval

import (
	"go/token"
	"log"
)

/*
 * Blocks and scopes
 */

// A definition can be a *Variable, *Constant, or Type.
type Def interface {
	Pos() token.Pos
}

type Variable struct {
	VarPos token.Pos
	// Index of this variable in the Frame structure
	Index int
	// Static type of this variable
	Type Type
	// Value of this variable.  This is only used by Scope.NewFrame;
	// therefore, it is useful for global scopes but cannot be used
	// in function scopes.
	Init Value
}

func (v *Variable) Pos() token.Pos {
	return v.VarPos
}

type Constant struct {
	ConstPos token.Pos
	Type     Type
	Value    Value
}

func (c *Constant) Pos() token.Pos {
	return c.ConstPos
}

type PkgIdent struct {
	PkgPos token.Pos
	path   string // the unique path to that package
	scope  *Scope // the scope holding the package definition(s)
}

func (p *PkgIdent) Pos() token.Pos {
	return p.PkgPos
}

// A block represents a definition block in which a name may not be
// defined more than once.
type block struct {
	// The block enclosing this one, including blocks in other
	// scopes.
	outer *block
	// The nested block currently being compiled, or nil.
	inner *block
	// The Scope containing this block.
	scope *Scope
	// The Variables, Constants, and Types defined in this block.
	defs map[string]Def
	// The index of the first variable defined in this block.
	// This must be greater than the index of any variable defined
	// in any parent of this block within the same Scope at the
	// time this block is entered.
	offset int
	// The number of Variables defined in this block.
	numVars int
	// If global, do not allocate new vars and consts in
	// the frame; assume that the refs will be compiled in
	// using defs[name].Init.
	global bool
}

// A Scope is the compile-time analogue of a Frame, which captures
// some subtree of blocks.
type Scope struct {
	// The root block of this scope.
	*block
	// The maximum number of variables required at any point in
	// this Scope.  This determines the number of slots needed in
	// Frame's created from this Scope at run-time.
	maxVars int
}

// A special scope for the universe, to be able to stash 'packages'
type universeScope struct {
	*Scope
	// a lookup-table for easy retrieval of packages by their 'path'
	pkgs map[string]*Scope
}

func (b *block) enterChild() *block {
	if b.inner != nil && b.inner.scope == b.scope {
		log.Panic("Failed to exit child block before entering another child")
	}
	sub := &block{
		outer:  b,
		scope:  b.scope,
		defs:   make(map[string]Def),
		offset: b.offset + b.numVars,
	}
	b.inner = sub
	return sub
}

func (b *block) exit() {
	if b.outer == nil {
		log.Panic("Cannot exit top-level block")
	}
	if b.outer.scope == b.scope {
		if b.outer.inner != b {
			log.Panic("Already exited block")
		}
		if b.inner != nil && b.inner.scope == b.scope {
			log.Panic("Exit of parent block without exit of child block")
		}
	}
	b.outer.inner = nil
}

func (b *block) ChildScope() *Scope {
	if b.inner != nil && b.inner.scope == b.scope {
		log.Panic("Failed to exit child block before entering a child scope")
	}
	sub := b.enterChild()
	sub.offset = 0
	sub.scope = &Scope{sub, 0}
	return sub.scope
}

func (b *block) undefine(name string) {
	delete(b.defs, name)
}

func (b *block) DefineVar(name string, pos token.Pos, t Type) (*Variable, Def) {
	if prev, ok := b.defs[name]; ok {
		return nil, prev
	}
	v := b.defineSlot(t, false)
	v.VarPos = pos
	b.defs[name] = v
	return v, nil
}

func (b *block) DefineTemp(t Type) *Variable { return b.defineSlot(t, true) }

func (b *block) defineSlot(t Type, temp bool) *Variable {
	if b.inner != nil && b.inner.scope == b.scope {
		log.Panic("Failed to exit child block before defining variable")
	}
	index := -1
	if !b.global || temp {
		index = b.offset + b.numVars
		b.numVars++
		if index >= b.scope.maxVars {
			b.scope.maxVars = index + 1
		}
	}
	v := &Variable{token.NoPos, index, t, nil}
	return v
}

func (b *block) DefineConst(name string, pos token.Pos, t Type, v Value) (*Constant, Def) {
	if prev, ok := b.defs[name]; ok {
		return nil, prev
	}
	c := &Constant{pos, t, v}
	b.defs[name] = c
	return c, nil
}

func (b *block) DefineType(name string, pos token.Pos, t Type) Type {
	if _, ok := b.defs[name]; ok {
		return nil
	}
	nt := &NamedType{pos, name, nil, true, make(map[string]Method)}
	if t != nil {
		nt.Complete(t)
	}
	b.defs[name] = nt
	return nt
}

func (b *block) DefinePackage(id, path string, pos token.Pos) (*PkgIdent, Def) {
	if prev, ok := b.defs[id]; ok {
		return nil, prev
	}
	p := &PkgIdent{pos, path, universe.pkgs[path]}
	b.defs[id] = p
	return p, nil
}

func (b *block) Lookup(name string) (bl *block, level int, def Def) {
	for b != nil {
		if d, ok := b.defs[name]; ok {
			return b, level, d
		}
		if b.outer != nil && b.scope != b.outer.scope {
			level++
		}
		b = b.outer
	}
	return nil, 0, nil
}

func (s *Scope) NewFrame(outer *Frame) *Frame { return outer.child(s.maxVars) }

/*
 * Frames
 */

type Frame struct {
	Outer *Frame
	Vars  []Value
}

func (f *Frame) Get(level int, index int) Value {
	for ; level > 0; level-- {
		f = f.Outer
	}
	return f.Vars[index]
}

func (f *Frame) child(numVars int) *Frame {
	// TODO(austin) This is probably rather expensive.  All values
	// require heap allocation and zeroing them when we execute a
	// definition typically requires some computation.
	return &Frame{f, make([]Value, numVars)}
}
<|endoftext|>


<commit_before>package fuzzaldrin

import (
	"math"
	"os"
	"strings"
)

func basenameScore(str, query string, _score float64) float64 {
	index := len(str) - 1
	for os.IsPathSeparator(str[index]) {
		index--
	}
	slashCount := 0
	lastCharacter := index
	var base string
	for index >= 0 {
		if os.IsPathSeparator(str[index]) {
			slashCount++
			base = str[index+1 : lastCharacter+1]
		} else if index == 0 {
			if lastCharacter < len(str)-1 {
				base = str[0 : lastCharacter+1]
			} else {
				base = str
			}
		}
		index--
	}

	// Basename matches count for more.
	if base == str {
		_score *= 2
	} else if base != "" {
		_score += score(base, query)
	}

	// Shallow files are scored higher
	segmentCount := slashCount + 1
	depth := math.Max(1.0, float64(10-segmentCount))
	_score *= depth * 0.01
	return _score
}

func score(str, query string) float64 {
	if str == query {
		return 1
	}

	if queryIsLastPathSegment(str, query) {
		return 1
	}

	totalScore := 0.0
	queryLength := len(query)
	strLength := len(str)

	indexInQuery := 0
	indexInStr := 0

	for indexInQuery < queryLength {
		ch := query[indexInQuery]
		indexInQuery++
		lcIndex := strings.Index(str, strings.ToLower(string(ch)))
		ucIndex := strings.Index(str, strings.ToUpper(string(ch)))
		minIndex := math.Min(float64(lcIndex), float64(ucIndex))
		if minIndex == -1 {
			minIndex = math.Max(float64(lcIndex), float64(ucIndex))
		}
		indexInStr = int(minIndex)
		if indexInStr == -1 {
			return 0
		}

		chScore := 0.1

		if str[indexInStr] == ch {
			chScore += 0.1
		}

		if indexInStr == 0 || os.IsPathSeparator(str[indexInStr-1]) {
			// Start of string bonus
			chScore += 0.8
		} else if strings.Contains("-_ ", string(str[indexInStr-1])) {
			// Start of word bonus
			chScore += 0.7
		}

		// Trim string to after current abbreviation match
		str = str[indexInStr+1 : len(str)]

		totalScore += chScore
	}

	queryScore := totalScore / float64(queryLength)
	return ((queryScore * (float64(queryLength) / float64(strLength))) + queryScore) / 2.0
}

func queryIsLastPathSegment(str, query string) bool {
	if len(str) <= len(query) {
		return false
	}
	if os.IsPathSeparator(str[len(str)-len(query)-1]) {
		return strings.Index(str, query) == (len(str) - len(query))
	}
	return false
}
<commit_msg>Fix variable name<commit_after>package fuzzaldrin

import (
	"math"
	"os"
	"strings"
)

func basenameScore(str, query string, calcScore float64) float64 {
	index := len(str) - 1
	for os.IsPathSeparator(str[index]) {
		index--
	}
	slashCount := 0
	lastCharacter := index
	var base string
	for index >= 0 {
		if os.IsPathSeparator(str[index]) {
			slashCount++
			base = str[index+1 : lastCharacter+1]
		} else if index == 0 {
			if lastCharacter < len(str)-1 {
				base = str[0 : lastCharacter+1]
			} else {
				base = str
			}
		}
		index--
	}

	// Basename matches count for more.
	if base == str {
		calcScore *= 2
	} else if base != "" {
		calcScore += score(base, query)
	}

	// Shallow files are scored higher
	segmentCount := slashCount + 1
	depth := math.Max(1.0, float64(10-segmentCount))
	calcScore *= depth * 0.01
	return calcScore
}

func score(str, query string) float64 {
	if str == query {
		return 1
	}

	if queryIsLastPathSegment(str, query) {
		return 1
	}

	totalScore := 0.0
	queryLength := len(query)
	strLength := len(str)

	indexInQuery := 0
	indexInStr := 0

	for indexInQuery < queryLength {
		ch := query[indexInQuery]
		indexInQuery++
		lcIndex := strings.Index(str, strings.ToLower(string(ch)))
		ucIndex := strings.Index(str, strings.ToUpper(string(ch)))
		minIndex := math.Min(float64(lcIndex), float64(ucIndex))
		if minIndex == -1 {
			minIndex = math.Max(float64(lcIndex), float64(ucIndex))
		}
		indexInStr = int(minIndex)
		if indexInStr == -1 {
			return 0
		}

		chScore := 0.1

		if str[indexInStr] == ch {
			chScore += 0.1
		}

		if indexInStr == 0 || os.IsPathSeparator(str[indexInStr-1]) {
			// Start of string bonus
			chScore += 0.8
		} else if strings.Contains("-_ ", string(str[indexInStr-1])) {
			// Start of word bonus
			chScore += 0.7
		}

		// Trim string to after current abbreviation match
		str = str[indexInStr+1 : len(str)]

		totalScore += chScore
	}

	queryScore := totalScore / float64(queryLength)
	return ((queryScore * (float64(queryLength) / float64(strLength))) + queryScore) / 2.0
}

func queryIsLastPathSegment(str, query string) bool {
	if len(str) <= len(query) {
		return false
	}
	if os.IsPathSeparator(str[len(str)-len(query)-1]) {
		return strings.Index(str, query) == (len(str) - len(query))
	}
	return false
}
<|endoftext|>


<commit_before>package router

import (
	"context"
	"errors"
	"fmt"
	"os"
	"sort"
	"strconv"
	"strings"
	"time"

	g "github.com/onsi/ginkgo"
	o "github.com/onsi/gomega"

	routeclientset "github.com/openshift/client-go/route/clientset/versioned"
	"github.com/openshift/origin/test/extended/router/h2spec"
	"github.com/openshift/origin/test/extended/router/shard"
	exutil "github.com/openshift/origin/test/extended/util"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/wait"
	e2e "k8s.io/kubernetes/test/e2e/framework"
	e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
)

const h2specDialTimeoutInSeconds = 15

type h2specFailingTest struct {
	TestCase   *h2spec.JUnitTestCase
	TestNumber int
}

var _ = g.Describe("[sig-network-edge][Conformance][Area:Networking][Feature:Router]", func() {
	defer g.GinkgoRecover()

	var (
		h2specServiceConfigPath     = exutil.FixturePath("testdata", "router", "router-h2spec.yaml")
		h2specRoutesConfigPath      = exutil.FixturePath("testdata", "router", "router-h2spec-routes.yaml")
		h2specRouterShardConfigPath = exutil.FixturePath("testdata", "router", "router-shard.yaml")

		oc              = exutil.NewCLI("router-h2spec")
		shardConfigPath string // computed
	)

	// this hook must be registered before the framework namespace teardown
	// hook
	g.AfterEach(func() {
		if g.CurrentGinkgoTestDescription().Failed {
			client := routeclientset.NewForConfigOrDie(oc.AdminConfig()).RouteV1().Routes(oc.KubeFramework().Namespace.Name)
			if routes, _ := client.List(context.Background(), metav1.ListOptions{}); routes != nil {
				outputIngress(routes.Items...)
			}
			exutil.DumpPodLogsStartingWith("h2spec", oc)
		}
		if len(shardConfigPath) > 0 {
			if err := oc.AsAdmin().Run("delete").Args("-n", "openshift-ingress-operator", "-f", shardConfigPath).Execute(); err != nil {
				e2e.Logf("deleting ingress controller failed: %v\n", err)
			}
		}
	})

	g.Describe("The HAProxy router", func() {
		g.It("should pass the h2spec conformance tests", func() {
			infra, err := oc.AdminConfigClient().ConfigV1().Infrastructures().Get(context.Background(), "cluster", metav1.GetOptions{})
			o.Expect(err).NotTo(o.HaveOccurred(), "failed to get cluster-wide infrastructure")
			if !platformHasHTTP2LoadBalancerService(infra.Status.PlatformStatus.Type) {
				g.Skip("Skip on platforms where the default router is not exposed by a load balancer service.")
			}

			g.By("Getting the default domain")
			defaultDomain, err := getDefaultIngressClusterDomainName(oc, time.Minute)
			o.Expect(err).NotTo(o.HaveOccurred(), "failed to find default domain name")

			g.By("Locating the router image reference")
			routerImage, err := exutil.FindRouterImage(oc)
			o.Expect(err).NotTo(o.HaveOccurred())

			g.By("Locating the canary image reference")
			canaryImage, err := getCanaryImage(oc)
			o.Expect(err).NotTo(o.HaveOccurred())

			g.By("Creating h2spec test service")
			err = oc.Run("new-app").Args("-f", h2specServiceConfigPath,
				"-p", "HAPROXY_IMAGE="+routerImage,
				"-p", "H2SPEC_IMAGE="+canaryImage).Execute()
			o.Expect(err).NotTo(o.HaveOccurred())

			e2e.ExpectNoError(e2epod.WaitForPodNameRunningInNamespace(oc.KubeClient(), "h2spec-haproxy", oc.KubeFramework().Namespace.Name))
			e2e.ExpectNoError(e2epod.WaitForPodNameRunningInNamespace(oc.KubeClient(), "h2spec", oc.KubeFramework().Namespace.Name))

			shardFQDN := oc.Namespace() + "." + defaultDomain

			// The new router shard is using a namespace
			// selector so label this test namespace to
			// match.
			g.By("By labelling the namespace")
			err = oc.AsAdmin().Run("label").Args("namespace", oc.Namespace(), "type="+oc.Namespace()).Execute()
			o.Expect(err).NotTo(o.HaveOccurred())

			g.By("Creating routes to test for h2spec compliance")
			err = oc.Run("new-app").Args("-f", h2specRoutesConfigPath,
				"-p", "DOMAIN="+shardFQDN,
				"-p", "TYPE="+oc.Namespace()).Execute()
			o.Expect(err).NotTo(o.HaveOccurred())

			g.By("Creating a test-specific router shard")
			shardConfigPath, err = shard.DeployNewRouterShard(oc, 10*time.Minute, shard.Config{
				FixturePath: h2specRouterShardConfigPath,
				Domain:      shardFQDN,
				Type:        oc.Namespace(),
			})
			o.Expect(err).NotTo(o.HaveOccurred(), "new router shard did not rollout")

			g.By("Getting LB service")
			shardService, err := getRouterService(oc, 5*time.Minute, "router-"+oc.Namespace())
			o.Expect(err).NotTo(o.HaveOccurred())
			o.Expect(shardService).NotTo(o.BeNil())
			o.Expect(shardService.Status.LoadBalancer.Ingress).ShouldNot(o.BeEmpty())

			var addrs []string

			if len(shardService.Status.LoadBalancer.Ingress[0].Hostname) > 0 {
				g.By("Waiting for LB hostname to register in DNS")
				addrs, err = resolveHost(oc, time.Minute, 15*time.Minute, shardService.Status.LoadBalancer.Ingress[0].Hostname)
				o.Expect(err).NotTo(o.HaveOccurred())
				o.Expect(addrs).NotTo(o.BeEmpty())
			} else {
				addrs = append(addrs, shardService.Status.LoadBalancer.Ingress[0].IP)
			}

			g.By("Waiting for route hostname to register in DNS")
			host := "h2spec-passthrough." + shardFQDN
			addrs, err = resolveHostAsAddress(oc, time.Minute, 15*time.Minute, host, addrs[0])
			o.Expect(err).NotTo(o.HaveOccurred())
			o.Expect(addrs).NotTo(o.BeEmpty())

			// ROUTER_H2SPEC_SAMPLE when set runs the
			// conformance tests for N iterations to
			// identify flaking tests.
			//
			// This should be enabled in development every
			// time we consume any new version of haproxy
			// be that a major, minor or a micro update to
			// continuously validate the set of test case
			// IDs that fail.
			if iterations := lookupEnv("ROUTER_H2SPEC_SAMPLE", ""); iterations != "" {
				n, err := strconv.Atoi(iterations)
				o.Expect(err).NotTo(o.HaveOccurred())
				o.Expect(n).To(o.BeNumerically(">", 0))
				runConformanceTestsAndLogAggregateFailures(oc, host, "h2spec", n)
				return
			}

			testSuites, err := runConformanceTests(oc, host, "h2spec")
			o.Expect(err).NotTo(o.HaveOccurred())
			o.Expect(testSuites).ShouldNot(o.BeEmpty())

			failures := failingTests(testSuites)
			failureCount := len(failures)

			g.By("Analyzing results")
			// https://github.com/haproxy/haproxy/issues/471#issuecomment-591420924
			//
			// 5. Streams and Multiplexing
			//   5.4. Error Handling
			//     5.4.1. Connection Error Handling
			//       using source address 10.131.0.37:34742
			//       × 2: Sends an invalid PING frame to receive GOAWAY frame
			//         -> An endpoint that encounters a connection error SHOULD first send a GOAWAY frame
			//            Expected: GOAWAY Frame (Error Code: PROTOCOL_ERROR)
			//              Actual: Connection closed
			//
			// 6. Frame Definitions
			//   6.9. WINDOW_UPDATE
			//     6.9.1. The Flow-Control Window
			//       using source address 10.131.0.37:34816
			//       × 2: Sends multiple WINDOW_UPDATE frames increasing the flow control window to above 2^31-1
			//         -> The endpoint MUST sends a GOAWAY frame with a FLOW_CONTROL_ERROR code.
			//            Expected: GOAWAY Frame (Error Code: FLOW_CONTROL_ERROR)
			//              Actual: Connection closed
			//
			// 147 tests, 145 passed, 0 skipped, 2 failed
			knownFailures := map[string]bool{
				"http2/5.4.1.2": true,
				"http2/6.9.1.2": true,
			}
			for _, f := range failures {
				if _, exists := knownFailures[f.ID()]; exists {
					failureCount -= 1
					e2e.Logf("TestCase ID: %q is a known failure; ignoring", f.ID())
				} else {
					e2e.Logf("TestCase ID: %q (%q) ****FAILED****", f.ID(), f.TestCase.ClassName)
				}
			}
			o.Expect(failureCount).Should(o.BeZero(), "expected zero failures")
		})
	})
})

func failingTests(testSuites []*h2spec.JUnitTestSuite) []h2specFailingTest {
	var failures []h2specFailingTest

	for _, ts := range testSuites {
		for i := 0; i < ts.Tests; i++ {
			if ts.TestCases[i].Error != nil {
				failures = append(failures, h2specFailingTest{
					TestNumber: i + 1,
					TestCase:   ts.TestCases[i],
				})
			}
		}
	}

	return failures
}

func runConformanceTests(oc *exutil.CLI, host, podName string) ([]*h2spec.JUnitTestSuite, error) {
	g.By("Running the h2spec CLI test")

	// this is the output file in the pod
	outputFile := "/tmp/h2spec-results"

	// h2spec will exit with non-zero if _any_ test in the suite
	// fails, or if there is a dial timeout, so we ignore the
	// error. If we can fetch the results and if we can decode the
	// results and we have > 0 test suites from the decoded
	// results then assume the test ran.
	output, _ := e2e.RunHostCmd(oc.Namespace(), podName, h2specCommand(h2specDialTimeoutInSeconds, host, outputFile))

	g.By("Copying results")
	data, err := e2e.RunHostCmd(oc.Namespace(), podName, fmt.Sprintf("cat %q", outputFile))
	if err != nil {
		return nil, err
	}
	if len(data) == 0 {
		return nil, errors.New("zero length results file")
	}

	g.By("Decoding results")
	testSuites, err := h2spec.DecodeJUnitReport(strings.NewReader(data))
	if err != nil {
		return nil, err
	}
	if len(testSuites) == 0 {
		return nil, errors.New("no test results found")
	}

	// Log what we consider a successful run
	e2e.Logf("h2spec results\n%s", output)
	return testSuites, nil
}

func runConformanceTestsAndLogAggregateFailures(oc *exutil.CLI, host, podName string, iterations int) {
	sortKeys := func(m map[string]int) []string {
		var index []string
		for k := range m {
			index = append(index, k)
		}
		sort.Strings(index)
		return index
	}

	printFailures := func(prefix string, m map[string]int) {
		for _, id := range sortKeys(m) {
			e2e.Logf("%sTestCase ID: %q, cumulative failures: %v", prefix, id, m[id])
		}
	}

	failuresByTestCaseID := map[string]int{}

	for i := 1; i <= iterations; i++ {
		testResults, err := runConformanceTests(oc, host, podName)
		if err != nil {
			e2e.Logf(err.Error())
			continue
		}
		failures := failingTests(testResults)
		e2e.Logf("Iteration %v/%v: had %v failures", i, iterations, len(failures))

		// Aggregate any new failures
		for _, f := range failures {
			failuresByTestCaseID[f.ID()]++
		}

		// Dump the current state at every iteration should
		// you wish to interrupt/abort the running test.
		printFailures("\t", failuresByTestCaseID)
	}

	e2e.Logf("Sampling completed: %v test cases failed", len(failuresByTestCaseID))
	printFailures("\t", failuresByTestCaseID)
}

func getHostnameForRoute(oc *exutil.CLI, routeName string) (string, error) {
	var hostname string
	ns := oc.KubeFramework().Namespace.Name
	if err := wait.Poll(time.Second, changeTimeoutSeconds*time.Second, func() (bool, error) {
		route, err := oc.RouteClient().RouteV1().Routes(ns).Get(context.Background(), routeName, metav1.GetOptions{})
		if err != nil {
			e2e.Logf("Error getting hostname for route %q: %v", routeName, err)
			return false, err
		}
		if len(route.Status.Ingress) == 0 || len(route.Status.Ingress[0].Host) == 0 {
			return false, nil
		}
		hostname = route.Status.Ingress[0].Host
		return true, nil
	}); err != nil {
		return "", err
	}
	return hostname, nil
}

func h2specCommand(timeout int, hostname, results string) string {
	return fmt.Sprintf("ingress-operator h2spec --timeout=%v --tls --insecure --strict --host=%q --junit-report=%q", timeout, hostname, results)
}

func (f h2specFailingTest) ID() string {
	return fmt.Sprintf("%s.%d", f.TestCase.Package, f.TestNumber)
}

func lookupEnv(key, defaultVal string) string {
	if val, ok := os.LookupEnv(key); ok {
		return val
	}
	return defaultVal
}
<commit_msg>test/extended/router: h2spec increase timeout and retry tests<commit_after>package router

import (
	"context"
	"errors"
	"fmt"
	"os"
	"sort"
	"strconv"
	"strings"
	"time"

	g "github.com/onsi/ginkgo"
	o "github.com/onsi/gomega"

	routeclientset "github.com/openshift/client-go/route/clientset/versioned"
	"github.com/openshift/origin/test/extended/router/h2spec"
	"github.com/openshift/origin/test/extended/router/shard"
	exutil "github.com/openshift/origin/test/extended/util"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/wait"
	e2e "k8s.io/kubernetes/test/e2e/framework"
	e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
)

const h2specDialTimeoutInSeconds = 30

type h2specFailingTest struct {
	TestCase   *h2spec.JUnitTestCase
	TestNumber int
}

var _ = g.Describe("[sig-network-edge][Conformance][Area:Networking][Feature:Router]", func() {
	defer g.GinkgoRecover()

	var (
		h2specServiceConfigPath     = exutil.FixturePath("testdata", "router", "router-h2spec.yaml")
		h2specRoutesConfigPath      = exutil.FixturePath("testdata", "router", "router-h2spec-routes.yaml")
		h2specRouterShardConfigPath = exutil.FixturePath("testdata", "router", "router-shard.yaml")

		oc              = exutil.NewCLI("router-h2spec")
		shardConfigPath string // computed
	)

	// this hook must be registered before the framework namespace teardown
	// hook
	g.AfterEach(func() {
		if g.CurrentGinkgoTestDescription().Failed {
			client := routeclientset.NewForConfigOrDie(oc.AdminConfig()).RouteV1().Routes(oc.KubeFramework().Namespace.Name)
			if routes, _ := client.List(context.Background(), metav1.ListOptions{}); routes != nil {
				outputIngress(routes.Items...)
			}
			exutil.DumpPodLogsStartingWith("h2spec", oc)
		}
		if len(shardConfigPath) > 0 {
			if err := oc.AsAdmin().Run("delete").Args("-n", "openshift-ingress-operator", "-f", shardConfigPath).Execute(); err != nil {
				e2e.Logf("deleting ingress controller failed: %v\n", err)
			}
		}
	})

	g.Describe("The HAProxy router", func() {
		g.It("should pass the h2spec conformance tests", func() {
			infra, err := oc.AdminConfigClient().ConfigV1().Infrastructures().Get(context.Background(), "cluster", metav1.GetOptions{})
			o.Expect(err).NotTo(o.HaveOccurred(), "failed to get cluster-wide infrastructure")
			if !platformHasHTTP2LoadBalancerService(infra.Status.PlatformStatus.Type) {
				g.Skip("Skip on platforms where the default router is not exposed by a load balancer service.")
			}

			g.By("Getting the default domain")
			defaultDomain, err := getDefaultIngressClusterDomainName(oc, time.Minute)
			o.Expect(err).NotTo(o.HaveOccurred(), "failed to find default domain name")

			g.By("Locating the router image reference")
			routerImage, err := exutil.FindRouterImage(oc)
			o.Expect(err).NotTo(o.HaveOccurred())

			g.By("Locating the canary image reference")
			canaryImage, err := getCanaryImage(oc)
			o.Expect(err).NotTo(o.HaveOccurred())

			g.By("Creating h2spec test service")
			err = oc.Run("new-app").Args("-f", h2specServiceConfigPath,
				"-p", "HAPROXY_IMAGE="+routerImage,
				"-p", "H2SPEC_IMAGE="+canaryImage).Execute()
			o.Expect(err).NotTo(o.HaveOccurred())

			e2e.ExpectNoError(e2epod.WaitForPodNameRunningInNamespace(oc.KubeClient(), "h2spec-haproxy", oc.KubeFramework().Namespace.Name))
			e2e.ExpectNoError(e2epod.WaitForPodNameRunningInNamespace(oc.KubeClient(), "h2spec", oc.KubeFramework().Namespace.Name))

			shardFQDN := oc.Namespace() + "." + defaultDomain

			// The new router shard is using a namespace
			// selector so label this test namespace to
			// match.
			g.By("By labelling the namespace")
			err = oc.AsAdmin().Run("label").Args("namespace", oc.Namespace(), "type="+oc.Namespace()).Execute()
			o.Expect(err).NotTo(o.HaveOccurred())

			g.By("Creating routes to test for h2spec compliance")
			err = oc.Run("new-app").Args("-f", h2specRoutesConfigPath,
				"-p", "DOMAIN="+shardFQDN,
				"-p", "TYPE="+oc.Namespace()).Execute()
			o.Expect(err).NotTo(o.HaveOccurred())

			g.By("Creating a test-specific router shard")
			shardConfigPath, err = shard.DeployNewRouterShard(oc, 10*time.Minute, shard.Config{
				FixturePath: h2specRouterShardConfigPath,
				Domain:      shardFQDN,
				Type:        oc.Namespace(),
			})
			o.Expect(err).NotTo(o.HaveOccurred(), "new router shard did not rollout")

			g.By("Getting LB service")
			shardService, err := getRouterService(oc, 5*time.Minute, "router-"+oc.Namespace())
			o.Expect(err).NotTo(o.HaveOccurred())
			o.Expect(shardService).NotTo(o.BeNil())
			o.Expect(shardService.Status.LoadBalancer.Ingress).ShouldNot(o.BeEmpty())

			var addrs []string

			if len(shardService.Status.LoadBalancer.Ingress[0].Hostname) > 0 {
				g.By("Waiting for LB hostname to register in DNS")
				addrs, err = resolveHost(oc, time.Minute, 15*time.Minute, shardService.Status.LoadBalancer.Ingress[0].Hostname)
				o.Expect(err).NotTo(o.HaveOccurred())
				o.Expect(addrs).NotTo(o.BeEmpty())
			} else {
				addrs = append(addrs, shardService.Status.LoadBalancer.Ingress[0].IP)
			}

			g.By("Waiting for route hostname to register in DNS")
			host := "h2spec-passthrough." + shardFQDN
			addrs, err = resolveHostAsAddress(oc, time.Minute, 15*time.Minute, host, addrs[0])
			o.Expect(err).NotTo(o.HaveOccurred())
			o.Expect(addrs).NotTo(o.BeEmpty())

			// ROUTER_H2SPEC_SAMPLE when set runs the
			// conformance tests for N iterations to
			// identify flaking tests.
			//
			// This should be enabled in development every
			// time we consume any new version of haproxy
			// be that a major, minor or a micro update to
			// continuously validate the set of test case
			// IDs that fail.
			if iterations := lookupEnv("ROUTER_H2SPEC_SAMPLE", ""); iterations != "" {
				n, err := strconv.Atoi(iterations)
				o.Expect(err).NotTo(o.HaveOccurred())
				o.Expect(n).To(o.BeNumerically(">", 0))
				runConformanceTestsAndLogAggregateFailures(oc, host, "h2spec", n)
				return
			}

			testSuites, err := runConformanceTests(oc, host, "h2spec", 5*time.Minute)
			o.Expect(err).NotTo(o.HaveOccurred())
			o.Expect(testSuites).ShouldNot(o.BeEmpty())

			failures := failingTests(testSuites)
			failureCount := len(failures)

			g.By("Analyzing results")
			// https://github.com/haproxy/haproxy/issues/471#issuecomment-591420924
			//
			// 5. Streams and Multiplexing
			//   5.4. Error Handling
			//     5.4.1. Connection Error Handling
			//       using source address 10.131.0.37:34742
			//       × 2: Sends an invalid PING frame to receive GOAWAY frame
			//         -> An endpoint that encounters a connection error SHOULD first send a GOAWAY frame
			//            Expected: GOAWAY Frame (Error Code: PROTOCOL_ERROR)
			//              Actual: Connection closed
			//
			// 6. Frame Definitions
			//   6.9. WINDOW_UPDATE
			//     6.9.1. The Flow-Control Window
			//       using source address 10.131.0.37:34816
			//       × 2: Sends multiple WINDOW_UPDATE frames increasing the flow control window to above 2^31-1
			//         -> The endpoint MUST sends a GOAWAY frame with a FLOW_CONTROL_ERROR code.
			//            Expected: GOAWAY Frame (Error Code: FLOW_CONTROL_ERROR)
			//              Actual: Connection closed
			//
			// 147 tests, 145 passed, 0 skipped, 2 failed
			knownFailures := map[string]bool{
				"http2/5.4.1.2": true,
				"http2/6.9.1.2": true,
			}
			for _, f := range failures {
				if _, exists := knownFailures[f.ID()]; exists {
					failureCount -= 1
					e2e.Logf("TestCase ID: %q is a known failure; ignoring", f.ID())
				} else {
					e2e.Logf("TestCase ID: %q (%q) ****FAILED****", f.ID(), f.TestCase.ClassName)
				}
			}
			o.Expect(failureCount).Should(o.BeZero(), "expected zero failures")
		})
	})
})

func failingTests(testSuites []*h2spec.JUnitTestSuite) []h2specFailingTest {
	var failures []h2specFailingTest

	for _, ts := range testSuites {
		for i := 0; i < ts.Tests; i++ {
			if ts.TestCases[i].Error != nil {
				failures = append(failures, h2specFailingTest{
					TestNumber: i + 1,
					TestCase:   ts.TestCases[i],
				})
			}
		}
	}

	return failures
}

func runConformanceTests(oc *exutil.CLI, host, podName string, timeout time.Duration) ([]*h2spec.JUnitTestSuite, error) {
	var testSuites []*h2spec.JUnitTestSuite

	if err := wait.Poll(time.Second, timeout, func() (bool, error) {
		g.By("Running the h2spec CLI test")

		// this is the output file in the pod
		outputFile := "/tmp/h2spec-results"

		// h2spec will exit with non-zero if _any_ test in the suite
		// fails, or if there is a dial timeout, so we log the
		// error. But if we can fetch the results and if we can decode the
		// results and we have > 0 test suites from the decoded
		// results then assume the test ran.
		output, err := e2e.RunHostCmd(oc.Namespace(), podName, h2specCommand(h2specDialTimeoutInSeconds, host, outputFile))
		if err != nil {
			e2e.Logf("error running h2spec: %v, but checking on result content", err)
		}

		g.By("Copying results")
		data, err := e2e.RunHostCmd(oc.Namespace(), podName, fmt.Sprintf("cat %q", outputFile))
		if err != nil {
			e2e.Logf("error copying results: %v, retrying...", err)
			return false, err
		}
		if len(data) == 0 {
			e2e.Logf("results file is zero length, retrying...")
			return false, errors.New("empty results")
		}

		g.By("Decoding results")
		testSuites, err = h2spec.DecodeJUnitReport(strings.NewReader(data))
		if err != nil {
			e2e.Logf("error decoding results: %v, retrying...", err)
			return false, err
		}
		if len(testSuites) == 0 {
			e2e.Logf("expected len(testSuites) > 0, retrying...")
			return false, errors.New("no test results found")
		}

		// Log what we consider a successful run
		e2e.Logf("h2spec results\n%s", output)
		return true, nil
	}); err != nil {
		return nil, err
	}

	return testSuites, nil
}

func runConformanceTestsAndLogAggregateFailures(oc *exutil.CLI, host, podName string, iterations int) {
	sortKeys := func(m map[string]int) []string {
		var index []string
		for k := range m {
			index = append(index, k)
		}
		sort.Strings(index)
		return index
	}

	printFailures := func(prefix string, m map[string]int) {
		for _, id := range sortKeys(m) {
			e2e.Logf("%sTestCase ID: %q, cumulative failures: %v", prefix, id, m[id])
		}
	}

	failuresByTestCaseID := map[string]int{}

	for i := 1; i <= iterations; i++ {
		testResults, err := runConformanceTests(oc, host, podName, 5*time.Minute)
		if err != nil {
			e2e.Logf(err.Error())
			continue
		}
		failures := failingTests(testResults)
		e2e.Logf("Iteration %v/%v: had %v failures", i, iterations, len(failures))

		// Aggregate any new failures
		for _, f := range failures {
			failuresByTestCaseID[f.ID()]++
		}

		// Dump the current state at every iteration should
		// you wish to interrupt/abort the running test.
		printFailures("\t", failuresByTestCaseID)
	}

	e2e.Logf("Sampling completed: %v test cases failed", len(failuresByTestCaseID))
	printFailures("\t", failuresByTestCaseID)
}

func getHostnameForRoute(oc *exutil.CLI, routeName string) (string, error) {
	var hostname string
	ns := oc.KubeFramework().Namespace.Name
	if err := wait.Poll(time.Second, changeTimeoutSeconds*time.Second, func() (bool, error) {
		route, err := oc.RouteClient().RouteV1().Routes(ns).Get(context.Background(), routeName, metav1.GetOptions{})
		if err != nil {
			e2e.Logf("Error getting hostname for route %q: %v", routeName, err)
			return false, err
		}
		if len(route.Status.Ingress) == 0 || len(route.Status.Ingress[0].Host) == 0 {
			return false, nil
		}
		hostname = route.Status.Ingress[0].Host
		return true, nil
	}); err != nil {
		return "", err
	}
	return hostname, nil
}

func h2specCommand(timeout int, hostname, results string) string {
	return fmt.Sprintf("ingress-operator h2spec --timeout=%v --tls --insecure --strict --host=%q --junit-report=%q", timeout, hostname, results)
}

func (f h2specFailingTest) ID() string {
	return fmt.Sprintf("%s.%d", f.TestCase.Package, f.TestNumber)
}

func lookupEnv(key, defaultVal string) string {
	if val, ok := os.LookupEnv(key); ok {
		return val
	}
	return defaultVal
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/csv"
	"errors"
	"io"
	"os"
)

type basicAuthData struct {
	user     string
	password string
}

type basicAuth struct {
	Users map[string]string
}

func newBasicAuthFromFile(path string) (*basicAuth, error) {
	r, err := os.Open(path)
	if err != nil {
		return nil, err
	}

	return newBasicAuth(r)
}

func newBasicAuth(file io.Reader) (*basicAuth, error) {
	csvReader := csv.NewReader(file)
	csvReader.Comma = ':'
	csvReader.Comment = '#'
	csvReader.TrimLeadingSpace = true

	records, err := csvReader.ReadAll()
	if err != nil {
		return nil, err
	}

	h := &basicAuth{Users: make(map[string]string)}

	for _, record := range records {
		if len(record) != 2 {
			return nil, errors.New("invalid basic auth file format")
		}
		h.Users[record[0]] = record[1]
	}

	if len(h.Users) == 0 {
		return nil, errors.New("auth file contains no data")
	}

	return h, nil
}

func (h *basicAuth) validate(authData *basicAuthData) bool {
	realPassword, exists := h.Users[authData.user]
	if !exists || realPassword != authData.password {
		return false
	}

	return true
}
<commit_msg>code style consistency<commit_after>package main

import (
	"encoding/csv"
	"errors"
	"io"
	"os"
)

type basicAuthData struct {
	user     string
	password string
}

type basicAuth struct {
	users map[string]string
}

func newBasicAuthFromFile(path string) (*basicAuth, error) {
	r, err := os.Open(path)
	if err != nil {
		return nil, err
	}

	return newBasicAuth(r)
}

func newBasicAuth(file io.Reader) (*basicAuth, error) {
	csvReader := csv.NewReader(file)
	csvReader.Comma = ':'
	csvReader.Comment = '#'
	csvReader.TrimLeadingSpace = true

	records, err := csvReader.ReadAll()
	if err != nil {
		return nil, err
	}

	h := &basicAuth{users: make(map[string]string)}

	for _, record := range records {
		if len(record) != 2 {
			return nil, errors.New("invalid basic auth file format")
		}
		h.users[record[0]] = record[1]
	}

	if len(h.users) == 0 {
		return nil, errors.New("auth file contains no data")
	}

	return h, nil
}

func (h *basicAuth) validate(authData *basicAuthData) bool {
	realPassword, exists := h.users[authData.user]
	if !exists || realPassword != authData.password {
		return false
	}

	return true
}
<|endoftext|>


<commit_before>// Copyright The OpenTelemetry 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 conventions

// OpenTelemetry Semantic Convention values for Resource attribute names.
// See: https://github.com/open-telemetry/opentelemetry-specification/tree/master/specification/resource/semantic_conventions/README.md
const (
	AttributeCloudAccount          = "cloud.account.id"
	AttributeCloudProvider         = "cloud.provider"
	AttributeCloudRegion           = "cloud.region"
	AttributeCloudZone             = "cloud.zone"
	AttributeContainerID           = "container.id"
	AttributeContainerImage        = "container.image.name"
	AttributeContainerName         = "container.name"
	AttributeContainerTag          = "container.image.tag"
	AttributeDeploymentEnvironment = "deployment.environment"
	AttributeFaasID                = "faas.id"
	AttributeFaasInstance          = "faas.instance"
	AttributeFaasName              = "faas.name"
	AttributeFaasVersion           = "faas.version"
	AttributeHostHostname          = "host.hostname"
	AttributeHostID                = "host.id"
	AttributeHostImageID           = "host.image.id"
	AttributeHostImageName         = "host.image.name"
	AttributeHostImageVersion      = "host.image.version"
	AttributeHostName              = "host.name"
	AttributeHostType              = "host.type"
	AttributeK8sCluster            = "k8s.cluster.name"
	AttributeK8sContainer          = "k8s.container.name"
	AttributeK8sCronJob            = "k8s.cronjob.name"
	AttributeK8sCronJobUID         = "k8s.cronjob.uid"
	AttributeK8sDaemonSet          = "k8s.daemonset.name"
	AttributeK8sDaemonSetUID       = "k8s.daemonset.uid"
	AttributeK8sDeployment         = "k8s.deployment.name"
	AttributeK8sDeploymentUID      = "k8s.deployment.uid"
	AttributeK8sJob                = "k8s.job.name"
	AttributeK8sJobUID             = "k8s.job.uid"
	AttributeK8sNamespace          = "k8s.namespace.name"
	AttributeK8sPod                = "k8s.pod.name"
	AttributeK8sPodUID             = "k8s.pod.uid"
	AttributeK8sReplicaSet         = "k8s.replicaset.name"
	AttributeK8sReplicaSetUID      = "k8s.replicaset.uid"
	AttributeK8sStatefulSet        = "k8s.statefulset.name"
	AttributeK8sStatefulSetUID     = "k8s.statefulset.uid"
	AttributeOSType                = "os.type"
	AttributeOSDescription         = "os.description"
	AttributeProcessCommand        = "process.command"
	AttributeProcessCommandLine    = "process.command_line"
	AttributeProcessExecutableName = "process.executable.name"
	AttributeProcessExecutablePath = "process.executable.path"
	AttributeProcessID             = "process.pid"
	AttributeProcessOwner          = "process.owner"
	AttributeServiceInstance       = "service.instance.id"
	AttributeServiceName           = "service.name"
	AttributeServiceNamespace      = "service.namespace"
	AttributeServiceVersion        = "service.version"
	AttributeTelemetryAutoVersion  = "telemetry.auto.version"
	AttributeTelemetrySDKLanguage  = "telemetry.sdk.language"
	AttributeTelemetrySDKName      = "telemetry.sdk.name"
	AttributeTelemetrySDKVersion   = "telemetry.sdk.version"
)

// OpenTelemetry Semantic Convention values for Resource attribute "telemetry.sdk.language" values.
// See: https://github.com/open-telemetry/opentelemetry-specification/tree/master/specification/resource/semantic_conventions/README.md
const (
	AttributeSDKLangValueCPP    = "cpp"
	AttributeSDKLangValueDotNET = "dotnet"
	AttributeSDKLangValueErlang = "erlang"
	AttributeSDKLangValueGo     = "go"
	AttributeSDKLangValueJava   = "java"
	AttributeSDKLangValueNodeJS = "nodejs"
	AttributeSDKLangValuePHP    = "php"
	AttributeSDKLangValuePython = "python"
	AttributeSDKLangValueRuby   = "ruby"
	AttributeSDKLangValueWebJS  = "webjs"
)

// GetResourceSemanticConventionAttributeNames a slice with all the Resource Semantic Conventions attribute names.
func GetResourceSemanticConventionAttributeNames() []string {
	return []string{
		AttributeCloudAccount,
		AttributeCloudProvider,
		AttributeCloudRegion,
		AttributeCloudZone,
		AttributeContainerID,
		AttributeContainerImage,
		AttributeContainerName,
		AttributeContainerTag,
		AttributeDeploymentEnvironment,
		AttributeFaasID,
		AttributeFaasInstance,
		AttributeFaasName,
		AttributeFaasVersion,
		AttributeHostHostname,
		AttributeHostID,
		AttributeHostImageID,
		AttributeHostImageName,
		AttributeHostImageVersion,
		AttributeHostName,
		AttributeHostType,
		AttributeK8sCluster,
		AttributeK8sContainer,
		AttributeK8sCronJob,
		AttributeK8sCronJobUID,
		AttributeK8sDaemonSet,
		AttributeK8sDaemonSetUID,
		AttributeK8sDeployment,
		AttributeK8sDeploymentUID,
		AttributeK8sJob,
		AttributeK8sJobUID,
		AttributeK8sNamespace,
		AttributeK8sPod,
		AttributeK8sPodUID,
		AttributeK8sReplicaSet,
		AttributeK8sReplicaSetUID,
		AttributeK8sStatefulSet,
		AttributeK8sStatefulSetUID,
		AttributeOSType,
		AttributeOSDescription,
		AttributeProcessCommand,
		AttributeProcessCommandLine,
		AttributeProcessExecutableName,
		AttributeProcessExecutablePath,
		AttributeProcessID,
		AttributeProcessOwner,
		AttributeServiceInstance,
		AttributeServiceName,
		AttributeServiceNamespace,
		AttributeServiceVersion,
		AttributeTelemetryAutoVersion,
		AttributeTelemetrySDKLanguage,
		AttributeTelemetrySDKName,
		AttributeTelemetrySDKVersion,
	}
}

// OpenTelemetry Semantic Convention values for general Span attribute names.
// See: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/span-general.md
const (
	AttributeComponent    = "component"
	AttributeEnduserID    = "enduser.id"
	AttributeEnduserRole  = "enduser.role"
	AttributeEnduserScope = "enduser.scope"
	AttributeNetHostIP    = "net.host.ip"
	AttributeNetHostName  = "net.host.name"
	AttributeNetHostPort  = "net.host.port"
	AttributeNetPeerIP    = "net.peer.ip"
	AttributeNetPeerName  = "net.peer.name"
	AttributeNetPeerPort  = "net.peer.port"
	AttributeNetTransport = "net.transport"
	AttributePeerService  = "peer.service"
)

// OpenTelemetry Semantic Convention values for component attribute values.
// Possibly being removed due to issue #336
const (
	ComponentTypeHTTP = "http"
	ComponentTypeGRPC = "grpc"
)

// OpenTelemetry Semantic Convention attribute names for HTTP related attributes
// See: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/http.md
const (
	AttributeHTTPClientIP                          = "http.client_ip"
	AttributeHTTPFlavor                            = "http.flavor"
	AttributeHTTPHost                              = "http.host"
	AttributeHTTPHostName                          = "host.name"
	AttributeHTTPHostPort                          = "host.port"
	AttributeHTTPMethod                            = "http.method"
	AttributeHTTPRequestContentLength              = "http.request_content_length"
	AttributeHTTPRequestContentLengthUncompressed  = "http.request_content_length_uncompressed"
	AttributeHTTPResponseContentLength             = "http.response_content_length"
	AttributeHTTPResponseContentLengthUncompressed = "http.response_content_length_uncompressed"
	AttributeHTTPRoute                             = "http.route"
	AttributeHTTPScheme                            = "http.scheme"
	AttributeHTTPServerName                        = "http.server_name"
	AttributeHTTPStatusCode                        = "http.status_code"
	AttributeHTTPStatusText                        = "http.status_text"
	AttributeHTTPTarget                            = "http.target"
	AttributeHTTPURL                               = "http.url"
	AttributeHTTPUserAgent                         = "http.user_agent"
)

// OpenTelemetry Semantic Convention attribute names for database related attributes
// See: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/database.md
const (
	AttributeDBConnectionString = "db.connection_string"

	AttributeDBCassandraKeyspace   = "db.cassandra.keyspace"
	AttributeDBHBaseNamespace      = "db.hbase.namespace"
	AttributeDBJDBCDriverClassname = "db.jdbc.driver_classname"
	AttributeDBMongoDBCollection   = "db.mongodb.collection"
	AttributeDBMsSQLInstanceName   = "db.mssql.instance_name"

	AttributeDBName               = "db.name"
	AttributeDBOperation          = "db.operation"
	AttributeDBRedisDatabaseIndex = "db.redis.database_index"
	AttributeDBStatement          = "db.statement"
	AttributeDBSystem             = "db.system"
	AttributeDBUser               = "db.user"
)

// OpenTelemetry Semantic Convention attribute names for gRPC related attributes
// See: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/rpc.md
const (
	AttributeMessageCompressedSize   = "message.compressed_size"
	AttributeMessageID               = "message.id"
	AttributeMessageType             = "message.type"
	AttributeMessageUncompressedSize = "message.uncompressed_size"
	AttributeRPCMethod               = "rpc.method"
	AttributeRPCService              = "rpc.service"
	AttributeRPCSystem               = "rpc.system"
	EventTypeMessage                 = "message"
	MessageTypeReceived              = "RECEIVED"
	MessageTypeSent                  = "SENT"
)

// OpenTelemetry Semantic Convention attribute names for FaaS related attributes
// See: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/faas.md
const (
	AttributeFaaSCron               = "faas.cron"
	AttributeFaaSDocumentCollection = "faas.document.collection"
	AttributeFaaSDocumentName       = "faas.document.name"
	AttributeFaaSDocumentOperation  = "faas.document.operation"
	AttributeFaaSDocumentTime       = "faas.document.time"
	AttributeFaaSExecution          = "faas.execution"
	AttributeFaaSTime               = "faas.time"
	AttributeFaaSTrigger            = "faas.trigger"
	FaaSTriggerDataSource           = "datasource"
	FaaSTriggerHTTP                 = "http"
	FaaSTriggerOther                = "other"
	FaaSTriggerPubSub               = "pubsub"
	FaaSTriggerTimer                = "timer"
)

// OpenTelemetry Semantic Convention attribute names for messaging system related attributes
// See: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/messaging.md
const (
	AttributeMessagingConversationID        = "messaging.conversation_id"
	AttributeMessagingDestination           = "messaging.destination"
	AttributeMessagingDestinationKind       = "messaging.destination_kind"
	AttributeMessagingMessageID             = "messaging.message_id"
	AttributeMessagingOperation             = "messaging.operation"
	AttributeMessagingPayloadCompressedSize = "messaging.message_payload_compressed_size_bytes"
	AttributeMessagingPayloadSize           = "messaging.message_payload_size_bytes"
	AttributeMessagingProtocol              = "messaging.protocol"
	AttributeMessagingProtocolVersion       = "messaging.protocol_version"
	AttributeMessagingSystem                = "messaging.system"
	AttributeMessagingTempDestination       = "messaging.temp_destination"
	AttributeMessagingURL                   = "messaging.url"
)

// OpenTelemetry Semantic Convention attribute names for exceptions
// See: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/exceptions.md
const (
	AttributeExceptionEventName  = "exception"
	AttributeExceptionMessage    = "exception.message"
	AttributeExceptionStacktrace = "exception.stacktrace"
	AttributeExceptionType       = "exception.type"
)
<commit_msg>Add cloud.provider semantic conventions (#1865)<commit_after>// Copyright The OpenTelemetry 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 conventions

// OpenTelemetry Semantic Convention values for Resource attribute names.
// See: https://github.com/open-telemetry/opentelemetry-specification/tree/master/specification/resource/semantic_conventions/README.md
const (
	AttributeCloudAccount          = "cloud.account.id"
	AttributeCloudProvider         = "cloud.provider"
	AttributeCloudRegion           = "cloud.region"
	AttributeCloudZone             = "cloud.zone"
	AttributeContainerID           = "container.id"
	AttributeContainerImage        = "container.image.name"
	AttributeContainerName         = "container.name"
	AttributeContainerTag          = "container.image.tag"
	AttributeDeploymentEnvironment = "deployment.environment"
	AttributeFaasID                = "faas.id"
	AttributeFaasInstance          = "faas.instance"
	AttributeFaasName              = "faas.name"
	AttributeFaasVersion           = "faas.version"
	AttributeHostHostname          = "host.hostname"
	AttributeHostID                = "host.id"
	AttributeHostImageID           = "host.image.id"
	AttributeHostImageName         = "host.image.name"
	AttributeHostImageVersion      = "host.image.version"
	AttributeHostName              = "host.name"
	AttributeHostType              = "host.type"
	AttributeK8sCluster            = "k8s.cluster.name"
	AttributeK8sContainer          = "k8s.container.name"
	AttributeK8sCronJob            = "k8s.cronjob.name"
	AttributeK8sCronJobUID         = "k8s.cronjob.uid"
	AttributeK8sDaemonSet          = "k8s.daemonset.name"
	AttributeK8sDaemonSetUID       = "k8s.daemonset.uid"
	AttributeK8sDeployment         = "k8s.deployment.name"
	AttributeK8sDeploymentUID      = "k8s.deployment.uid"
	AttributeK8sJob                = "k8s.job.name"
	AttributeK8sJobUID             = "k8s.job.uid"
	AttributeK8sNamespace          = "k8s.namespace.name"
	AttributeK8sPod                = "k8s.pod.name"
	AttributeK8sPodUID             = "k8s.pod.uid"
	AttributeK8sReplicaSet         = "k8s.replicaset.name"
	AttributeK8sReplicaSetUID      = "k8s.replicaset.uid"
	AttributeK8sStatefulSet        = "k8s.statefulset.name"
	AttributeK8sStatefulSetUID     = "k8s.statefulset.uid"
	AttributeOSType                = "os.type"
	AttributeOSDescription         = "os.description"
	AttributeProcessCommand        = "process.command"
	AttributeProcessCommandLine    = "process.command_line"
	AttributeProcessExecutableName = "process.executable.name"
	AttributeProcessExecutablePath = "process.executable.path"
	AttributeProcessID             = "process.pid"
	AttributeProcessOwner          = "process.owner"
	AttributeServiceInstance       = "service.instance.id"
	AttributeServiceName           = "service.name"
	AttributeServiceNamespace      = "service.namespace"
	AttributeServiceVersion        = "service.version"
	AttributeTelemetryAutoVersion  = "telemetry.auto.version"
	AttributeTelemetrySDKLanguage  = "telemetry.sdk.language"
	AttributeTelemetrySDKName      = "telemetry.sdk.name"
	AttributeTelemetrySDKVersion   = "telemetry.sdk.version"
)

// OpenTelemetry Semantic Convention values for Resource attribute "telemetry.sdk.language" values.
// See: https://github.com/open-telemetry/opentelemetry-specification/tree/master/specification/resource/semantic_conventions/README.md
const (
	AttributeSDKLangValueCPP    = "cpp"
	AttributeSDKLangValueDotNET = "dotnet"
	AttributeSDKLangValueErlang = "erlang"
	AttributeSDKLangValueGo     = "go"
	AttributeSDKLangValueJava   = "java"
	AttributeSDKLangValueNodeJS = "nodejs"
	AttributeSDKLangValuePHP    = "php"
	AttributeSDKLangValuePython = "python"
	AttributeSDKLangValueRuby   = "ruby"
	AttributeSDKLangValueWebJS  = "webjs"
)

// OpenTelemetry Semantic Convention values for Resource attribute "cloud.provider" values.
// See: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/resource/semantic_conventions/cloud.md
const (
	AttributeCloudProviderAWS   = "aws"
	AttributeCloudProviderAzure = "azure"
	AttributeCloudProviderGCP   = "gcp"
)

// GetResourceSemanticConventionAttributeNames a slice with all the Resource Semantic Conventions attribute names.
func GetResourceSemanticConventionAttributeNames() []string {
	return []string{
		AttributeCloudAccount,
		AttributeCloudProvider,
		AttributeCloudRegion,
		AttributeCloudZone,
		AttributeContainerID,
		AttributeContainerImage,
		AttributeContainerName,
		AttributeContainerTag,
		AttributeDeploymentEnvironment,
		AttributeFaasID,
		AttributeFaasInstance,
		AttributeFaasName,
		AttributeFaasVersion,
		AttributeHostHostname,
		AttributeHostID,
		AttributeHostImageID,
		AttributeHostImageName,
		AttributeHostImageVersion,
		AttributeHostName,
		AttributeHostType,
		AttributeK8sCluster,
		AttributeK8sContainer,
		AttributeK8sCronJob,
		AttributeK8sCronJobUID,
		AttributeK8sDaemonSet,
		AttributeK8sDaemonSetUID,
		AttributeK8sDeployment,
		AttributeK8sDeploymentUID,
		AttributeK8sJob,
		AttributeK8sJobUID,
		AttributeK8sNamespace,
		AttributeK8sPod,
		AttributeK8sPodUID,
		AttributeK8sReplicaSet,
		AttributeK8sReplicaSetUID,
		AttributeK8sStatefulSet,
		AttributeK8sStatefulSetUID,
		AttributeOSType,
		AttributeOSDescription,
		AttributeProcessCommand,
		AttributeProcessCommandLine,
		AttributeProcessExecutableName,
		AttributeProcessExecutablePath,
		AttributeProcessID,
		AttributeProcessOwner,
		AttributeServiceInstance,
		AttributeServiceName,
		AttributeServiceNamespace,
		AttributeServiceVersion,
		AttributeTelemetryAutoVersion,
		AttributeTelemetrySDKLanguage,
		AttributeTelemetrySDKName,
		AttributeTelemetrySDKVersion,
	}
}

// OpenTelemetry Semantic Convention values for general Span attribute names.
// See: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/span-general.md
const (
	AttributeComponent    = "component"
	AttributeEnduserID    = "enduser.id"
	AttributeEnduserRole  = "enduser.role"
	AttributeEnduserScope = "enduser.scope"
	AttributeNetHostIP    = "net.host.ip"
	AttributeNetHostName  = "net.host.name"
	AttributeNetHostPort  = "net.host.port"
	AttributeNetPeerIP    = "net.peer.ip"
	AttributeNetPeerName  = "net.peer.name"
	AttributeNetPeerPort  = "net.peer.port"
	AttributeNetTransport = "net.transport"
	AttributePeerService  = "peer.service"
)

// OpenTelemetry Semantic Convention values for component attribute values.
// Possibly being removed due to issue #336
const (
	ComponentTypeHTTP = "http"
	ComponentTypeGRPC = "grpc"
)

// OpenTelemetry Semantic Convention attribute names for HTTP related attributes
// See: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/http.md
const (
	AttributeHTTPClientIP                          = "http.client_ip"
	AttributeHTTPFlavor                            = "http.flavor"
	AttributeHTTPHost                              = "http.host"
	AttributeHTTPHostName                          = "host.name"
	AttributeHTTPHostPort                          = "host.port"
	AttributeHTTPMethod                            = "http.method"
	AttributeHTTPRequestContentLength              = "http.request_content_length"
	AttributeHTTPRequestContentLengthUncompressed  = "http.request_content_length_uncompressed"
	AttributeHTTPResponseContentLength             = "http.response_content_length"
	AttributeHTTPResponseContentLengthUncompressed = "http.response_content_length_uncompressed"
	AttributeHTTPRoute                             = "http.route"
	AttributeHTTPScheme                            = "http.scheme"
	AttributeHTTPServerName                        = "http.server_name"
	AttributeHTTPStatusCode                        = "http.status_code"
	AttributeHTTPStatusText                        = "http.status_text"
	AttributeHTTPTarget                            = "http.target"
	AttributeHTTPURL                               = "http.url"
	AttributeHTTPUserAgent                         = "http.user_agent"
)

// OpenTelemetry Semantic Convention attribute names for database related attributes
// See: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/database.md
const (
	AttributeDBConnectionString = "db.connection_string"

	AttributeDBCassandraKeyspace   = "db.cassandra.keyspace"
	AttributeDBHBaseNamespace      = "db.hbase.namespace"
	AttributeDBJDBCDriverClassname = "db.jdbc.driver_classname"
	AttributeDBMongoDBCollection   = "db.mongodb.collection"
	AttributeDBMsSQLInstanceName   = "db.mssql.instance_name"

	AttributeDBName               = "db.name"
	AttributeDBOperation          = "db.operation"
	AttributeDBRedisDatabaseIndex = "db.redis.database_index"
	AttributeDBStatement          = "db.statement"
	AttributeDBSystem             = "db.system"
	AttributeDBUser               = "db.user"
)

// OpenTelemetry Semantic Convention attribute names for gRPC related attributes
// See: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/rpc.md
const (
	AttributeMessageCompressedSize   = "message.compressed_size"
	AttributeMessageID               = "message.id"
	AttributeMessageType             = "message.type"
	AttributeMessageUncompressedSize = "message.uncompressed_size"
	AttributeRPCMethod               = "rpc.method"
	AttributeRPCService              = "rpc.service"
	AttributeRPCSystem               = "rpc.system"
	EventTypeMessage                 = "message"
	MessageTypeReceived              = "RECEIVED"
	MessageTypeSent                  = "SENT"
)

// OpenTelemetry Semantic Convention attribute names for FaaS related attributes
// See: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/faas.md
const (
	AttributeFaaSCron               = "faas.cron"
	AttributeFaaSDocumentCollection = "faas.document.collection"
	AttributeFaaSDocumentName       = "faas.document.name"
	AttributeFaaSDocumentOperation  = "faas.document.operation"
	AttributeFaaSDocumentTime       = "faas.document.time"
	AttributeFaaSExecution          = "faas.execution"
	AttributeFaaSTime               = "faas.time"
	AttributeFaaSTrigger            = "faas.trigger"
	FaaSTriggerDataSource           = "datasource"
	FaaSTriggerHTTP                 = "http"
	FaaSTriggerOther                = "other"
	FaaSTriggerPubSub               = "pubsub"
	FaaSTriggerTimer                = "timer"
)

// OpenTelemetry Semantic Convention attribute names for messaging system related attributes
// See: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/messaging.md
const (
	AttributeMessagingConversationID        = "messaging.conversation_id"
	AttributeMessagingDestination           = "messaging.destination"
	AttributeMessagingDestinationKind       = "messaging.destination_kind"
	AttributeMessagingMessageID             = "messaging.message_id"
	AttributeMessagingOperation             = "messaging.operation"
	AttributeMessagingPayloadCompressedSize = "messaging.message_payload_compressed_size_bytes"
	AttributeMessagingPayloadSize           = "messaging.message_payload_size_bytes"
	AttributeMessagingProtocol              = "messaging.protocol"
	AttributeMessagingProtocolVersion       = "messaging.protocol_version"
	AttributeMessagingSystem                = "messaging.system"
	AttributeMessagingTempDestination       = "messaging.temp_destination"
	AttributeMessagingURL                   = "messaging.url"
)

// OpenTelemetry Semantic Convention attribute names for exceptions
// See: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/exceptions.md
const (
	AttributeExceptionEventName  = "exception"
	AttributeExceptionMessage    = "exception.message"
	AttributeExceptionStacktrace = "exception.stacktrace"
	AttributeExceptionType       = "exception.type"
)
<|endoftext|>


<commit_before><commit_msg>Read the right username field when performing account deactivation (#1954)<commit_after><|endoftext|>