<commit_before>/**
 * @file status.go
 * @author Mikhail Klementyev jollheef<AT>riseup.net
 * @license GNU GPLv3
 * @date September, 2015
 * @brief queries for status table
 */

package steward

import "database/sql"

type ServiceState int

const (
	// Service is online, serves the requests, stores and
	// returns flags and behaves as expected
	STATUS_UP ServiceState = iota
	// Service is online, but behaves not as expected, e.g. if HTTP server
	// listens the port, but doesn't respond on request
	STATUS_MUMBLE
	// Service is online, but past flags cannot be retrieved
	STATUS_CORRUPT
	// Service is offline
	STATUS_DOWN
	// Checker error
	STATUS_ERROR
	// Unknown
	STATUS_UNKNOWN
)

func (state ServiceState) String() string {
	switch state {
	case STATUS_UP:
		return "ok"
	case STATUS_MUMBLE:
		return "mumble"
	case STATUS_CORRUPT:
		return "corrupt"
	case STATUS_DOWN:
		return "down"
	case STATUS_ERROR:
		return "error"
	case STATUS_UNKNOWN:
		return "unknown"
	}

	return "undefined"
}

type Status struct {
	Round     int
	TeamId    int
	ServiceId int
	State     ServiceState
}

func createStatusTable(db *sql.DB) (err error) {

	_, err = db.Exec(`
	CREATE TABLE IF NOT EXISTS "status" (
		id	SERIAL PRIMARY KEY,
		round	INTEGER NOT NULL,
		team_id	INTEGER NOT NULL,
		service_id	INTEGER NOT NULL,
		state	INTEGER NOT NULL,
		timestamp	TIMESTAMP with time zone DEFAULT now()
	)`)

	return
}

func PutStatus(db *sql.DB, status Status) (err error) {

	stmt, err := db.Prepare("INSERT INTO status (round, team_id, " +
		"service_id, state) VALUES ($1, $2, $3, $4)")
	if err != nil {
		return
	}

	defer stmt.Close()

	_, err = stmt.Exec(status.Round, status.TeamId, status.ServiceId,
		status.State)
	if err != nil {
		return
	}

	return
}

func GetStates(db *sql.DB, halfStatus Status) (states []ServiceState,
	err error) {

	stmt, err := db.Prepare(
		"SELECT state FROM status WHERE round=$1 AND team_id=$2 " +
			"AND service_id=$3")
	if err != nil {
		return
	}

	defer stmt.Close()

	rows, err := stmt.Query(halfStatus.Round, halfStatus.TeamId,
		halfStatus.ServiceId)
	if err != nil {
		return
	}

	defer rows.Close()

	for rows.Next() {
		var state int

		err = rows.Scan(&state)
		if err != nil {
			return
		}

		states = append(states, ServiceState(state))
	}

	return
}

func GetState(db *sql.DB, halfStatus Status) (state ServiceState, err error) {

	stmt, err := db.Prepare(
		"SELECT state FROM status WHERE round=$1 AND team_id=$2 " +
			"AND service_id=$3 " +
			"AND ID = (SELECT MAX(ID) FROM status " +
			"WHERE round=$1 AND team_id=$2 AND service_id=$3)")
	if err != nil {
		return
	}

	defer stmt.Close()

	err = stmt.QueryRow(halfStatus.Round, halfStatus.TeamId,
		halfStatus.ServiceId).Scan(&state)
	if err != nil {
		return
	}

	return
}
<commit_msg>Change string representation of STATUS_UP from "ok" to "up"<commit_after>/**
 * @file status.go
 * @author Mikhail Klementyev jollheef<AT>riseup.net
 * @license GNU GPLv3
 * @date September, 2015
 * @brief queries for status table
 */

package steward

import "database/sql"

type ServiceState int

const (
	// Service is online, serves the requests, stores and
	// returns flags and behaves as expected
	STATUS_UP ServiceState = iota
	// Service is online, but behaves not as expected, e.g. if HTTP server
	// listens the port, but doesn't respond on request
	STATUS_MUMBLE
	// Service is online, but past flags cannot be retrieved
	STATUS_CORRUPT
	// Service is offline
	STATUS_DOWN
	// Checker error
	STATUS_ERROR
	// Unknown
	STATUS_UNKNOWN
)

func (state ServiceState) String() string {
	switch state {
	case STATUS_UP:
		return "up"
	case STATUS_MUMBLE:
		return "mumble"
	case STATUS_CORRUPT:
		return "corrupt"
	case STATUS_DOWN:
		return "down"
	case STATUS_ERROR:
		return "error"
	case STATUS_UNKNOWN:
		return "unknown"
	}

	return "undefined"
}

type Status struct {
	Round     int
	TeamId    int
	ServiceId int
	State     ServiceState
}

func createStatusTable(db *sql.DB) (err error) {

	_, err = db.Exec(`
	CREATE TABLE IF NOT EXISTS "status" (
		id	SERIAL PRIMARY KEY,
		round	INTEGER NOT NULL,
		team_id	INTEGER NOT NULL,
		service_id	INTEGER NOT NULL,
		state	INTEGER NOT NULL,
		timestamp	TIMESTAMP with time zone DEFAULT now()
	)`)

	return
}

func PutStatus(db *sql.DB, status Status) (err error) {

	stmt, err := db.Prepare("INSERT INTO status (round, team_id, " +
		"service_id, state) VALUES ($1, $2, $3, $4)")
	if err != nil {
		return
	}

	defer stmt.Close()

	_, err = stmt.Exec(status.Round, status.TeamId, status.ServiceId,
		status.State)
	if err != nil {
		return
	}

	return
}

func GetStates(db *sql.DB, halfStatus Status) (states []ServiceState,
	err error) {

	stmt, err := db.Prepare(
		"SELECT state FROM status WHERE round=$1 AND team_id=$2 " +
			"AND service_id=$3")
	if err != nil {
		return
	}

	defer stmt.Close()

	rows, err := stmt.Query(halfStatus.Round, halfStatus.TeamId,
		halfStatus.ServiceId)
	if err != nil {
		return
	}

	defer rows.Close()

	for rows.Next() {
		var state int

		err = rows.Scan(&state)
		if err != nil {
			return
		}

		states = append(states, ServiceState(state))
	}

	return
}

func GetState(db *sql.DB, halfStatus Status) (state ServiceState, err error) {

	stmt, err := db.Prepare(
		"SELECT state FROM status WHERE round=$1 AND team_id=$2 " +
			"AND service_id=$3 " +
			"AND ID = (SELECT MAX(ID) FROM status " +
			"WHERE round=$1 AND team_id=$2 AND service_id=$3)")
	if err != nil {
		return
	}

	defer stmt.Close()

	err = stmt.QueryRow(halfStatus.Round, halfStatus.TeamId,
		halfStatus.ServiceId).Scan(&state)
	if err != nil {
		return
	}

	return
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"
	"strings"

	cmn "github.com/tendermint/tmlibs/common"
	"github.com/tendermint/tmlibs/log"
	monitor "github.com/tendermint/tools/tm-monitor/monitor"
)

var version = "0.3.0"

var logger = log.NewNopLogger()

func main() {
	var listenAddr string
	var noton bool

	flag.StringVar(&listenAddr, "listen-addr", "tcp://0.0.0.0:46670", "HTTP and Websocket server listen address")
	flag.BoolVar(&noton, "no-ton", false, "Do not show ton (table of nodes)")

	flag.Usage = func() {
		fmt.Println(`Tendermint monitor watches over one or more Tendermint core
applications, collecting and providing various statistics to the user.

Usage:
	tm-monitor [-no-ton] [-listen-addr="tcp://0.0.0.0:46670"] [endpoints]

Examples:
	# monitor single instance
	tm-monitor localhost:46657

	# monitor a few instances by providing comma-separated list of RPC endpoints
	tm-monitor host1:46657,host2:46657`)
		fmt.Println("Flags:")
		flag.PrintDefaults()
	}

	flag.Parse()

	if flag.NArg() == 0 {
		flag.Usage()
		os.Exit(1)
	}

	if noton {
		logger = log.NewTMLogger(log.NewSyncWriter(os.Stdout))
	}

	m := startMonitor(flag.Arg(0))

	startRPC(listenAddr, m, logger)

	var ton *Ton
	if !noton {
		ton = NewTon(m)
		ton.Start()
	}

	cmn.TrapSignal(func() {
		if !noton {
			ton.Stop()
		}
		m.Stop()
	})
}

func startMonitor(endpoints string) *monitor.Monitor {
	m := monitor.NewMonitor()
	m.SetLogger(logger.With("component", "monitor"))

	for _, e := range strings.Split(endpoints, ",") {
		n := monitor.NewNode(e)
		n.SetLogger(logger.With("node", e))
		if err := m.Monitor(n); err != nil {
			panic(err)
		}
	}

	if err := m.Start(); err != nil {
		panic(err)
	}

	return m
}
<commit_msg>update tm-monitor version to 0.3.1<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
	"strings"

	cmn "github.com/tendermint/tmlibs/common"
	"github.com/tendermint/tmlibs/log"
	monitor "github.com/tendermint/tools/tm-monitor/monitor"
)

var version = "0.3.1"

var logger = log.NewNopLogger()

func main() {
	var listenAddr string
	var noton bool

	flag.StringVar(&listenAddr, "listen-addr", "tcp://0.0.0.0:46670", "HTTP and Websocket server listen address")
	flag.BoolVar(&noton, "no-ton", false, "Do not show ton (table of nodes)")

	flag.Usage = func() {
		fmt.Println(`Tendermint monitor watches over one or more Tendermint core
applications, collecting and providing various statistics to the user.

Usage:
	tm-monitor [-no-ton] [-listen-addr="tcp://0.0.0.0:46670"] [endpoints]

Examples:
	# monitor single instance
	tm-monitor localhost:46657

	# monitor a few instances by providing comma-separated list of RPC endpoints
	tm-monitor host1:46657,host2:46657`)
		fmt.Println("Flags:")
		flag.PrintDefaults()
	}

	flag.Parse()

	if flag.NArg() == 0 {
		flag.Usage()
		os.Exit(1)
	}

	if noton {
		logger = log.NewTMLogger(log.NewSyncWriter(os.Stdout))
	}

	m := startMonitor(flag.Arg(0))

	startRPC(listenAddr, m, logger)

	var ton *Ton
	if !noton {
		ton = NewTon(m)
		ton.Start()
	}

	cmn.TrapSignal(func() {
		if !noton {
			ton.Stop()
		}
		m.Stop()
	})
}

func startMonitor(endpoints string) *monitor.Monitor {
	m := monitor.NewMonitor()
	m.SetLogger(logger.With("component", "monitor"))

	for _, e := range strings.Split(endpoints, ",") {
		n := monitor.NewNode(e)
		n.SetLogger(logger.With("node", e))
		if err := m.Monitor(n); err != nil {
			panic(err)
		}
	}

	if err := m.Start(); err != nil {
		panic(err)
	}

	return m
}
<|endoftext|>


<commit_before>// Package tools contains other helper functions too small to justify their own package
// NOTE: Subject to change, do not rely on this package from outside git-lfs source
package tools

import (
	"bufio"
	"encoding/hex"
	"fmt"
	"io"
	"os"
	"path"
	"path/filepath"
	"regexp"
	"strings"
	"sync"
)

var localDirSet = NewStringSetFromSlice([]string{".", "./", ".\\"})

// FileOrDirExists determines if a file/dir exists, returns IsDir() results too.
func FileOrDirExists(path string) (exists bool, isDir bool) {
	fi, err := os.Stat(path)
	if err != nil {
		return false, false
	} else {
		return true, fi.IsDir()
	}
}

// FileExists determines if a file (NOT dir) exists.
func FileExists(path string) bool {
	ret, isDir := FileOrDirExists(path)
	return ret && !isDir
}

// DirExists determines if a dir (NOT file) exists.
func DirExists(path string) bool {
	ret, isDir := FileOrDirExists(path)
	return ret && isDir
}

// FileExistsOfSize determines if a file exists and is of a specific size.
func FileExistsOfSize(path string, sz int64) bool {
	fi, err := os.Stat(path)

	if err != nil {
		return false
	}

	return !fi.IsDir() && fi.Size() == sz
}

// ResolveSymlinks ensures that if the path supplied is a symlink, it is
// resolved to the actual concrete path
func ResolveSymlinks(path string) string {
	if len(path) == 0 {
		return path
	}

	if resolved, err := filepath.EvalSymlinks(path); err == nil {
		return resolved
	}
	return path
}

// RenameFileCopyPermissions moves srcfile to destfile, replacing destfile if
// necessary and also copying the permissions of destfile if it already exists
func RenameFileCopyPermissions(srcfile, destfile string) error {
	info, err := os.Stat(destfile)
	if os.IsNotExist(err) {
		// no original file
	} else if err != nil {
		return err
	} else {
		if err := os.Chmod(srcfile, info.Mode()); err != nil {
			return fmt.Errorf("can't set filemode on file %q: %v", srcfile, err)
		}
	}

	if err := os.Rename(srcfile, destfile); err != nil {
		return fmt.Errorf("cannot replace %q with %q: %v", destfile, srcfile, err)
	}
	return nil
}

// CleanPaths splits the given `paths` argument by the delimiter argument, and
// then "cleans" that path according to the path.Clean function (see
// https://golang.org/pkg/path#Clean).
// Note always cleans to '/' path separators regardless of platform (git friendly)
func CleanPaths(paths, delim string) (cleaned []string) {
	// If paths is an empty string, splitting it will yield [""], which will
	// become the path ".". To avoid this, bail out if trimmed paths
	// argument is empty.
	if paths = strings.TrimSpace(paths); len(paths) == 0 {
		return
	}

	for _, part := range strings.Split(paths, delim) {
		part = strings.TrimSpace(part)

		cleaned = append(cleaned, path.Clean(part))
	}

	return cleaned
}

// VerifyFileHash reads a file and verifies whether the SHA is correct
// Returns an error if there is a problem
func VerifyFileHash(oid, path string) error {
	f, err := os.Open(path)
	if err != nil {
		return err
	}
	defer f.Close()

	h := NewLfsContentHash()
	_, err = io.Copy(h, f)
	if err != nil {
		return err
	}

	calcOid := hex.EncodeToString(h.Sum(nil))
	if calcOid != oid {
		return fmt.Errorf("File %q has an invalid hash %s, expected %s", path, calcOid, oid)
	}

	return nil
}

