<commit_before>package lib

import (
	"github.com/Symantec/Dominator/lib/filesystem"
	"github.com/Symantec/Dominator/lib/hash"
	"github.com/Symantec/Dominator/lib/image"
	"github.com/Symantec/Dominator/lib/log"
	"github.com/Symantec/Dominator/lib/log/debuglogger"
	subproto "github.com/Symantec/Dominator/proto/sub"
	"path"
	"sort"
	"time"
)

// Returns true if there is a failure due to missing computed files.
func (sub *Sub) buildUpdateRequest(image *image.Image,
	request *subproto.UpdateRequest, deleteMissingComputedFiles bool,
	slogger log.Logger) bool {
	var logger log.DebugLogger
	if l, ok := slogger.(log.DebugLogger); ok {
		logger = l
	} else {
		logger = debuglogger.New(slogger)
	}
	sub.requiredFS = image.FileSystem
	sub.filter = image.Filter
	request.Triggers = image.Triggers
	sub.requiredInodeToSubInode = make(map[uint64]uint64)
	sub.inodesMapped = make(map[uint64]struct{})
	sub.inodesChanged = make(map[uint64]bool)
	sub.inodesCreated = make(map[uint64]string)
	sub.subObjectCacheUsage = make(map[hash.Hash]uint64, len(sub.ObjectCache))
	// Populate subObjectCacheUsage.
	for _, hash := range sub.ObjectCache {
		sub.subObjectCacheUsage[hash] = 0
	}
	if !filesystem.CompareDirectoriesMetadata(&sub.FileSystem.DirectoryInode,
		&sub.requiredFS.DirectoryInode, nil) {
		makeDirectory(request, &sub.requiredFS.DirectoryInode, "/", false)
	}
	if sub.compareDirectories(request,
		&sub.FileSystem.DirectoryInode, &sub.requiredFS.DirectoryInode,
		"/", deleteMissingComputedFiles, logger) {
		return true
	}
	// Look for multiply used objects and tell the sub.
	for obj, useCount := range sub.subObjectCacheUsage {
		if useCount > 1 {
			if request.MultiplyUsedObjects == nil {
				request.MultiplyUsedObjects = make(map[hash.Hash]uint64)
			}
			request.MultiplyUsedObjects[obj] = useCount
		}
	}
	return false
}

// Returns true if there is a failure due to missing computed files.
func (sub *Sub) compareDirectories(request *subproto.UpdateRequest,
	subDirectory, requiredDirectory *filesystem.DirectoryInode,
	myPathName string, deleteMissingComputedFiles bool,
	logger log.DebugLogger) bool {
	// First look for entries that should be deleted.
	if sub.filter != nil && subDirectory != nil {
		for name := range subDirectory.EntriesByName {
			pathname := path.Join(myPathName, name)
			if sub.filter.Match(pathname) {
				continue
			}
			if _, ok := requiredDirectory.EntriesByName[name]; !ok {
				request.PathsToDelete = append(request.PathsToDelete, pathname)
			}
		}
	}
	// For the love of repeatable unit tests, sort before looping.
	names := make([]string, 0, len(requiredDirectory.EntriesByName))
	for name := range requiredDirectory.EntriesByName {
		names = append(names, name)
	}
	sort.Strings(names)
	for _, name := range names {
		requiredEntry := requiredDirectory.EntriesByName[name]
		pathname := path.Join(myPathName, name)
		if sub.filter != nil && sub.filter.Match(pathname) {
			continue
		}
		var subEntry *filesystem.DirectoryEntry
		if subDirectory != nil {
			if se, ok := subDirectory.EntriesByName[name]; ok {
				subEntry = se
			}
		}
		requiredInode := requiredEntry.Inode()
		if _, ok := requiredInode.(*filesystem.ComputedRegularInode); ok {
			// Replace with computed file.
			inode, ok := sub.ComputedInodes[pathname]
			if !ok {
				if deleteMissingComputedFiles {
					if subEntry != nil {
						request.PathsToDelete = append(request.PathsToDelete,
							pathname)
					}
					continue
				}
				logger.Printf(
					"compareDirectories(%s): missing computed file: %s\n",
					sub, pathname)
				return true
			}
			setComputedFileMtime(inode, subEntry)
			newEntry := new(filesystem.DirectoryEntry)
			newEntry.Name = name
			newEntry.InodeNumber = requiredEntry.InodeNumber
			newEntry.SetInode(inode)
			requiredEntry = newEntry
		}
		if subEntry == nil {
			sub.addEntry(request, requiredEntry, pathname, logger)
		} else {
			sub.compareEntries(request, subEntry, requiredEntry, pathname,
				logger)
		}
		// If a directory: descend (possibly with the directory for the sub).
		if requiredInode, ok := requiredInode.(*filesystem.DirectoryInode); ok {
			var subInode *filesystem.DirectoryInode
			if subEntry != nil {
				if si, ok := subEntry.Inode().(*filesystem.DirectoryInode); ok {
					subInode = si
				}
			}
			sub.compareDirectories(request, subInode, requiredInode, pathname,
				deleteMissingComputedFiles, logger)
		}
	}
	return false
}

func setComputedFileMtime(requiredInode *filesystem.RegularInode,
	subEntry *filesystem.DirectoryEntry) {
	if requiredInode.MtimeSeconds >= 0 {
		return
	}
	if subEntry != nil {
		subInode := subEntry.Inode()
		if subInode, ok := subInode.(*filesystem.RegularInode); ok {
			if requiredInode.Hash == subInode.Hash {
				requiredInode.MtimeNanoSeconds = subInode.MtimeNanoSeconds
				requiredInode.MtimeSeconds = subInode.MtimeSeconds
				return
			}
		}
	}
	requiredInode.MtimeSeconds = time.Now().Unix()
}

func (sub *Sub) addEntry(request *subproto.UpdateRequest,
	requiredEntry *filesystem.DirectoryEntry, myPathName string,
	logger log.DebugLogger) {
	requiredInode := requiredEntry.Inode()
	if requiredInode, ok := requiredInode.(*filesystem.DirectoryInode); ok {
		makeDirectory(request, requiredInode, myPathName, true)
	} else {
		sub.addInode(request, requiredEntry, myPathName, logger)
	}
}

func (sub *Sub) compareEntries(request *subproto.UpdateRequest,
	subEntry, requiredEntry *filesystem.DirectoryEntry, myPathName string,
	logger log.DebugLogger) {
	subInode := subEntry.Inode()
	requiredInode := requiredEntry.Inode()
	sameType, sameMetadata, sameData := filesystem.CompareInodes(
		subInode, requiredInode, nil)
	if requiredInode, ok := requiredInode.(*filesystem.DirectoryInode); ok {
		if sameMetadata {
			return
		}
		if sameType {
			makeDirectory(request, requiredInode, myPathName, false)
		} else {
			makeDirectory(request, requiredInode, myPathName, true)
		}
		return
	}
	if sameType && sameData && sameMetadata {
		if sub.relink(request, subEntry, requiredEntry, myPathName, logger) {
			logger.Debugf(0, "identical relink OK for %s\n", myPathName)
			return
		}
	} else if sameType && sameData {
		if sub.relink(request, subEntry, requiredEntry, myPathName,
			logger) {
			logger.Debugf(0, "relink OK for %s\n", myPathName)
			sub.updateMetadata(request, requiredEntry, myPathName)
			return
		}
	}
	sub.addInode(request, requiredEntry, myPathName, logger)
}

func (sub *Sub) relink(request *subproto.UpdateRequest,
	subEntry, requiredEntry *filesystem.DirectoryEntry,
	myPathName string, logger log.DebugLogger) bool {
	subInum, ok := sub.requiredInodeToSubInode[requiredEntry.InodeNumber]
	if !ok {
		if _, mapped := sub.inodesMapped[subEntry.InodeNumber]; mapped {
			return false
		}
		sub.requiredInodeToSubInode[requiredEntry.InodeNumber] =
			subEntry.InodeNumber
		logger.Debugf(0, "mapping sub inum: %d for: %s\n",
			subEntry.InodeNumber, myPathName)
		sub.inodesMapped[subEntry.InodeNumber] = struct{}{}
		return true
	}
	if subInum == subEntry.InodeNumber {
		return true
	}
	makeHardlink(request,
		myPathName, sub.FileSystem.InodeToFilenamesTable()[subInum][0])
	return true
}

func makeHardlink(request *subproto.UpdateRequest, newLink, target string) {
	var hardlink subproto.Hardlink
	hardlink.NewLink = newLink
	hardlink.Target = target
	request.HardlinksToMake = append(request.HardlinksToMake, hardlink)
}

func (sub *Sub) updateMetadata(request *subproto.UpdateRequest,
	requiredEntry *filesystem.DirectoryEntry, myPathName string) {
	if sub.inodesChanged[requiredEntry.InodeNumber] {
		return
	}
	var inode subproto.Inode
	inode.Name = myPathName
	inode.GenericInode = requiredEntry.Inode()
	request.InodesToChange = append(request.InodesToChange, inode)
	sub.inodesChanged[requiredEntry.InodeNumber] = true
}

func makeDirectory(request *subproto.UpdateRequest,
	requiredInode *filesystem.DirectoryInode, pathName string, create bool) {
	var newInode subproto.Inode
	newInode.Name = pathName
	var newDirectoryInode filesystem.DirectoryInode
	newDirectoryInode.Mode = requiredInode.Mode
	newDirectoryInode.Uid = requiredInode.Uid
	newDirectoryInode.Gid = requiredInode.Gid
	newInode.GenericInode = &newDirectoryInode
	if create {
		request.DirectoriesToMake = append(request.DirectoriesToMake, newInode)
	} else {
		request.InodesToChange = append(request.InodesToChange, newInode)
	}
}

func (sub *Sub) addInode(request *subproto.UpdateRequest,
	requiredEntry *filesystem.DirectoryEntry, myPathName string,
	logger log.DebugLogger) {
	requiredInode := requiredEntry.Inode()
	logger.Debugf(0, "addInode(%s, %d) Uid=%d\n",
		myPathName, requiredEntry.InodeNumber, requiredInode.GetUid())
	if name, ok := sub.inodesCreated[requiredEntry.InodeNumber]; ok {
		logger.Debugf(0, "make link: %s to %s\n", myPathName, name)
		makeHardlink(request, myPathName, name)
		return
	}
	// Try to find a sibling inode.
	names := sub.requiredFS.InodeToFilenamesTable()[requiredEntry.InodeNumber]
	subFS := sub.FileSystem
	if len(names) > 1 {
		var sameDataInode filesystem.GenericInode
		var sameDataName string
		for _, name := range names {
			if name == myPathName {
				logger.Debugf(0, "skipping self comparison: %s\n", name)
				continue
			}
			if inum, found := subFS.FilenameToInodeTable()[name]; found {
				subInode := sub.FileSystem.InodeTable[inum]
				_, sameMetadata, sameData := filesystem.CompareInodes(
					subInode, requiredInode, nil)
				if sameMetadata && sameData {
					logger.Debugf(0, "make sibling link: %s to %s (uid=%d)\n",
						myPathName, name, subInode.GetUid())
					makeHardlink(request, myPathName, name)
					return
				}
				if sameData {
					sameDataInode = subInode
					sameDataName = name
				}
			}
		}
		if sameDataInode != nil {
			logger.Debugf(0, "same data make link: %s to %s (uid=%d)\n",
				myPathName, sameDataName, sameDataInode.GetUid())
			sub.updateMetadata(request, requiredEntry, sameDataName)
			makeHardlink(request, myPathName, sameDataName)
			return
		}
	}
	if inode, ok := requiredEntry.Inode().(*filesystem.RegularInode); ok {
		if inode.Size > 0 {
			if _, ok := sub.subObjectCacheUsage[inode.Hash]; ok {
				sub.subObjectCacheUsage[inode.Hash]++
			} else {
				// Not in object cache: grab it from file-system.
				logger.Debugf(0, "copy to cache for: %s\n", myPathName)
				request.FilesToCopyToCache = append(
					request.FilesToCopyToCache,
					sub.getFileToCopy(myPathName, inode.Hash))
				sub.subObjectCacheUsage[inode.Hash] = 1
			}
		}
	}
	var inode subproto.Inode
	inode.Name = myPathName
	inode.GenericInode = requiredEntry.Inode()
	request.InodesToMake = append(request.InodesToMake, inode)
	sub.inodesCreated[requiredEntry.InodeNumber] = myPathName
}

func (sub *Sub) getFileToCopy(myPathName string,
	hashVal hash.Hash) subproto.FileToCopyToCache {
	subFS := sub.FileSystem
	requiredFS := sub.requiredFS
	inos, ok := subFS.HashToInodesTable()[hashVal]
	if !ok {
		panic("No object in cache for: " + myPathName)
	}
	file := subproto.FileToCopyToCache{
		Name: subFS.InodeToFilenamesTable()[inos[0]][0],
		Hash: hashVal,
	}
	// Try to find an inode where all its links will be deleted and mark one of
	// the links (filenames) to be hardlinked instead of copied into the cache.
	for _, iNum := range inos {
		filenames := subFS.InodeToFilenamesTable()[iNum]
		for _, filename := range filenames {
			if _, ok := requiredFS.FilenameToInodeTable()[filename]; ok {
				filenames = nil
				break
			}
			if sub.filter == nil || sub.filter.Match(filename) {
				filenames = nil
				break
			}
		}
		if filenames != nil {
			file.DoHardlink = true
			break
		}
	}
	return file
}
<commit_msg>Use log/log/debuglogger.Upgrade() in dom/lib.Sub.BuildUpdateRequest().<commit_after>package lib