// FilenamePassesIncludeExcludeFilter returns whether a given filename passes the include / exclude path filters
// Only paths that are in includePaths and outside excludePaths are passed
// If includePaths is empty that filter always passes and the same with excludePaths
// Both path lists support wildcard matches
func FilenamePassesIncludeExcludeFilter(filename string, includePaths, excludePaths []string) bool {
	if len(includePaths) == 0 && len(excludePaths) == 0 {
		return true
	}

	if len(includePaths) > 0 {
		matched := false
		for _, inc := range includePaths {
			matched = FileMatch(inc, filename)
			if matched {
				break
			}
		}
		if !matched {
			return false
		}
	}

	if len(excludePaths) > 0 {
		for _, ex := range excludePaths {
			if FileMatch(ex, filename) {
				return false
			}
		}
	}

	return true
}

// FileMatch is a revised version of filepath.Match which makes it behave more
// like gitignore
func FileMatch(pattern, name string) bool {
	pattern = filepath.Clean(pattern)
	name = filepath.Clean(name)

	// Special case local dir, matches all (inc subpaths)
	if _, local := localDirSet[pattern]; local {
		return true
	}

	if matched, _ := filepath.Match(pattern, name); matched {
		return true
	}

	// special case * when there are no path separators
	// filepath.Match never allows * to match a path separator, which is correct
	// for gitignore IF the pattern includes a path separator, but not otherwise
	// So *.txt should match in any subdir, as should test*, but sub/*.txt would
	// only match directly in the sub dir
	// Don't need to test cross-platform separators as both cleaned above
	if !strings.Contains(pattern, string(filepath.Separator)) &&
		strings.Contains(pattern, "*") {
		regpattern := strings.Replace(pattern, "*", ".*", -1)
		if regexp.MustCompile(regpattern).MatchString(name) {
			return true
		}
	}
	// Also support matching a parent directory without a wildcard
	if strings.HasPrefix(name, pattern+string(filepath.Separator)) {
		return true
	}

	return false

}

// Returned from FastWalk with parent directory context
// This is needed because FastWalk can provide paths out of order so the
// parent dir cannot be implied
type FastWalkInfo struct {
	ParentDir string
	Info      os.FileInfo
}

// FastWalk is a more optimal implementation of filepath.Walk
// It differs in the following ways:
//  * Provides a channel of information instead of using a callback func
//  * Uses goroutines to parallelise large dirs and descent into subdirs
//  * Does not provide sorted output; parents will always be before children but
//    there are no other guarantees. Use parentDir in the FastWalkInfo struct to
//    determine absolute path rather than tracking it yourself like filepath.Walk
//  * Supports include / exclude filters
// Both dir and include/exclude paths can be relative or absolute, but they must
// all be of the same type. includePaths/excludePaths can be nil.
func FastWalk(dir string, includePaths, excludePaths []string) (<-chan FastWalkInfo, <-chan error) {
	return FastWalkWithExcludeFiles(dir, "", includePaths, excludePaths)
}

// FastWalkWithExcludeFiles is like FastWalk but with the additional option to
// load any file named excludeFilename in any directory, and add its contents
// to the excludePaths list for that directory and children.
func FastWalkWithExcludeFiles(dir, excludeFilename string,
	includePaths, excludePaths []string) (<-chan FastWalkInfo, <-chan error) {
	fiChan := make(chan FastWalkInfo, 256)
	errChan := make(chan error, 10)

	go fastWalkFromRoot(dir, excludeFilename, includePaths, excludePaths, fiChan, errChan)

	return fiChan, errChan
}

// FastWalkGitRepo behaves like FastWalkWithExcludeFiles, preconfigured to ignore
// the git repo itself (.git) and to load exclude patterns from .gitignore
func FastWalkGitRepo(dir string) (<-chan FastWalkInfo, <-chan error) {
	// Ignore all git metadata including subrepos
	excludePaths := []string{filepath.Join("*", ".git")}
	return FastWalkWithExcludeFiles(dir, ".gitignore", nil, excludePaths)
}

func fastWalkFromRoot(dir string, excludeFilename string,
	includePaths, excludePaths []string, fiChan chan<- FastWalkInfo, errChan chan<- error) {

	dirFi, err := os.Stat(dir)
	if err != nil {
		errChan <- err
		return
	}

	// This waitgroup will be incremented for each nested goroutine
	var waitg sync.WaitGroup

	fastWalkItem(filepath.Dir(dir), dirFi, excludeFilename, includePaths, excludePaths, fiChan, errChan, &waitg)

	waitg.Wait()
	close(fiChan)
	close(errChan)

}

// Main recursive implementation of fast walk
// Increment waitg.Add(1) for each new goroutine launched internally
func fastWalkItem(parentDir string, itemFi os.FileInfo, excludeFilename string,
	includePaths, excludePaths []string, fiChan chan<- FastWalkInfo, errChan chan<- error,
	waitg *sync.WaitGroup) {

	fullPath := filepath.Join(parentDir, itemFi.Name())

	if !FilenamePassesIncludeExcludeFilter(fullPath, includePaths, excludePaths) {
		return
	}

	fiChan <- FastWalkInfo{ParentDir: parentDir, Info: itemFi}

	if !itemFi.IsDir() {
		// Nothing more to do if this is not a dir
		return
	}

	if len(excludeFilename) > 0 {
		possibleExcludeFile := filepath.Join(fullPath, excludeFilename)
		if FileExists(possibleExcludeFile) {
			var err error
			excludePaths, err = loadExcludeFilename(possibleExcludeFile, fullPath, excludePaths)
			if err != nil {
				errChan <- err
			}
		}
	}

	// The absolute optimal way to scan would be File.Readdirnames but we
	// still need the Stat() to know whether something is a dir, so use
	// File.Readdir instead. Means we can provide os.FileInfo to callers like
	// filepath.Walk as a bonus.
	df, err := os.Open(fullPath)
	if err != nil {
		errChan <- err
		return
	}
	defer df.Close()
	jobSize := 100
	for children, err := df.Readdir(jobSize); err == nil; children, err = df.Readdir(jobSize) {
		// Parallelise all dirs, and chop large dirs into batches
		waitg.Add(1)
		go func(subitems []os.FileInfo) {
			for _, childFi := range subitems {
				fastWalkItem(fullPath, childFi, excludeFilename, includePaths, excludePaths, fiChan, errChan, waitg)
			}
			waitg.Done()
		}(children)

	}
	if err != nil && err != io.EOF {
		errChan <- err
	}

}

// loadExcludeFilename reads the given file in gitignore format and returns a
// revised array of exclude paths if there are any changes.
// If any changes are made a copy of the array is taken so the original is not
// modified
func loadExcludeFilename(filename, parentDir string, excludePaths []string) ([]string, error) {

	f, err := os.OpenFile(filename, os.O_RDONLY, 0644)
	if err != nil {
		return excludePaths, err
	}
	defer f.Close()

	retPaths := excludePaths
	modified := false

	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		// Skip blanks, comments and negations (not supported right now)
		if len(line) == 0 || strings.HasPrefix(line, "#") || strings.HasPrefix(line, "!") {
			continue
		}

		if !modified {
			// copy on write
			retPaths = make([]string, len(excludePaths))
			copy(retPaths, excludePaths)
			modified = true
		}

		path := line
		// Add pattern in context if exclude has separator, or no wildcard
		// Allow for both styles of separator at this point
		if strings.ContainsAny(path, "/\\") ||
			!strings.Contains(path, "*") {
			path = filepath.Join(parentDir, line)
		}
		retPaths = append(retPaths, path)
	}

	return retPaths, nil

}
<commit_msg>Filematch should quote non-wildcards and only match to end not start<commit_after>// Package tools contains other helper functions too small to justify their own package
// NOTE: Subject to change, do not rely on this package from outside git-lfs source
package tools

import (
	"bufio"
	"encoding/hex"
	"fmt"
	"io"
	"os"
	"path"
	"path/filepath"
	"regexp"
	"strings"
	"sync"
)

var localDirSet = NewStringSetFromSlice([]string{".", "./", ".\\"})

// FileOrDirExists determines if a file/dir exists, returns IsDir() results too.
func FileOrDirExists(path string) (exists bool, isDir bool) {
	fi, err := os.Stat(path)
	if err != nil {
		return false, false
	} else {
		return true, fi.IsDir()
	}
}

// FileExists determines if a file (NOT dir) exists.
func FileExists(path string) bool {
	ret, isDir := FileOrDirExists(path)
	return ret && !isDir
}

// DirExists determines if a dir (NOT file) exists.
func DirExists(path string) bool {
	ret, isDir := FileOrDirExists(path)
	return ret && isDir
}

// FileExistsOfSize determines if a file exists and is of a specific size.
func FileExistsOfSize(path string, sz int64) bool {
	fi, err := os.Stat(path)

	if err != nil {
		return false
	}

	return !fi.IsDir() && fi.Size() == sz
}

// ResolveSymlinks ensures that if the path supplied is a symlink, it is
// resolved to the actual concrete path
func ResolveSymlinks(path string) string {
	if len(path) == 0 {
		return path
	}

	if resolved, err := filepath.EvalSymlinks(path); err == nil {
		return resolved
	}
	return path
}

// RenameFileCopyPermissions moves srcfile to destfile, replacing destfile if
// necessary and also copying the permissions of destfile if it already exists
func RenameFileCopyPermissions(srcfile, destfile string) error {
	info, err := os.Stat(destfile)
	if os.IsNotExist(err) {
		// no original file
	} else if err != nil {
		return err
	} else {
		if err := os.Chmod(srcfile, info.Mode()); err != nil {
			return fmt.Errorf("can't set filemode on file %q: %v", srcfile, err)
		}
	}

	if err := os.Rename(srcfile, destfile); err != nil {
		return fmt.Errorf("cannot replace %q with %q: %v", destfile, srcfile, err)
	}
	return nil
}

// CleanPaths splits the given `paths` argument by the delimiter argument, and
// then "cleans" that path according to the path.Clean function (see
// https://golang.org/pkg/path#Clean).
// Note always cleans to '/' path separators regardless of platform (git friendly)
func CleanPaths(paths, delim string) (cleaned []string) {
	// If paths is an empty string, splitting it will yield [""], which will
	// become the path ".". To avoid this, bail out if trimmed paths
	// argument is empty.
	if paths = strings.TrimSpace(paths); len(paths) == 0 {
		return
	}

	for _, part := range strings.Split(paths, delim) {
		part = strings.TrimSpace(part)

		cleaned = append(cleaned, path.Clean(part))
	}

	return cleaned
}

// VerifyFileHash reads a file and verifies whether the SHA is correct
// Returns an error if there is a problem
func VerifyFileHash(oid, path string) error {
	f, err := os.Open(path)
	if err != nil {
		return err
	}
	defer f.Close()

	h := NewLfsContentHash()
	_, err = io.Copy(h, f)
	if err != nil {
		return err
	}

	calcOid := hex.EncodeToString(h.Sum(nil))
	if calcOid != oid {
		return fmt.Errorf("File %q has an invalid hash %s, expected %s", path, calcOid, oid)
	}

	return nil
}

// FilenamePassesIncludeExcludeFilter returns whether a given filename passes the include / exclude path filters
// Only paths that are in includePaths and outside excludePaths are passed
// If includePaths is empty that filter always passes and the same with excludePaths
// Both path lists support wildcard matches
func FilenamePassesIncludeExcludeFilter(filename string, includePaths, excludePaths []string) bool {
	if len(includePaths) == 0 && len(excludePaths) == 0 {
		return true
	}

	if len(includePaths) > 0 {
		matched := false
		for _, inc := range includePaths {
			matched = FileMatch(inc, filename)
			if matched {
				break
			}
		}
		if !matched {
			return false
		}
	}

	if len(excludePaths) > 0 {
		for _, ex := range excludePaths {
			if FileMatch(ex, filename) {
				return false
			}
		}
	}

	return true
}

// FileMatch is a revised version of filepath.Match which makes it behave more
// like gitignore
func FileMatch(pattern, name string) bool {
	pattern = filepath.Clean(pattern)
	name = filepath.Clean(name)

	// Special case local dir, matches all (inc subpaths)
	if _, local := localDirSet[pattern]; local {
		return true
	}

	if matched, _ := filepath.Match(pattern, name); matched {
		return true
	}

	// special case * when there are no path separators
	// filepath.Match never allows * to match a path separator, which is correct
	// for gitignore IF the pattern includes a path separator, but not otherwise
	// So *.txt should match in any subdir, as should test*, but sub/*.txt would
	// only match directly in the sub dir
	// Don't need to test cross-platform separators as both cleaned above
	if !strings.Contains(pattern, string(filepath.Separator)) &&
		strings.Contains(pattern, "*") {
		pattern = regexp.QuoteMeta(pattern)
		regpattern := fmt.Sprintf("%s$", strings.Replace(pattern, "\\*", ".*", -1))
		if regexp.MustCompile(regpattern).MatchString(name) {
			return true
		}
	}
	// Also support matching a parent directory without a wildcard
	if strings.HasPrefix(name, pattern+string(filepath.Separator)) {
		return true
	}

	return false

}

// Returned from FastWalk with parent directory context
// This is needed because FastWalk can provide paths out of order so the
// parent dir cannot be implied
type FastWalkInfo struct {
	ParentDir string
	Info      os.FileInfo
}

// FastWalk is a more optimal implementation of filepath.Walk
// It differs in the following ways:
//  * Provides a channel of information instead of using a callback func
//  * Uses goroutines to parallelise large dirs and descent into subdirs
//  * Does not provide sorted output; parents will always be before children but
//    there are no other guarantees. Use parentDir in the FastWalkInfo struct to
//    determine absolute path rather than tracking it yourself like filepath.Walk
//  * Supports include / exclude filters
// Both dir and include/exclude paths can be relative or absolute, but they must
// all be of the same type. includePaths/excludePaths can be nil.
func FastWalk(dir string, includePaths, excludePaths []string) (<-chan FastWalkInfo, <-chan error) {
	return FastWalkWithExcludeFiles(dir, "", includePaths, excludePaths)
}

// FastWalkWithExcludeFiles is like FastWalk but with the additional option to
// load any file named excludeFilename in any directory, and add its contents
// to the excludePaths list for that directory and children.
func FastWalkWithExcludeFiles(dir, excludeFilename string,
	includePaths, excludePaths []string) (<-chan FastWalkInfo, <-chan error) {
	fiChan := make(chan FastWalkInfo, 256)
	errChan := make(chan error, 10)

	go fastWalkFromRoot(dir, excludeFilename, includePaths, excludePaths, fiChan, errChan)

	return fiChan, errChan
}

// FastWalkGitRepo behaves like FastWalkWithExcludeFiles, preconfigured to ignore
// the git repo itself (.git) and to load exclude patterns from .gitignore
func FastWalkGitRepo(dir string) (<-chan FastWalkInfo, <-chan error) {
	// Ignore all git metadata including subrepos
	excludePaths := []string{filepath.Join("*", ".git")}
	return FastWalkWithExcludeFiles(dir, ".gitignore", nil, excludePaths)
}

func fastWalkFromRoot(dir string, excludeFilename string,
	includePaths, excludePaths []string, fiChan chan<- FastWalkInfo, errChan chan<- error) {

	dirFi, err := os.Stat(dir)
	if err != nil {
		errChan <- err
		return
	}

	// This waitgroup will be incremented for each nested goroutine
	var waitg sync.WaitGroup

	fastWalkItem(filepath.Dir(dir), dirFi, excludeFilename, includePaths, excludePaths, fiChan, errChan, &waitg)

	waitg.Wait()
	close(fiChan)
	close(errChan)

}

// Main recursive implementation of fast walk
// Increment waitg.Add(1) for each new goroutine launched internally
func fastWalkItem(parentDir string, itemFi os.FileInfo, excludeFilename string,
	includePaths, excludePaths []string, fiChan chan<- FastWalkInfo, errChan chan<- error,
	waitg *sync.WaitGroup) {

	fullPath := filepath.Join(parentDir, itemFi.Name())

	if !FilenamePassesIncludeExcludeFilter(fullPath, includePaths, excludePaths) {
		return
	}

	fiChan <- FastWalkInfo{ParentDir: parentDir, Info: itemFi}

	if !itemFi.IsDir() {
		// Nothing more to do if this is not a dir
		return
	}

	if len(excludeFilename) > 0 {
		possibleExcludeFile := filepath.Join(fullPath, excludeFilename)
		if FileExists(possibleExcludeFile) {
			var err error
			excludePaths, err = loadExcludeFilename(possibleExcludeFile, fullPath, excludePaths)
			if err != nil {
				errChan <- err
			}
		}
	}

	// The absolute optimal way to scan would be File.Readdirnames but we
	// still need the Stat() to know whether something is a dir, so use
	// File.Readdir instead. Means we can provide os.FileInfo to callers like
	// filepath.Walk as a bonus.
	df, err := os.Open(fullPath)
	if err != nil {
		errChan <- err
		return
	}
	defer df.Close()
	jobSize := 100
	for children, err := df.Readdir(jobSize); err == nil; children, err = df.Readdir(jobSize) {
		// Parallelise all dirs, and chop large dirs into batches
		waitg.Add(1)
		go func(subitems []os.FileInfo) {
			for _, childFi := range subitems {
				fastWalkItem(fullPath, childFi, excludeFilename, includePaths, excludePaths, fiChan, errChan, waitg)
			}
			waitg.Done()
		}(children)

	}
	if err != nil && err != io.EOF {
		errChan <- err
	}

}

// loadExcludeFilename reads the given file in gitignore format and returns a
// revised array of exclude paths if there are any changes.
// If any changes are made a copy of the array is taken so the original is not
// modified
func loadExcludeFilename(filename, parentDir string, excludePaths []string) ([]string, error) {

	f, err := os.OpenFile(filename, os.O_RDONLY, 0644)
	if err != nil {
		return excludePaths, err
	}
	defer f.Close()

	retPaths := excludePaths
	modified := false

	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		// Skip blanks, comments and negations (not supported right now)
		if len(line) == 0 || strings.HasPrefix(line, "#") || strings.HasPrefix(line, "!") {
			continue
		}

		if !modified {
			// copy on write
			retPaths = make([]string, len(excludePaths))
			copy(retPaths, excludePaths)
			modified = true
		}

		path := line
		// Add pattern in context if exclude has separator, or no wildcard
		// Allow for both styles of separator at this point
		if strings.ContainsAny(path, "/\\") ||
			!strings.Contains(path, "*") {
			path = filepath.Join(parentDir, line)
		}
		retPaths = append(retPaths, path)
	}

	return retPaths, nil

}
<|endoftext|>


<commit_before>package gotosca

// TopologyStructure as defined in
//http://docs.oasis-open.org/tosca/TOSCA-Simple-Profile-YAML/v1.0/csd03/TOSCA-Simple-Profile-YAML-v1.0-csd03.html
type TopologyTemplateStruct struct {
	ToscaDefinitionsVersion string `yaml:"tosca_definitions_version"` // A.9.3.1 tosca_definitions_version
	Description             string `yaml:"description"`
	DlsDefinitions          struct {
	} `yaml:"dsl_definitions"` // 15 Using YAML Macros to simplify templates
	TopologyTemplate struct {
		Inputs map[string]struct {
			Type        string      `yaml:"type"`
			Description string      `yaml:"description"`
			Constraints interface{} `yaml:"constraints"`
		} `yaml:"inputs,omitempty"`
		NodeTemplates map[string]struct {
			NodeType     string                 `yaml:"type"`
			Properties   map[string]interface{} `yaml:"properties,omitempty"`
			Capabilities map[string]struct {
				Properties map[string]string `yaml:"properties,omitempty"`
			} `yaml:"capabilities,omitempty"`
		} `yaml:"node_templates"`
		Outputs map[string]struct {
			Value       interface{} `yaml:"value"`
			Description string      `yaml:"description"`
		} `yaml:"outputs,omitempty"`
	} `yaml:"topology_template"`
}
<commit_msg>An enumeration of properties as in Annexe B of the TOSCA definition<commit_after>package gotosca

const (
	PropertyFunctions []string = []string{"get_input", "get_property", "get_attribute"} // B 4.1
)

// TopologyStructure as defined in
//http://docs.oasis-open.org/tosca/TOSCA-Simple-Profile-YAML/v1.0/csd03/TOSCA-Simple-Profile-YAML-v1.0-csd03.html
type TopologyTemplateStruct struct {
	ToscaDefinitionsVersion string `yaml:"tosca_definitions_version"` // A.9.3.1 tosca_definitions_version
	Description             string `yaml:"description"`
	DlsDefinitions          struct {
	} `yaml:"dsl_definitions"` // 15 Using YAML Macros to simplify templates
	TopologyTemplate struct {
		Inputs map[string]struct {
			Type        string      `yaml:"type"`
			Description string      `yaml:"description"`
			Constraints interface{} `yaml:"constraints"`
		} `yaml:"inputs,omitempty"`
		NodeTemplates map[string]struct {
			NodeType     string                 `yaml:"type"`
			Properties   map[string]interface{} `yaml:"properties,omitempty"`
			Capabilities map[string]struct {
				Properties map[string]string `yaml:"properties,omitempty"`
			} `yaml:"capabilities,omitempty"`
		} `yaml:"node_templates"`
		Outputs map[string]struct {
			Value       interface{} `yaml:"value"`
			Description string      `yaml:"description"`
		} `yaml:"outputs,omitempty"`
	} `yaml:"topology_template"`
}
<|endoftext|>


<commit_before>package uatparse

import ()

const (
	BLOCK_WIDTH      = float64(48.0 / 60.0)
	WIDE_BLOCK_WIDTH = float64(96.0 / 60.0)
	BLOCK_HEIGHT     = float64(4.0 / 60.0)
	BLOCK_THRESHOLD  = 405000
	BLOCKS_PER_RING  = 450
)

type NEXRADBlock struct {
	radar_type uint32
	scale      int
	latNorth   float64
	lonWest    float64
	height     float64
	width      float64
	intensity  []uint8 // Really only 4-bit values.
}

func block_location(block_num int, ns_flag bool, scale_factor int) (float64, float64, float64, float64) {
	var realScale float64
	if scale_factor == 1 {
		realScale = float64(5.0)
	} else if scale_factor == 2 {
		realScale = float64(9.0)
	} else {
		realScale = float64(1.0)
	}

	if block_num >= BLOCK_THRESHOLD {
		block_num = block_num & ^1
	}

	raw_lat := float64(BLOCK_HEIGHT * float64(int(float64(block_num)/float64(BLOCKS_PER_RING))))
	raw_lon := float64(block_num%BLOCKS_PER_RING) * BLOCK_WIDTH

	var lonSize float64
	if block_num >= BLOCK_THRESHOLD {
		lonSize = WIDE_BLOCK_WIDTH * realScale
	} else {
		lonSize = BLOCK_WIDTH * realScale
	}

	latSize := BLOCK_HEIGHT * realScale

	if ns_flag { // Southern hemisphere.
		raw_lat = 0 - raw_lat
	} else {
		raw_lat = raw_lat + BLOCK_HEIGHT
	}

	if raw_lon > 180.0 {
		raw_lon = raw_lon - 360.0
	}

	return raw_lat, raw_lon, latSize, lonSize

}

func (f *UATFrame) decodeNexradFrame() {
	if len(f.FISB_data) < 4 { // Short read.
		return
	}

	rle_flag := (uint32(f.FISB_data[0]) & 0x80) != 0
	ns_flag := (uint32(f.FISB_data[0]) & 0x40) != 0
	block_num := ((int(f.FISB_data[0]) & 0x0f) << 16) | (int(f.FISB_data[1]) << 8) | (int(f.FISB_data[2]))
	scale_factor := (int(f.FISB_data[0]) & 0x30) >> 4

	if rle_flag { // Single bin, RLE encoded.
		lat, lon, h, w := block_location(block_num, ns_flag, scale_factor)
		var tmp NEXRADBlock
		tmp.radar_type = f.Product_id
		tmp.scale = scale_factor
		tmp.latNorth = lat
		tmp.lonWest = lon
		tmp.height = h
		tmp.width = w
		tmp.intensity = make([]uint8, 0)

		intensityData := f.FISB_data[3:]
		for _, v := range intensityData {
			intensity := uint8(v) & 0x7
			runlength := (uint8(v) >> 3) + 1
			for runlength > 0 {
				tmp.intensity = append(tmp.intensity, intensity)
				runlength--
			}
		}
		f.NEXRAD = []NEXRADBlock{tmp}
	} else {
		var row_start int
		var row_size int
		if block_num >= 405000 {
			row_start = block_num - ((block_num - 405000) % 225)
			row_size = 225
		} else {
			row_start = block_num - (block_num % 450)
			row_size = 450
		}

		row_offset := block_num - row_start

		L := int(f.FISB_data[3] & 15)

		if len(f.FISB_data) < L+3 { // Short read.
			return
		}

		for i := 0; i < L; i++ {
			var bb int
			if i == 0 {
				bb = (int(f.FISB_data[3]) & 0xF0) | 0x08
			} else {
				bb = int(f.FISB_data[i+3])
			}

			for j := 0; j < 8; j++ {
				if bb&(1<<uint(j)) != 0 {
					row_x := (row_offset + 8*i + j - 3) % row_size
					bn := row_start + row_x
					lat, lon, h, w := block_location(bn, ns_flag, scale_factor)
					var tmp NEXRADBlock
					tmp.radar_type = f.Product_id
					tmp.scale = scale_factor
					tmp.latNorth = lat
					tmp.lonWest = lon
					tmp.height = h
					tmp.width = w
					tmp.intensity = make([]uint8, 0)
					for k := 0; k < 128; k++ {
						z := uint8(0)
						if f.Product_id == 64 {
							z = 1
						}
						tmp.intensity = append(tmp.intensity, z)
					}
					f.NEXRAD = append(f.NEXRAD, tmp)
				}
			}
		}
	}

}
<commit_msg>Make NEXRADBlock fields exportable.<commit_after>package uatparse

import ()

const (
	BLOCK_WIDTH      = float64(48.0 / 60.0)
	WIDE_BLOCK_WIDTH = float64(96.0 / 60.0)
	BLOCK_HEIGHT     = float64(4.0 / 60.0)
	BLOCK_THRESHOLD  = 405000
	BLOCKS_PER_RING  = 450
)

type NEXRADBlock struct {
	Radar_Type uint32
	Scale      int
	LatNorth   float64
	LonWest    float64
	Height     float64
	Width      float64
	Intensity  []uint8 // Really only 4-bit values.
}

func block_location(block_num int, ns_flag bool, scale_factor int) (float64, float64, float64, float64) {
	var realScale float64
	if scale_factor == 1 {
		realScale = float64(5.0)
	} else if scale_factor == 2 {
		realScale = float64(9.0)
	} else {
		realScale = float64(1.0)
	}

	if block_num >= BLOCK_THRESHOLD {
		block_num = block_num & ^1
	}

	raw_lat := float64(BLOCK_HEIGHT * float64(int(float64(block_num)/float64(BLOCKS_PER_RING))))
	raw_lon := float64(block_num%BLOCKS_PER_RING) * BLOCK_WIDTH

	var lonSize float64
	if block_num >= BLOCK_THRESHOLD {
		lonSize = WIDE_BLOCK_WIDTH * realScale
	} else {
		lonSize = BLOCK_WIDTH * realScale
	}

	latSize := BLOCK_HEIGHT * realScale

	if ns_flag { // Southern hemisphere.
		raw_lat = 0 - raw_lat
	} else {
		raw_lat = raw_lat + BLOCK_HEIGHT
	}

	if raw_lon > 180.0 {
		raw_lon = raw_lon - 360.0
	}

	return raw_lat, raw_lon, latSize, lonSize

}