import (
	"github.com/Symantec/Dominator/lib/filesystem"
	"github.com/Symantec/Dominator/lib/hash"
	"github.com/Symantec/Dominator/lib/image"
	"github.com/Symantec/Dominator/lib/log"
	"github.com/Symantec/Dominator/lib/log/debuglogger"
	subproto "github.com/Symantec/Dominator/proto/sub"
	"path"
	"sort"
	"time"
)

// Returns true if there is a failure due to missing computed files.
func (sub *Sub) buildUpdateRequest(image *image.Image,
	request *subproto.UpdateRequest, deleteMissingComputedFiles bool,
	slogger log.Logger) bool {
	logger := debuglogger.Upgrade(slogger)
	sub.requiredFS = image.FileSystem
	sub.filter = image.Filter
	request.Triggers = image.Triggers
	sub.requiredInodeToSubInode = make(map[uint64]uint64)
	sub.inodesMapped = make(map[uint64]struct{})
	sub.inodesChanged = make(map[uint64]bool)
	sub.inodesCreated = make(map[uint64]string)
	sub.subObjectCacheUsage = make(map[hash.Hash]uint64, len(sub.ObjectCache))
	// Populate subObjectCacheUsage.
	for _, hash := range sub.ObjectCache {
		sub.subObjectCacheUsage[hash] = 0
	}
	if !filesystem.CompareDirectoriesMetadata(&sub.FileSystem.DirectoryInode,
		&sub.requiredFS.DirectoryInode, nil) {
		makeDirectory(request, &sub.requiredFS.DirectoryInode, "/", false)
	}
	if sub.compareDirectories(request,
		&sub.FileSystem.DirectoryInode, &sub.requiredFS.DirectoryInode,
		"/", deleteMissingComputedFiles, logger) {
		return true
	}
	// Look for multiply used objects and tell the sub.
	for obj, useCount := range sub.subObjectCacheUsage {
		if useCount > 1 {
			if request.MultiplyUsedObjects == nil {
				request.MultiplyUsedObjects = make(map[hash.Hash]uint64)
			}
			request.MultiplyUsedObjects[obj] = useCount
		}
	}
	return false
}

// Returns true if there is a failure due to missing computed files.
func (sub *Sub) compareDirectories(request *subproto.UpdateRequest,
	subDirectory, requiredDirectory *filesystem.DirectoryInode,
	myPathName string, deleteMissingComputedFiles bool,
	logger log.DebugLogger) bool {
	// First look for entries that should be deleted.
	if sub.filter != nil && subDirectory != nil {
		for name := range subDirectory.EntriesByName {
			pathname := path.Join(myPathName, name)
			if sub.filter.Match(pathname) {
				continue
			}
			if _, ok := requiredDirectory.EntriesByName[name]; !ok {
				request.PathsToDelete = append(request.PathsToDelete, pathname)
			}
		}
	}
	// For the love of repeatable unit tests, sort before looping.
	names := make([]string, 0, len(requiredDirectory.EntriesByName))
	for name := range requiredDirectory.EntriesByName {
		names = append(names, name)
	}
	sort.Strings(names)
	for _, name := range names {
		requiredEntry := requiredDirectory.EntriesByName[name]
		pathname := path.Join(myPathName, name)
		if sub.filter != nil && sub.filter.Match(pathname) {
			continue
		}
		var subEntry *filesystem.DirectoryEntry
		if subDirectory != nil {
			if se, ok := subDirectory.EntriesByName[name]; ok {
				subEntry = se
			}
		}
		requiredInode := requiredEntry.Inode()
		if _, ok := requiredInode.(*filesystem.ComputedRegularInode); ok {
			// Replace with computed file.
			inode, ok := sub.ComputedInodes[pathname]
			if !ok {
				if deleteMissingComputedFiles {
					if subEntry != nil {
						request.PathsToDelete = append(request.PathsToDelete,
							pathname)
					}
					continue
				}
				logger.Printf(
					"compareDirectories(%s): missing computed file: %s\n",
					sub, pathname)
				return true
			}
			setComputedFileMtime(inode, subEntry)
			newEntry := new(filesystem.DirectoryEntry)
			newEntry.Name = name
			newEntry.InodeNumber = requiredEntry.InodeNumber
			newEntry.SetInode(inode)
			requiredEntry = newEntry
		}
		if subEntry == nil {
			sub.addEntry(request, requiredEntry, pathname, logger)
		} else {
			sub.compareEntries(request, subEntry, requiredEntry, pathname,
				logger)
		}
		// If a directory: descend (possibly with the directory for the sub).
		if requiredInode, ok := requiredInode.(*filesystem.DirectoryInode); ok {
			var subInode *filesystem.DirectoryInode
			if subEntry != nil {
				if si, ok := subEntry.Inode().(*filesystem.DirectoryInode); ok {
					subInode = si
				}
			}
			sub.compareDirectories(request, subInode, requiredInode, pathname,
				deleteMissingComputedFiles, logger)
		}
	}
	return false
}

func setComputedFileMtime(requiredInode *filesystem.RegularInode,
	subEntry *filesystem.DirectoryEntry) {
	if requiredInode.MtimeSeconds >= 0 {
		return
	}
	if subEntry != nil {
		subInode := subEntry.Inode()
		if subInode, ok := subInode.(*filesystem.RegularInode); ok {
			if requiredInode.Hash == subInode.Hash {
				requiredInode.MtimeNanoSeconds = subInode.MtimeNanoSeconds
				requiredInode.MtimeSeconds = subInode.MtimeSeconds
				return
			}
		}
	}
	requiredInode.MtimeSeconds = time.Now().Unix()
}

func (sub *Sub) addEntry(request *subproto.UpdateRequest,
	requiredEntry *filesystem.DirectoryEntry, myPathName string,
	logger log.DebugLogger) {
	requiredInode := requiredEntry.Inode()
	if requiredInode, ok := requiredInode.(*filesystem.DirectoryInode); ok {
		makeDirectory(request, requiredInode, myPathName, true)
	} else {
		sub.addInode(request, requiredEntry, myPathName, logger)
	}
}

func (sub *Sub) compareEntries(request *subproto.UpdateRequest,
	subEntry, requiredEntry *filesystem.DirectoryEntry, myPathName string,
	logger log.DebugLogger) {
	subInode := subEntry.Inode()
	requiredInode := requiredEntry.Inode()
	sameType, sameMetadata, sameData := filesystem.CompareInodes(
		subInode, requiredInode, nil)
	if requiredInode, ok := requiredInode.(*filesystem.DirectoryInode); ok {
		if sameMetadata {
			return
		}
		if sameType {
			makeDirectory(request, requiredInode, myPathName, false)
		} else {
			makeDirectory(request, requiredInode, myPathName, true)
		}
		return
	}
	if sameType && sameData && sameMetadata {
		if sub.relink(request, subEntry, requiredEntry, myPathName, logger) {
			logger.Debugf(0, "identical relink OK for %s\n", myPathName)
			return
		}
	} else if sameType && sameData {
		if sub.relink(request, subEntry, requiredEntry, myPathName,
			logger) {
			logger.Debugf(0, "relink OK for %s\n", myPathName)
			sub.updateMetadata(request, requiredEntry, myPathName)
			return
		}
	}
	sub.addInode(request, requiredEntry, myPathName, logger)
}

func (sub *Sub) relink(request *subproto.UpdateRequest,
	subEntry, requiredEntry *filesystem.DirectoryEntry,
	myPathName string, logger log.DebugLogger) bool {
	subInum, ok := sub.requiredInodeToSubInode[requiredEntry.InodeNumber]
	if !ok {
		if _, mapped := sub.inodesMapped[subEntry.InodeNumber]; mapped {
			return false
		}
		sub.requiredInodeToSubInode[requiredEntry.InodeNumber] =
			subEntry.InodeNumber
		logger.Debugf(0, "mapping sub inum: %d for: %s\n",
			subEntry.InodeNumber, myPathName)
		sub.inodesMapped[subEntry.InodeNumber] = struct{}{}
		return true
	}
	if subInum == subEntry.InodeNumber {
		return true
	}
	makeHardlink(request,
		myPathName, sub.FileSystem.InodeToFilenamesTable()[subInum][0])
	return true
}

func makeHardlink(request *subproto.UpdateRequest, newLink, target string) {
	var hardlink subproto.Hardlink
	hardlink.NewLink = newLink
	hardlink.Target = target
	request.HardlinksToMake = append(request.HardlinksToMake, hardlink)
}

func (sub *Sub) updateMetadata(request *subproto.UpdateRequest,
	requiredEntry *filesystem.DirectoryEntry, myPathName string) {
	if sub.inodesChanged[requiredEntry.InodeNumber] {
		return
	}
	var inode subproto.Inode
	inode.Name = myPathName
	inode.GenericInode = requiredEntry.Inode()
	request.InodesToChange = append(request.InodesToChange, inode)
	sub.inodesChanged[requiredEntry.InodeNumber] = true
}

func makeDirectory(request *subproto.UpdateRequest,
	requiredInode *filesystem.DirectoryInode, pathName string, create bool) {
	var newInode subproto.Inode
	newInode.Name = pathName
	var newDirectoryInode filesystem.DirectoryInode
	newDirectoryInode.Mode = requiredInode.Mode
	newDirectoryInode.Uid = requiredInode.Uid
	newDirectoryInode.Gid = requiredInode.Gid
	newInode.GenericInode = &newDirectoryInode
	if create {
		request.DirectoriesToMake = append(request.DirectoriesToMake, newInode)
	} else {
		request.InodesToChange = append(request.InodesToChange, newInode)
	}
}

func (sub *Sub) addInode(request *subproto.UpdateRequest,
	requiredEntry *filesystem.DirectoryEntry, myPathName string,
	logger log.DebugLogger) {
	requiredInode := requiredEntry.Inode()
	logger.Debugf(0, "addInode(%s, %d) Uid=%d\n",
		myPathName, requiredEntry.InodeNumber, requiredInode.GetUid())
	if name, ok := sub.inodesCreated[requiredEntry.InodeNumber]; ok {
		logger.Debugf(0, "make link: %s to %s\n", myPathName, name)
		makeHardlink(request, myPathName, name)
		return
	}
	// Try to find a sibling inode.
	names := sub.requiredFS.InodeToFilenamesTable()[requiredEntry.InodeNumber]
	subFS := sub.FileSystem
	if len(names) > 1 {
		var sameDataInode filesystem.GenericInode
		var sameDataName string
		for _, name := range names {
			if name == myPathName {
				logger.Debugf(0, "skipping self comparison: %s\n", name)
				continue
			}
			if inum, found := subFS.FilenameToInodeTable()[name]; found {
				subInode := sub.FileSystem.InodeTable[inum]
				_, sameMetadata, sameData := filesystem.CompareInodes(
					subInode, requiredInode, nil)
				if sameMetadata && sameData {
					logger.Debugf(0, "make sibling link: %s to %s (uid=%d)\n",
						myPathName, name, subInode.GetUid())
					makeHardlink(request, myPathName, name)
					return
				}
				if sameData {
					sameDataInode = subInode
					sameDataName = name
				}
			}
		}
		if sameDataInode != nil {
			logger.Debugf(0, "same data make link: %s to %s (uid=%d)\n",
				myPathName, sameDataName, sameDataInode.GetUid())
			sub.updateMetadata(request, requiredEntry, sameDataName)
			makeHardlink(request, myPathName, sameDataName)
			return
		}
	}
	if inode, ok := requiredEntry.Inode().(*filesystem.RegularInode); ok {
		if inode.Size > 0 {
			if _, ok := sub.subObjectCacheUsage[inode.Hash]; ok {
				sub.subObjectCacheUsage[inode.Hash]++
			} else {
				// Not in object cache: grab it from file-system.
				logger.Debugf(0, "copy to cache for: %s\n", myPathName)
				request.FilesToCopyToCache = append(
					request.FilesToCopyToCache,
					sub.getFileToCopy(myPathName, inode.Hash))
				sub.subObjectCacheUsage[inode.Hash] = 1
			}
		}
	}
	var inode subproto.Inode
	inode.Name = myPathName
	inode.GenericInode = requiredEntry.Inode()
	request.InodesToMake = append(request.InodesToMake, inode)
	sub.inodesCreated[requiredEntry.InodeNumber] = myPathName
}

func (sub *Sub) getFileToCopy(myPathName string,
	hashVal hash.Hash) subproto.FileToCopyToCache {
	subFS := sub.FileSystem
	requiredFS := sub.requiredFS
	inos, ok := subFS.HashToInodesTable()[hashVal]
	if !ok {
		panic("No object in cache for: " + myPathName)
	}
	file := subproto.FileToCopyToCache{
		Name: subFS.InodeToFilenamesTable()[inos[0]][0],
		Hash: hashVal,
	}
	// Try to find an inode where all its links will be deleted and mark one of
	// the links (filenames) to be hardlinked instead of copied into the cache.
	for _, iNum := range inos {
		filenames := subFS.InodeToFilenamesTable()[iNum]
		for _, filename := range filenames {
			if _, ok := requiredFS.FilenameToInodeTable()[filename]; ok {
				filenames = nil
				break
			}
			if sub.filter == nil || sub.filter.Match(filename) {
				filenames = nil
				break
			}
		}
		if filenames != nil {
			file.DoHardlink = true
			break
		}
	}
	return file
}
<|endoftext|>