func (f *UATFrame) decodeNexradFrame() {
	if len(f.FISB_data) < 4 { // Short read.
		return
	}

	rle_flag := (uint32(f.FISB_data[0]) & 0x80) != 0
	ns_flag := (uint32(f.FISB_data[0]) & 0x40) != 0
	block_num := ((int(f.FISB_data[0]) & 0x0f) << 16) | (int(f.FISB_data[1]) << 8) | (int(f.FISB_data[2]))
	scale_factor := (int(f.FISB_data[0]) & 0x30) >> 4

	if rle_flag { // Single bin, RLE encoded.
		lat, lon, h, w := block_location(block_num, ns_flag, scale_factor)
		var tmp NEXRADBlock
		tmp.Radar_Type = f.Product_id
		tmp.Scale = scale_factor
		tmp.LatNorth = lat
		tmp.LonWest = lon
		tmp.Height = h
		tmp.Width = w
		tmp.Intensity = make([]uint8, 0)

		intensityData := f.FISB_data[3:]
		for _, v := range intensityData {
			intensity := uint8(v) & 0x7
			runlength := (uint8(v) >> 3) + 1
			for runlength > 0 {
				tmp.Intensity = append(tmp.Intensity, intensity)
				runlength--
			}
		}
		f.NEXRAD = []NEXRADBlock{tmp}
	} else {
		var row_start int
		var row_size int
		if block_num >= 405000 {
			row_start = block_num - ((block_num - 405000) % 225)
			row_size = 225
		} else {
			row_start = block_num - (block_num % 450)
			row_size = 450
		}

		row_offset := block_num - row_start

		L := int(f.FISB_data[3] & 15)

		if len(f.FISB_data) < L+3 { // Short read.
			return
		}

		for i := 0; i < L; i++ {
			var bb int
			if i == 0 {
				bb = (int(f.FISB_data[3]) & 0xF0) | 0x08
			} else {
				bb = int(f.FISB_data[i+3])
			}

			for j := 0; j < 8; j++ {
				if bb&(1<<uint(j)) != 0 {
					row_x := (row_offset + 8*i + j - 3) % row_size
					bn := row_start + row_x
					lat, lon, h, w := block_location(bn, ns_flag, scale_factor)
					var tmp NEXRADBlock
					tmp.Radar_Type = f.Product_id
					tmp.Scale = scale_factor
					tmp.LatNorth = lat
					tmp.LonWest = lon
					tmp.Height = h
					tmp.Width = w
					tmp.Intensity = make([]uint8, 0)
					for k := 0; k < 128; k++ {
						z := uint8(0)
						if f.Product_id == 64 {
							z = 1
						}
						tmp.Intensity = append(tmp.Intensity, z)
					}
					f.NEXRAD = append(f.NEXRAD, tmp)
				}
			}
		}
	}

}
<|endoftext|>


<commit_before>package tritium

func (ins *Instruction) Iterate(itFunc func(*Instruction)) {
	itFunc(ins)
	if ins.Children == nil {
		return
	}
	for _, child := range(ins.Children) {
		child.Iterate(itFunc)
	}
}<commit_msg>Helper functions for creating and manipulating instructions.<commit_after>package tritium

import (
  "goprotobuf.googlecode.com/hg/proto"
  t "tritium/tokenizer"
)

func (ins *Instruction) Iterate(itFunc func(*Instruction)) {
	itFunc(ins)
	if ins.Children == nil {
		return
	}
	for _, child := range(ins.Children) {
		child.Iterate(itFunc)
	}
}

func List(instrs ...*Instruction) []*Instruction {
  return append(make([]*Instruction, 0), instrs...)
}

func MakeText(text *t.Token) *Instruction {
  return &Instruction {
    Type: NewInstruction_InstructionType(Instruction_TEXT),
    Value: proto.String(text.Value),
    LineNumber: proto.Int32(text.LineNum),
  }
}

func MakePosition(pos *t.Token) *Instruction {
  return &Instruction {
    Type: NewInstruction_InstructionType(Instruction_POSITION),
    Value: proto.String(pos.Value),
    LineNumber: proto.Int32(pos.LineNum),
  }
}

func MakeImport(path *t.Token) *Instruction {
  return &Instruction {
    Type: NewInstruction_InstructionType(Instruction_IMPORT),
    Value: proto.String(path.Value),
    LineNumber: proto.Int32(path.LineNum),
  }
}

func MakeLocalVar(name *t.Token, val *Instruction, children []*Instruction) *Instruction {
  return &Instruction {
    Type: NewInstruction_InstructionType(Instruction_LOCAL_VAR),
    Value: proto.String(name.Value),
    Arguments: List(val),
    Children: children,
    LineNumber: proto.Int32(name.LineNum),
  }
}

func MakeFunctionCall(name *t.Token, args []*Instruction, body []*Instruction) *Instruction {
  return &Instruction {
    Type: NewInstruction_InstructionType(Instruction_FUNCTION_CALL),
    Value: proto.String(name.Value),
    Arguments: args,
    Children: body,
    LineNumber: proto.Int32(name.LineNum),
  }
}

func MakeBlock(children []*Instruction) *Instruction {
  return &Instruction {
    Type: NewInstruction_InstructionType(Instruction_BLOCK),
    Children: children,
    LineNumber: children[0].LineNumber,
  }
}

func (instr *Instruction) Append(more ...*Instruction) {
  if instr.Children == nil {
    instr.Children = more
  } else {
    instr.Children = append(instr.Children, more...)
  }
}

func (instr *Instruction) ConcatList(more []*Instruction) {
  instr.Append(more...)
}

func (instr *Instruction) ConcatBlock (more *Instruction) {
  instr.Append(more.Children...)
}<|endoftext|>


<commit_before>package pixelgl

import (
	"github.com/go-gl/glfw/v3.2/glfw"
)

// Joystick is a joystick or controller.
type Joystick int

// List all of the joysticks.
const (
	Joystick1  = Joystick(glfw.Joystick1)
	Joystick2  = Joystick(glfw.Joystick2)
	Joystick3  = Joystick(glfw.Joystick3)
	Joystick4  = Joystick(glfw.Joystick4)
	Joystick5  = Joystick(glfw.Joystick5)
	Joystick6  = Joystick(glfw.Joystick6)
	Joystick7  = Joystick(glfw.Joystick7)
	Joystick8  = Joystick(glfw.Joystick8)
	Joystick9  = Joystick(glfw.Joystick9)
	Joystick10 = Joystick(glfw.Joystick10)
	Joystick11 = Joystick(glfw.Joystick11)
	Joystick12 = Joystick(glfw.Joystick12)
	Joystick13 = Joystick(glfw.Joystick13)
	Joystick14 = Joystick(glfw.Joystick14)
	Joystick15 = Joystick(glfw.Joystick15)
	Joystick16 = Joystick(glfw.Joystick16)
)

// JoystickPresent returns if the joystick is currently connected.
//
// This API is experimental.
func (w *Window) JoystickPresent(js Joystick) bool {
	return w.currJoy.connected[js]
}

// JoystickName returns the name of the joystick. A disconnected joystick will return an
// empty string.
//
// This API is experimental.
func (w *Window) JoystickName(js Joystick) string {
	return w.currJoy.name[js]
}

// JoystickButtonCount returns the number of buttons a connected joystick has.
//
// This API is experimental.
func (w *Window) JoystickButtonCount(js Joystick) int {
	return len(w.currJoy.buttons[js])
}

// JoystickAxisCount returns the number of axes a connected joystick has.
//
// This API is experimental.
func (w *Window) JoystickAxisCount(js Joystick) int {
	return len(w.currJoy.axis[js])
}

// JoystickPressed returns whether the joystick Button is currently pressed down.
// If the button index is out of range, this will return false.
//
// This API is experimental.
func (w *Window) JoystickPressed(js Joystick, button int) bool {
	return w.currJoy.getButton(js, button)
}

// JoystickJustPressed returns whether the joystick Button has just been pressed down.
// If the button index is out of range, this will return false.
//
// This API is experimental.
func (w *Window) JoystickJustPressed(js Joystick, button int) bool {
	return w.currJoy.getButton(js, button) && !w.prevJoy.getButton(js, button)
}

// JoystickJustReleased returns whether the joystick Button has just been released up.
// If the button index is out of range, this will return false.
//
// This API is experimental.
func (w *Window) JoystickJustReleased(js Joystick, button int) bool {
	return !w.currJoy.getButton(js, button) && w.prevJoy.getButton(js, button)
}

// JoystickAxis returns the value of a joystick axis at the last call to Window.Update.
// If the axis index is out of range, this will return 0.
//
// This API is experimental.
func (w *Window) JoystickAxis(js Joystick, axis int) float64 {
	return w.currJoy.getAxis(js, axis)
}

// Used internally during Window.UpdateInput to update the state of the joysticks.
func (w *Window) updateJoystickInput() {
	for js := Joystick1; js < Joystick16; js++ {
		// Determine and store if the joystick was connected
		joystickPresent := glfw.JoystickPresent(glfw.Joystick(js))
		w.tempJoy.connected[js] = joystickPresent

		if joystickPresent {
			w.tempJoy.buttons[js] = glfw.GetJoystickButtons(glfw.Joystick(js))
			w.tempJoy.axis[js] = glfw.GetJoystickAxes(glfw.Joystick(js))

			if !w.currJoy.connected[js] {
				// The joystick was recently connected, we get the name
				w.tempJoy.name[js] = glfw.GetJoystickName(glfw.Joystick(js))
			} else {
				// Use the name from the previous one
				w.tempJoy.name[js] = w.currJoy.name[js]
			}
		} else {
			w.tempJoy.buttons[js] = []byte{}
			w.tempJoy.axis[js] = []float32{}
			w.tempJoy.name[js] = ""
		}
	}

	w.prevJoy = w.currJoy
	w.currJoy = w.tempJoy
}

type joystickState struct {
	connected [Joystick16]bool
	name      [Joystick16]string
	buttons   [Joystick16][]byte
	axis      [Joystick16][]float32
}

// Returns if a button on a joystick is down, returning false if the button or joystick is invalid.
func (js *joystickState) getButton(joystick Joystick, button int) bool {
	// Check that the joystick and button is valid, return false by default
	if js.buttons[joystick] == nil || button >= len(js.buttons[joystick]) || button < 0 {
		return false
	}
	return js.buttons[joystick][byte(button)] == 1
}

// Returns the value of a joystick axis, returning 0 if the button or joystick is invalid.
func (js *joystickState) getAxis(joystick Joystick, axis int) float64 {
	// Check that the joystick and axis is valid, return 0 by default.
	if js.axis[joystick] == nil || axis >= len(js.axis[joystick]) || axis < 0 {
		return 0
	}
	return float64(js.axis[joystick][axis])
}
<commit_msg>Correctly index joysticks in internal state<commit_after>package pixelgl

import (
	"github.com/go-gl/glfw/v3.2/glfw"
)

// Joystick is a joystick or controller.
type Joystick int

// List all of the joysticks.
const (
	Joystick1  = Joystick(glfw.Joystick1)
	Joystick2  = Joystick(glfw.Joystick2)
	Joystick3  = Joystick(glfw.Joystick3)
	Joystick4  = Joystick(glfw.Joystick4)
	Joystick5  = Joystick(glfw.Joystick5)
	Joystick6  = Joystick(glfw.Joystick6)
	Joystick7  = Joystick(glfw.Joystick7)
	Joystick8  = Joystick(glfw.Joystick8)
	Joystick9  = Joystick(glfw.Joystick9)
	Joystick10 = Joystick(glfw.Joystick10)
	Joystick11 = Joystick(glfw.Joystick11)
	Joystick12 = Joystick(glfw.Joystick12)
	Joystick13 = Joystick(glfw.Joystick13)
	Joystick14 = Joystick(glfw.Joystick14)
	Joystick15 = Joystick(glfw.Joystick15)
	Joystick16 = Joystick(glfw.Joystick16)

	JoystickLast = Joystick(glfw.JoystickLast)
)

// JoystickPresent returns if the joystick is currently connected.
//
// This API is experimental.
func (w *Window) JoystickPresent(js Joystick) bool {
	return w.currJoy.connected[js]
}

// JoystickName returns the name of the joystick. A disconnected joystick will return an
// empty string.
//
// This API is experimental.
func (w *Window) JoystickName(js Joystick) string {
	return w.currJoy.name[js]
}

// JoystickButtonCount returns the number of buttons a connected joystick has.
//
// This API is experimental.
func (w *Window) JoystickButtonCount(js Joystick) int {
	return len(w.currJoy.buttons[js])
}

// JoystickAxisCount returns the number of axes a connected joystick has.
//
// This API is experimental.
func (w *Window) JoystickAxisCount(js Joystick) int {
	return len(w.currJoy.axis[js])
}

// JoystickPressed returns whether the joystick Button is currently pressed down.
// If the button index is out of range, this will return false.
//
// This API is experimental.
func (w *Window) JoystickPressed(js Joystick, button int) bool {
	return w.currJoy.getButton(js, button)
}

// JoystickJustPressed returns whether the joystick Button has just been pressed down.
// If the button index is out of range, this will return false.
//
// This API is experimental.
func (w *Window) JoystickJustPressed(js Joystick, button int) bool {
	return w.currJoy.getButton(js, button) && !w.prevJoy.getButton(js, button)
}

// JoystickJustReleased returns whether the joystick Button has just been released up.
// If the button index is out of range, this will return false.
//
// This API is experimental.
func (w *Window) JoystickJustReleased(js Joystick, button int) bool {
	return !w.currJoy.getButton(js, button) && w.prevJoy.getButton(js, button)
}

// JoystickAxis returns the value of a joystick axis at the last call to Window.Update.
// If the axis index is out of range, this will return 0.
//
// This API is experimental.
func (w *Window) JoystickAxis(js Joystick, axis int) float64 {
	return w.currJoy.getAxis(js, axis)
}

// Used internally during Window.UpdateInput to update the state of the joysticks.
func (w *Window) updateJoystickInput() {
	for js := Joystick1; js <= Joystick16; js++ {
		// Determine and store if the joystick was connected
		joystickPresent := glfw.JoystickPresent(glfw.Joystick(js))
		w.tempJoy.connected[js] = joystickPresent

		if joystickPresent {
			w.tempJoy.buttons[js] = glfw.GetJoystickButtons(glfw.Joystick(js))
			w.tempJoy.axis[js] = glfw.GetJoystickAxes(glfw.Joystick(js))

			if !w.currJoy.connected[js] {
				// The joystick was recently connected, we get the name
				w.tempJoy.name[js] = glfw.GetJoystickName(glfw.Joystick(js))
			} else {
				// Use the name from the previous one
				w.tempJoy.name[js] = w.currJoy.name[js]
			}
		} else {
			w.tempJoy.buttons[js] = []byte{}
			w.tempJoy.axis[js] = []float32{}
			w.tempJoy.name[js] = ""
		}
	}

	w.prevJoy = w.currJoy
	w.currJoy = w.tempJoy
}

type joystickState struct {
	connected [Joystick16 + 1]bool
	name      [Joystick16 + 1]string
	buttons   [Joystick16 + 1][]byte
	axis      [Joystick16 + 1][]float32
}

// Returns if a button on a joystick is down, returning false if the button or joystick is invalid.
func (js *joystickState) getButton(joystick Joystick, button int) bool {
	// Check that the joystick and button is valid, return false by default
	if js.buttons[joystick] == nil || button >= len(js.buttons[joystick]) || button < 0 {
		return false
	}
	return js.buttons[joystick][byte(button)] == 1
}