<commit_before>// Package llog is a generic logging library used by leven labs. The log methods
// come in different severities: Debug, Info, Warn, Error, and Fatal.
//
// The log methods take in a string describing the error, and a set of key/value
// pairs giving the specific context around the error. The string is intended to
// always be the same no matter what, while the key/value pairs give information
// like which userID the error happened to, or any other relevant dynamic
// information.
//
// By default logs will be output to Stdout, without a timestamp attached to
// them, and only showing entries of level Info or above. All of these can be
// configured.
//
// All public functions in this package are thread-safe and can be called at any
// time. The public variables in this package are NOT thread-safe and should
// only be modified before any logging takes place
//
// Examples:
//
//	Info("Something important has occured")
//	Error("Could not open file", llog.KV{"filename": filename, "err": err})
//
package llog

import (
	"fmt"
	"io"
	"os"
	"strings"
	"sync"
	"time"
)

// Out is the io.Writer all log entries will be written to. It can be changed to
// anything you like, but the change should happen before any logging occurs. If
// an error occurs while writing to Out the entry will be written to Stdout
// instead
var Out io.Writer = os.Stdout
var defaultOut = os.Stdout

// DisplayTimestamp determines whether or not a timestamp is displayed in the
// log messages. By default one is not displayed. This can be changed by it
// should only be changed before any logging occurs
var DisplayTimestamp bool

// Level describes the severity of a particular log message
type Level int

// All defined log levels
const (
	DebugLevel Level = iota
	InfoLevel
	WarnLevel
	ErrorLevel
	FatalLevel
)

func (l Level) String() string {
	switch l {
	case DebugLevel:
		return "DEBUG"
	case InfoLevel:
		return "INFO"
	case WarnLevel:
		return "WARN"
	case ErrorLevel:
		return "ERROR"
	case FatalLevel:
		return "FATAL"
	}
	return "unknown level"
}

var currLevel = InfoLevel
var currLevelLock sync.RWMutex

// GetLevel returns the current log level
func GetLevel() Level {
	currLevelLock.RLock()
	defer currLevelLock.RUnlock()
	return currLevel
}

// SetLevel sets the current minimum log level which will be written to Out
func SetLevel(l Level) {
	currLevelLock.Lock()
	defer currLevelLock.Unlock()
	currLevel = l
}

// SetLevelFromString attempts to interpret the given string as a log level and
// sets the current log level to that. If the string can't be interpreted an
// error is returned and the log level remains what it was
func SetLevelFromString(ls string) error {
	switch strings.ToUpper(ls) {
	case "DEBUG":
		SetLevel(DebugLevel)
	case "INFO":
		SetLevel(InfoLevel)
	case "WARN":
		SetLevel(WarnLevel)
	case "ERROR":
		SetLevel(ErrorLevel)
	case "FATAL":
		SetLevel(FatalLevel)
	default:
		return fmt.Errorf("unknown log level %q", ls)
	}

	return nil
}

// KV is used to provide context to a log entry in the form of a dynamic set of
// key/value pairs which can be different for every entry.
type KV map[string]interface{}

type entry struct {
	level   Level
	msg     string
	kv      KV
	blockCh chan struct{} // can be nil
}

var (
	prefix         = []byte("~ ")
	separator      = []byte(" --")
	separatorSpace = append(separator, ' ')
	tsPrefix       = []byte("[")
	tsSuffix       = []byte("] ")
	space          = []byte(" ")
	equals         = []byte("=")
	newline        = []byte("\n")
)

func writeHelper(b []byte, w io.Writer, lastErr error) error {
	if lastErr != nil {
		return lastErr
	}
	_, lastErr = w.Write(b)
	return lastErr
}

func (e entry) printOut(w io.Writer, displayTS bool) error {
	var err error
	err = writeHelper(prefix, w, err)
	if displayTS {
		err = writeHelper(tsPrefix, w, err)
		err = writeHelper([]byte(time.Now().String()), w, err)
		err = writeHelper(tsSuffix, w, err)
	}
	err = writeHelper([]byte(e.level.String()), w, err)
	err = writeHelper(separatorSpace, w, err)
	err = writeHelper([]byte(e.msg), w, err)
	if len(e.kv) > 0 {
		err = writeHelper(separator, w, err)
		for k, v := range e.kv {
			err = writeHelper(space, w, err)
			err = writeHelper([]byte(k), w, err)
			err = writeHelper(equals, w, err)
			vstr := fmt.Sprint(v)
			err = writeHelper([]byte(fmt.Sprintf("%q", vstr)), w, err)
		}
	}
	err = writeHelper(newline, w, err)

	return err
}

type syncer interface {
	Sync()
}

type flusher interface {
	Flush()
}

var entryCh = make(chan entry)

func init() {
	go func() {
		for e := range entryCh {
			err := e.printOut(Out, DisplayTimestamp)

			// If we couldn't write the entry to Out we write an error to that
			// effect to Stdout, then try to write the original entry as well
			if err != nil && Out != defaultOut {
				erre := entry{
					level: ErrorLevel,
					msg:   "Could not write to error Out",
					kv: KV{
						"err": err,
					},
				}
				erre.printOut(defaultOut, DisplayTimestamp)
				e.printOut(defaultOut, DisplayTimestamp)
			}

			// If the error level is fatal this is the last entry we should ever
			// write. We do want to attempt to flush Out though, in case it's
			// buffered, otherwise exiting now will cause the fatal message to
			// never be shown. We try to cast to either an interface with a Sync
			// or a Flush command as a form of ghetto reflection, to see if the
			// writer has either, and use one if found.
			if e.level == FatalLevel {
				if so, ok := Out.(syncer); ok {
					so.Sync()
				} else if fo, ok := Out.(flusher); ok {
					fo.Flush()
				}
			}

			if e.blockCh != nil {
				close(e.blockCh)
			}
		}
	}()
}

func kvNormalize(kv []KV) KV {
	if len(kv) > 0 {
		return kv[0]
	}
	return nil
}

func logEntry(l Level, msg string, kv []KV, blockCh chan struct{}) {
	if l >= GetLevel() {
		entryCh <- entry{
			level:   l,
			msg:     msg,
			kv:      kvNormalize(kv),
			blockCh: blockCh,
		}
	}
}

// LogFunc is the function signature used by the different log functions (Debug,
// Info, Warn, Error, and Fatal). It's useful for writing wrapper functions
type LogFunc func(string, ...KV)

// Debug writes a Debug message to Out, with an optional set of key/value pairs
func Debug(msg string, kv ...KV) {
	logEntry(DebugLevel, msg, kv, nil)
}

// Info writes an Info message to Out, with an optional set of key/value pairs
func Info(msg string, kv ...KV) {
	logEntry(InfoLevel, msg, kv, nil)
}

// Warn writes a Warn message to Out, with an optional set of key/value pairs
func Warn(msg string, kv ...KV) {
	logEntry(WarnLevel, msg, kv, nil)
}

// Error writes an Error message to Out, with an optional set of key/value pairs
func Error(msg string, kv ...KV) {
	logEntry(ErrorLevel, msg, kv, nil)
}

// Fatal writes a Fatal message to Out, with an optional set of key/value pairs.
// Once written the process will be exited with an exit code of 1
func Fatal(msg string, kv ...KV) {
	blockCh := make(chan struct{})
	logEntry(FatalLevel, msg, kv, blockCh)
	<-blockCh
	os.Exit(1)
}
<commit_msg>copy the KV entry passed in, otherwise it could be modified from the caller while it's being printed<commit_after>// Package llog is a generic logging library used by leven labs. The log methods
// come in different severities: Debug, Info, Warn, Error, and Fatal.
//
// The log methods take in a string describing the error, and a set of key/value
// pairs giving the specific context around the error. The string is intended to
// always be the same no matter what, while the key/value pairs give information
// like which userID the error happened to, or any other relevant dynamic
// information.
//
// By default logs will be output to Stdout, without a timestamp attached to
// them, and only showing entries of level Info or above. All of these can be
// configured.
//
// All public functions in this package are thread-safe and can be called at any
// time. The public variables in this package are NOT thread-safe and should
// only be modified before any logging takes place
//
// Examples:
//
//	Info("Something important has occured")
//	Error("Could not open file", llog.KV{"filename": filename, "err": err})
//
package llog

import (
	"fmt"
	"io"
	"os"
	"strings"
	"sync"
	"time"
)

// Out is the io.Writer all log entries will be written to. It can be changed to
// anything you like, but the change should happen before any logging occurs. If
// an error occurs while writing to Out the entry will be written to Stdout
// instead
var Out io.Writer = os.Stdout
var defaultOut = os.Stdout

// DisplayTimestamp determines whether or not a timestamp is displayed in the
// log messages. By default one is not displayed. This can be changed by it
// should only be changed before any logging occurs
var DisplayTimestamp bool

// Level describes the severity of a particular log message
type Level int

// All defined log levels
const (
	DebugLevel Level = iota
	InfoLevel
	WarnLevel
	ErrorLevel
	FatalLevel
)

func (l Level) String() string {
	switch l {
	case DebugLevel:
		return "DEBUG"
	case InfoLevel:
		return "INFO"
	case WarnLevel:
		return "WARN"
	case ErrorLevel:
		return "ERROR"
	case FatalLevel:
		return "FATAL"
	}
	return "unknown level"
}

var currLevel = InfoLevel
var currLevelLock sync.RWMutex

// GetLevel returns the current log level
func GetLevel() Level {
	currLevelLock.RLock()
	defer currLevelLock.RUnlock()
	return currLevel
}

// SetLevel sets the current minimum log level which will be written to Out
func SetLevel(l Level) {
	currLevelLock.Lock()
	defer currLevelLock.Unlock()
	currLevel = l
}

// SetLevelFromString attempts to interpret the given string as a log level and
// sets the current log level to that. If the string can't be interpreted an
// error is returned and the log level remains what it was
func SetLevelFromString(ls string) error {
	switch strings.ToUpper(ls) {
	case "DEBUG":
		SetLevel(DebugLevel)
	case "INFO":
		SetLevel(InfoLevel)
	case "WARN":
		SetLevel(WarnLevel)
	case "ERROR":
		SetLevel(ErrorLevel)
	case "FATAL":
		SetLevel(FatalLevel)
	default:
		return fmt.Errorf("unknown log level %q", ls)
	}

	return nil
}

// KV is used to provide context to a log entry in the form of a dynamic set of
// key/value pairs which can be different for every entry.
type KV map[string]interface{}

type entry struct {
	level   Level
	msg     string
	kv      KV
	blockCh chan struct{} // can be nil
}

var (
	prefix         = []byte("~ ")
	separator      = []byte(" --")
	separatorSpace = append(separator, ' ')
	tsPrefix       = []byte("[")
	tsSuffix       = []byte("] ")
	space          = []byte(" ")
	equals         = []byte("=")
	newline        = []byte("\n")
)

func writeHelper(b []byte, w io.Writer, lastErr error) error {
	if lastErr != nil {
		return lastErr
	}
	_, lastErr = w.Write(b)
	return lastErr
}

func (e entry) printOut(w io.Writer, displayTS bool) error {
	var err error
	err = writeHelper(prefix, w, err)
	if displayTS {
		err = writeHelper(tsPrefix, w, err)
		err = writeHelper([]byte(time.Now().String()), w, err)
		err = writeHelper(tsSuffix, w, err)
	}
	err = writeHelper([]byte(e.level.String()), w, err)
	err = writeHelper(separatorSpace, w, err)
	err = writeHelper([]byte(e.msg), w, err)
	if len(e.kv) > 0 {
		err = writeHelper(separator, w, err)
		for k, v := range e.kv {
			err = writeHelper(space, w, err)
			err = writeHelper([]byte(k), w, err)
			err = writeHelper(equals, w, err)
			vstr := fmt.Sprint(v)
			err = writeHelper([]byte(fmt.Sprintf("%q", vstr)), w, err)
		}
	}
	err = writeHelper(newline, w, err)

	return err
}

type syncer interface {
	Sync()
}

type flusher interface {
	Flush()
}

var entryCh = make(chan entry)

func init() {
	go func() {
		for e := range entryCh {
			err := e.printOut(Out, DisplayTimestamp)

			// If we couldn't write the entry to Out we write an error to that
			// effect to Stdout, then try to write the original entry as well
			if err != nil && Out != defaultOut {
				erre := entry{
					level: ErrorLevel,
					msg:   "Could not write to error Out",
					kv: KV{
						"err": err,
					},
				}
				erre.printOut(defaultOut, DisplayTimestamp)
				e.printOut(defaultOut, DisplayTimestamp)
			}

			// If the error level is fatal this is the last entry we should ever
			// write. We do want to attempt to flush Out though, in case it's
			// buffered, otherwise exiting now will cause the fatal message to
			// never be shown. We try to cast to either an interface with a Sync
			// or a Flush command as a form of ghetto reflection, to see if the
			// writer has either, and use one if found.
			if e.level == FatalLevel {
				if so, ok := Out.(syncer); ok {
					so.Sync()
				} else if fo, ok := Out.(flusher); ok {
					fo.Flush()
				}
			}

			if e.blockCh != nil {
				close(e.blockCh)
			}
		}
	}()
}

func kvNormalize(kv []KV) KV {
	if len(kv) > 0 {
		kvCopy := make(KV, len(kv[0]))
		for k, v := range kv[0] {
			kvCopy[k] = v
		}
		return kvCopy
	}
	return nil
}

func logEntry(l Level, msg string, kv []KV, blockCh chan struct{}) {
	if l >= GetLevel() {
		entryCh <- entry{
			level:   l,
			msg:     msg,
			kv:      kvNormalize(kv),
			blockCh: blockCh,
		}
	}
}

// LogFunc is the function signature used by the different log functions (Debug,
// Info, Warn, Error, and Fatal). It's useful for writing wrapper functions
type LogFunc func(string, ...KV)

// Debug writes a Debug message to Out, with an optional set of key/value pairs
func Debug(msg string, kv ...KV) {
	logEntry(DebugLevel, msg, kv, nil)
}

// Info writes an Info message to Out, with an optional set of key/value pairs
func Info(msg string, kv ...KV) {
	logEntry(InfoLevel, msg, kv, nil)
}

// Warn writes a Warn message to Out, with an optional set of key/value pairs
func Warn(msg string, kv ...KV) {
	logEntry(WarnLevel, msg, kv, nil)
}

// Error writes an Error message to Out, with an optional set of key/value pairs
func Error(msg string, kv ...KV) {
	logEntry(ErrorLevel, msg, kv, nil)
}

// Fatal writes a Fatal message to Out, with an optional set of key/value pairs.
// Once written the process will be exited with an exit code of 1
func Fatal(msg string, kv ...KV) {
	blockCh := make(chan struct{})
	logEntry(FatalLevel, msg, kv, blockCh)
	<-blockCh
	os.Exit(1)
}
<|endoftext|>


<commit_before>package sql

import (
	"bytes"
	"io"
	"testing"
)

func Test_stackEmpty(t *testing.T) {
	s := newStack()
	if !s.empty() {
		t.Fatal("new stack is not empty")
	}

	if s.peek() != rune(0) {
		t.Fatal("peek of empty stack does not return correct value")
	}
}

func Test_stackSingle(t *testing.T) {
	s := newStack()
	s.push('x')
	if s.empty() {
		t.Fatal("non-empty stack marked as empty")
	}

	if s.peek() != 'x' {
		t.Fatal("peek of stack with single entry does not return correct value")
	}

	if s.pop() != 'x' {
		t.Fatal("pop of stack with single entry does not return correct value")
	}

	if !s.empty() {
		t.Fatal("popped stack is not empty")
	}
}

func Test_stackMulti(t *testing.T) {
	s := newStack()
	s.push('x')
	s.push('y')
	s.push('z')

	if s.pop() != 'z' {
		t.Fatal("pop of 1st multi stack does not return correct value")
	}
	if s.pop() != 'y' {
		t.Fatal("pop of 2nd multi stack does not return correct value")
	}
	if s.pop() != 'x' {
		t.Fatal("pop of 3rd multi stack does not return correct value")
	}

	if !s.empty() {
		t.Fatal("popped mstack is not empty")
	}
}

func Test_ScannerNew(t *testing.T) {
	s := NewScanner(nil)
	if s == nil {
		t.Fatalf("failed to create basic Scanner")
	}
}

func Test_ScannerEmpty(t *testing.T) {
	r := bytes.NewBufferString("")
	s := NewScanner(r)

	_, err := s.Scan()
	if err != io.EOF {
		t.Fatal("Scan of empty string did not return EOF")
	}
}

func Test_ScannerSemi(t *testing.T) {
	r := bytes.NewBufferString(";")
	s := NewScanner(r)

	l, err := s.Scan()
	if err != nil {
		t.Fatal("Scan of single semicolon failed")
	}
	if l != ";" {
		t.Fatal("Scan of single semicolon returned incorrect value")
	}
	_, err = s.Scan()
	if err != io.EOF {
		t.Fatal("Scan of empty string after semicolon did not return EOF")
	}
}

func Test_ScannerSingleStatement(t *testing.T) {
	r := bytes.NewBufferString("SELECT * FROM foo;")
	s := NewScanner(r)

	l, err := s.Scan()
	if err != nil {
		t.Fatal("Scan of single statement failed")
	}
	if l != "SELECT * FROM foo;" {
		t.Fatal("Scan of single statement returned incorrect value")
	}
	_, err = s.Scan()
	if err != io.EOF {
		t.Fatal("Scan of empty string after statement did not return EOF")
	}
}

func Test_ScannerSingleStatementQuotes(t *testing.T) {
	r := bytes.NewBufferString(`SELECT * FROM "foo";`)
	s := NewScanner(r)

	l, err := s.Scan()
	if err != nil {
		t.Fatal("Scan of single statement failed")
	}
	if l != `SELECT * FROM "foo";` {
		t.Fatal("Scan of single statement returned incorrect value")
	}
	_, err = s.Scan()
	if err != io.EOF {
		t.Fatal("Scan of empty string after statement did not return EOF")
	}
}

func Test_ScannerSingleStatementQuotesEmbedded(t *testing.T) {
	r := bytes.NewBufferString(`SELECT * FROM ";SELECT * FROM '"foo"'";`)
	s := NewScanner(r)

	l, err := s.Scan()
	if err != nil {
		t.Fatal("Scan of single statement failed")
	}
	if l != `SELECT * FROM ";SELECT * FROM '"foo"'";` {
		t.Fatal("Scan of single statement returned incorrect value")
	}
	_, err = s.Scan()
	if err != io.EOF {
		t.Fatal("Scan of empty string after statement did not return EOF")
	}
}

func Test_ScannerMultiStatement(t *testing.T) {
	r := bytes.NewBufferString("SELECT * FROM foo;SELECT * FROM bar;")
	s := NewScanner(r)

	l, err := s.Scan()
	if err != nil {
		t.Fatal("Scan of multi statement failed")
	}
	if l != "SELECT * FROM foo;" {
		t.Fatal("Scan of first of multi statement returned incorrect value")
	}

	l, err = s.Scan()
	if err != nil {
		t.Fatal("Scan of multi statement failed")
	}
	if l != "SELECT * FROM bar;" {
		t.Fatal("Scan of second of multi statement returned incorrect value")
	}
}

func Test_ScannerMultiStatementQuotesEmbedded(t *testing.T) {
	r := bytes.NewBufferString(`SELECT * FROM "foo;barx";SELECT * FROM bar;`)
	s := NewScanner(r)

	l, err := s.Scan()
	if err != nil {
		t.Fatal("Scan of multi statement failed")
	}
	if l != `SELECT * FROM "foo;barx";` {
		t.Fatal("Scan of first of multi statement returned incorrect value")
	}

	l, err = s.Scan()
	if err != nil {
		t.Fatal("Scan of multi statement failed")
	}
	if l != "SELECT * FROM bar;" {
		t.Fatal("Scan of second of multi statement returned incorrect value")
	}
}

// XX I am missing this case: '"' ????
<commit_msg>Move to table-driven tests for SQL parser<commit_after>package sql

import (
	"bytes"
	"io"
	"strings"
	"testing"
)

func Test_stackEmpty(t *testing.T) {
	s := newStack()
	if !s.empty() {
		t.Fatal("new stack is not empty")
	}

	if s.peek() != rune(0) {
		t.Fatal("peek of empty stack does not return correct value")
	}
}

func Test_stackSingle(t *testing.T) {
	s := newStack()
	s.push('x')
	if s.empty() {
		t.Fatal("non-empty stack marked as empty")
	}

	if s.peek() != 'x' {
		t.Fatal("peek of stack with single entry does not return correct value")
	}

	if s.pop() != 'x' {
		t.Fatal("pop of stack with single entry does not return correct value")
	}

	if !s.empty() {
		t.Fatal("popped stack is not empty")
	}
}

func Test_stackMulti(t *testing.T) {
	s := newStack()
	s.push('x')
	s.push('y')
	s.push('z')

	if s.pop() != 'z' {
		t.Fatal("pop of 1st multi stack does not return correct value")
	}
	if s.pop() != 'y' {
		t.Fatal("pop of 2nd multi stack does not return correct value")
	}
	if s.pop() != 'x' {
		t.Fatal("pop of 3rd multi stack does not return correct value")
	}

	if !s.empty() {
		t.Fatal("popped mstack is not empty")
	}
}

func Test_ScannerNew(t *testing.T) {
	s := NewScanner(nil)
	if s == nil {
		t.Fatalf("failed to create basic Scanner")
	}
}

func Test_ScannerEmpty(t *testing.T) {
	r := bytes.NewBufferString("")
	s := NewScanner(r)

	_, err := s.Scan()
	if err != io.EOF {
		t.Fatal("Scan of empty string did not return EOF")
	}
}

func Test_ScannerSemi(t *testing.T) {
	r := bytes.NewBufferString(";")
	s := NewScanner(r)

	l, err := s.Scan()
	if err != nil {
		t.Fatal("Scan of single semicolon failed")
	}
	if l != ";" {
		t.Fatal("Scan of single semicolon returned incorrect value")
	}
	_, err = s.Scan()
	if err != io.EOF {
		t.Fatal("Scan of empty string after semicolon did not return EOF")
	}
}

func Test_ScannerSingleStatement(t *testing.T) {
	r := bytes.NewBufferString("SELECT * FROM foo;")
	s := NewScanner(r)

	l, err := s.Scan()
	if err != nil {
		t.Fatal("Scan of single statement failed")
	}
	if l != "SELECT * FROM foo;" {
		t.Fatal("Scan of single statement returned incorrect value")
	}
	_, err = s.Scan()
	if err != io.EOF {
		t.Fatal("Scan of empty string after statement did not return EOF")
	}
}

func Test_ScannerSingleStatementQuotes(t *testing.T) {
	r := bytes.NewBufferString(`SELECT * FROM "foo";`)
	s := NewScanner(r)

	l, err := s.Scan()
	if err != nil {
		t.Fatal("Scan of single statement failed")
	}
	if l != `SELECT * FROM "foo";` {
		t.Fatal("Scan of single statement returned incorrect value")
	}
	_, err = s.Scan()
	if err != io.EOF {
		t.Fatal("Scan of empty string after statement did not return EOF")
	}
}

func Test_ScannerSingleStatementQuotesEmbedded(t *testing.T) {
	r := bytes.NewBufferString(`SELECT * FROM ";SELECT * FROM '"foo"'";`)
	s := NewScanner(r)

	l, err := s.Scan()
	if err != nil {
		t.Fatal("Scan of single statement failed")
	}
	if l != `SELECT * FROM ";SELECT * FROM '"foo"'";` {
		t.Fatal("Scan of single statement returned incorrect value")
	}
	_, err = s.Scan()
	if err != io.EOF {
		t.Fatal("Scan of empty string after statement did not return EOF")
	}
}

func Test_ScannerMultiStatement(t *testing.T) {
	e := []string{`SELECT * FROM foo;`, `SELECT * FROM bar;`}
	r := bytes.NewBufferString(strings.Join(e, ""))
	s := NewScanner(r)

	for i := range e {
		l, err := s.Scan()
		if err != nil {
			t.Fatal("Scan of multi statement failed")
		}

		if l != e[i] {
			t.Fatalf("Scan of multi statement returned incorrect value, exp %s, got %s", e[i], l)
		}
	}
}

func Test_ScannerMultiStatementQuotesEmbedded(t *testing.T) {
	e := []string{`SELECT * FROM "foo;barx";`, `SELECT * FROM bar;`}
	r := bytes.NewBufferString(strings.Join(e, ""))
	s := NewScanner(r)

	for i := range e {
		l, err := s.Scan()
		if err != nil {
			t.Fatal("Scan of multi statement failed")
		}

		if l != e[i] {
			t.Fatalf("Scan of multi statement returned incorrect value, exp %s, got %s", e[i], l)
		}
	}
}

// XX I am missing this case: '"' ????
<|endoftext|>


<commit_before>// Copyright 2014 Marc-Antoine Ruel. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.

package interfaceGUID

import (
	"fmt"
	"reflect"
	"testing"

	"github.com/maruel/ut"
)

func ExampleCalculate() {
	type Foo interface {
		Baz1(int) error
		Baz2()
	}
	fmt.Printf("%s\n", Calculate(reflect.TypeOf((*Foo)(nil)).Elem()))
	// Output:
	// ee2fbefc01ff399e106213e5eab0f3a36c245f195195bd36a2058c115637e05d
}

func TestCalculatePrivateName(t *testing.T) {
	type value1 int
	type value2 int
	ut.AssertEqual(t, "26461de90bccabfde78889464de27c40b1a962fdc312a7b6284dd21593ffcd83", Calculate(reflect.TypeOf((*value1)(nil)).Elem()))
	ut.AssertEqual(t, "26461de90bccabfde78889464de27c40b1a962fdc312a7b6284dd21593ffcd83", Calculate(reflect.TypeOf((*value2)(nil)).Elem()))
}

func TestCalculateFooBase(t *testing.T) {
	// All the TestCalculateFoo* are relative to this test case.
	type Foo interface {
		Baz1(int) error
		Baz2()
	}
	type Bar interface {
		GetFoo(int) Foo
	}
	ut.AssertEqual(t, "18f7adec8708b2fd00336fb6d88911e465618c9429c823ca8f70c36f1794fcf1", Calculate(reflect.TypeOf((*Bar)(nil)).Elem()))
}

func TestCalculateFooMethodType(t *testing.T) {
	// Inner interface method has different argument.
	type Foo interface {
		Baz1(int64) error
		Baz2()
	}
	type Bar interface {
		GetFoo(int) Foo
	}
	ut.AssertEqual(t, "7945944c9adf174cbac6e4b6bac219bb4eedd802818cbdc6477b6b3f7b9f9cb1", Calculate(reflect.TypeOf((*Bar)(nil)).Elem()))
}

func TestCalculateFooMethodOrder(t *testing.T) {
	// Inner interface has same methods but in different order.
	type Foo interface {
		Baz2()
		Baz1(int) error
	}
	type Bar interface {
		GetFoo(int) Foo
	}
	ut.AssertEqual(t, "18f7adec8708b2fd00336fb6d88911e465618c9429c823ca8f70c36f1794fcf1", Calculate(reflect.TypeOf((*Bar)(nil)).Elem()))
}