// Returns the value of a joystick axis, returning 0 if the button or joystick is invalid.
func (js *joystickState) getAxis(joystick Joystick, axis int) float64 {
	// Check that the joystick and axis is valid, return 0 by default.
	if js.axis[joystick] == nil || axis >= len(js.axis[joystick]) || axis < 0 {
		return 0
	}
	return float64(js.axis[joystick][axis])
}
<|endoftext|>


<commit_before>package api

import (
	"fmt"

	"github.com/grafana/grafana/pkg/api/dtos"
	"github.com/grafana/grafana/pkg/bus"
	"github.com/grafana/grafana/pkg/middleware"
	"github.com/grafana/grafana/pkg/models"
	"github.com/grafana/grafana/pkg/services/alerting"
)

func ValidateOrgAlert(c *middleware.Context) {
	id := c.ParamsInt64(":alertId")
	query := models.GetAlertByIdQuery{Id: id}

	if err := bus.Dispatch(&query); err != nil {
		c.JsonApiErr(404, "Alert not found", nil)
		return
	}

	if c.OrgId != query.Result.OrgId {
		c.JsonApiErr(403, "You are not allowed to edit/view alert", nil)
		return
	}
}

func GetAlertStatesForDashboard(c *middleware.Context) Response {
	dashboardId := c.QueryInt64("dashboardId")

	if dashboardId == 0 {
		return ApiError(400, "Missing query parameter dashboardId", nil)
	}

	query := models.GetAlertStatesForDashboardQuery{
		OrgId:       c.OrgId,
		DashboardId: c.QueryInt64("dashboardId"),
	}

	if err := bus.Dispatch(&query); err != nil {
		return ApiError(500, "Failed to fetch alert states", err)
	}

	return Json(200, query.Result)
}

// GET /api/alerts
func GetAlerts(c *middleware.Context) Response {
	query := models.GetAlertsQuery{
		OrgId:       c.OrgId,
		DashboardId: c.QueryInt64("dashboardId"),
		PanelId:     c.QueryInt64("panelId"),
		Limit:       c.QueryInt64("limit"),
	}

	states := c.QueryStrings("state")
	if len(states) > 0 {
		query.State = states
	}

	if err := bus.Dispatch(&query); err != nil {
		return ApiError(500, "List alerts failed", err)
	}

	dashboardIds := make([]int64, 0)
	alertDTOs := make([]*dtos.AlertRule, 0)
	for _, alert := range query.Result {
		dashboardIds = append(dashboardIds, alert.DashboardId)
		alertDTOs = append(alertDTOs, &dtos.AlertRule{
			Id:             alert.Id,
			DashboardId:    alert.DashboardId,
			PanelId:        alert.PanelId,
			Name:           alert.Name,
			Message:        alert.Message,
			State:          alert.State,
			EvalDate:       alert.EvalDate,
			NewStateDate:   alert.NewStateDate,
			ExecutionError: alert.ExecutionError,
		})
	}

	dashboardsQuery := models.GetDashboardsQuery{
		DashboardIds: dashboardIds,
	}

	if len(alertDTOs) > 0 {
		if err := bus.Dispatch(&dashboardsQuery); err != nil {
			return ApiError(500, "List alerts failed", err)
		}
	}

	//TODO: should be possible to speed this up with lookup table
	for _, alert := range alertDTOs {
		for _, dash := range dashboardsQuery.Result {
			if alert.DashboardId == dash.Id {
				alert.DashbboardUri = "db/" + dash.Slug
			}
		}
	}

	return Json(200, alertDTOs)
}

// POST /api/alerts/test
func AlertTest(c *middleware.Context, dto dtos.AlertTestCommand) Response {
	if _, idErr := dto.Dashboard.Get("id").Int64(); idErr != nil {
		return ApiError(400, "The dashboard needs to be saved at least once before you can test an alert rule", nil)
	}

	backendCmd := alerting.AlertTestCommand{
		OrgId:     c.OrgId,
		Dashboard: dto.Dashboard,
		PanelId:   dto.PanelId,
	}

	if err := bus.Dispatch(&backendCmd); err != nil {
		if validationErr, ok := err.(alerting.ValidationError); ok {
			return ApiError(422, validationErr.Error(), nil)
		}
		return ApiError(500, "Failed to test rule", err)
	}

	res := backendCmd.Result

	dtoRes := &dtos.AlertTestResult{
		Firing:         res.Firing,
		ConditionEvals: res.ConditionEvals,
	}

	if res.Error != nil {
		dtoRes.Error = res.Error.Error()
	}

	for _, log := range res.Logs {
		dtoRes.Logs = append(dtoRes.Logs, &dtos.AlertTestResultLog{Message: log.Message, Data: log.Data})
	}
	for _, match := range res.EvalMatches {
		dtoRes.EvalMatches = append(dtoRes.EvalMatches, &dtos.EvalMatch{Metric: match.Metric, Value: match.Value})
	}

	dtoRes.TimeMs = fmt.Sprintf("%1.3fms", res.GetDurationMs())

	return Json(200, dtoRes)
}

// GET /api/alerts/:id
func GetAlert(c *middleware.Context) Response {
	id := c.ParamsInt64(":alertId")
	query := models.GetAlertByIdQuery{Id: id}

	if err := bus.Dispatch(&query); err != nil {
		return ApiError(500, "List alerts failed", err)
	}

	return Json(200, &query.Result)
}

// DEL /api/alerts/:id
func DelAlert(c *middleware.Context) Response {
	alertId := c.ParamsInt64(":alertId")

	if alertId == 0 {
		return ApiError(401, "Failed to parse alertid", nil)
	}

	cmd := models.DeleteAlertCommand{AlertId: alertId}

	if err := bus.Dispatch(&cmd); err != nil {
		return ApiError(500, "Failed to delete alert", err)
	}

	var resp = map[string]interface{}{"alertId": alertId}
	return Json(200, resp)
}

func GetAlertNotifications(c *middleware.Context) Response {
	query := &models.GetAllAlertNotificationsQuery{OrgId: c.OrgId}

	if err := bus.Dispatch(query); err != nil {
		return ApiError(500, "Failed to get alert notifications", err)
	}

	result := make([]*dtos.AlertNotification, 0)

	for _, notification := range query.Result {
		result = append(result, &dtos.AlertNotification{
			Id:        notification.Id,
			Name:      notification.Name,
			Type:      notification.Type,
			IsDefault: notification.IsDefault,
			Created:   notification.Created,
			Updated:   notification.Updated,
		})
	}

	return Json(200, result)
}

func GetAlertNotificationById(c *middleware.Context) Response {
	query := &models.GetAlertNotificationsQuery{
		OrgId: c.OrgId,
		Id:    c.ParamsInt64("notificationId"),
	}

	if err := bus.Dispatch(query); err != nil {
		return ApiError(500, "Failed to get alert notifications", err)
	}

	return Json(200, query.Result)
}

func CreateAlertNotification(c *middleware.Context, cmd models.CreateAlertNotificationCommand) Response {
	cmd.OrgId = c.OrgId

	if err := bus.Dispatch(&cmd); err != nil {
		return ApiError(500, "Failed to create alert notification", err)
	}

	return Json(200, cmd.Result)
}

func UpdateAlertNotification(c *middleware.Context, cmd models.UpdateAlertNotificationCommand) Response {
	cmd.OrgId = c.OrgId

	if err := bus.Dispatch(&cmd); err != nil {
		return ApiError(500, "Failed to update alert notification", err)
	}

	return Json(200, cmd.Result)
}

func DeleteAlertNotification(c *middleware.Context) Response {
	cmd := models.DeleteAlertNotificationCommand{
		OrgId: c.OrgId,
		Id:    c.ParamsInt64("notificationId"),
	}

	if err := bus.Dispatch(&cmd); err != nil {
		return ApiError(500, "Failed to delete alert notification", err)
	}

	return ApiSuccess("Notification deleted")
}

//POST /api/alert-notifications/test
func NotificationTest(c *middleware.Context, dto dtos.NotificationTestCommand) Response {
	cmd := &alerting.NotificationTestCommand{
		Name:     dto.Name,
		Type:     dto.Type,
		Settings: dto.Settings,
	}

	if err := bus.Dispatch(cmd); err != nil {
		return ApiError(500, "Failed to send alert notifications", err)
	}

	return ApiSuccess("Test notification sent")
}

//POST /api/alerts/:alertId/pause
func PauseAlert(c *middleware.Context, dto dtos.PauseAlertCommand) Response {
	alertId := c.ParamsInt64("alertId")
	cmd := models.PauseAlertCommand{
		OrgId:    c.OrgId,
		AlertIds: []int64{alertId},
		Paused:   dto.Paused,
	}

	if err := bus.Dispatch(&cmd); err != nil {
		return ApiError(500, "", err)
	}

	var response models.AlertStateType = models.AlertStatePending
	pausedState := "un paused"
	if cmd.Paused {
		response = models.AlertStatePaused
		pausedState = "paused"
	}

	result := map[string]interface{}{
		"alertId": alertId,
		"state":   response,
		"message": "alert " + pausedState,
	}

	return Json(200, result)
}

//POST /api/admin/pause-all-alerts
func PauseAllAlerts(c *middleware.Context, dto dtos.PauseAllAlertsCommand) Response {
	updateCmd := models.PauseAllAlertCommand{
		Paused: dto.Paused,
	}

	if err := bus.Dispatch(&updateCmd); err != nil {
		return ApiError(500, "Failed to pause alerts", err)
	}

	var response models.AlertStateType = models.AlertStatePending
	pausedState := "un paused"
	if updateCmd.Paused {
		response = models.AlertStatePaused
		pausedState = "paused"
	}

	result := map[string]interface{}{
		"state":          response,
		"message":        "alert " + pausedState,
		"alertsAffected": updateCmd.ResultCount,
	}

	return Json(200, result)
}
<commit_msg>fix(alerting): typo<commit_after>package api

import (
	"fmt"

	"github.com/grafana/grafana/pkg/api/dtos"
	"github.com/grafana/grafana/pkg/bus"
	"github.com/grafana/grafana/pkg/middleware"
	"github.com/grafana/grafana/pkg/models"
	"github.com/grafana/grafana/pkg/services/alerting"
)

func ValidateOrgAlert(c *middleware.Context) {
	id := c.ParamsInt64(":alertId")
	query := models.GetAlertByIdQuery{Id: id}

	if err := bus.Dispatch(&query); err != nil {
		c.JsonApiErr(404, "Alert not found", nil)
		return
	}

	if c.OrgId != query.Result.OrgId {
		c.JsonApiErr(403, "You are not allowed to edit/view alert", nil)
		return
	}
}

func GetAlertStatesForDashboard(c *middleware.Context) Response {
	dashboardId := c.QueryInt64("dashboardId")

	if dashboardId == 0 {
		return ApiError(400, "Missing query parameter dashboardId", nil)
	}

	query := models.GetAlertStatesForDashboardQuery{
		OrgId:       c.OrgId,
		DashboardId: c.QueryInt64("dashboardId"),
	}

	if err := bus.Dispatch(&query); err != nil {
		return ApiError(500, "Failed to fetch alert states", err)
	}

	return Json(200, query.Result)
}

// GET /api/alerts
func GetAlerts(c *middleware.Context) Response {
	query := models.GetAlertsQuery{
		OrgId:       c.OrgId,
		DashboardId: c.QueryInt64("dashboardId"),
		PanelId:     c.QueryInt64("panelId"),
		Limit:       c.QueryInt64("limit"),
	}

	states := c.QueryStrings("state")
	if len(states) > 0 {
		query.State = states
	}

	if err := bus.Dispatch(&query); err != nil {
		return ApiError(500, "List alerts failed", err)
	}

	dashboardIds := make([]int64, 0)
	alertDTOs := make([]*dtos.AlertRule, 0)
	for _, alert := range query.Result {
		dashboardIds = append(dashboardIds, alert.DashboardId)
		alertDTOs = append(alertDTOs, &dtos.AlertRule{
			Id:             alert.Id,
			DashboardId:    alert.DashboardId,
			PanelId:        alert.PanelId,
			Name:           alert.Name,
			Message:        alert.Message,
			State:          alert.State,
			EvalDate:       alert.EvalDate,
			NewStateDate:   alert.NewStateDate,
			ExecutionError: alert.ExecutionError,
		})
	}

	dashboardsQuery := models.GetDashboardsQuery{
		DashboardIds: dashboardIds,
	}

	if len(alertDTOs) > 0 {
		if err := bus.Dispatch(&dashboardsQuery); err != nil {
			return ApiError(500, "List alerts failed", err)
		}
	}

	//TODO: should be possible to speed this up with lookup table
	for _, alert := range alertDTOs {
		for _, dash := range dashboardsQuery.Result {
			if alert.DashboardId == dash.Id {
				alert.DashbboardUri = "db/" + dash.Slug
			}
		}
	}

	return Json(200, alertDTOs)
}

// POST /api/alerts/test
func AlertTest(c *middleware.Context, dto dtos.AlertTestCommand) Response {
	if _, idErr := dto.Dashboard.Get("id").Int64(); idErr != nil {
		return ApiError(400, "The dashboard needs to be saved at least once before you can test an alert rule", nil)
	}

	backendCmd := alerting.AlertTestCommand{
		OrgId:     c.OrgId,
		Dashboard: dto.Dashboard,
		PanelId:   dto.PanelId,
	}

	if err := bus.Dispatch(&backendCmd); err != nil {
		if validationErr, ok := err.(alerting.ValidationError); ok {
			return ApiError(422, validationErr.Error(), nil)
		}
		return ApiError(500, "Failed to test rule", err)
	}

	res := backendCmd.Result

	dtoRes := &dtos.AlertTestResult{
		Firing:         res.Firing,
		ConditionEvals: res.ConditionEvals,
	}

	if res.Error != nil {
		dtoRes.Error = res.Error.Error()
	}

	for _, log := range res.Logs {
		dtoRes.Logs = append(dtoRes.Logs, &dtos.AlertTestResultLog{Message: log.Message, Data: log.Data})
	}
	for _, match := range res.EvalMatches {
		dtoRes.EvalMatches = append(dtoRes.EvalMatches, &dtos.EvalMatch{Metric: match.Metric, Value: match.Value})
	}

	dtoRes.TimeMs = fmt.Sprintf("%1.3fms", res.GetDurationMs())

	return Json(200, dtoRes)
}

// GET /api/alerts/:id
func GetAlert(c *middleware.Context) Response {
	id := c.ParamsInt64(":alertId")
	query := models.GetAlertByIdQuery{Id: id}

	if err := bus.Dispatch(&query); err != nil {
		return ApiError(500, "List alerts failed", err)
	}

	return Json(200, &query.Result)
}

// DEL /api/alerts/:id
func DelAlert(c *middleware.Context) Response {
	alertId := c.ParamsInt64(":alertId")

	if alertId == 0 {
		return ApiError(401, "Failed to parse alertid", nil)
	}

	cmd := models.DeleteAlertCommand{AlertId: alertId}

	if err := bus.Dispatch(&cmd); err != nil {
		return ApiError(500, "Failed to delete alert", err)
	}

	var resp = map[string]interface{}{"alertId": alertId}
	return Json(200, resp)
}

func GetAlertNotifications(c *middleware.Context) Response {
	query := &models.GetAllAlertNotificationsQuery{OrgId: c.OrgId}

	if err := bus.Dispatch(query); err != nil {
		return ApiError(500, "Failed to get alert notifications", err)
	}

	result := make([]*dtos.AlertNotification, 0)

	for _, notification := range query.Result {
		result = append(result, &dtos.AlertNotification{
			Id:        notification.Id,
			Name:      notification.Name,
			Type:      notification.Type,
			IsDefault: notification.IsDefault,
			Created:   notification.Created,
			Updated:   notification.Updated,
		})
	}

	return Json(200, result)
}

func GetAlertNotificationById(c *middleware.Context) Response {
	query := &models.GetAlertNotificationsQuery{
		OrgId: c.OrgId,
		Id:    c.ParamsInt64("notificationId"),
	}

	if err := bus.Dispatch(query); err != nil {
		return ApiError(500, "Failed to get alert notifications", err)
	}

	return Json(200, query.Result)
}

func CreateAlertNotification(c *middleware.Context, cmd models.CreateAlertNotificationCommand) Response {
	cmd.OrgId = c.OrgId

	if err := bus.Dispatch(&cmd); err != nil {
		return ApiError(500, "Failed to create alert notification", err)
	}

	return Json(200, cmd.Result)
}

func UpdateAlertNotification(c *middleware.Context, cmd models.UpdateAlertNotificationCommand) Response {
	cmd.OrgId = c.OrgId

	if err := bus.Dispatch(&cmd); err != nil {
		return ApiError(500, "Failed to update alert notification", err)
	}

	return Json(200, cmd.Result)
}

func DeleteAlertNotification(c *middleware.Context) Response {
	cmd := models.DeleteAlertNotificationCommand{
		OrgId: c.OrgId,
		Id:    c.ParamsInt64("notificationId"),
	}

	if err := bus.Dispatch(&cmd); err != nil {
		return ApiError(500, "Failed to delete alert notification", err)
	}

	return ApiSuccess("Notification deleted")
}

//POST /api/alert-notifications/test
func NotificationTest(c *middleware.Context, dto dtos.NotificationTestCommand) Response {
	cmd := &alerting.NotificationTestCommand{
		Name:     dto.Name,
		Type:     dto.Type,
		Settings: dto.Settings,
	}

	if err := bus.Dispatch(cmd); err != nil {
		return ApiError(500, "Failed to send alert notifications", err)
	}

	return ApiSuccess("Test notification sent")
}

//POST /api/alerts/:alertId/pause
func PauseAlert(c *middleware.Context, dto dtos.PauseAlertCommand) Response {
	alertId := c.ParamsInt64("alertId")
	cmd := models.PauseAlertCommand{
		OrgId:    c.OrgId,
		AlertIds: []int64{alertId},
		Paused:   dto.Paused,
	}

	if err := bus.Dispatch(&cmd); err != nil {
		return ApiError(500, "", err)
	}

	var response models.AlertStateType = models.AlertStatePending
	pausedState := "un paused"
	if cmd.Paused {
		response = models.AlertStatePaused
		pausedState = "paused"
	}

	result := map[string]interface{}{
		"alertId": alertId,
		"state":   response,
		"message": "alert " + pausedState,
	}

	return Json(200, result)
}

//POST /api/admin/pause-all-alerts
func PauseAllAlerts(c *middleware.Context, dto dtos.PauseAllAlertsCommand) Response {
	updateCmd := models.PauseAllAlertCommand{
		Paused: dto.Paused,
	}

	if err := bus.Dispatch(&updateCmd); err != nil {
		return ApiError(500, "Failed to pause alerts", err)
	}

	var response models.AlertStateType = models.AlertStatePending
	pausedState := "un paused"
	if updateCmd.Paused {
		response = models.AlertStatePaused
		pausedState = "paused"
	}

	result := map[string]interface{}{
		"state":          response,
		"message":        "alerts " + pausedState,
		"alertsAffected": updateCmd.ResultCount,
	}

	return Json(200, result)
}
<|endoftext|>


<commit_before>package filesys

import (
	"context"
	"os"
	"path"
	"path/filepath"
	"time"

	"bazil.org/fuse"
	"bazil.org/fuse/fs"
	"github.com/chrislusf/seaweedfs/weed/filer2"
	"github.com/chrislusf/seaweedfs/weed/glog"
	"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
)

type Dir struct {
	Path       string
	wfs        *WFS
	attributes *filer_pb.FuseAttributes
}

var _ = fs.Node(&Dir{})
var _ = fs.NodeCreater(&Dir{})
var _ = fs.NodeMkdirer(&Dir{})
var _ = fs.NodeRequestLookuper(&Dir{})
var _ = fs.HandleReadDirAller(&Dir{})
var _ = fs.NodeRemover(&Dir{})
var _ = fs.NodeRenamer(&Dir{})
var _ = fs.NodeSetattrer(&Dir{})

func (dir *Dir) Attr(context context.Context, attr *fuse.Attr) error {

	// https://github.com/bazil/fuse/issues/196
	attr.Valid = time.Second

	if dir.Path == "/" {
		attr.Mode = os.ModeDir | 0777
		return nil
	}

	item := dir.wfs.listDirectoryEntriesCache.Get(dir.Path)
	if item != nil && !item.Expired() {
		entry := item.Value().(*filer_pb.Entry)

		attr.Mtime = time.Unix(entry.Attributes.Mtime, 0)
		attr.Ctime = time.Unix(entry.Attributes.Crtime, 0)
		attr.Mode = os.FileMode(entry.Attributes.FileMode)
		attr.Gid = entry.Attributes.Gid
		attr.Uid = entry.Attributes.Uid

		return nil
	}

	parent, name := filepath.Split(dir.Path)

	err := dir.wfs.withFilerClient(func(client filer_pb.SeaweedFilerClient) error {

		request := &filer_pb.LookupDirectoryEntryRequest{
			Directory: parent,
			Name:      name,
		}

		glog.V(1).Infof("read dir %s attr: %v", dir.Path, request)
		resp, err := client.LookupDirectoryEntry(context, request)
		if err != nil {
			glog.V(0).Infof("read dir %s attr %v: %v", dir.Path, request, err)
			return err
		}

		if resp.Entry != nil {
			dir.attributes = resp.Entry.Attributes
		}

		dir.wfs.listDirectoryEntriesCache.Set(dir.Path, resp.Entry, dir.wfs.option.EntryCacheTtl)

		return nil
	})

	if err != nil {
		return err
	}

	// glog.V(1).Infof("dir %s: %v", dir.Path, attributes)
	// glog.V(1).Infof("dir %s permission: %v", dir.Path, os.FileMode(attributes.FileMode))

	attr.Mode = os.FileMode(dir.attributes.FileMode) | os.ModeDir

	attr.Mtime = time.Unix(dir.attributes.Mtime, 0)
	attr.Ctime = time.Unix(dir.attributes.Crtime, 0)
	attr.Gid = dir.attributes.Gid
	attr.Uid = dir.attributes.Uid

	return nil
}

func (dir *Dir) newFile(name string, entry *filer_pb.Entry) *File {
	return &File{
		Name:  name,
		dir:   dir,
		wfs:   dir.wfs,
		entry: entry,
	}
}

func (dir *Dir) Create(ctx context.Context, req *fuse.CreateRequest,
	resp *fuse.CreateResponse) (fs.Node, fs.Handle, error) {

	request := &filer_pb.CreateEntryRequest{
		Directory: dir.Path,
		Entry: &filer_pb.Entry{
			Name:        req.Name,
			IsDirectory: req.Mode&os.ModeDir > 0,
			Attributes: &filer_pb.FuseAttributes{
				Mtime:       time.Now().Unix(),
				Crtime:      time.Now().Unix(),
				FileMode:    uint32(req.Mode),
				Uid:         req.Uid,
				Gid:         req.Gid,
				Collection:  dir.wfs.option.Collection,
				Replication: dir.wfs.option.Replication,
				TtlSec:      dir.wfs.option.TtlSec,
			},
		},
	}
	glog.V(1).Infof("create: %v", request)

	if request.Entry.IsDirectory {
		if err := dir.wfs.withFilerClient(func(client filer_pb.SeaweedFilerClient) error {
			if _, err := client.CreateEntry(ctx, request); err != nil {
				glog.V(0).Infof("create %s/%s: %v", dir.Path, req.Name, err)
				return fuse.EIO
			}
			return nil
		}); err != nil {
			return nil, nil, err
		}
	}

	file := dir.newFile(req.Name, request.Entry)
	if !request.Entry.IsDirectory {
		file.isOpen = true
	}
	fh := dir.wfs.AcquireHandle(file, req.Uid, req.Gid)
	fh.dirtyMetadata = true
	return file, fh, nil

}

func (dir *Dir) Mkdir(ctx context.Context, req *fuse.MkdirRequest) (fs.Node, error) {

	err := dir.wfs.withFilerClient(func(client filer_pb.SeaweedFilerClient) error {

		request := &filer_pb.CreateEntryRequest{
			Directory: dir.Path,
			Entry: &filer_pb.Entry{
				Name:        req.Name,
				IsDirectory: true,
				Attributes: &filer_pb.FuseAttributes{
					Mtime:    time.Now().Unix(),
					Crtime:   time.Now().Unix(),
					FileMode: uint32(req.Mode),
					Uid:      req.Uid,
					Gid:      req.Gid,
				},
			},
		}

		glog.V(1).Infof("mkdir: %v", request)
		if _, err := client.CreateEntry(ctx, request); err != nil {
			glog.V(0).Infof("mkdir %s/%s: %v", dir.Path, req.Name, err)
			return fuse.EIO
		}

		return nil
	})

	if err == nil {
		node := &Dir{Path: path.Join(dir.Path, req.Name), wfs: dir.wfs}
		return node, nil
	}

	return nil, err
}

func (dir *Dir) Lookup(ctx context.Context, req *fuse.LookupRequest, resp *fuse.LookupResponse) (node fs.Node, err error) {

	var entry *filer_pb.Entry

	item := dir.wfs.listDirectoryEntriesCache.Get(path.Join(dir.Path, req.Name))
	if item != nil && !item.Expired() {
		entry = item.Value().(*filer_pb.Entry)
	}

	if entry == nil {
		err = dir.wfs.withFilerClient(func(client filer_pb.SeaweedFilerClient) error {

			request := &filer_pb.LookupDirectoryEntryRequest{
				Directory: dir.Path,
				Name:      req.Name,
			}

			glog.V(4).Infof("lookup directory entry: %v", request)
			resp, err := client.LookupDirectoryEntry(ctx, request)
			if err != nil {
				// glog.V(0).Infof("lookup %s/%s: %v", dir.Path, name, err)
				return fuse.ENOENT
			}

			entry = resp.Entry

			dir.wfs.listDirectoryEntriesCache.Set(path.Join(dir.Path, entry.Name), entry, dir.wfs.option.EntryCacheTtl)

			return nil
		})
	}

	if entry != nil {
		if entry.IsDirectory {
			node = &Dir{Path: path.Join(dir.Path, req.Name), wfs: dir.wfs, attributes: entry.Attributes}
		} else {
			node = dir.newFile(req.Name, entry)
		}

		resp.EntryValid = time.Duration(0)
		resp.Attr.Mtime = time.Unix(entry.Attributes.Mtime, 0)
		resp.Attr.Ctime = time.Unix(entry.Attributes.Crtime, 0)
		resp.Attr.Mode = os.FileMode(entry.Attributes.FileMode)
		resp.Attr.Gid = entry.Attributes.Gid
		resp.Attr.Uid = entry.Attributes.Uid

		return node, nil
	}

	return nil, fuse.ENOENT
}

func (dir *Dir) ReadDirAll(ctx context.Context) (ret []fuse.Dirent, err error) {

	err = dir.wfs.withFilerClient(func(client filer_pb.SeaweedFilerClient) error {

		request := &filer_pb.ListEntriesRequest{
			Directory: dir.Path,
			Limit:     uint32(dir.wfs.option.DirListingLimit),
		}

		glog.V(4).Infof("read directory: %v", request)
		resp, err := client.ListEntries(ctx, request)
		if err != nil {
			glog.V(0).Infof("list %s: %v", dir.Path, err)
			return fuse.EIO
		}

		cacheTtl := estimatedCacheTtl(len(resp.Entries))

		for _, entry := range resp.Entries {
			if entry.IsDirectory {
				dirent := fuse.Dirent{Name: entry.Name, Type: fuse.DT_Dir}
				ret = append(ret, dirent)
			} else {
				dirent := fuse.Dirent{Name: entry.Name, Type: fuse.DT_File}
				ret = append(ret, dirent)
			}
			dir.wfs.listDirectoryEntriesCache.Set(path.Join(dir.Path, entry.Name), entry, cacheTtl)
		}

		return nil
	})

	return ret, err
}

func (dir *Dir) Remove(ctx context.Context, req *fuse.RemoveRequest) error {

	return dir.wfs.withFilerClient(func(client filer_pb.SeaweedFilerClient) error {

		request := &filer_pb.DeleteEntryRequest{
			Directory:    dir.Path,
			Name:         req.Name,
			IsDeleteData: true,
		}

		glog.V(1).Infof("remove directory entry: %v", request)
		_, err := client.DeleteEntry(ctx, request)
		if err != nil {
			glog.V(0).Infof("remove %s/%s: %v", dir.Path, req.Name, err)
			return fuse.EIO
		}

		dir.wfs.listDirectoryEntriesCache.Delete(path.Join(dir.Path, req.Name))

		return nil
	})

}