func TestCalculateFooInnerName(t *testing.T) {
	// Inner interface has different name.
	type Foo2 interface {
		Baz1(int) error
		Baz2()
	}
	type Bar interface {
		GetFoo(int) Foo2
	}
	ut.AssertEqual(t, "ac703259173d15ced210292885a05c6bd0d7d766d97a6fec32fe6e950d81593a", Calculate(reflect.TypeOf((*Bar)(nil)).Elem()))
}

func TestCalculateFooPrivate(t *testing.T) {
	// Inner interface has private method.
	type Foo interface {
		Baz1(int) error
		Baz2()
		privateFn()
	}
	type Bar interface {
		GetFoo(int) Foo
	}
	ut.AssertEqual(t, "2afcbe99c3d2df8d2273723de6046a2922284a22bbe3ec91d2e312ea7b092fe5", Calculate(reflect.TypeOf((*Bar)(nil)).Elem()))
}

func TestCalculateFooPrivateDifferentName(t *testing.T) {
	// Inner interface has private method but with a different name.
	type Foo interface {
		Baz1(int) error
		Baz2()
		privateFn2()
	}
	type Bar interface {
		GetFoo(int) Foo
	}
	ut.AssertEqual(t, "20b6295934efe4f25ead7ccf461e8894d99ee94a2f4e34e4200010c67aa2d621", Calculate(reflect.TypeOf((*Bar)(nil)).Elem()))
}

func TestCalculateFooReturn(t *testing.T) {
	// Inner interface method has different return type.
	type Foo interface {
		Baz1(int) string
		Baz2()
	}
	type Bar interface {
		GetFoo(int) Foo
	}
	ut.AssertEqual(t, "aa65c9ef02f86f231200d0e3ac25f642e18a97730967a8a24203454ae20f73eb", Calculate(reflect.TypeOf((*Bar)(nil)).Elem()))
}

func TestCalculateFooBarRenamed(t *testing.T) {
	// The type has a different name, the hash value doesn't change.
	type Foo interface {
		Baz1(int) error
		Baz2()
	}
	type Bar interface {
		GetFoo(int) Foo
	}
	ut.AssertEqual(t, "18f7adec8708b2fd00336fb6d88911e465618c9429c823ca8f70c36f1794fcf1", Calculate(reflect.TypeOf((*Bar)(nil)).Elem()))
}

func TestCalculateMethodSelfReferencing(t *testing.T) {
	// The type is self-referencing.
	type Foo interface {
		Bar() Foo
	}
	ut.AssertEqual(t, "891e72350ead590aaa4b8ff9b4c829ef6c9af819d5c041fbccfa8f54304e25ca", Calculate(reflect.TypeOf((*Foo)(nil)).Elem()))
}

func TestCalculateTypeSelfReferencing(t *testing.T) {
	// The type is self-referencing.
	type Foo *Foo
	ut.AssertEqual(t, "5a4f72702c70491229d3751c92ea8b403c4aacd7d9df54f79e27c439e8ee8f74", Calculate(reflect.TypeOf((*Foo)(nil)).Elem()))
}

// These types are used in TestCalculateMutuallyReferenced. Go doesn't support
// function-local mutually referencing types so they have to be defined at file
// scope.
type MutuallyReferenced1 interface {
	Foo() MutuallyReferenced2
}
type MutuallyReferenced2 interface {
	Bar() MutuallyReferenced1
}

func TestCalculateMutuallyReferenced(t *testing.T) {
	// The types are mutually referencing.
	ut.AssertEqual(t, "109f2b3b0bcd2050177c557664fca8b7174e2bb6b6b688939f8b15d08cdd5ec1", Calculate(reflect.TypeOf((*MutuallyReferenced1)(nil)).Elem()))
}
<commit_msg>Remove dependency on github.com/maruel/ut<commit_after>// Copyright 2014 Marc-Antoine Ruel. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.

package interfaceGUID

import (
	"fmt"
	"reflect"
	"testing"
)

func ExampleCalculate() {
	type Foo interface {
		Baz1(int) error
		Baz2()
	}
	fmt.Printf("%s\n", Calculate(reflect.TypeOf((*Foo)(nil)).Elem()))
	// Output:
	// ee2fbefc01ff399e106213e5eab0f3a36c245f195195bd36a2058c115637e05d
}

func TestCalculatePrivateName(t *testing.T) {
	type value1 int
	type value2 int
	run(t, "26461de90bccabfde78889464de27c40b1a962fdc312a7b6284dd21593ffcd83", reflect.TypeOf((*value1)(nil)).Elem())
	run(t, "26461de90bccabfde78889464de27c40b1a962fdc312a7b6284dd21593ffcd83", reflect.TypeOf((*value2)(nil)).Elem())
}

func TestCalculateFooBase(t *testing.T) {
	// All the TestCalculateFoo* are relative to this test case.
	type Foo interface {
		Baz1(int) error
		Baz2()
	}
	type Bar interface {
		GetFoo(int) Foo
	}
	run(t, "18f7adec8708b2fd00336fb6d88911e465618c9429c823ca8f70c36f1794fcf1", reflect.TypeOf((*Bar)(nil)).Elem())
}

func TestCalculateFooMethodType(t *testing.T) {
	// Inner interface method has different argument.
	type Foo interface {
		Baz1(int64) error
		Baz2()
	}
	type Bar interface {
		GetFoo(int) Foo
	}
	run(t, "7945944c9adf174cbac6e4b6bac219bb4eedd802818cbdc6477b6b3f7b9f9cb1", reflect.TypeOf((*Bar)(nil)).Elem())
}

func TestCalculateFooMethodOrder(t *testing.T) {
	// Inner interface has same methods but in different order.
	type Foo interface {
		Baz2()
		Baz1(int) error
	}
	type Bar interface {
		GetFoo(int) Foo
	}
	run(t, "18f7adec8708b2fd00336fb6d88911e465618c9429c823ca8f70c36f1794fcf1", reflect.TypeOf((*Bar)(nil)).Elem())
}

func TestCalculateFooInnerName(t *testing.T) {
	// Inner interface has different name.
	type Foo2 interface {
		Baz1(int) error
		Baz2()
	}
	type Bar interface {
		GetFoo(int) Foo2
	}
	run(t, "ac703259173d15ced210292885a05c6bd0d7d766d97a6fec32fe6e950d81593a", reflect.TypeOf((*Bar)(nil)).Elem())
}

func TestCalculateFooPrivate(t *testing.T) {
	// Inner interface has private method.
	type Foo interface {
		Baz1(int) error
		Baz2()
		privateFn()
	}
	type Bar interface {
		GetFoo(int) Foo
	}
	run(t, "2afcbe99c3d2df8d2273723de6046a2922284a22bbe3ec91d2e312ea7b092fe5", reflect.TypeOf((*Bar)(nil)).Elem())
}

func TestCalculateFooPrivateDifferentName(t *testing.T) {
	// Inner interface has private method but with a different name.
	type Foo interface {
		Baz1(int) error
		Baz2()
		privateFn2()
	}
	type Bar interface {
		GetFoo(int) Foo
	}
	run(t, "20b6295934efe4f25ead7ccf461e8894d99ee94a2f4e34e4200010c67aa2d621", reflect.TypeOf((*Bar)(nil)).Elem())
}

func TestCalculateFooReturn(t *testing.T) {
	// Inner interface method has different return type.
	type Foo interface {
		Baz1(int) string
		Baz2()
	}
	type Bar interface {
		GetFoo(int) Foo
	}
	run(t, "aa65c9ef02f86f231200d0e3ac25f642e18a97730967a8a24203454ae20f73eb", reflect.TypeOf((*Bar)(nil)).Elem())
}

func TestCalculateFooBarRenamed(t *testing.T) {
	// The type has a different name, the hash value doesn't change.
	type Foo interface {
		Baz1(int) error
		Baz2()
	}
	type Bar interface {
		GetFoo(int) Foo
	}
	run(t, "18f7adec8708b2fd00336fb6d88911e465618c9429c823ca8f70c36f1794fcf1", reflect.TypeOf((*Bar)(nil)).Elem())
}

func TestCalculateMethodSelfReferencing(t *testing.T) {
	// The type is self-referencing.
	type Foo interface {
		Bar() Foo
	}
	run(t, "891e72350ead590aaa4b8ff9b4c829ef6c9af819d5c041fbccfa8f54304e25ca", reflect.TypeOf((*Foo)(nil)).Elem())
}

func TestCalculateTypeSelfReferencing(t *testing.T) {
	// The type is self-referencing.
	type Foo *Foo
	run(t, "5a4f72702c70491229d3751c92ea8b403c4aacd7d9df54f79e27c439e8ee8f74", reflect.TypeOf((*Foo)(nil)).Elem())
}

// These types are used in TestCalculateMutuallyReferenced. Go doesn't support
// function-local mutually referencing types so they have to be defined at file
// scope.
type MutuallyReferenced1 interface {
	Foo() MutuallyReferenced2
}
type MutuallyReferenced2 interface {
	Bar() MutuallyReferenced1
}

func TestCalculateMutuallyReferenced(t *testing.T) {
	// The types are mutually referencing.
	run(t, "109f2b3b0bcd2050177c557664fca8b7174e2bb6b6b688939f8b15d08cdd5ec1", reflect.TypeOf((*MutuallyReferenced1)(nil)).Elem())
}

//

func run(t *testing.T, expected string, v reflect.Type) {
	if actual := Calculate(v); actual != expected {
		t.Fatalf("Calculate(%v) = %s; expected %s", v, actual, expected)
	}
}
<|endoftext|>


<commit_before>package v1

import (
	"net/http"

	"github.com/gitpods/gitpods/internal/api/v1/models"
	"github.com/gitpods/gitpods/internal/api/v1/restapi"
	"github.com/gitpods/gitpods/internal/api/v1/restapi/operations"
	"github.com/gitpods/gitpods/internal/api/v1/restapi/operations/users"
	"github.com/gitpods/gitpods/user"
	"github.com/go-openapi/loads"
	"github.com/go-openapi/runtime/middleware"
	"github.com/go-openapi/strfmt"
)

type API struct {
	Handler http.Handler
}

func New(us user.Service) (*API, error) {
	swaggerSpec, err := loads.Analyzed(restapi.SwaggerJSON, "")
	if err != nil {
		return nil, err
	}

	gitpodsAPI := operations.NewGitpodsAPI(swaggerSpec)

	gitpodsAPI.UsersListUsersHandler = ListUsersHandler(us)
	gitpodsAPI.UsersGetUserHandler = GetUserHandler(us)
	gitpodsAPI.UsersUpdateUserHandler = UpdateUserHandlerFunc(us)

	return &API{
		Handler: gitpodsAPI.Serve(nil),
	}, nil
}

func convertUser(u *user.User) *models.User {
	return &models.User{
		ID:        strfmt.UUID(u.ID),
		Email:     strfmt.Email(u.Email),
		Username:  &u.Username,
		Name:      u.Name,
		CreatedAt: strfmt.DateTime(u.Created),
		UpdatedAt: strfmt.DateTime(u.Updated),
	}
}

func ListUsersHandler(us user.Service) users.ListUsersHandlerFunc {
	return func(params users.ListUsersParams) middleware.Responder {
		list, err := us.FindAll(params.HTTPRequest.Context())
		if err != nil {
			return users.NewListUsersDefault(http.StatusInternalServerError)
		}

		var payload []*models.User

		for _, u := range list {
			payload = append(payload, convertUser(u))
		}

		return users.NewListUsersOK().WithPayload(payload)
	}
}

func GetUserHandler(us user.Service) users.GetUserHandlerFunc {
	return func(params users.GetUserParams) middleware.Responder {
		u, err := us.FindByUsername(params.HTTPRequest.Context(), params.Username)
		if err != nil {
			if err == user.NotFoundError {
				message := "user not found"
				return users.NewGetUserNotFound().WithPayload(&models.Error{
					Message: &message,
				})
			}
			return users.NewGetUserDefault(http.StatusInternalServerError)
		}

		return users.NewGetUserOK().WithPayload(convertUser(u))
	}
}

func UpdateUserHandlerFunc(us user.Service) users.UpdateUserHandlerFunc {
	return func(params users.UpdateUserParams) middleware.Responder {
		old, err := us.FindByUsername(params.HTTPRequest.Context(), params.Username)
		if err != nil {
			if err == user.NotFoundError {
				message := "user not found"
				return users.NewUpdateUserNotFound().WithPayload(&models.Error{
					Message: &message,
				})
			}
			return users.NewUpdateUserDefault(http.StatusInternalServerError)
		}

		old.Name = *params.Body.Name

		updated, err := us.Update(params.HTTPRequest.Context(), old)
		if err != nil {
			message := "updated user is invalid"
			return users.NewUpdateUserUnprocessableEntity().WithPayload(&models.ValidationError{
				Message: &message,
			})
		}

		return users.NewUpdateUserOK().WithPayload(convertUser(updated))
	}
}
<commit_msg>Name OpenAPI handlers consistently<commit_after>package v1

import (
	"net/http"

	"github.com/gitpods/gitpods/internal/api/v1/models"
	"github.com/gitpods/gitpods/internal/api/v1/restapi"
	"github.com/gitpods/gitpods/internal/api/v1/restapi/operations"
	"github.com/gitpods/gitpods/internal/api/v1/restapi/operations/users"
	"github.com/gitpods/gitpods/user"
	"github.com/go-openapi/loads"
	"github.com/go-openapi/runtime/middleware"
	"github.com/go-openapi/strfmt"
)

type API struct {
	Handler http.Handler
}

func New(us user.Service) (*API, error) {
	swaggerSpec, err := loads.Analyzed(restapi.SwaggerJSON, "")
	if err != nil {
		return nil, err
	}

	gitpodsAPI := operations.NewGitpodsAPI(swaggerSpec)

	gitpodsAPI.UsersListUsersHandler = ListUsersHandler(us)
	gitpodsAPI.UsersGetUserHandler = GetUserHandler(us)
	gitpodsAPI.UsersUpdateUserHandler = UpdateUserHandler(us)

	return &API{
		Handler: gitpodsAPI.Serve(nil),
	}, nil
}

func convertUser(u *user.User) *models.User {
	return &models.User{
		ID:        strfmt.UUID(u.ID),
		Email:     strfmt.Email(u.Email),
		Username:  &u.Username,
		Name:      u.Name,
		CreatedAt: strfmt.DateTime(u.Created),
		UpdatedAt: strfmt.DateTime(u.Updated),
	}
}

func ListUsersHandler(us user.Service) users.ListUsersHandlerFunc {
	return func(params users.ListUsersParams) middleware.Responder {
		list, err := us.FindAll(params.HTTPRequest.Context())
		if err != nil {
			return users.NewListUsersDefault(http.StatusInternalServerError)
		}

		var payload []*models.User

		for _, u := range list {
			payload = append(payload, convertUser(u))
		}

		return users.NewListUsersOK().WithPayload(payload)
	}
}

func GetUserHandler(us user.Service) users.GetUserHandlerFunc {
	return func(params users.GetUserParams) middleware.Responder {
		u, err := us.FindByUsername(params.HTTPRequest.Context(), params.Username)
		if err != nil {
			if err == user.NotFoundError {
				message := "user not found"
				return users.NewGetUserNotFound().WithPayload(&models.Error{
					Message: &message,
				})
			}
			return users.NewGetUserDefault(http.StatusInternalServerError)
		}

		return users.NewGetUserOK().WithPayload(convertUser(u))
	}
}

func UpdateUserHandler(us user.Service) users.UpdateUserHandlerFunc {
	return func(params users.UpdateUserParams) middleware.Responder {
		old, err := us.FindByUsername(params.HTTPRequest.Context(), params.Username)
		if err != nil {
			if err == user.NotFoundError {
				message := "user not found"
				return users.NewUpdateUserNotFound().WithPayload(&models.Error{
					Message: &message,
				})
			}
			return users.NewUpdateUserDefault(http.StatusInternalServerError)
		}

		old.Name = *params.Body.Name

		updated, err := us.Update(params.HTTPRequest.Context(), old)
		if err != nil {
			message := "updated user is invalid"
			return users.NewUpdateUserUnprocessableEntity().WithPayload(&models.ValidationError{
				Message: &message,
			})
		}

		return users.NewUpdateUserOK().WithPayload(convertUser(updated))
	}
}
<|endoftext|>


<commit_before>// Copyright © 2017 Circonus, Inc. <support@circonus.com>
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//

package check

import (
	"crypto/tls"
	stdlog "log"
	"net"
	"net/url"
	"path/filepath"
	"sync"
	"time"

	"github.com/circonus-labs/circonus-agent/internal/config"
	"github.com/circonus-labs/circonus-agent/internal/config/defaults"
	cgm "github.com/circonus-labs/circonus-gometrics"
	"github.com/circonus-labs/circonus-gometrics/api"
	"github.com/pkg/errors"
	"github.com/rs/zerolog"
	"github.com/rs/zerolog/log"
	"github.com/spf13/viper"
)

// metricStates holds the status of known metrics persisted to metrics.json in defaults.StatePath
type metricStates map[string]string

// Check exposes the check bundle management interface
type Check struct {
	statusActiveMetric    string
	statusActiveBroker    string
	brokerMaxResponseTime time.Duration
	brokerMaxRetries      int
	bundle                *api.CheckBundle
	client                API
	lastRefresh           time.Time
	logger                zerolog.Logger
	manage                bool
	metricStates          *metricStates
	metricStateUpdate     bool
	refreshTTL            time.Duration
	revConfig             *ReverseConfig
	stateFile             string
	statePath             string
	sync.Mutex
}

// Meta contains check id meta data
type Meta struct {
	BundleID string
	CheckIDs []string
}

// ReverseConfig contains the reverse configuration for the check
type ReverseConfig struct {
	BrokerAddr *net.TCPAddr
	BrokerID   string
	ReverseURL *url.URL
	TLSConfig  *tls.Config
}

// New returns a new check instance
func New(apiClient API) (*Check, error) {
	// NOTE: TBD, make broker max retries and response time configurable
	c := Check{
		brokerMaxResponseTime: 500 * time.Millisecond,
		brokerMaxRetries:      5,
		bundle:                nil,
		logger:                log.With().Str("pkg", "check").Logger(),
		manage:                false,
		metricStateUpdate:     false,
		refreshTTL:            time.Duration(0),
		statePath:             viper.GetString(config.KeyCheckMetricStateDir),
		statusActiveBroker:    "active",
		statusActiveMetric:    "active",
	}

	c.stateFile = filepath.Join(c.statePath, "metrics.json")

	isCreate := viper.GetBool(config.KeyCheckCreate)
	isManaged := viper.GetBool(config.KeyCheckEnableNewMetrics)
	isReverse := viper.GetBool(config.KeyReverse)
	cid := viper.GetString(config.KeyCheckBundleID)
	needCheck := false

	if isReverse || isManaged || (isCreate && cid == "") {
		needCheck = true
	}

	if !needCheck {
		c.logger.Info().Msg("check management disabled")
		return &c, nil // if we don't need a check, return a NOP object
	}

	if apiClient == nil {
		// create an API client
		cfg := &api.Config{
			TokenKey: viper.GetString(config.KeyAPITokenKey),
			TokenApp: viper.GetString(config.KeyAPITokenApp),
			URL:      viper.GetString(config.KeyAPIURL),
			Log:      stdlog.New(c.logger.With().Str("pkg", "check.api").Logger(), "", 0),
			Debug:    viper.GetBool(config.KeyDebugCGM),
		}
		client, err := api.New(cfg)
		if err != nil {
			return nil, errors.Wrap(err, "creating circonus api client")
		}
		apiClient = client
	}

	c.client = apiClient

	if isManaged {
		// preload the last known metric states so that states coming down
		// from the API when fetching the check bundle will be merged into
		// the known states since the fresh states have a higher precedence
		if ok, err := c.verifyStatePath(); ok {
			ms, err := c.loadState()
			if err != nil {
				c.logger.Error().Err(err).Msg("unable to load existing metric states, all metrics considered existing")
			} else {
				c.metricStates = ms
				c.logger.Debug().Interface("metric_states", len(*c.metricStates)).Msg("loaded metric states")
			}
		} else {
			if err != nil {
				c.logger.Error().Err(err).Msg("verify state path")
			}
			c.logger.Warn().Str("state_path", c.statePath).Msg("encountered state path issue(s), disabling check-enable-new-metrics")
			viper.Set(config.KeyCheckEnableNewMetrics, false)
			isManaged = false
			c.manage = false
		}
	}

	// fetch or create check bundle
	if err := c.setCheck(); err != nil {
		return nil, errors.Wrap(err, "unable to configure check")
	}

	// ensure a) the global check bundle id is set and b) it is set correctly to the
	// check bundle actually being used - need to do this even if the check was
	// created initially since user 'nobody' cannot create or update the configuration
	viper.Set(config.KeyCheckBundleID, c.bundle.CID)

	if !isManaged {
		return &c, nil
	}

	// check metrics refresh ttl
	ttl, err := time.ParseDuration(viper.GetString(config.KeyCheckMetricRefreshTTL))
	if err != nil {
		return nil, errors.Wrap(err, "parsing check metric refresh TTL")
	}
	if ttl == time.Duration(0) {
		ttl, err = time.ParseDuration(defaults.CheckMetricRefreshTTL)
		if err != nil {
			return nil, errors.Wrap(err, "parsing default check metric refresh TTL")
		}
	}

	c.refreshTTL = ttl
	c.manage = isManaged

	return &c, nil
}

// CheckMeta returns check bundle id and check ids
func (c *Check) CheckMeta() (*Meta, error) {
	if c.bundle != nil {
		return &Meta{
			BundleID: c.bundle.CID,
			CheckIDs: c.bundle.Checks,
		}, nil
	}
	return nil, errors.New("check not initialized")
}

// RefreshCheckConfig re-loads the check bundle using the API and reconfigures reverse (if needed)
func (c *Check) RefreshCheckConfig() error {
	c.Lock()
	defer c.Unlock()
	c.logger.Debug().Msg("refreshing check configuration using API")
	return c.setCheck()
}

// GetReverseConfig returns the reverse configuration to use for the broker
func (c *Check) GetReverseConfig() (*ReverseConfig, error) {
	c.Lock()
	defer c.Unlock()

	if c.revConfig == nil {
		return nil, errors.New("invalid reverse configuration")
	}
	return c.revConfig, nil
}

// EnableNewMetrics updates the check bundle enabling any new metrics
func (c *Check) EnableNewMetrics(m *cgm.Metrics) error {
	c.Lock()
	defer c.Unlock()

	if !c.manage {
		return nil
	}

	if !c.metricStateUpdate {
		// let first submission of metrics go through if no state file
		// use case where agent is replacing an existing nad install (check already exists)
		if c.metricStates == nil {
			c.logger.Debug().Msg("no existing metric states, triggering load")
			c.metricStateUpdate = true
			return nil
		}

		if time.Since(c.lastRefresh) > c.refreshTTL {
			c.logger.Debug().Dur("since_last", time.Since(c.lastRefresh)).Dur("ttl", c.refreshTTL).Msg("TTL triggering metric state refresh")
			c.metricStateUpdate = true
		}
	}

	if c.metricStateUpdate {
		err := c.setMetricStates(nil)
		if err != nil {
			return errors.Wrap(err, "updating metric states")
		}
	}

	c.logger.Debug().Msg("scanning for new metrics")

	newMetrics := map[string]api.CheckBundleMetric{}

	for mn, mv := range *m {
		if _, known := (*c.metricStates)[mn]; !known {
			newMetrics[mn] = c.configMetric(mn, mv)
			c.logger.Debug().Interface("metric", newMetrics[mn]).Interface("mv", mv).Msg("found new metric")
		}
	}

	if len(newMetrics) > 0 {
		if err := c.updateCheckBundleMetrics(&newMetrics); err != nil {
			c.logger.Error().Err(err).Msg("adding mew metrics to check bundle")
		}
	}

	return nil
}
<commit_msg>fix: typo in error message<commit_after>// Copyright © 2017 Circonus, Inc. <support@circonus.com>
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//

package check

import (
	"crypto/tls"
	stdlog "log"
	"net"
	"net/url"
	"path/filepath"
	"sync"
	"time"

	"github.com/circonus-labs/circonus-agent/internal/config"
	"github.com/circonus-labs/circonus-agent/internal/config/defaults"
	cgm "github.com/circonus-labs/circonus-gometrics"
	"github.com/circonus-labs/circonus-gometrics/api"
	"github.com/pkg/errors"
	"github.com/rs/zerolog"
	"github.com/rs/zerolog/log"
	"github.com/spf13/viper"
)

// metricStates holds the status of known metrics persisted to metrics.json in defaults.StatePath
type metricStates map[string]string

// Check exposes the check bundle management interface
type Check struct {
	statusActiveMetric    string
	statusActiveBroker    string
	brokerMaxResponseTime time.Duration
	brokerMaxRetries      int
	bundle                *api.CheckBundle
	client                API
	lastRefresh           time.Time
	logger                zerolog.Logger
	manage                bool
	metricStates          *metricStates
	metricStateUpdate     bool
	refreshTTL            time.Duration
	revConfig             *ReverseConfig
	stateFile             string
	statePath             string
	sync.Mutex
}

// Meta contains check id meta data
type Meta struct {
	BundleID string
	CheckIDs []string
}

// ReverseConfig contains the reverse configuration for the check
type ReverseConfig struct {
	BrokerAddr *net.TCPAddr
	BrokerID   string
	ReverseURL *url.URL
	TLSConfig  *tls.Config
}