func (dir *Dir) Setattr(ctx context.Context, req *fuse.SetattrRequest, resp *fuse.SetattrResponse) error {

	glog.V(3).Infof("%v dir setattr %+v, fh=%d", dir.Path, req, req.Handle)
	if req.Valid.Mode() {
		dir.attributes.FileMode = uint32(req.Mode)
	}

	if req.Valid.Uid() {
		dir.attributes.Uid = req.Uid
	}

	if req.Valid.Gid() {
		dir.attributes.Gid = req.Gid
	}

	if req.Valid.Mtime() {
		dir.attributes.Mtime = req.Mtime.Unix()
	}

	parentDir, name := filer2.FullPath(dir.Path).DirAndName()
	return dir.wfs.withFilerClient(func(client filer_pb.SeaweedFilerClient) error {

		request := &filer_pb.UpdateEntryRequest{
			Directory: parentDir,
			Entry: &filer_pb.Entry{
				Name:       name,
				Attributes: dir.attributes,
			},
		}

		glog.V(1).Infof("set attr directory entry: %v", request)
		_, err := client.UpdateEntry(ctx, request)
		if err != nil {
			glog.V(0).Infof("UpdateEntry %s: %v", dir.Path, err)
			return fuse.EIO
		}

		dir.wfs.listDirectoryEntriesCache.Delete(dir.Path)

		return nil
	})

}

func estimatedCacheTtl(numEntries int) time.Duration {
	if numEntries < 100 {
		// 30 ms per entry
		return 3 * time.Second
	}
	if numEntries < 1000 {
		// 10 ms per entry
		return 10 * time.Second
	}
	if numEntries < 10000 {
		// 10 ms per entry
		return 100 * time.Second
	}
	if numEntries < 100000 {
		// 10 ms per entry
		return 1000 * time.Second
	}
	// 2 ms per entry
	return time.Duration(numEntries*2) * time.Millisecond
}
<commit_msg>weed mount ls paginate the results<commit_after>package filesys

import (
	"context"
	"os"
	"path"
	"path/filepath"
	"time"

	"bazil.org/fuse"
	"bazil.org/fuse/fs"
	"github.com/chrislusf/seaweedfs/weed/filer2"
	"github.com/chrislusf/seaweedfs/weed/glog"
	"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
)

type Dir struct {
	Path       string
	wfs        *WFS
	attributes *filer_pb.FuseAttributes
}

var _ = fs.Node(&Dir{})
var _ = fs.NodeCreater(&Dir{})
var _ = fs.NodeMkdirer(&Dir{})
var _ = fs.NodeRequestLookuper(&Dir{})
var _ = fs.HandleReadDirAller(&Dir{})
var _ = fs.NodeRemover(&Dir{})
var _ = fs.NodeRenamer(&Dir{})
var _ = fs.NodeSetattrer(&Dir{})

func (dir *Dir) Attr(context context.Context, attr *fuse.Attr) error {

	// https://github.com/bazil/fuse/issues/196
	attr.Valid = time.Second

	if dir.Path == "/" {
		attr.Mode = os.ModeDir | 0777
		return nil
	}

	item := dir.wfs.listDirectoryEntriesCache.Get(dir.Path)
	if item != nil && !item.Expired() {
		entry := item.Value().(*filer_pb.Entry)

		attr.Mtime = time.Unix(entry.Attributes.Mtime, 0)
		attr.Ctime = time.Unix(entry.Attributes.Crtime, 0)
		attr.Mode = os.FileMode(entry.Attributes.FileMode)
		attr.Gid = entry.Attributes.Gid
		attr.Uid = entry.Attributes.Uid

		return nil
	}

	parent, name := filepath.Split(dir.Path)

	err := dir.wfs.withFilerClient(func(client filer_pb.SeaweedFilerClient) error {

		request := &filer_pb.LookupDirectoryEntryRequest{
			Directory: parent,
			Name:      name,
		}

		glog.V(1).Infof("read dir %s attr: %v", dir.Path, request)
		resp, err := client.LookupDirectoryEntry(context, request)
		if err != nil {
			glog.V(0).Infof("read dir %s attr %v: %v", dir.Path, request, err)
			return err
		}

		if resp.Entry != nil {
			dir.attributes = resp.Entry.Attributes
		}

		dir.wfs.listDirectoryEntriesCache.Set(dir.Path, resp.Entry, dir.wfs.option.EntryCacheTtl)

		return nil
	})

	if err != nil {
		return err
	}

	// glog.V(1).Infof("dir %s: %v", dir.Path, attributes)
	// glog.V(1).Infof("dir %s permission: %v", dir.Path, os.FileMode(attributes.FileMode))

	attr.Mode = os.FileMode(dir.attributes.FileMode) | os.ModeDir

	attr.Mtime = time.Unix(dir.attributes.Mtime, 0)
	attr.Ctime = time.Unix(dir.attributes.Crtime, 0)
	attr.Gid = dir.attributes.Gid
	attr.Uid = dir.attributes.Uid

	return nil
}

func (dir *Dir) newFile(name string, entry *filer_pb.Entry) *File {
	return &File{
		Name:  name,
		dir:   dir,
		wfs:   dir.wfs,
		entry: entry,
	}
}

func (dir *Dir) Create(ctx context.Context, req *fuse.CreateRequest,
	resp *fuse.CreateResponse) (fs.Node, fs.Handle, error) {

	request := &filer_pb.CreateEntryRequest{
		Directory: dir.Path,
		Entry: &filer_pb.Entry{
			Name:        req.Name,
			IsDirectory: req.Mode&os.ModeDir > 0,
			Attributes: &filer_pb.FuseAttributes{
				Mtime:       time.Now().Unix(),
				Crtime:      time.Now().Unix(),
				FileMode:    uint32(req.Mode),
				Uid:         req.Uid,
				Gid:         req.Gid,
				Collection:  dir.wfs.option.Collection,
				Replication: dir.wfs.option.Replication,
				TtlSec:      dir.wfs.option.TtlSec,
			},
		},
	}
	glog.V(1).Infof("create: %v", request)

	if request.Entry.IsDirectory {
		if err := dir.wfs.withFilerClient(func(client filer_pb.SeaweedFilerClient) error {
			if _, err := client.CreateEntry(ctx, request); err != nil {
				glog.V(0).Infof("create %s/%s: %v", dir.Path, req.Name, err)
				return fuse.EIO
			}
			return nil
		}); err != nil {
			return nil, nil, err
		}
	}

	file := dir.newFile(req.Name, request.Entry)
	if !request.Entry.IsDirectory {
		file.isOpen = true
	}
	fh := dir.wfs.AcquireHandle(file, req.Uid, req.Gid)
	fh.dirtyMetadata = true
	return file, fh, nil

}

func (dir *Dir) Mkdir(ctx context.Context, req *fuse.MkdirRequest) (fs.Node, error) {

	err := dir.wfs.withFilerClient(func(client filer_pb.SeaweedFilerClient) error {

		request := &filer_pb.CreateEntryRequest{
			Directory: dir.Path,
			Entry: &filer_pb.Entry{
				Name:        req.Name,
				IsDirectory: true,
				Attributes: &filer_pb.FuseAttributes{
					Mtime:    time.Now().Unix(),
					Crtime:   time.Now().Unix(),
					FileMode: uint32(req.Mode),
					Uid:      req.Uid,
					Gid:      req.Gid,
				},
			},
		}

		glog.V(1).Infof("mkdir: %v", request)
		if _, err := client.CreateEntry(ctx, request); err != nil {
			glog.V(0).Infof("mkdir %s/%s: %v", dir.Path, req.Name, err)
			return fuse.EIO
		}

		return nil
	})

	if err == nil {
		node := &Dir{Path: path.Join(dir.Path, req.Name), wfs: dir.wfs}
		return node, nil
	}

	return nil, err
}

func (dir *Dir) Lookup(ctx context.Context, req *fuse.LookupRequest, resp *fuse.LookupResponse) (node fs.Node, err error) {

	var entry *filer_pb.Entry

	item := dir.wfs.listDirectoryEntriesCache.Get(path.Join(dir.Path, req.Name))
	if item != nil && !item.Expired() {
		entry = item.Value().(*filer_pb.Entry)
	}

	if entry == nil {
		err = dir.wfs.withFilerClient(func(client filer_pb.SeaweedFilerClient) error {

			request := &filer_pb.LookupDirectoryEntryRequest{
				Directory: dir.Path,
				Name:      req.Name,
			}

			glog.V(4).Infof("lookup directory entry: %v", request)
			resp, err := client.LookupDirectoryEntry(ctx, request)
			if err != nil {
				// glog.V(0).Infof("lookup %s/%s: %v", dir.Path, name, err)
				return fuse.ENOENT
			}

			entry = resp.Entry

			dir.wfs.listDirectoryEntriesCache.Set(path.Join(dir.Path, entry.Name), entry, dir.wfs.option.EntryCacheTtl)

			return nil
		})
	}

	if entry != nil {
		if entry.IsDirectory {
			node = &Dir{Path: path.Join(dir.Path, req.Name), wfs: dir.wfs, attributes: entry.Attributes}
		} else {
			node = dir.newFile(req.Name, entry)
		}

		resp.EntryValid = time.Duration(0)
		resp.Attr.Mtime = time.Unix(entry.Attributes.Mtime, 0)
		resp.Attr.Ctime = time.Unix(entry.Attributes.Crtime, 0)
		resp.Attr.Mode = os.FileMode(entry.Attributes.FileMode)
		resp.Attr.Gid = entry.Attributes.Gid
		resp.Attr.Uid = entry.Attributes.Uid

		return node, nil
	}

	return nil, fuse.ENOENT
}

func (dir *Dir) ReadDirAll(ctx context.Context) (ret []fuse.Dirent, err error) {

	err = dir.wfs.withFilerClient(func(client filer_pb.SeaweedFilerClient) error {

		paginationLimit := 1024
		remaining := dir.wfs.option.DirListingLimit

		lastEntryName := ""

		for remaining >= 0 {

			request := &filer_pb.ListEntriesRequest{
				Directory:         dir.Path,
				StartFromFileName: lastEntryName,
				Limit:             uint32(paginationLimit),
			}

			glog.V(4).Infof("read directory: %v", request)
			resp, err := client.ListEntries(ctx, request)
			if err != nil {
				glog.V(0).Infof("list %s: %v", dir.Path, err)
				return fuse.EIO
			}

			cacheTtl := estimatedCacheTtl(len(resp.Entries))

			for _, entry := range resp.Entries {
				if entry.IsDirectory {
					dirent := fuse.Dirent{Name: entry.Name, Type: fuse.DT_Dir}
					ret = append(ret, dirent)
				} else {
					dirent := fuse.Dirent{Name: entry.Name, Type: fuse.DT_File}
					ret = append(ret, dirent)
				}
				dir.wfs.listDirectoryEntriesCache.Set(path.Join(dir.Path, entry.Name), entry, cacheTtl)
				lastEntryName = entry.Name
			}

			remaining -= len(resp.Entries)

			if len(resp.Entries) < paginationLimit {
				break
			}

		}

		return nil
	})

	return ret, err
}

func (dir *Dir) Remove(ctx context.Context, req *fuse.RemoveRequest) error {

	return dir.wfs.withFilerClient(func(client filer_pb.SeaweedFilerClient) error {

		request := &filer_pb.DeleteEntryRequest{
			Directory:    dir.Path,
			Name:         req.Name,
			IsDeleteData: true,
		}

		glog.V(1).Infof("remove directory entry: %v", request)
		_, err := client.DeleteEntry(ctx, request)
		if err != nil {
			glog.V(0).Infof("remove %s/%s: %v", dir.Path, req.Name, err)
			return fuse.EIO
		}

		dir.wfs.listDirectoryEntriesCache.Delete(path.Join(dir.Path, req.Name))

		return nil
	})

}

func (dir *Dir) Setattr(ctx context.Context, req *fuse.SetattrRequest, resp *fuse.SetattrResponse) error {

	glog.V(3).Infof("%v dir setattr %+v, fh=%d", dir.Path, req, req.Handle)
	if req.Valid.Mode() {
		dir.attributes.FileMode = uint32(req.Mode)
	}

	if req.Valid.Uid() {
		dir.attributes.Uid = req.Uid
	}

	if req.Valid.Gid() {
		dir.attributes.Gid = req.Gid
	}

	if req.Valid.Mtime() {
		dir.attributes.Mtime = req.Mtime.Unix()
	}

	parentDir, name := filer2.FullPath(dir.Path).DirAndName()
	return dir.wfs.withFilerClient(func(client filer_pb.SeaweedFilerClient) error {

		request := &filer_pb.UpdateEntryRequest{
			Directory: parentDir,
			Entry: &filer_pb.Entry{
				Name:       name,
				Attributes: dir.attributes,
			},
		}

		glog.V(1).Infof("set attr directory entry: %v", request)
		_, err := client.UpdateEntry(ctx, request)
		if err != nil {
			glog.V(0).Infof("UpdateEntry %s: %v", dir.Path, err)
			return fuse.EIO
		}

		dir.wfs.listDirectoryEntriesCache.Delete(dir.Path)

		return nil
	})

}

func estimatedCacheTtl(numEntries int) time.Duration {
	if numEntries < 100 {
		// 30 ms per entry
		return 3 * time.Second
	}
	if numEntries < 1000 {
		// 10 ms per entry
		return 10 * time.Second
	}
	if numEntries < 10000 {
		// 10 ms per entry
		return 100 * time.Second
	}

	// 2 ms per entry
	return time.Duration(numEntries*2) * time.Millisecond
}
<|endoftext|>


<commit_before>// Copyright 2018 Prometheus Team
// 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 labels

import (
	"regexp"
	"strings"

	"github.com/pkg/errors"
)

var (
	re      = regexp.MustCompile(`(?:\s?)(\w+)(=|=~|!=|!~)(?:\"([^"=~!]+)\"|([^"=~!]+)|\"\")`)
	typeMap = map[string]MatchType{
		"=":  MatchEqual,
		"!=": MatchNotEqual,
		"=~": MatchRegexp,
		"!~": MatchNotRegexp,
	}
)

func ParseMatchers(s string) ([]*Matcher, error) {
	matchers := []*Matcher{}
	s = strings.TrimPrefix(s, "{")
	s = strings.TrimSuffix(s, "}")

	var insideQuotes bool
	var token string
	var tokens []string
	for _, r := range s {
		if !insideQuotes && r == ',' {
			tokens = append(tokens, token)
			token = ""
			continue
		}
		token += string(r)
		if r == '"' {
			insideQuotes = !insideQuotes
		}
	}
	if token != "" {
		tokens = append(tokens, token)
	}
	for _, token := range tokens {
		m, err := ParseMatcher(token)
		if err != nil {
			return nil, err
		}
		matchers = append(matchers, m)
	}

	return matchers, nil
}