// New returns a new check instance
func New(apiClient API) (*Check, error) {
	// NOTE: TBD, make broker max retries and response time configurable
	c := Check{
		brokerMaxResponseTime: 500 * time.Millisecond,
		brokerMaxRetries:      5,
		bundle:                nil,
		logger:                log.With().Str("pkg", "check").Logger(),
		manage:                false,
		metricStateUpdate:     false,
		refreshTTL:            time.Duration(0),
		statePath:             viper.GetString(config.KeyCheckMetricStateDir),
		statusActiveBroker:    "active",
		statusActiveMetric:    "active",
	}

	c.stateFile = filepath.Join(c.statePath, "metrics.json")

	isCreate := viper.GetBool(config.KeyCheckCreate)
	isManaged := viper.GetBool(config.KeyCheckEnableNewMetrics)
	isReverse := viper.GetBool(config.KeyReverse)
	cid := viper.GetString(config.KeyCheckBundleID)
	needCheck := false

	if isReverse || isManaged || (isCreate && cid == "") {
		needCheck = true
	}

	if !needCheck {
		c.logger.Info().Msg("check management disabled")
		return &c, nil // if we don't need a check, return a NOP object
	}

	if apiClient == nil {
		// create an API client
		cfg := &api.Config{
			TokenKey: viper.GetString(config.KeyAPITokenKey),
			TokenApp: viper.GetString(config.KeyAPITokenApp),
			URL:      viper.GetString(config.KeyAPIURL),
			Log:      stdlog.New(c.logger.With().Str("pkg", "check.api").Logger(), "", 0),
			Debug:    viper.GetBool(config.KeyDebugCGM),
		}
		client, err := api.New(cfg)
		if err != nil {
			return nil, errors.Wrap(err, "creating circonus api client")
		}
		apiClient = client
	}

	c.client = apiClient

	if isManaged {
		// preload the last known metric states so that states coming down
		// from the API when fetching the check bundle will be merged into
		// the known states since the fresh states have a higher precedence
		if ok, err := c.verifyStatePath(); ok {
			ms, err := c.loadState()
			if err != nil {
				c.logger.Error().Err(err).Msg("unable to load existing metric states, all metrics considered existing")
			} else {
				c.metricStates = ms
				c.logger.Debug().Interface("metric_states", len(*c.metricStates)).Msg("loaded metric states")
			}
		} else {
			if err != nil {
				c.logger.Error().Err(err).Msg("verify state path")
			}
			c.logger.Warn().Str("state_path", c.statePath).Msg("encountered state path issue(s), disabling check-enable-new-metrics")
			viper.Set(config.KeyCheckEnableNewMetrics, false)
			isManaged = false
			c.manage = false
		}
	}

	// fetch or create check bundle
	if err := c.setCheck(); err != nil {
		return nil, errors.Wrap(err, "unable to configure check")
	}

	// ensure a) the global check bundle id is set and b) it is set correctly to the
	// check bundle actually being used - need to do this even if the check was
	// created initially since user 'nobody' cannot create or update the configuration
	viper.Set(config.KeyCheckBundleID, c.bundle.CID)

	if !isManaged {
		return &c, nil
	}

	// check metrics refresh ttl
	ttl, err := time.ParseDuration(viper.GetString(config.KeyCheckMetricRefreshTTL))
	if err != nil {
		return nil, errors.Wrap(err, "parsing check metric refresh TTL")
	}
	if ttl == time.Duration(0) {
		ttl, err = time.ParseDuration(defaults.CheckMetricRefreshTTL)
		if err != nil {
			return nil, errors.Wrap(err, "parsing default check metric refresh TTL")
		}
	}

	c.refreshTTL = ttl
	c.manage = isManaged

	return &c, nil
}

// CheckMeta returns check bundle id and check ids
func (c *Check) CheckMeta() (*Meta, error) {
	if c.bundle != nil {
		return &Meta{
			BundleID: c.bundle.CID,
			CheckIDs: c.bundle.Checks,
		}, nil
	}
	return nil, errors.New("check not initialized")
}

// RefreshCheckConfig re-loads the check bundle using the API and reconfigures reverse (if needed)
func (c *Check) RefreshCheckConfig() error {
	c.Lock()
	defer c.Unlock()
	c.logger.Debug().Msg("refreshing check configuration using API")
	return c.setCheck()
}

// GetReverseConfig returns the reverse configuration to use for the broker
func (c *Check) GetReverseConfig() (*ReverseConfig, error) {
	c.Lock()
	defer c.Unlock()

	if c.revConfig == nil {
		return nil, errors.New("invalid reverse configuration")
	}
	return c.revConfig, nil
}

// EnableNewMetrics updates the check bundle enabling any new metrics
func (c *Check) EnableNewMetrics(m *cgm.Metrics) error {
	c.Lock()
	defer c.Unlock()

	if !c.manage {
		return nil
	}

	if !c.metricStateUpdate {
		// let first submission of metrics go through if no state file
		// use case where agent is replacing an existing nad install (check already exists)
		if c.metricStates == nil {
			c.logger.Debug().Msg("no existing metric states, triggering load")
			c.metricStateUpdate = true
			return nil
		}

		if time.Since(c.lastRefresh) > c.refreshTTL {
			c.logger.Debug().Dur("since_last", time.Since(c.lastRefresh)).Dur("ttl", c.refreshTTL).Msg("TTL triggering metric state refresh")
			c.metricStateUpdate = true
		}
	}

	if c.metricStateUpdate {
		err := c.setMetricStates(nil)
		if err != nil {
			return errors.Wrap(err, "updating metric states")
		}
	}

	c.logger.Debug().Msg("scanning for new metrics")

	newMetrics := map[string]api.CheckBundleMetric{}

	for mn, mv := range *m {
		if _, known := (*c.metricStates)[mn]; !known {
			newMetrics[mn] = c.configMetric(mn, mv)
			c.logger.Debug().Interface("metric", newMetrics[mn]).Interface("mv", mv).Msg("found new metric")
		}
	}

	if len(newMetrics) > 0 {
		if err := c.updateCheckBundleMetrics(&newMetrics); err != nil {
			c.logger.Error().Err(err).Msg("adding new metrics to check bundle")
		}
	}

	return nil
}
<|endoftext|>


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

// Package internal contains data used through x/pkgsite.
package internal

const (
	ExperimentCommandTOC                = "command-toc"
	ExperimentInsertSymbols             = "insert-symbols"
	ExperimentInteractivePlayground     = "interactive-playground"
	ExperimentNotAtLatest               = "not-at-latest"
	ExperimentRetractions               = "retractions"
	ExperimentSymbolHistoryVersionsPage = "symbol-history-versions-page"
	ExperimentUnitMetaWithLatest        = "unit-meta-with-latest"
)

// Experiments represents all of the active experiments in the codebase and
// a description of each experiment.
var Experiments = map[string]string{
	ExperimentCommandTOC:                "Enable the table of contents for command documention pages.",
	ExperimentInsertSymbols:             "Insert data into symbols, package_symbols, and documentation_symbols.",
	ExperimentInteractivePlayground:     "Enable interactive example playground on the unit page.",
	ExperimentNotAtLatest:               "Enable the display of a 'not at latest' badge.",
	ExperimentRetractions:               "Retrieve and display retraction and deprecation information.",
	ExperimentSymbolHistoryVersionsPage: "Show package API history on the versions page.",
	ExperimentUnitMetaWithLatest:        "Use latest-version information for GetUnitMeta.",
}

// Experiment holds data associated with an experimental feature for frontend
// or worker.
type Experiment struct {
	// This struct is used to decode dynamic config (see
	// internal/config/dynconfig). Make sure that changes to this struct are
	// coordinated with the deployment of config files.

	// Name is the name of the feature.
	Name string

	// Rollout is the percentage of requests enrolled in the experiment.
	Rollout uint

	// Description provides a description of the experiment.
	Description string
}
<commit_msg>internal/experiment: remove unused experiments<commit_after>// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package internal contains data used through x/pkgsite.
package internal

const (
	ExperimentInsertSymbols             = "insert-symbols"
	ExperimentNotAtLatest               = "not-at-latest"
	ExperimentRetractions               = "retractions"
	ExperimentSymbolHistoryVersionsPage = "symbol-history-versions-page"
	ExperimentUnitMetaWithLatest        = "unit-meta-with-latest"
)

// Experiments represents all of the active experiments in the codebase and
// a description of each experiment.
var Experiments = map[string]string{
	ExperimentInsertSymbols:             "Insert data into symbols, package_symbols, and documentation_symbols.",
	ExperimentNotAtLatest:               "Enable the display of a 'not at latest' badge.",
	ExperimentRetractions:               "Retrieve and display retraction and deprecation information.",
	ExperimentSymbolHistoryVersionsPage: "Show package API history on the versions page.",
	ExperimentUnitMetaWithLatest:        "Use latest-version information for GetUnitMeta.",
}

// Experiment holds data associated with an experimental feature for frontend
// or worker.
type Experiment struct {
	// This struct is used to decode dynamic config (see
	// internal/config/dynconfig). Make sure that changes to this struct are
	// coordinated with the deployment of config files.

	// Name is the name of the feature.
	Name string

	// Rollout is the percentage of requests enrolled in the experiment.
	Rollout uint

	// Description provides a description of the experiment.
	Description string
}
<|endoftext|>


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

// +build !js

package ui

import (
	"fmt"
	"runtime"
	"time"

	"github.com/go-gl/glfw/v3.1/glfw"
	"github.com/hajimehoshi/ebiten/internal/graphics/opengl"
)

func Now() int64 {
	return time.Now().UnixNano()
}

var currentUI *userInterface

func Init() *opengl.Context {
	runtime.LockOSThread()

	err := glfw.Init()
	if err != nil {
		panic(fmt.Sprintf("glfw.Init() fails: %v", err))
	}
	glfw.WindowHint(glfw.Visible, glfw.False)
	glfw.WindowHint(glfw.Resizable, glfw.False)
	glfw.WindowHint(glfw.ContextVersionMajor, 2)
	glfw.WindowHint(glfw.ContextVersionMinor, 1)

	// As start, create an window with temporary size to create OpenGL context thread.
	window, err := glfw.CreateWindow(16, 16, "", nil, nil)
	if err != nil {
		panic(err)
	}

	u := &userInterface{
		window: window,
	}
	ch := make(chan struct{})
	go func() {
		runtime.LockOSThread()
		u.window.MakeContextCurrent()
		glfw.SwapInterval(1)
		u.context = opengl.NewContext()
		close(ch)
		u.context.Loop()
	}()
	currentUI = u
	<-ch
	u.context.Init()

	return u.context
}

func Start(width, height, scale int, title string) (actualScale int, err error) {
	return currentUI.start(width, height, scale, title)
}

func Terminate() {
	currentUI.terminate()
}

func DoEvents() error {
	return currentUI.doEvents()
}

func IsClosed() bool {
	return currentUI.isClosed()
}

func SwapBuffers() {
	currentUI.swapBuffers()
}

func SetScreenSize(width, height int) (bool, int) {
	result := currentUI.setScreenSize(width, height, currentUI.scale)
	return result, currentUI.actualScale()
}

func SetScreenScale(scale int) (bool, int) {
	result := currentUI.setScreenSize(currentUI.width, currentUI.height, scale)
	return result, currentUI.actualScale()
}

type userInterface struct {
	window            *glfw.Window
	width             int
	height            int
	scale             int
	deviceScaleFactor float64
	framebufferScale  int
	context           *opengl.Context
}

func (u *userInterface) start(width, height, scale int, title string) (actualScale int, err error) {
	m := glfw.GetPrimaryMonitor()
	v := m.GetVideoMode()
	mw, _ := m.GetPhysicalSize()
	u.deviceScaleFactor = 1
	u.framebufferScale = 1
	// mw can be 0 on some environment like Linux VM
	if 0 < mw {
		dpi := float64(v.Width) * 25.4 / float64(mw)
		u.deviceScaleFactor = dpi / 96
	}

	u.setScreenSize(width, height, scale)
	u.window.SetTitle(title)
	u.window.Show()

	s := int(float64(scale) * u.deviceScaleFactor)
	x := (v.Width - width*s) / 2
	y := (v.Height - height*s) / 3
	u.window.SetPos(x, y)

	return u.actualScale(), nil
}

func (u *userInterface) actualScale() int {
	return int(float64(u.scale)*u.deviceScaleFactor) * u.framebufferScale
}

func (u *userInterface) pollEvents() error {
	glfw.PollEvents()
	return updateInput(u.window, u.scale)
}

func (u *userInterface) doEvents() error {
	if err := u.pollEvents(); err != nil {
		return err
	}
	for u.window.GetAttrib(glfw.Focused) == 0 {
		time.Sleep(time.Second / 60)
		if err := u.pollEvents(); err != nil {
			return err
		}
	}
	return nil
}

func (u *userInterface) terminate() {
	glfw.Terminate()
}

func (u *userInterface) isClosed() bool {
	return u.window.ShouldClose()
}

func (u *userInterface) swapBuffers() {
	// Call glFinish before glfwSwapBuffer to make sure
	// all OpenGL tasks are executed.
	u.context.Finish()
	u.context.RunOnContextThread(func() {
		u.window.SwapBuffers()
	})
}

func (u *userInterface) setScreenSize(width, height, scale int) bool {
	if u.width == width && u.height == height && u.scale == scale {
		return false
	}

	// To make sure the current existing framebuffers are rendered,
	// swap buffers here before SetSize is called.
	u.context.BindZeroFramebuffer()
	u.swapBuffers()

	ch := make(chan struct{})
	window := u.window
	window.SetFramebufferSizeCallback(func(w *glfw.Window, width, height int) {
		window.SetFramebufferSizeCallback(nil)
		close(ch)
	})
	s := int(float64(scale) * u.deviceScaleFactor)
	window.SetSize(width*s, height*s)

event:
	for {
		glfw.PollEvents()
		select {
		case <-ch:
			break event
		default:
		}
	}
	// This is usually 1, but sometimes more than 1 (e.g. Retina Mac)
	fw, _ := window.GetFramebufferSize()
	u.framebufferScale = fw / width / s
	u.width = width
	u.height = height
	u.scale = scale
	return true
}
<commit_msg>ui: Refactoring<commit_after>// Copyright 2015 Hajime Hoshi
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build !js

package ui

import (
	"fmt"
	"runtime"
	"time"

	"github.com/go-gl/glfw/v3.1/glfw"
	"github.com/hajimehoshi/ebiten/internal/graphics/opengl"
)

func Now() int64 {
	return time.Now().UnixNano()
}

var currentUI *userInterface

func Init() *opengl.Context {
	runtime.LockOSThread()

	err := glfw.Init()
	if err != nil {
		panic(fmt.Sprintf("glfw.Init() fails: %v", err))
	}
	glfw.WindowHint(glfw.Visible, glfw.False)
	glfw.WindowHint(glfw.Resizable, glfw.False)
	glfw.WindowHint(glfw.ContextVersionMajor, 2)
	glfw.WindowHint(glfw.ContextVersionMinor, 1)

	// As start, create an window with temporary size to create OpenGL context thread.
	window, err := glfw.CreateWindow(16, 16, "", nil, nil)
	if err != nil {
		panic(err)
	}

	u := &userInterface{
		window: window,
	}
	ch := make(chan struct{})
	go func() {
		runtime.LockOSThread()
		u.window.MakeContextCurrent()
		glfw.SwapInterval(1)
		u.context = opengl.NewContext()
		close(ch)
		u.context.Loop()
	}()
	currentUI = u
	<-ch
	u.context.Init()

	return u.context
}

func Start(width, height, scale int, title string) (framebufferScale int, err error) {
	return currentUI.start(width, height, scale, title)
}

func Terminate() {
	currentUI.terminate()
}

func DoEvents() error {
	return currentUI.doEvents()
}

func IsClosed() bool {
	return currentUI.isClosed()
}

func SwapBuffers() {
	currentUI.swapBuffers()
}

func SetScreenSize(width, height int) (bool, int) {
	result := currentUI.setScreenSize(width, height, currentUI.scale)
	return result, currentUI.framebufferScale()
}

func SetScreenScale(scale int) (bool, int) {
	result := currentUI.setScreenSize(currentUI.width, currentUI.height, scale)
	return result, currentUI.framebufferScale()
}

type userInterface struct {
	window            *glfw.Window
	width             int
	height            int
	scale             int
	deviceScaleFactor float64
	framebufferScale_ int
	context           *opengl.Context
}

func (u *userInterface) start(width, height, scale int, title string) (framebufferScale int, err error) {
	m := glfw.GetPrimaryMonitor()
	v := m.GetVideoMode()
	mw, _ := m.GetPhysicalSize()
	u.deviceScaleFactor = 1
	u.framebufferScale_ = 1
	// mw can be 0 on some environment like Linux VM
	if 0 < mw {
		dpi := float64(v.Width) * 25.4 / float64(mw)
		u.deviceScaleFactor = dpi / 96
	}

	u.setScreenSize(width, height, scale)
	u.window.SetTitle(title)
	u.window.Show()

	x := (v.Width - width*u.windowScale()) / 2
	y := (v.Height - height*u.windowScale()) / 3
	u.window.SetPos(x, y)

	return u.framebufferScale(), nil
}

func (u *userInterface) windowScale() int {
	return int(float64(u.scale) * u.deviceScaleFactor)
}

func (u *userInterface) framebufferScale() int {
	return u.windowScale() * u.framebufferScale_
}

func (u *userInterface) pollEvents() error {
	glfw.PollEvents()
	return updateInput(u.window, u.scale)
}

func (u *userInterface) doEvents() error {
	if err := u.pollEvents(); err != nil {
		return err
	}
	for u.window.GetAttrib(glfw.Focused) == 0 {
		time.Sleep(time.Second / 60)
		if err := u.pollEvents(); err != nil {
			return err
		}
	}
	return nil
}

func (u *userInterface) terminate() {
	glfw.Terminate()
}

func (u *userInterface) isClosed() bool {
	return u.window.ShouldClose()
}

func (u *userInterface) swapBuffers() {
	// Call glFinish before glfwSwapBuffer to make sure
	// all OpenGL tasks are executed.
	u.context.Finish()
	u.context.RunOnContextThread(func() {
		u.window.SwapBuffers()
	})
}

func (u *userInterface) setScreenSize(width, height, scale int) bool {
	if u.width == width && u.height == height && u.scale == scale {
		return false
	}
	// These properties should be set first since this affects windowScale().
	u.width = width
	u.height = height
	u.scale = scale

	// To make sure the current existing framebuffers are rendered,
	// swap buffers here before SetSize is called.
	u.context.BindZeroFramebuffer()
	u.swapBuffers()

	ch := make(chan struct{})
	window := u.window
	window.SetFramebufferSizeCallback(func(w *glfw.Window, width, height int) {
		window.SetFramebufferSizeCallback(nil)
		close(ch)
	})
	window.SetSize(width*u.windowScale(), height*u.windowScale())

event:
	for {
		glfw.PollEvents()
		select {
		case <-ch:
			break event
		default:
		}
	}
	// This is usually 1, but sometimes more than 1 (e.g. Retina Mac)
	fw, _ := window.GetFramebufferSize()
	u.framebufferScale_ = fw / width / u.windowScale()
	return true
}
<|endoftext|>


<commit_before>// Don't run these on Windows, because newline handling means they
// don't pass (TODO: report a Go bug?)

// +build !windows

package interp_test

import (
	"bytes"
	"fmt"
	"strings"

	"github.com/benhoyt/goawk/interp"
	"github.com/benhoyt/goawk/parser"
)

func Example() {
	input := bytes.NewReader([]byte("foo bar\n\nbaz buz"))
	err := interp.Exec("$0 { print $1 }", " ", input, nil)
	if err != nil {
		fmt.Println(err)
		return
	}
	// Output:
	// foo
	// baz
}

func Example_fieldsep() {
	// Use ',' as the field separator
	input := bytes.NewReader([]byte("1,2\n3,4"))
	err := interp.Exec("{ print $1, $2 }", ",", input, nil)
	if err != nil {
		fmt.Println(err)
		return
	}
	// Output:
	// 1 2
	// 3 4
}

func Example_program() {
	src := "{ print NR, tolower($0) }"
	input := "A\naB\nAbC"

	prog, err := parser.ParseProgram([]byte(src), nil)
	if err != nil {
		fmt.Println(err)
		return
	}
	config := &interp.Config{
		Stdin: bytes.NewReader([]byte(input)),
		Vars:  []string{"OFS", ":"},
	}
	_, err = interp.ExecProgram(prog, config)
	if err != nil {
		fmt.Println(err)
		return
	}
	// Output:
	// 1:a
	// 2:ab
	// 3:abc
}

func Example_funcs() {
	src := `BEGIN { print sum(), sum(1), sum(2, 3, 4), repeat("xyz", 3) }`

	parserConfig := &parser.ParserConfig{
		Funcs: map[string]interface{}{
			"sum": func(args ...float64) float64 {
				sum := 0.0
				for _, a := range args {
					sum += a
				}
				return sum
			},
			"repeat": strings.Repeat,
		},
	}
	prog, err := parser.ParseProgram([]byte(src), parserConfig)
	if err != nil {
		fmt.Println(err)
		return
	}
	interpConfig := &interp.Config{
		Funcs: parserConfig.Funcs,
	}
	_, err = interp.ExecProgram(prog, interpConfig)
	if err != nil {
		fmt.Println(err)
		return
	}
	// Output:
	// 0 1 9 xyzxyzxyz
}
<commit_msg>Run 'go fmt' using Go 1.17 (adds //go:build line)<commit_after>// Don't run these on Windows, because newline handling means they
// don't pass (TODO: report a Go bug?)

//go:build !windows
// +build !windows

package interp_test

import (
	"bytes"
	"fmt"
	"strings"

	"github.com/benhoyt/goawk/interp"
	"github.com/benhoyt/goawk/parser"
)

func Example() {
	input := bytes.NewReader([]byte("foo bar\n\nbaz buz"))
	err := interp.Exec("$0 { print $1 }", " ", input, nil)
	if err != nil {
		fmt.Println(err)
		return
	}
	// Output:
	// foo
	// baz
}

func Example_fieldsep() {
	// Use ',' as the field separator
	input := bytes.NewReader([]byte("1,2\n3,4"))
	err := interp.Exec("{ print $1, $2 }", ",", input, nil)
	if err != nil {
		fmt.Println(err)
		return
	}
	// Output:
	// 1 2
	// 3 4
}

func Example_program() {
	src := "{ print NR, tolower($0) }"
	input := "A\naB\nAbC"

	prog, err := parser.ParseProgram([]byte(src), nil)
	if err != nil {
		fmt.Println(err)
		return
	}
	config := &interp.Config{
		Stdin: bytes.NewReader([]byte(input)),
		Vars:  []string{"OFS", ":"},
	}
	_, err = interp.ExecProgram(prog, config)
	if err != nil {
		fmt.Println(err)
		return
	}
	// Output:
	// 1:a
	// 2:ab
	// 3:abc
}

func Example_funcs() {
	src := `BEGIN { print sum(), sum(1), sum(2, 3, 4), repeat("xyz", 3) }`

	parserConfig := &parser.ParserConfig{
		Funcs: map[string]interface{}{
			"sum": func(args ...float64) float64 {
				sum := 0.0
				for _, a := range args {
					sum += a
				}
				return sum
			},
			"repeat": strings.Repeat,
		},
	}
	prog, err := parser.ParseProgram([]byte(src), parserConfig)
	if err != nil {
		fmt.Println(err)
		return
	}
	interpConfig := &interp.Config{
		Funcs: parserConfig.Funcs,
	}
	_, err = interp.ExecProgram(prog, interpConfig)
	if err != nil {
		fmt.Println(err)
		return
	}
	// Output:
	// 0 1 9 xyzxyzxyz
}
<|endoftext|>


<commit_before>package gannoy

import (
	"io"
	"os/exec"
	"strings"
	"syscall"

	"github.com/coreos/go-semver/semver"
)

type Locker interface {
	ReadLock(uintptr, int64, int64) error
	WriteLock(uintptr, int64, int64) error
	UnLock(uintptr, int64, int64) error
}

func newLocker() Locker {
	bytes, err := exec.Command("uname", "-sr").Output()
	if err != nil {
		return Flock{}
	}
	kernel := strings.Split(strings.TrimRight(string(bytes), "\n"), " ")
	if len(kernel) != 2 {
		return Flock{}
	}
	if kernel[0] == "Linux" && !semver.New(kernel[1]).LessThan(*semver.New("3.15.0")) {
		return Fcntl{}
	}
	return Flock{}
}

// Only Linux and kernel version 3.15 or later.
// This depends on open file description lock (F_OFD_SETLKW).
type Fcntl struct {
}

const F_OFD_SETLKW = 38

func (f Fcntl) ReadLock(fd uintptr, start, len int64) error {
	return f.fcntl(syscall.F_RDLCK, fd, start, len)
}

func (f Fcntl) WriteLock(fd uintptr, start, len int64) error {
	return f.fcntl(syscall.F_WRLCK, fd, start, len)
}

func (f Fcntl) UnLock(fd uintptr, start, len int64) error {
	return f.fcntl(syscall.F_UNLCK, fd, start, len)
}

func (f Fcntl) fcntl(typ int16, fd uintptr, start, len int64) error {
	return syscall.FcntlFlock(fd, F_OFD_SETLKW, &syscall.Flock_t{
		Start:  start,
		Len:    len,
		Type:   typ,
		Whence: io.SeekStart,
	})
}

type Flock struct {
}

func (f Flock) ReadLock(fd uintptr, start, len int64) error {
	return f.flock(fd, syscall.LOCK_SH)
}

func (f Flock) WriteLock(fd uintptr, start, len int64) error {
	return f.flock(fd, syscall.LOCK_EX)
}

func (f Flock) UnLock(fd uintptr, start, len int64) error {
	return f.flock(fd, syscall.LOCK_UN)
}

func (f Flock) flock(fd uintptr, how int) error {
	return syscall.Flock(int(fd), how)
}
<commit_msg>split function to validate kernel version<commit_after>package gannoy

import (
	"io"
	"os/exec"
	"strings"
	"syscall"

	"github.com/coreos/go-semver/semver"
)

type Locker interface {
	ReadLock(uintptr, int64, int64) error
	WriteLock(uintptr, int64, int64) error
	UnLock(uintptr, int64, int64) error
}

func newLocker() Locker {
	bytes, err := exec.Command("uname", "-sr").Output()
	if err != nil {
		return Flock{}
	}
	if validateKernel(bytes) {
		return Fcntl{}
	}
	return Flock{}
}

func validateKernel(bytes []byte) bool {
	kernel := strings.Split(strings.TrimRight(string(bytes), "\n"), " ")
	if kernel[0] == "Linux" && !semver.New(kernel[1]).LessThan(*semver.New("3.15.0")) {
		return true
	}
	return false
}

// Only Linux and kernel version 3.15 or later.
// This depends on open file description lock (F_OFD_SETLKW).
type Fcntl struct {
}

const F_OFD_SETLKW = 38

func (f Fcntl) ReadLock(fd uintptr, start, len int64) error {
	return f.fcntl(syscall.F_RDLCK, fd, start, len)
}

func (f Fcntl) WriteLock(fd uintptr, start, len int64) error {
	return f.fcntl(syscall.F_WRLCK, fd, start, len)
}

func (f Fcntl) UnLock(fd uintptr, start, len int64) error {
	return f.fcntl(syscall.F_UNLCK, fd, start, len)
}

func (f Fcntl) fcntl(typ int16, fd uintptr, start, len int64) error {
	return syscall.FcntlFlock(fd, F_OFD_SETLKW, &syscall.Flock_t{
		Start:  start,
		Len:    len,
		Type:   typ,
		Whence: io.SeekStart,
	})
}

type Flock struct {
}

func (f Flock) ReadLock(fd uintptr, start, len int64) error {
	return f.flock(fd, syscall.LOCK_SH)
}

func (f Flock) WriteLock(fd uintptr, start, len int64) error {
	return f.flock(fd, syscall.LOCK_EX)
}

func (f Flock) UnLock(fd uintptr, start, len int64) error {
	return f.flock(fd, syscall.LOCK_UN)
}

func (f Flock) flock(fd uintptr, how int) error {
	return syscall.Flock(int(fd), how)
}
<|endoftext|>