func ParseMatcher(s string) (*Matcher, error) {
	var (
		name, value string
		matchType   MatchType
	)

	ms := re.FindStringSubmatch(s)
	if len(ms) < 4 {
		return nil, errors.Errorf("bad matcher format: %s", s)
	}

	name = ms[1]
	if name == "" {
		return nil, errors.New("failed to parse label name")
	}

	matchType, found := typeMap[ms[2]]
	if !found {
		return nil, errors.New("failed to find match operator")
	}

	if ms[3] != "" {
		value = ms[3]
	} else {
		value = ms[4]
	}

	return NewMatcher(matchType, name, value)
}
<commit_msg>Add doc comments to labels.ParseMatcher and labels.ParseMatchers<commit_after>// Copyright 2018 Prometheus Team
// 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 labels

import (
	"regexp"
	"strings"

	"github.com/pkg/errors"
)

var (
	re      = regexp.MustCompile(`(?:\s?)(\w+)(=|=~|!=|!~)(?:\"([^"=~!]+)\"|([^"=~!]+)|\"\")`)
	typeMap = map[string]MatchType{
		"=":  MatchEqual,
		"!=": MatchNotEqual,
		"=~": MatchRegexp,
		"!~": MatchNotRegexp,
	}
)

// ParseMatchers parses a comma-separated list of Matchers. A leading '{' and/or
// a trailing '}' is optional and will be trimmed before further
// parsing. Individual Matchers are separated by commas outside of quoted parts
// of the input string. Those commas may be followed by whitespace. Parts of the
// string inside unescaped double quotes ('"…"') are considered quoted (and
// commas don't act as separators there). If double quotes are escaped with a
// single backslash ('\"'), they are ignored for the purpose of identifying
// quoted parts of the input string. If the input string, after trimming the
// optional trailing '}', ends with a comma, followed by optional whitespace,
// this comma and whitespace will be trimmed.
//
// Examples for valid input strings:
//   {foo = "bar", dings != "bums", }
//   foo=bar,dings!=bums
//   {quote="She said: \"Hi, ladies! That's gender-neutral…\""}
//   statuscode=~"5.."
//
// See ParseMatcher for details how an individual Matcher is parsed.
func ParseMatchers(s string) ([]*Matcher, error) {
	matchers := []*Matcher{}
	s = strings.TrimPrefix(s, "{")
	s = strings.TrimSuffix(s, "}")

	var insideQuotes bool
	var token string
	var tokens []string
	for _, r := range s {
		if !insideQuotes && r == ',' {
			tokens = append(tokens, token)
			token = ""
			continue
		}
		token += string(r)
		if r == '"' {
			insideQuotes = !insideQuotes
		}
	}
	if token != "" {
		tokens = append(tokens, token)
	}
	for _, token := range tokens {
		m, err := ParseMatcher(token)
		if err != nil {
			return nil, err
		}
		matchers = append(matchers, m)
	}

	return matchers, nil
}

// ParseMatcher parses a matcher with a syntax inspired by PromQL and
// OpenMetrics. This syntax is convenient to describe filters and selectors in
// UIs and config files. To support the interactive nature of the use cases, the
// parser is in various aspects fairly tolerant.
//
// The syntax of a matcher consists of three tokens: (1) A valid Prometheus
// label name. (2) One of '=', '!=', '=~', or '!~', with the same meaning as
// known from PromQL selectors. (3) A UTF-8 string, which may be enclosed in
// double quotes. Before or after each token, there may be any amount of
// whitespace, which will be discarded. The 3rd token may be the empty
// string. Within the 3rd token, OpenMetrics escaping rules apply: '\"' for a
// double-quote, '\n' for a line feed, '\\' for a literal backslash. Unescaped
// '"' must not occur inside the 3rd token (only as the 1st or last
// character). However, literal line feed characters are tolerated, as are
// single '\' characters not followed by '\', 'n', or '"'. They act as a literal
// backslash in that case.
func ParseMatcher(s string) (*Matcher, error) {
	var (
		name, value string
		matchType   MatchType
	)

	ms := re.FindStringSubmatch(s)
	if len(ms) < 4 {
		return nil, errors.Errorf("bad matcher format: %s", s)
	}

	name = ms[1]
	if name == "" {
		return nil, errors.New("failed to parse label name")
	}

	matchType, found := typeMap[ms[2]]
	if !found {
		return nil, errors.New("failed to find match operator")
	}

	if ms[3] != "" {
		value = ms[3]
	} else {
		value = ms[4]
	}

	return NewMatcher(matchType, name, value)
}
<|endoftext|>


<commit_before>// errchk $G $D/$F.go

// Copyright 2010 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.

// Test cases for revised conversion rules.

package main

func main() {
	type NewInt int
	i0 := 0
	var i1 int = 1
	var i2 NewInt = 1
	i0 = i0
	i0 = i1
	i0 = int(i2)
	i1 = i0
	i1 = i1
	i1 = int(i2)
	i2 = NewInt(i0)
	i2 = NewInt(i1)
	i2 = i2

	type A1 [3]int
	type A2 [3]NewInt
	var a0 [3]int
	var a1 A1
	var a2 A2
	a0 = a0
	a0 = a1
	a0 = [3]int(a2) // ERROR "cannot"
	a1 = a0
	a1 = a1
	a1 = A1(a2) // ERROR "cannot"
	a2 = A2(a0) // ERROR "cannot"
	a2 = A2(a1) // ERROR "cannot"
	a2 = a2

	type S1 struct {
		x int
	}
	type S2 struct {
		x NewInt
	}
	var s0 struct {
		x int
	}
	var s1 S1
	var s2 S2
	s0 = s0
	s0 = s1
	s0 = struct {
		x int
	}(s2) // ERROR "cannot"
	s1 = s0
	s1 = s1
	s1 = S1(s2) // ERROR "cannot"
	s2 = S2(s0) // ERROR "cannot"
	s2 = S2(s1) // ERROR "cannot"
	s2 = s2

	type P1 *int
	type P2 *NewInt
	var p0 *int
	var p1 P1
	var p2 P2
	p0 = p0
	p0 = p1
	p0 = (*int)(p2) // ERROR "cannot"
	p1 = p0
	p1 = p1
	p1 = P1(p2) // ERROR "cannot"
	p2 = P2(p0) // ERROR "cannot"
	p2 = P2(p1) // ERROR "cannot"
	p2 = p2

	type Q1 *struct {
		x int
	}
	type Q2 *S1
	var q0 *struct {
		x int
	}
	var q1 Q1
	var q2 Q2
	q0 = q0
	q0 = q1
	q0 = (*struct {
		x int
	})(q2) // legal because of special conversion exception for pointers
	q1 = q0
	q1 = q1
	q1 = Q1(q2)    // ERROR "cannot"
	q2 = (*S1)(q0) // legal because of special conversion exception for pointers
	q2 = Q2(q1)    // ERROR "cannot"
	q2 = q2

	type F1 func(x NewInt) int
	type F2 func(x int) NewInt
	var f0 func(x NewInt) int
	var f1 F1
	var f2 F2
	f0 = f0
	f0 = f1
	f0 = func(x NewInt) int(f2) // ERROR "cannot"
	f1 = f0
	f1 = f1
	f1 = F1(f2) // ERROR "cannot"
	f2 = F2(f0) // ERROR "cannot"
	f2 = F2(f1) // ERROR "cannot"
	f2 = f2

	type X1 interface {
		f() int
	}
	type X2 interface {
		f() NewInt
	}
	var x0 interface {
		f() int
	}
	var x1 X1
	var x2 X2
	x0 = x0
	x0 = x1
	x0 = interface {
		f() int
	}(x2) // ERROR "cannot"|"need type assertion"
	x1 = x0
	x1 = x1
	x1 = X1(x2) // ERROR "cannot"|"need type assertion"
	x2 = X2(x0) // ERROR "cannot"|"need type assertion"
	x2 = X2(x1) // ERROR "cannot"|"need type assertion"
	x2 = x2

	type L1 []int
	type L2 []NewInt
	var l0 []int
	var l1 L1
	var l2 L2
	l0 = l0
	l0 = l1
	l0 = []int(l2) // ERROR "cannot"
	l1 = l0
	l1 = l1
	l1 = L1(l2) // ERROR "cannot"
	l2 = L2(l0) // ERROR "cannot"
	l2 = L2(l1) // ERROR "cannot"
	l2 = l2

	type M1 map[string]int
	type M2 map[string]NewInt
	var m0 []int
	var m1 L1
	var m2 L2
	m0 = m0
	m0 = m1
	m0 = []int(m2) // ERROR "cannot"
	m1 = m0
	m1 = m1
	m1 = L1(m2) // ERROR "cannot"
	m2 = L2(m0) // ERROR "cannot"
	m2 = L2(m1) // ERROR "cannot"
	m2 = m2

	type C1 chan int
	type C2 chan NewInt
	var c0 chan int
	var c1 C1
	var c2 C2
	c0 = c0
	c0 = c1
	c0 = chan int(c2) // ERROR "cannot"
	c1 = c0
	c1 = c1
	c1 = C1(c2) // ERROR "cannot"
	c2 = C2(c0) // ERROR "cannot"
	c2 = C2(c1) // ERROR "cannot"
	c2 = c2

	// internal compiler error (6g and gccgo)
	type T interface{}
	var _ T = 17 // assignment compatible
	_ = T(17)    // internal compiler error even though assignment compatible
}
<commit_msg>bug284: make conversion legal, fix error patterns<commit_after>// errchk $G -e $D/$F.go

// Copyright 2010 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.

// Test cases for revised conversion rules.

package main

func main() {
	type NewInt int
	i0 := 0
	var i1 int = 1
	var i2 NewInt = 1
	i0 = i0
	i0 = i1
	i0 = int(i2)
	i1 = i0
	i1 = i1
	i1 = int(i2)
	i2 = NewInt(i0)
	i2 = NewInt(i1)
	i2 = i2

	type A1 [3]int
	type A2 [3]NewInt
	var a0 [3]int
	var a1 A1
	var a2 A2
	a0 = a0
	a0 = a1
	a0 = [3]int(a2) // ERROR "cannot"
	a1 = a0
	a1 = a1
	a1 = A1(a2) // ERROR "cannot"
	a2 = A2(a0) // ERROR "cannot"
	a2 = A2(a1) // ERROR "cannot"
	a2 = a2

	type S1 struct {
		x int
	}
	type S2 struct {
		x NewInt
	}
	var s0 struct {
		x int
	}
	var s1 S1
	var s2 S2
	s0 = s0
	s0 = s1
	s0 = struct {
		x int
	}(s2) // ERROR "cannot"
	s1 = s0
	s1 = s1
	s1 = S1(s2) // ERROR "cannot"
	s2 = S2(s0) // ERROR "cannot"
	s2 = S2(s1) // ERROR "cannot"
	s2 = s2

	type P1 *int
	type P2 *NewInt
	var p0 *int
	var p1 P1
	var p2 P2
	p0 = p0
	p0 = p1
	p0 = (*int)(p2) // ERROR "cannot"
	p1 = p0
	p1 = p1
	p1 = P1(p2) // ERROR "cannot"
	p2 = P2(p0) // ERROR "cannot"
	p2 = P2(p1) // ERROR "cannot"
	p2 = p2

	type Q1 *struct {
		x int
	}
	type Q2 *S1
	var q0 *struct {
		x int
	}
	var q1 Q1
	var q2 Q2
	var ps1 *S1
	q0 = q0
	q0 = q1
	q0 = (*struct {
		x int
	})(ps1) // legal because of special conversion exception for pointers
	q0 = (*struct {
		x int
	})(q2) // ERROR "cannot"
	q1 = q0
	q1 = q1
	q1 = Q1(q2)    // ERROR "cannot"
	q2 = (*S1)(q0) // legal because of special conversion exception for pointers
	q2 = Q2(q1)    // ERROR "cannot"
	q2 = q2

	type F1 func(x NewInt) int
	type F2 func(x int) NewInt
	var f0 func(x NewInt) int
	var f1 F1
	var f2 F2
	f0 = f0
	f0 = f1
	f0 = func(x NewInt) int(f2) // ERROR "cannot"
	f1 = f0
	f1 = f1
	f1 = F1(f2) // ERROR "cannot"
	f2 = F2(f0) // ERROR "cannot"
	f2 = F2(f1) // ERROR "cannot"
	f2 = f2

	type X1 interface {
		f() int
	}
	type X2 interface {
		f() NewInt
	}
	var x0 interface {
		f() int
	}
	var x1 X1
	var x2 X2
	x0 = x0
	x0 = x1
	x0 = interface {
		f() int
	}(x2) // ERROR "cannot|need type assertion"
	x1 = x0
	x1 = x1
	x1 = X1(x2) // ERROR "cannot|need type assertion"
	x2 = X2(x0) // ERROR "cannot|need type assertion"
	x2 = X2(x1) // ERROR "cannot|need type assertion"
	x2 = x2

	type L1 []int
	type L2 []NewInt
	var l0 []int
	var l1 L1
	var l2 L2
	l0 = l0
	l0 = l1
	l0 = []int(l2) // ERROR "cannot"
	l1 = l0
	l1 = l1
	l1 = L1(l2) // ERROR "cannot"
	l2 = L2(l0) // ERROR "cannot"
	l2 = L2(l1) // ERROR "cannot"
	l2 = l2

	type M1 map[string]int
	type M2 map[string]NewInt
	var m0 []int
	var m1 L1
	var m2 L2
	m0 = m0
	m0 = m1
	m0 = []int(m2) // ERROR "cannot"
	m1 = m0
	m1 = m1
	m1 = L1(m2) // ERROR "cannot"
	m2 = L2(m0) // ERROR "cannot"
	m2 = L2(m1) // ERROR "cannot"
	m2 = m2

	type C1 chan int
	type C2 chan NewInt
	var c0 chan int
	var c1 C1
	var c2 C2
	c0 = c0
	c0 = c1
	c0 = chan int(c2) // ERROR "cannot"
	c1 = c0
	c1 = c1
	c1 = C1(c2) // ERROR "cannot"
	c2 = C2(c0) // ERROR "cannot"
	c2 = C2(c1) // ERROR "cannot"
	c2 = c2

	// internal compiler error (6g and gccgo)
	type T interface{}
	var _ T = 17 // assignment compatible
	_ = T(17)    // internal compiler error even though assignment compatible
}
<|endoftext|>