<commit_before>package schedule

import (
	"math/rand"
	"time"
)

// RandomSchedule creates random commits over the past 365 days.
// These commits will be created in the location specified in the command.
func RandomSchedule(min, max) {

	days := getDaysSinceThisDayLastYear()
	for day := range days {
		rnd := getRandomNumber(min, max)
		// save into structure representing the commits over the last year
		// start worker, which will execute all commits using some sort of
		// commit generator
	}
}

// getRandomNumber returns a number in the range of min and max.
func getRandomNumber(min, max int) int {
	return rand.Intn(max-min) + min
}

// getDaysSinceThisDayLastYear returns a slice of days since todays date
// last year. E.g. 01.01.2015 starts at the 01.01.2014.
func getDaysSinceThisDayLastYear() []time.Time {
	daysSinceThisDayLastYear := make([]time.Time)
	now := time.Now()
	day := getDayLastYear(now)
	for day <= now {
		daysSinceThisDayLastYear.append(day)
		day = day.AddDate(0, 0, 1)
	}
	return daysSinceThisDayLastYear
}

// getDayLastYear returns the daya date minus one year, except the
// 29.02 will map to 28.02.
func getDayLastYear(day time.Time) time.Time {
	if isLeapDay(day) {
		// adjust for one year and one day
		return day.AddDate(-1, 0, -1)
	} else {
		return day.AddDate(-1, 0, 0)
	}
}

// isLeapDay checks if a given datetime is the 29.02 or not.
func isLeapDay(today time.Time) bool {
	_, month, day := today.Date()
	return (day == 29 && month == 2)
}
<commit_msg>Use enum for month.<commit_after>package schedule

import (
	"math/rand"
	"time"
)

// RandomSchedule creates random commits over the past 365 days.
// These commits will be created in the location specified in the command.
func RandomSchedule(min, max) {

	days := getDaysSinceThisDayLastYear()
	for day := range days {
		rnd := getRandomNumber(min, max)
		// save into structure representing the commits over the last year
		// start worker, which will execute all commits using some sort of
		// commit generator
	}
}

// getRandomNumber returns a number in the range of min and max.
func getRandomNumber(min, max int) int {
	return rand.Intn(max-min) + min
}

// getDaysSinceThisDayLastYear returns a slice of days since todays date
// last year. E.g. 01.01.2015 starts at the 01.01.2014.
func getDaysSinceThisDayLastYear() []time.Time {
	daysSinceThisDayLastYear := make([]time.Time)
	now := time.Now()
	day := getDayLastYear(now)
	for day <= now {
		daysSinceThisDayLastYear.append(day)
		day = day.AddDate(0, 0, 1)
	}
	return daysSinceThisDayLastYear
}

// getDayLastYear returns the daya date minus one year, except the
// 29.02 will map to 28.02.
func getDayLastYear(day time.Time) time.Time {
	if isLeapDay(day) {
		// adjust for one year and one day
		return day.AddDate(-1, 0, -1)
	} else {
		return day.AddDate(-1, 0, 0)
	}
}

// isLeapDay checks if a given datetime is the 29.02 or not.
func isLeapDay(today time.Time) bool {
	_, month, day := today.Date()
	return (day == 29 && month == time.February)
}
<|endoftext|>


<commit_before>package restic

import (
	"fmt"
	"os"
	"os/user"
	"strconv"
	"syscall"
	"time"

	"github.com/restic/restic/debug"
)

func (node *Node) OpenForReading() (*os.File, error) {
	return os.OpenFile(node.path, os.O_RDONLY|syscall.O_NOATIME, 0)
}

func (node *Node) fillExtra(path string, fi os.FileInfo) error {
	stat, ok := fi.Sys().(*syscall.Stat_t)
	if !ok {
		return nil
	}

	node.ChangeTime = time.Unix(stat.Ctim.Unix())
	node.AccessTime = time.Unix(stat.Atim.Unix())
	node.UID = stat.Uid
	node.GID = stat.Gid

	// TODO: cache uid lookup
	if u, err := user.LookupId(strconv.Itoa(int(stat.Uid))); err == nil {
		node.User = u.Username
	}

	// TODO: implement getgrnam() or use https://github.com/kless/osutil
	// if g, nil := user.LookupId(strconv.Itoa(int(stat.Uid))); err == nil {
	// 	node.User = u.Username
	// }

	node.Inode = stat.Ino

	var err error

	switch node.Type {
	case "file":
		node.Size = uint64(stat.Size)
		node.Links = uint64(stat.Nlink)
	case "dir":
		// nothing to do
	case "symlink":
		node.LinkTarget, err = os.Readlink(path)
	case "dev":
		node.Device = stat.Rdev
	case "chardev":
		node.Device = stat.Rdev
	case "fifo":
		// nothing to do
	case "socket":
		// nothing to do
	default:
		panic(fmt.Sprintf("invalid node type %q", node.Type))
	}

	return err
}

func (node *Node) createDevAt(path string) error {
	return syscall.Mknod(path, syscall.S_IFBLK|0600, int(node.Device))
}

func (node *Node) createCharDevAt(path string) error {
	return syscall.Mknod(path, syscall.S_IFCHR|0600, int(node.Device))
}

func (node *Node) createFifoAt(path string) error {
	return syscall.Mkfifo(path, 0600)
}

func (node *Node) isNewer(path string, fi os.FileInfo) bool {
	// if this node has a type other than "file", treat as if content has changed
	if node.Type != "file" {
		debug.Log("node.isNewer", "node %v is newer: not file", path)
		return true
	}

	// if the name or type has changed, this is surely something different
	tpe := nodeTypeFromFileInfo(path, fi)
	if node.Name != fi.Name() || node.Type != tpe {
		debug.Log("node.isNewer", "node %v is newer: name or type changed", path)
		return true
	}

	// collect extended stat
	stat := fi.Sys().(*syscall.Stat_t)

	changeTime := time.Unix(stat.Ctim.Unix())
	inode := stat.Ino
	size := uint64(stat.Size)

	// if timestamps or inodes differ, content has changed
	if node.ModTime != fi.ModTime() ||
		node.ChangeTime != changeTime ||
		node.Inode != inode ||
		node.Size != size {
		debug.Log("node.isNewer", "node %v is newer: timestamp, size or inode changed", path)
		return true
	}

	// otherwise the node is assumed to have the same content
	debug.Log("node.isNewer", "node %v is not newer", path)
	return false
}
<commit_msg>Fix O_NOATIME permission error<commit_after>package restic

import (
	"fmt"
	"os"
	"os/user"
	"strconv"
	"syscall"
	"time"

	"github.com/restic/restic/debug"
)

func (node *Node) OpenForReading() (*os.File, error) {
	file, err := os.OpenFile(node.path, os.O_RDONLY|syscall.O_NOATIME, 0)
	if os.IsPermission(err) {
		return os.OpenFile(node.path, os.O_RDONLY, 0)
	}
	return file, err
}

func (node *Node) fillExtra(path string, fi os.FileInfo) error {
	stat, ok := fi.Sys().(*syscall.Stat_t)
	if !ok {
		return nil
	}

	node.ChangeTime = time.Unix(stat.Ctim.Unix())
	node.AccessTime = time.Unix(stat.Atim.Unix())
	node.UID = stat.Uid
	node.GID = stat.Gid

	// TODO: cache uid lookup
	if u, err := user.LookupId(strconv.Itoa(int(stat.Uid))); err == nil {
		node.User = u.Username
	}

	// TODO: implement getgrnam() or use https://github.com/kless/osutil
	// if g, nil := user.LookupId(strconv.Itoa(int(stat.Uid))); err == nil {
	// 	node.User = u.Username
	// }

	node.Inode = stat.Ino

	var err error

	switch node.Type {
	case "file":
		node.Size = uint64(stat.Size)
		node.Links = uint64(stat.Nlink)
	case "dir":
		// nothing to do
	case "symlink":
		node.LinkTarget, err = os.Readlink(path)
	case "dev":
		node.Device = stat.Rdev
	case "chardev":
		node.Device = stat.Rdev
	case "fifo":
		// nothing to do
	case "socket":
		// nothing to do
	default:
		panic(fmt.Sprintf("invalid node type %q", node.Type))
	}

	return err
}

func (node *Node) createDevAt(path string) error {
	return syscall.Mknod(path, syscall.S_IFBLK|0600, int(node.Device))
}

func (node *Node) createCharDevAt(path string) error {
	return syscall.Mknod(path, syscall.S_IFCHR|0600, int(node.Device))
}

func (node *Node) createFifoAt(path string) error {
	return syscall.Mkfifo(path, 0600)
}

func (node *Node) isNewer(path string, fi os.FileInfo) bool {
	// if this node has a type other than "file", treat as if content has changed
	if node.Type != "file" {
		debug.Log("node.isNewer", "node %v is newer: not file", path)
		return true
	}

	// if the name or type has changed, this is surely something different
	tpe := nodeTypeFromFileInfo(path, fi)
	if node.Name != fi.Name() || node.Type != tpe {
		debug.Log("node.isNewer", "node %v is newer: name or type changed", path)
		return true
	}

	// collect extended stat
	stat := fi.Sys().(*syscall.Stat_t)

	changeTime := time.Unix(stat.Ctim.Unix())
	inode := stat.Ino
	size := uint64(stat.Size)

	// if timestamps or inodes differ, content has changed
	if node.ModTime != fi.ModTime() ||
		node.ChangeTime != changeTime ||
		node.Inode != inode ||
		node.Size != size {
		debug.Log("node.isNewer", "node %v is newer: timestamp, size or inode changed", path)
		return true
	}

	// otherwise the node is assumed to have the same content
	debug.Log("node.isNewer", "node %v is not newer", path)
	return false
}
<|endoftext|>


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

// GCE one-step image import tool
package main

import (
	"fmt"
	"log"
	"os"

	"github.com/GoogleCloudPlatform/compute-image-tools/cli_tools/common/utils/logging/service"
	"github.com/GoogleCloudPlatform/compute-image-tools/cli_tools/gce_onestep_image_import/onestep_importer"
)

func main() {
	log.SetPrefix("[import-image-from-cloud] ")
	// 1. Parse flags
	importerArgs, err := importer.NewOneStepImportArguments(os.Args[1:])
	if err != nil {
		log.Println(err)
		os.Exit(1)
	}

	importEntry := func() (service.Loggable, error) {
		return importer.Run(importerArgs)
	}

	// 2. Run Onestep Importer
	if err := service.RunWithServerLogging(
		service.OneStepImageImportAction, initLoggingParams(importerArgs), importerArgs.ProjectPtr, importEntry); err != nil {
		os.Exit(1)
	}
}

func initLoggingParams(args *importer.OneStepImportArguments) service.InputParams {
	return service.InputParams{
		OnestepImageImportParams: &service.OnestepImageImportParams{
			CommonParams: &service.CommonParams{
				ClientID:                args.ClientID,
				ClientVersion:           args.ClientVersion,
				Network:                 args.Network,
				Subnet:                  args.Subnet,
				Zone:                    args.Zone,
				Timeout:                 args.Timeout.String(),
				Project:                 *args.ProjectPtr,
				ObfuscatedProject:       service.Hash(*args.ProjectPtr),
				Labels:                  fmt.Sprintf("%v", args.Labels),
				ScratchBucketGcsPath:    args.ScratchBucketGcsPath,
				Oauth:                   args.Oauth,
				ComputeEndpointOverride: args.ComputeEndpoint,
				DisableGcsLogging:       args.GcsLogsDisabled,
				DisableCloudLogging:     args.CloudLogsDisabled,
				DisableStdoutLogging:    args.StdoutLogsDisabled,
			},
			ImageName:             args.ImageName,
			OS:                    args.OS,
			NoGuestEnvironment:    args.NoGuestEnvironment,
			Family:                args.Family,
			Description:           args.Description,
			NoExternalIP:          args.NoExternalIP,
			StorageLocation:       args.StorageLocation,
			ComputeServiceAccount: args.ComputeServiceAccount,
			AWSAMIID:              args.AWSAMIID,
			AWSAMIExportLocation:  args.AWSAMIExportLocation,
			AWSSourceAMIFilePath:  args.AWSSourceAMIFilePath,
		},
	}
}
<commit_msg>rename onestep import log prefix (#1522)<commit_after>//  Copyright 2020 Google Inc. All Rights Reserved.
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.

// GCE one-step image import tool
package main

import (
	"fmt"
	"log"
	"os"

	"github.com/GoogleCloudPlatform/compute-image-tools/cli_tools/common/utils/logging/service"
	"github.com/GoogleCloudPlatform/compute-image-tools/cli_tools/gce_onestep_image_import/onestep_importer"
)

func main() {
	log.SetPrefix("[import-image-from-external-cloud] ")
	// 1. Parse flags
	importerArgs, err := importer.NewOneStepImportArguments(os.Args[1:])
	if err != nil {
		log.Println(err)
		os.Exit(1)
	}

	importEntry := func() (service.Loggable, error) {
		return importer.Run(importerArgs)
	}

	// 2. Run Onestep Importer
	if err := service.RunWithServerLogging(
		service.OneStepImageImportAction, initLoggingParams(importerArgs), importerArgs.ProjectPtr, importEntry); err != nil {
		os.Exit(1)
	}
}

func initLoggingParams(args *importer.OneStepImportArguments) service.InputParams {
	return service.InputParams{
		OnestepImageImportParams: &service.OnestepImageImportParams{
			CommonParams: &service.CommonParams{
				ClientID:                args.ClientID,
				ClientVersion:           args.ClientVersion,
				Network:                 args.Network,
				Subnet:                  args.Subnet,
				Zone:                    args.Zone,
				Timeout:                 args.Timeout.String(),
				Project:                 *args.ProjectPtr,
				ObfuscatedProject:       service.Hash(*args.ProjectPtr),
				Labels:                  fmt.Sprintf("%v", args.Labels),
				ScratchBucketGcsPath:    args.ScratchBucketGcsPath,
				Oauth:                   args.Oauth,
				ComputeEndpointOverride: args.ComputeEndpoint,
				DisableGcsLogging:       args.GcsLogsDisabled,
				DisableCloudLogging:     args.CloudLogsDisabled,
				DisableStdoutLogging:    args.StdoutLogsDisabled,
			},
			ImageName:             args.ImageName,
			OS:                    args.OS,
			NoGuestEnvironment:    args.NoGuestEnvironment,
			Family:                args.Family,
			Description:           args.Description,
			NoExternalIP:          args.NoExternalIP,
			StorageLocation:       args.StorageLocation,
			ComputeServiceAccount: args.ComputeServiceAccount,
			AWSAMIID:              args.AWSAMIID,
			AWSAMIExportLocation:  args.AWSAMIExportLocation,
			AWSSourceAMIFilePath:  args.AWSSourceAMIFilePath,
		},
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package plugin

import (
	"bytes"
	"context"
	"fmt"
	"strings"
	"time"

	githubql "github.com/shurcooL/githubv4"
	"github.com/sirupsen/logrus"

	"k8s.io/test-infra/prow/github"
	"k8s.io/test-infra/prow/labels"
	"k8s.io/test-infra/prow/pluginhelp"
	"k8s.io/test-infra/prow/plugins"
)

const (
	// PluginName is the name of this plugin
	PluginName         = labels.NeedsRebase
	needsRebaseMessage = "PR needs rebase."
)

var sleep = time.Sleep

type githubClient interface {
	GetIssueLabels(org, repo string, number int) ([]github.Label, error)
	CreateComment(org, repo string, number int, comment string) error
	BotName() (string, error)
	AddLabel(org, repo string, number int, label string) error
	RemoveLabel(org, repo string, number int, label string) error
	IsMergeable(org, repo string, number int, sha string) (bool, error)
	DeleteStaleComments(org, repo string, number int, comments []github.IssueComment, isStale func(github.IssueComment) bool) error
	Query(context.Context, interface{}, map[string]interface{}) error
	GetPullRequest(org, repo string, number int) (*github.PullRequest, error)
}

type commentPruner interface {
	PruneComments(shouldPrune func(github.IssueComment) bool)
}

// HelpProvider constructs the PluginHelp for this plugin that takes into account enabled repositories.
// HelpProvider defines the type for function that construct the PluginHelp for plugins.
func HelpProvider(enabledRepos []string) (*pluginhelp.PluginHelp, error) {
	return &pluginhelp.PluginHelp{
			Description: `The needs-rebase plugin manages the '` + labels.NeedsRebase + `' label by removing it from Pull Requests that are mergeable and adding it to those which are not.
The plugin reacts to commit changes on PRs in addition to periodically scanning all open PRs for any changes to mergeability that could have resulted from changes in other PRs.`,
		},
		nil
}

// HandlePullRequestEvent handles a GitHub pull request event and adds or removes a
// "needs-rebase" label based on whether the GitHub api considers the PR mergeable
func HandlePullRequestEvent(log *logrus.Entry, ghc githubClient, pre *github.PullRequestEvent) error {
	if pre.Action != github.PullRequestActionOpened && pre.Action != github.PullRequestActionSynchronize && pre.Action != github.PullRequestActionReopened {
		return nil
	}

	return handle(log, ghc, &pre.PullRequest)
}

// HandleIssueCommentEvent handles a GitHub issue comment event and adds or removes a
// "needs-rebase" label if the issue is a PR based on whether the GitHub api considers
// the PR mergeable
func HandleIssueCommentEvent(log *logrus.Entry, ghc githubClient, ice *github.IssueCommentEvent) error {
	if !ice.Issue.IsPullRequest() {
		return nil
	}
	pr, err := ghc.GetPullRequest(ice.Repo.Owner.Login, ice.Repo.Name, ice.Issue.Number)
	if err != nil {
		return err
	}

	return handle(log, ghc, pr)
}

// handle handles a GitHub PR to determine if the "needs-rebase"
// label needs to be added or removed. It depends on GitHub mergeability check
// to decide the need for a rebase.
func handle(log *logrus.Entry, ghc githubClient, pr *github.PullRequest) error {
	if pr.Merged {
		return nil
	}
	// Before checking mergeability wait a few seconds to give github a chance to calculate it.
	// This initial delay prevents us from always wasting the first API token.
	sleep(time.Second * 5)

	org := pr.Base.Repo.Owner.Login
	repo := pr.Base.Repo.Name
	number := pr.Number
	sha := pr.Head.SHA

	mergeable, err := ghc.IsMergeable(org, repo, number, sha)
	if err != nil {
		return err
	}
	issueLabels, err := ghc.GetIssueLabels(org, repo, number)
	if err != nil {
		return err
	}
	hasLabel := github.HasLabel(labels.NeedsRebase, issueLabels)

	return takeAction(log, ghc, org, repo, number, pr.User.Login, hasLabel, mergeable)
}

// HandleAll checks all orgs and repos that enabled this plugin for open PRs to
// determine if the "needs-rebase" label needs to be added or removed. It
// depends on GitHub's mergeability check to decide the need for a rebase.
func HandleAll(log *logrus.Entry, ghc githubClient, config *plugins.Configuration) error {
	log.Info("Checking all PRs.")
	orgs, repos := config.EnabledReposForExternalPlugin(PluginName)
	if len(orgs) == 0 && len(repos) == 0 {
		log.Warnf("No repos have been configured for the %s plugin", PluginName)
		return nil
	}
	var buf bytes.Buffer
	fmt.Fprint(&buf, "archived: false is:pr is:open")
	for _, org := range orgs {
		fmt.Fprintf(&buf, " org:\"%s\"", org)
	}
	for _, repo := range repos {
		fmt.Fprintf(&buf, " repo:\"%s\"", repo)
	}
	prs, err := search(context.Background(), log, ghc, buf.String())
	if err != nil {
		return err
	}
	log.Infof("Considering %d PRs.", len(prs))

	for _, pr := range prs {
		// Skip PRs that are calculating mergeability. They will be updated by event or next loop.
		if pr.Mergeable == githubql.MergeableStateUnknown {
			continue
		}
		org := string(pr.Repository.Owner.Login)
		repo := string(pr.Repository.Name)
		num := int(pr.Number)
		l := log.WithFields(logrus.Fields{
			"org":  org,
			"repo": repo,
			"pr":   num,
		})
		hasLabel := false
		for _, label := range pr.Labels.Nodes {
			if label.Name == labels.NeedsRebase {
				hasLabel = true
				break
			}
		}
		err := takeAction(
			l,
			ghc,
			org,
			repo,
			num,
			string(pr.Author.Login),
			hasLabel,
			pr.Mergeable == githubql.MergeableStateMergeable,
		)
		if err != nil {
			l.WithError(err).Error("Error handling PR.")
		}
	}
	return nil
}

// takeAction adds or removes the "needs-rebase" label based on the current
// state of the PR (hasLabel and mergeable). It also handles adding and
// removing GitHub comments notifying the PR author that a rebase is needed.
func takeAction(log *logrus.Entry, ghc githubClient, org, repo string, num int, author string, hasLabel, mergeable bool) error {
	if !mergeable && !hasLabel {
		if err := ghc.AddLabel(org, repo, num, labels.NeedsRebase); err != nil {
			log.WithError(err).Errorf("Failed to add %q label.", labels.NeedsRebase)
		}
		msg := plugins.FormatSimpleResponse(author, needsRebaseMessage)
		return ghc.CreateComment(org, repo, num, msg)
	} else if mergeable && hasLabel {
		// remove label and prune comment
		if err := ghc.RemoveLabel(org, repo, num, labels.NeedsRebase); err != nil {
			log.WithError(err).Errorf("Failed to remove %q label.", labels.NeedsRebase)
		}
		botName, err := ghc.BotName()
		if err != nil {
			return err
		}
		return ghc.DeleteStaleComments(org, repo, num, nil, shouldPrune(botName))
	}
	return nil
}

func shouldPrune(botName string) func(github.IssueComment) bool {
	return func(ic github.IssueComment) bool {
		return github.NormLogin(botName) == github.NormLogin(ic.User.Login) &&
			strings.Contains(ic.Body, needsRebaseMessage)
	}
}

func search(ctx context.Context, log *logrus.Entry, ghc githubClient, q string) ([]pullRequest, error) {
	var ret []pullRequest
	vars := map[string]interface{}{
		"query":        githubql.String(q),
		"searchCursor": (*githubql.String)(nil),
	}
	var totalCost int
	var remaining int
	for {
		sq := searchQuery{}
		if err := ghc.Query(ctx, &sq, vars); err != nil {
			return nil, err
		}
		totalCost += int(sq.RateLimit.Cost)
		remaining = int(sq.RateLimit.Remaining)
		for _, n := range sq.Search.Nodes {
			ret = append(ret, n.PullRequest)
		}
		if !sq.Search.PageInfo.HasNextPage {
			break
		}
		vars["searchCursor"] = githubql.NewString(sq.Search.PageInfo.EndCursor)
	}
	log.Infof("Search for query \"%s\" cost %d point(s). %d remaining.", q, totalCost, remaining)
	return ret, nil
}

// TODO(spxtr): Add useful information for frontend stuff such as links.
type pullRequest struct {
	Number githubql.Int
	Author struct {
		Login githubql.String
	}
	Repository struct {
		Name  githubql.String
		Owner struct {
			Login githubql.String
		}
	}
	Labels struct {
		Nodes []struct {
			Name githubql.String
		}
	} `graphql:"labels(first:100)"`
	Mergeable githubql.MergeableState
}

type searchQuery struct {
	RateLimit struct {
		Cost      githubql.Int
		Remaining githubql.Int
	}
	Search struct {
		PageInfo struct {
			HasNextPage githubql.Boolean
			EndCursor   githubql.String
		}
		Nodes []struct {
			PullRequest pullRequest `graphql:"... on PullRequest"`
		}
	} `graphql:"search(type: ISSUE, first: 100, after: $searchCursor, query: $query)"`
}
<commit_msg>Fix typo in needs-rebase GitHub query<commit_after>/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package plugin

import (
	"bytes"
	"context"
	"fmt"
	"strings"
	"time"

	githubql "github.com/shurcooL/githubv4"
	"github.com/sirupsen/logrus"

	"k8s.io/test-infra/prow/github"
	"k8s.io/test-infra/prow/labels"
	"k8s.io/test-infra/prow/pluginhelp"
	"k8s.io/test-infra/prow/plugins"
)

const (
	// PluginName is the name of this plugin
	PluginName         = labels.NeedsRebase
	needsRebaseMessage = "PR needs rebase."
)

var sleep = time.Sleep

type githubClient interface {
	GetIssueLabels(org, repo string, number int) ([]github.Label, error)
	CreateComment(org, repo string, number int, comment string) error
	BotName() (string, error)
	AddLabel(org, repo string, number int, label string) error
	RemoveLabel(org, repo string, number int, label string) error
	IsMergeable(org, repo string, number int, sha string) (bool, error)
	DeleteStaleComments(org, repo string, number int, comments []github.IssueComment, isStale func(github.IssueComment) bool) error
	Query(context.Context, interface{}, map[string]interface{}) error
	GetPullRequest(org, repo string, number int) (*github.PullRequest, error)
}

type commentPruner interface {
	PruneComments(shouldPrune func(github.IssueComment) bool)
}

// HelpProvider constructs the PluginHelp for this plugin that takes into account enabled repositories.
// HelpProvider defines the type for function that construct the PluginHelp for plugins.
func HelpProvider(enabledRepos []string) (*pluginhelp.PluginHelp, error) {
	return &pluginhelp.PluginHelp{
			Description: `The needs-rebase plugin manages the '` + labels.NeedsRebase + `' label by removing it from Pull Requests that are mergeable and adding it to those which are not.
The plugin reacts to commit changes on PRs in addition to periodically scanning all open PRs for any changes to mergeability that could have resulted from changes in other PRs.`,
		},
		nil
}

// HandlePullRequestEvent handles a GitHub pull request event and adds or removes a
// "needs-rebase" label based on whether the GitHub api considers the PR mergeable
func HandlePullRequestEvent(log *logrus.Entry, ghc githubClient, pre *github.PullRequestEvent) error {
	if pre.Action != github.PullRequestActionOpened && pre.Action != github.PullRequestActionSynchronize && pre.Action != github.PullRequestActionReopened {
		return nil
	}

	return handle(log, ghc, &pre.PullRequest)
}

// HandleIssueCommentEvent handles a GitHub issue comment event and adds or removes a
// "needs-rebase" label if the issue is a PR based on whether the GitHub api considers
// the PR mergeable
func HandleIssueCommentEvent(log *logrus.Entry, ghc githubClient, ice *github.IssueCommentEvent) error {
	if !ice.Issue.IsPullRequest() {
		return nil
	}
	pr, err := ghc.GetPullRequest(ice.Repo.Owner.Login, ice.Repo.Name, ice.Issue.Number)
	if err != nil {
		return err
	}

	return handle(log, ghc, pr)
}

// handle handles a GitHub PR to determine if the "needs-rebase"
// label needs to be added or removed. It depends on GitHub mergeability check
// to decide the need for a rebase.
func handle(log *logrus.Entry, ghc githubClient, pr *github.PullRequest) error {
	if pr.Merged {
		return nil
	}
	// Before checking mergeability wait a few seconds to give github a chance to calculate it.
	// This initial delay prevents us from always wasting the first API token.
	sleep(time.Second * 5)

	org := pr.Base.Repo.Owner.Login
	repo := pr.Base.Repo.Name
	number := pr.Number
	sha := pr.Head.SHA

	mergeable, err := ghc.IsMergeable(org, repo, number, sha)
	if err != nil {
		return err
	}
	issueLabels, err := ghc.GetIssueLabels(org, repo, number)
	if err != nil {
		return err
	}
	hasLabel := github.HasLabel(labels.NeedsRebase, issueLabels)

	return takeAction(log, ghc, org, repo, number, pr.User.Login, hasLabel, mergeable)
}

// HandleAll checks all orgs and repos that enabled this plugin for open PRs to
// determine if the "needs-rebase" label needs to be added or removed. It
// depends on GitHub's mergeability check to decide the need for a rebase.
func HandleAll(log *logrus.Entry, ghc githubClient, config *plugins.Configuration) error {
	log.Info("Checking all PRs.")
	orgs, repos := config.EnabledReposForExternalPlugin(PluginName)
	if len(orgs) == 0 && len(repos) == 0 {
		log.Warnf("No repos have been configured for the %s plugin", PluginName)
		return nil
	}
	var buf bytes.Buffer
	fmt.Fprint(&buf, "archived:false is:pr is:open")
	for _, org := range orgs {
		fmt.Fprintf(&buf, " org:\"%s\"", org)
	}
	for _, repo := range repos {
		fmt.Fprintf(&buf, " repo:\"%s\"", repo)
	}
	prs, err := search(context.Background(), log, ghc, buf.String())
	if err != nil {
		return err
	}
	log.Infof("Considering %d PRs.", len(prs))

	for _, pr := range prs {
		// Skip PRs that are calculating mergeability. They will be updated by event or next loop.
		if pr.Mergeable == githubql.MergeableStateUnknown {
			continue
		}
		org := string(pr.Repository.Owner.Login)
		repo := string(pr.Repository.Name)
		num := int(pr.Number)
		l := log.WithFields(logrus.Fields{
			"org":  org,
			"repo": repo,
			"pr":   num,
		})
		hasLabel := false
		for _, label := range pr.Labels.Nodes {
			if label.Name == labels.NeedsRebase {
				hasLabel = true
				break
			}
		}
		err := takeAction(
			l,
			ghc,
			org,
			repo,
			num,
			string(pr.Author.Login),
			hasLabel,
			pr.Mergeable == githubql.MergeableStateMergeable,
		)
		if err != nil {
			l.WithError(err).Error("Error handling PR.")
		}
	}
	return nil
}

// takeAction adds or removes the "needs-rebase" label based on the current
// state of the PR (hasLabel and mergeable). It also handles adding and
// removing GitHub comments notifying the PR author that a rebase is needed.
func takeAction(log *logrus.Entry, ghc githubClient, org, repo string, num int, author string, hasLabel, mergeable bool) error {
	if !mergeable && !hasLabel {
		if err := ghc.AddLabel(org, repo, num, labels.NeedsRebase); err != nil {
			log.WithError(err).Errorf("Failed to add %q label.", labels.NeedsRebase)
		}
		msg := plugins.FormatSimpleResponse(author, needsRebaseMessage)
		return ghc.CreateComment(org, repo, num, msg)
	} else if mergeable && hasLabel {
		// remove label and prune comment
		if err := ghc.RemoveLabel(org, repo, num, labels.NeedsRebase); err != nil {
			log.WithError(err).Errorf("Failed to remove %q label.", labels.NeedsRebase)
		}
		botName, err := ghc.BotName()
		if err != nil {
			return err
		}
		return ghc.DeleteStaleComments(org, repo, num, nil, shouldPrune(botName))
	}
	return nil
}

func shouldPrune(botName string) func(github.IssueComment) bool {
	return func(ic github.IssueComment) bool {
		return github.NormLogin(botName) == github.NormLogin(ic.User.Login) &&
			strings.Contains(ic.Body, needsRebaseMessage)
	}
}

func search(ctx context.Context, log *logrus.Entry, ghc githubClient, q string) ([]pullRequest, error) {
	var ret []pullRequest
	vars := map[string]interface{}{
		"query":        githubql.String(q),
		"searchCursor": (*githubql.String)(nil),
	}
	var totalCost int
	var remaining int
	for {
		sq := searchQuery{}
		if err := ghc.Query(ctx, &sq, vars); err != nil {
			return nil, err
		}
		totalCost += int(sq.RateLimit.Cost)
		remaining = int(sq.RateLimit.Remaining)
		for _, n := range sq.Search.Nodes {
			ret = append(ret, n.PullRequest)
		}
		if !sq.Search.PageInfo.HasNextPage {
			break
		}
		vars["searchCursor"] = githubql.NewString(sq.Search.PageInfo.EndCursor)
	}
	log.Infof("Search for query \"%s\" cost %d point(s). %d remaining.", q, totalCost, remaining)
	return ret, nil
}

// TODO(spxtr): Add useful information for frontend stuff such as links.
type pullRequest struct {
	Number githubql.Int
	Author struct {
		Login githubql.String
	}
	Repository struct {
		Name  githubql.String
		Owner struct {
			Login githubql.String
		}
	}
	Labels struct {
		Nodes []struct {
			Name githubql.String
		}
	} `graphql:"labels(first:100)"`
	Mergeable githubql.MergeableState
}

type searchQuery struct {
	RateLimit struct {
		Cost      githubql.Int
		Remaining githubql.Int
	}
	Search struct {
		PageInfo struct {
			HasNextPage githubql.Boolean
			EndCursor   githubql.String
		}
		Nodes []struct {
			PullRequest pullRequest `graphql:"... on PullRequest"`
		}
	} `graphql:"search(type: ISSUE, first: 100, after: $searchCursor, query: $query)"`
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"

	"github.com/pgombola/knowmads/client"
)

func main() {
	nodes := client.Status()
	for _, node := range nodes {
		fmt.Printf("ID=%v;Name=%v;Drain=%v\n", node.ID, node.Name, node.Drain)
	}
}
<commit_msg>Change name of package to 'gomad'<commit_after>package main

import (
	"fmt"

	"github.com/pgombola/gomad/client"
)

func main() {
	nodes := client.Status()
	for _, node := range nodes {
		fmt.Printf("ID=%v;Name=%v;Drain=%v\n", node.ID, node.Name, node.Drain)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"

	"github.com/pgombola/gomad/client"
)

func main() {
	nodes := client.Status()
	for _, node := range nodes {
		fmt.Printf("ID=%v;Name=%v;Drain=%v\n", node.ID, node.Name, node.Drain)
	}
}
<commit_msg>Added flags for nomad server and for getting node status<commit_after>package main

import (
	"flag"
	"fmt"
	"os"

	"github.com/pgombola/gomad/client"
)

var (
	nomad string
	hosts bool
)

func init() {
	flag.StringVar(&nomad, "nomad", "", "Host address and port of nomad server")
	flag.BoolVar(&hosts, "hosts", false, "Retrieve the status of the hosts")
}

func main() {
	flag.Parse()

	if nomad == "" {
		fmt.Println("nomad flag must be set.")
		os.Exit(-1)
	}

	if hosts {
		nodes := client.Status("http://" + nomad)
		for _, node := range nodes {
			fmt.Printf("ID=%v;Name=%v;Drain=%v\n", node.ID, node.Name, node.Drain)
		}
	}
}
<|endoftext|>


<commit_before>package nodos

import (
	"github.com/mattn/go-colorable"
	"io"
)

func CoInitializeEx(res uintptr, opt uintptr) {
	coInitializeEx(res, opt)
}

func CoUninitialize() {
	coUninitialize()
}

func IsEscapeSequenceAvailable() bool {
	return true
}

func GetConsole() io.Writer {
	return colorable.NewColorableStdout()
}
<commit_msg>Remove unused function: "nodos".IsEscapeSequenceAvailable<commit_after>package nodos

import (
	"github.com/mattn/go-colorable"
	"io"
)

func CoInitializeEx(res uintptr, opt uintptr) {
	coInitializeEx(res, opt)
}

func CoUninitialize() {
	coUninitialize()
}

func GetConsole() io.Writer {
	return colorable.NewColorableStdout()
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"flag"
	"fmt"
	"log"
	"math/rand"
	"net/http"
	_ "net/http/pprof"
	"os"
	"sync"
	"sync/atomic"
	"time"

	"golang.org/x/net/trace"

	"github.com/bmatsuo/lmdb-go/lmdb"
	"github.com/boltdb/bolt"
	"github.com/dgraph-io/badger"
	"github.com/dgraph-io/badger-bench/store"
	"github.com/dgraph-io/badger/options"
	"github.com/dgraph-io/badger/y"
	"github.com/paulbellamy/ratecounter"
	"github.com/pkg/profile"
)

const mil float64 = 1000000

var (
	which     = flag.String("kv", "badger", "Which KV store to use. Options: badger, rocksdb, lmdb, bolt")
	numKeys   = flag.Float64("keys_mil", 10.0, "How many million keys to write.")
	valueSize = flag.Int("valsz", 128, "Value size in bytes.")
	dir       = flag.String("dir", "", "Base dir for writes.")
	mode      = flag.String("profile.mode", "", "enable profiling mode, one of [cpu, mem, mutex, block]")
)

func fillEntry(e *badger.Entry) {
	k := rand.Int() % int(*numKeys*mil)
	key := fmt.Sprintf("vsz=%05d-k=%010d", *valueSize, k) // 22 bytes.
	if cap(e.Key) < len(key) {
		e.Key = make([]byte, 2*len(key))
	}
	e.Key = e.Key[:len(key)]
	copy(e.Key, key)

	rand.Read(e.Value)
	e.Meta = 0
}

var bdb *badger.KV
var rdb *store.Store
var lmdbEnv *lmdb.Env
var lmdbDBI lmdb.DBI
var boltdb *bolt.DB

func writeBatch(entries []*badger.Entry) int {
	for _, e := range entries {
		fillEntry(e)
	}

	if bdb != nil {
		bdb.BatchSet(entries)
		for _, e := range entries {
			y.Check(e.Error)
		}
	}

	if rdb != nil {
		rb := rdb.NewWriteBatch()
		defer rb.Destroy()

		for _, e := range entries {
			rb.Put(e.Key, e.Value)
		}
		y.Check(rdb.WriteBatch(rb))
	}

	if lmdbEnv != nil {
		err := lmdbEnv.Update(func(txn *lmdb.Txn) error {
			for _, e := range entries {
				err := txn.Put(lmdbDBI, e.Key, e.Value, 0)
				if err != nil {
					return err
				}
			}
			return nil
		})
		y.Check(err)

	}

	if boltdb != nil {
		err := boltdb.Batch(func(txn *bolt.Tx) error {
			boltBkt := txn.Bucket([]byte("bench"))
			y.AssertTrue(boltBkt != nil)
			for _, e := range entries {
				if err := boltBkt.Put(e.Key, e.Value); err != nil {
					return err
				}
			}
			return nil
		})
		y.Check(err)
	}

	return len(entries)
}

func humanize(n int64) string {
	if n >= 1000000 {
		return fmt.Sprintf("%6.2fM", float64(n)/1000000.0)
	}
	if n >= 1000 {
		return fmt.Sprintf("%6.2fK", float64(n)/1000.0)
	}
	return fmt.Sprintf("%5.2f", float64(n))
}

func main() {
	flag.Parse()
	switch *mode {
	case "cpu":
		defer profile.Start(profile.CPUProfile).Stop()
	case "mem":
		defer profile.Start(profile.MemProfile).Stop()
	case "mutex":
		defer profile.Start(profile.MutexProfile).Stop()
	case "block":
		defer profile.Start(profile.BlockProfile).Stop()
	default:
		// do nothing
	}

	trace.AuthRequest = func(req *http.Request) (any, sensitive bool) {
		return true, true
	}

	nw := *numKeys * mil
	fmt.Printf("TOTAL KEYS TO WRITE: %s\n", humanize(int64(nw)))
	opt := badger.DefaultOptions
	opt.TableLoadingMode = options.MemoryMap
	opt.ValueLogLoadingMode = options.MemoryMap
	opt.ValueGCRunInterval = 10 * time.Hour
	opt.Dir = *dir + "/badger"
	opt.ValueDir = opt.Dir
	opt.SyncWrites = false

	var err error

	var init bool

	if *which == "badger" {
		init = true
		fmt.Println("Init Badger")
		y.Check(os.RemoveAll(*dir + "/badger"))
		os.MkdirAll(*dir+"/badger", 0777)
		bdb, err = badger.NewKV(&opt)
		if err != nil {
			log.Fatalf("while opening badger: %v", err)
		}
	} else if *which == "rocksdb" {
		init = true
		fmt.Println("Init Rocks")
		os.RemoveAll(*dir + "/rocks")
		os.MkdirAll(*dir+"/rocks", 0777)
		rdb, err = store.NewStore(*dir + "/rocks")
		y.Check(err)
	} else if *which == "lmdb" {
		init = true
		fmt.Println("Init lmdb")
		os.RemoveAll(*dir + "/lmdb")
		os.MkdirAll(*dir+"/lmdb", 0777)

		lmdbEnv, err = lmdb.NewEnv()
		y.Check(err)
		err = lmdbEnv.SetMaxDBs(1)
		y.Check(err)
		err = lmdbEnv.SetMapSize(1 << 38) // ~273Gb
		y.Check(err)

		err = lmdbEnv.Open(*dir+"/lmdb", lmdb.NoSync, 0777)
		y.Check(err)

		// Acquire handle
		err := lmdbEnv.Update(func(txn *lmdb.Txn) error {
			var err error
			lmdbDBI, err = txn.CreateDBI("bench")
			return err
		})
		y.Check(err)
	} else if *which == "bolt" {
		init = true
		fmt.Println("Init BoltDB")
		os.RemoveAll(*dir + "/bolt")
		os.MkdirAll(*dir+"/bolt", 0777)
		boltdb, err = bolt.Open(*dir+"/bolt/bolt.db", 0777, bolt.DefaultOptions)
		y.Check(err)
		boltdb.NoSync = true // Set this to speed up writes
		err = boltdb.Update(func(txn *bolt.Tx) error {
			var err error
			_, err = txn.CreateBucketIfNotExists([]byte("bench"))
			return err
		})
		y.Check(err)

	} else {
		log.Fatalf("Invalid value for option kv: '%s'", *which)
	}

	if !init {
		log.Fatalf("Invalid arguments. Unable to init any store.")
	}

	rc := ratecounter.NewRateCounter(time.Minute)
	var counter int64
	ctx, cancel := context.WithCancel(context.Background())
	go func() {
		var count int64
		t := time.NewTicker(time.Second)
		defer t.Stop()
		for {
			select {
			case <-t.C:
				fmt.Printf("[%04d] Write key rate per minute: %s. Total: %s\n",
					count,
					humanize(rc.Rate()),
					humanize(atomic.LoadInt64(&counter)))
				count++
			case <-ctx.Done():
				return
			}
		}
	}()
	go func() {
		if err := http.ListenAndServe("0.0.0.0:8081", nil); err != nil {
			log.Fatalf("While opening http. Error: %v", err)
		}
	}()

	N := 12
	var wg sync.WaitGroup
	for i := 0; i < N; i++ {
		wg.Add(1)
		go func(proc int) {
			entries := make([]*badger.Entry, 1000)
			for i := 0; i < len(entries); i++ {
				e := new(badger.Entry)
				e.Key = make([]byte, 22)
				e.Value = make([]byte, *valueSize)
				entries[i] = e
			}

			var written float64
			for written < nw/float64(N) {
				wrote := float64(writeBatch(entries))

				wi := int64(wrote)
				atomic.AddInt64(&counter, wi)
				rc.Incr(wi)

				written += wrote
			}
			wg.Done()
		}(i)
	}
	// 	wg.Add(1) // Block
	wg.Wait()
	cancel()

	if bdb != nil {
		fmt.Println("closing badger")
		bdb.Close()
	}

	if rdb != nil {
		fmt.Println("closing rocks")
		rdb.Close()
	}

	if lmdbEnv != nil {

		fmt.Println("closing lmdb")
		lmdbEnv.CloseDBI(lmdbDBI)
		lmdbEnv.Close()
	}

	if boltdb != nil {
		fmt.Println("closing bolt")
		boltdb.Close()
	}

	fmt.Printf("\nWROTE %d KEYS\n", atomic.LoadInt64(&counter))
}
<commit_msg>Remove non-existent option<commit_after>package main

import (
	"context"
	"flag"
	"fmt"
	"log"
	"math/rand"
	"net/http"
	_ "net/http/pprof"
	"os"
	"sync"
	"sync/atomic"
	"time"

	"golang.org/x/net/trace"

	"github.com/bmatsuo/lmdb-go/lmdb"
	"github.com/boltdb/bolt"
	"github.com/dgraph-io/badger"
	"github.com/dgraph-io/badger-bench/store"
	"github.com/dgraph-io/badger/options"
	"github.com/dgraph-io/badger/y"
	"github.com/paulbellamy/ratecounter"
	"github.com/pkg/profile"
)

const mil float64 = 1000000

var (
	which     = flag.String("kv", "badger", "Which KV store to use. Options: badger, rocksdb, lmdb, bolt")
	numKeys   = flag.Float64("keys_mil", 10.0, "How many million keys to write.")
	valueSize = flag.Int("valsz", 128, "Value size in bytes.")
	dir       = flag.String("dir", "", "Base dir for writes.")
	mode      = flag.String("profile.mode", "", "enable profiling mode, one of [cpu, mem, mutex, block]")
)

func fillEntry(e *badger.Entry) {
	k := rand.Int() % int(*numKeys*mil)
	key := fmt.Sprintf("vsz=%05d-k=%010d", *valueSize, k) // 22 bytes.
	if cap(e.Key) < len(key) {
		e.Key = make([]byte, 2*len(key))
	}
	e.Key = e.Key[:len(key)]
	copy(e.Key, key)

	rand.Read(e.Value)
	e.Meta = 0
}

var bdb *badger.KV
var rdb *store.Store
var lmdbEnv *lmdb.Env
var lmdbDBI lmdb.DBI
var boltdb *bolt.DB

func writeBatch(entries []*badger.Entry) int {
	for _, e := range entries {
		fillEntry(e)
	}

	if bdb != nil {
		bdb.BatchSet(entries)
		for _, e := range entries {
			y.Check(e.Error)
		}
	}

	if rdb != nil {
		rb := rdb.NewWriteBatch()
		defer rb.Destroy()

		for _, e := range entries {
			rb.Put(e.Key, e.Value)
		}
		y.Check(rdb.WriteBatch(rb))
	}

	if lmdbEnv != nil {
		err := lmdbEnv.Update(func(txn *lmdb.Txn) error {
			for _, e := range entries {
				err := txn.Put(lmdbDBI, e.Key, e.Value, 0)
				if err != nil {
					return err
				}
			}
			return nil
		})
		y.Check(err)

	}

	if boltdb != nil {
		err := boltdb.Batch(func(txn *bolt.Tx) error {
			boltBkt := txn.Bucket([]byte("bench"))
			y.AssertTrue(boltBkt != nil)
			for _, e := range entries {
				if err := boltBkt.Put(e.Key, e.Value); err != nil {
					return err
				}
			}
			return nil
		})
		y.Check(err)
	}

	return len(entries)
}

func humanize(n int64) string {
	if n >= 1000000 {
		return fmt.Sprintf("%6.2fM", float64(n)/1000000.0)
	}
	if n >= 1000 {
		return fmt.Sprintf("%6.2fK", float64(n)/1000.0)
	}
	return fmt.Sprintf("%5.2f", float64(n))
}

func main() {
	flag.Parse()
	switch *mode {
	case "cpu":
		defer profile.Start(profile.CPUProfile).Stop()
	case "mem":
		defer profile.Start(profile.MemProfile).Stop()
	case "mutex":
		defer profile.Start(profile.MutexProfile).Stop()
	case "block":
		defer profile.Start(profile.BlockProfile).Stop()
	default:
		// do nothing
	}

	trace.AuthRequest = func(req *http.Request) (any, sensitive bool) {
		return true, true
	}

	nw := *numKeys * mil
	fmt.Printf("TOTAL KEYS TO WRITE: %s\n", humanize(int64(nw)))
	opt := badger.DefaultOptions
	opt.TableLoadingMode = options.MemoryMap
	opt.ValueGCRunInterval = 10 * time.Hour
	opt.Dir = *dir + "/badger"
	opt.ValueDir = opt.Dir
	opt.SyncWrites = false

	var err error

	var init bool

	if *which == "badger" {
		init = true
		fmt.Println("Init Badger")
		y.Check(os.RemoveAll(*dir + "/badger"))
		os.MkdirAll(*dir+"/badger", 0777)
		bdb, err = badger.NewKV(&opt)
		if err != nil {
			log.Fatalf("while opening badger: %v", err)
		}
	} else if *which == "rocksdb" {
		init = true
		fmt.Println("Init Rocks")
		os.RemoveAll(*dir + "/rocks")
		os.MkdirAll(*dir+"/rocks", 0777)
		rdb, err = store.NewStore(*dir + "/rocks")
		y.Check(err)
	} else if *which == "lmdb" {
		init = true
		fmt.Println("Init lmdb")
		os.RemoveAll(*dir + "/lmdb")
		os.MkdirAll(*dir+"/lmdb", 0777)

		lmdbEnv, err = lmdb.NewEnv()
		y.Check(err)
		err = lmdbEnv.SetMaxDBs(1)
		y.Check(err)
		err = lmdbEnv.SetMapSize(1 << 38) // ~273Gb
		y.Check(err)

		err = lmdbEnv.Open(*dir+"/lmdb", lmdb.NoSync, 0777)
		y.Check(err)

		// Acquire handle
		err := lmdbEnv.Update(func(txn *lmdb.Txn) error {
			var err error
			lmdbDBI, err = txn.CreateDBI("bench")
			return err
		})
		y.Check(err)
	} else if *which == "bolt" {
		init = true
		fmt.Println("Init BoltDB")
		os.RemoveAll(*dir + "/bolt")
		os.MkdirAll(*dir+"/bolt", 0777)
		boltdb, err = bolt.Open(*dir+"/bolt/bolt.db", 0777, bolt.DefaultOptions)
		y.Check(err)
		boltdb.NoSync = true // Set this to speed up writes
		err = boltdb.Update(func(txn *bolt.Tx) error {
			var err error
			_, err = txn.CreateBucketIfNotExists([]byte("bench"))
			return err
		})
		y.Check(err)

	} else {
		log.Fatalf("Invalid value for option kv: '%s'", *which)
	}

	if !init {
		log.Fatalf("Invalid arguments. Unable to init any store.")
	}

	rc := ratecounter.NewRateCounter(time.Minute)
	var counter int64
	ctx, cancel := context.WithCancel(context.Background())
	go func() {
		var count int64
		t := time.NewTicker(time.Second)
		defer t.Stop()
		for {
			select {
			case <-t.C:
				fmt.Printf("[%04d] Write key rate per minute: %s. Total: %s\n",
					count,
					humanize(rc.Rate()),
					humanize(atomic.LoadInt64(&counter)))
				count++
			case <-ctx.Done():
				return
			}
		}
	}()
	go func() {
		if err := http.ListenAndServe("0.0.0.0:8081", nil); err != nil {
			log.Fatalf("While opening http. Error: %v", err)
		}
	}()

	N := 12
	var wg sync.WaitGroup
	for i := 0; i < N; i++ {
		wg.Add(1)
		go func(proc int) {
			entries := make([]*badger.Entry, 1000)
			for i := 0; i < len(entries); i++ {
				e := new(badger.Entry)
				e.Key = make([]byte, 22)
				e.Value = make([]byte, *valueSize)
				entries[i] = e
			}

			var written float64
			for written < nw/float64(N) {
				wrote := float64(writeBatch(entries))

				wi := int64(wrote)
				atomic.AddInt64(&counter, wi)
				rc.Incr(wi)

				written += wrote
			}
			wg.Done()
		}(i)
	}
	// 	wg.Add(1) // Block
	wg.Wait()
	cancel()

	if bdb != nil {
		fmt.Println("closing badger")
		bdb.Close()
	}

	if rdb != nil {
		fmt.Println("closing rocks")
		rdb.Close()
	}

	if lmdbEnv != nil {

		fmt.Println("closing lmdb")
		lmdbEnv.CloseDBI(lmdbDBI)
		lmdbEnv.Close()
	}

	if boltdb != nil {
		fmt.Println("closing bolt")
		boltdb.Close()
	}

	fmt.Printf("\nWROTE %d KEYS\n", atomic.LoadInt64(&counter))
}
<|endoftext|>


<commit_before>package gwr

import (
	"io"
	"log"
	"strings"
	"text/template"
)

// TODO: punts on any locking concerns
// TODO: .emit(interface{}) vs chan interface{}

// NOTE: This approach is perhaps overfit to the json module's marshalling
// mindset.  A better interface (for performance) would work by passing a
// writer to the specific encoder, rather than a []byte-returning Marshal
// function.  This would be possible perhaps using something like
// io.MultiWriter.

// MarshaledDataSource wraps a format-agnostic data source and provides one or
// more formats for it
type MarshaledDataSource struct {
	source      GenericDataSource
	formats     map[string]GenericDataFormat
	formatNames []string
	watchers    map[string]*marshaledWatcher
	watching    bool
}

// GenericDataWatcher is a type alias for the function signature passed to
// source.Watch.
type GenericDataWatcher func(interface{}) bool

// GenericDataSource is a format-agnostic data source
type GenericDataSource interface {
	// Info returns a description of the data source
	Info() GenericDataSourceInfo

	// Get should return any data available for the data source.  A nil value
	// should  result in a ErrNotGetable.  If a generic data source wants a
	// marshaled null value, its Get must return a non-nil interface value.
	Get() interface{}

	// GetInit should return any inital data to send to a new watch stream.
	// Similarly to Get a nil value will not be marshaled, but no error will be
	// returned to the Watch request.
	GetInit() interface{}

	// Watch sets the current (singular!) watcher.  Implementations must call
	// the passed watcher until it returns false, or until a new watcher is
	// passed by a future call of Watch.
	Watch(GenericDataWatcher)
}

// GenericDataSourceInfo describes a format-agnostic data source
type GenericDataSourceInfo struct {
	Name         string
	Attrs        map[string]interface{}
	TextTemplate *template.Template
}

// GenericDataFormat provides both a data marshaling protocol and a framing
// protocol for the watch stream.  Any marshaling or framing error should cause
// a break in any watch streams subscribed to this format.
type GenericDataFormat interface {
	// Marshal serializes the passed data from GenericDataSource.Get.
	MarshalGet(interface{}) ([]byte, error)

	// Marshal serializes the passed data from GenericDataSource.GetInit.
	MarshalInit(interface{}) ([]byte, error)

	// Marshal serializes data passed to a GenericDataWatcher.
	MarshalItem(interface{}) ([]byte, error)

	// FrameItem wraps a MarshalItem-ed byte buffer for a watch stream.
	FrameItem([]byte) ([]byte, error)
}

// marshaledWatcher manages all of the low level io.Writers for a given format.
// Instances are created once for each MarshaledDataSource.
//
// MarshaledDataSource then manages calling marshaledWatcher.emit for each data
// item as long as there is one valid io.Writer for a given format.  Once the
// last marshaledWatcher goes idle, the underlying GenericDataSource watch is
// ended.
type marshaledWatcher struct {
	source  GenericDataSource
	format  GenericDataFormat
	writers []io.Writer
}

func newMarshaledWatcher(source GenericDataSource, format GenericDataFormat) *marshaledWatcher {
	gw := &marshaledWatcher{source: source, format: format}
	return gw
}

func (gw *marshaledWatcher) init(w io.Writer) error {
	if data := gw.source.GetInit(); data != nil {
		format := gw.format
		buf, err := format.MarshalInit(data)
		if err != nil {
			log.Printf("inital marshaling error %v", err)
			return err
		}
		buf, err = format.FrameItem(buf)
		if err != nil {
			log.Printf("inital framing error %v", err)
			return err
		}
		_, err = w.Write(buf)
		if err != nil {
			return err
		}
	}
	gw.writers = append(gw.writers, w)
	return nil
}

func (gw *marshaledWatcher) emit(data interface{}) bool {
	if len(gw.writers) == 0 {
		return false
	}
	buf, err := gw.format.MarshalItem(data)
	if err != nil {
		log.Printf("item marshaling error %v", err)
		return false
	}
	buf, err = gw.format.FrameItem(buf)
	if err != nil {
		log.Printf("item framing error %v", err)
		return false
	}

	// TODO: avoid blocking fan out, parallelize; error back-propagation then
	// needs to happen over another channel

	var failed []int // TODO: could carry this rather than allocate on failure
	for i, w := range gw.writers {
		if _, err := w.Write(buf); err != nil {
			if failed == nil {
				failed = make([]int, 0, len(gw.writers))
			}
			failed = append(failed, i)
		}
	}
	if len(failed) == 0 {
		return true
	}

	var (
		okay []io.Writer
		remain = len(gw.writers) - len(failed)
	)
	if remain > 0 {
		okay = make([]io.Writer, 0, remain)
	}
	for i, w := range gw.writers {
		if i != failed[0] {
			okay = append(okay, w)
		}
		if i >= failed[0] {
			failed = failed[1:]
			if len(failed) == 0 {
				if j := i + 1; j < len(gw.writers) {
					okay = append(okay, gw.writers[j:]...)
				}
				break
			}
		}
	}
	gw.writers = okay

	return len(gw.writers) != 0
}

// NewMarshaledDataSource creates a MarshaledDataSource for a given
// format-agnostic data source and a map of marshalers
func NewMarshaledDataSource(
	source GenericDataSource,
	formats map[string]GenericDataFormat,
) *MarshaledDataSource {
	var formatNames []string

	// we need room for json and text defaults plus any specified
	n := len(formats)
	if formats["json"] == nil {
		n++
	}
	if formats["text"] == nil {
		// may over estimate by one if source has no TextTemplate; probably not
		// a big deal
		n++
	}
	watchers := make(map[string]*marshaledWatcher, n)

	// standard json protocol
	if formats["json"] == nil {
		formatNames = append(formatNames, "json")
		watchers["json"] = newMarshaledWatcher(source, LDJSONMarshal)
	}

	// convenience templated text protocol
	if tt := source.Info().TextTemplate; tt != nil && formats["text"] == nil {
		formatNames = append(formatNames, "text")
		watchers["text"] = newMarshaledWatcher(source, NewTemplatedMarshal(tt))
	}

	// TODO: source should be able to declare some formats in addition to any
	// integratgor

	for name, format := range formats {
		formatNames = append(formatNames, name)
		watchers[name] = newMarshaledWatcher(source, format)
	}

	return &MarshaledDataSource{
		source:      source,
		formats:     formats,
		formatNames: formatNames,
		watchers:    watchers,
	}
}

// Info returns the generic data source description, plus any format specific
// description
func (mds *MarshaledDataSource) Info() DataSourceInfo {
	info := mds.source.Info()
	// TODO: any need for per-format Attrs?
	return DataSourceInfo{
		Name:    info.Name,
		Formats: mds.formatNames,
		Attrs:   info.Attrs,
	}
}

// Get marshals the agnostic data source's Get data to the writer
func (mds *MarshaledDataSource) Get(formatName string, w io.Writer) error {
	format, ok := mds.formats[strings.ToLower(formatName)]
	if !ok {
		return ErrUnsupportedFormat
	}
	data := mds.source.Get()
	if data == nil {
		return ErrNotGetable
	}
	buf, err := format.MarshalGet(data)
	if err != nil {
		log.Printf("get marshaling error %v", err)
		return err
	}
	_, err = w.Write(buf)
	return err
}

// Watch marshals any agnostic data source GetInit data to the writer, and then
// retains a reference to the writer so that any future agnostic data source
// Watch(emit)'ed data gets marshaled to it as well
func (mds *MarshaledDataSource) Watch(formatName string, w io.Writer) error {
	watcher, ok := mds.watchers[strings.ToLower(formatName)]
	if !ok {
		return ErrUnsupportedFormat
	}

	if err := watcher.init(w); err != nil {
		return err
	}

	// TODO: we could optimize the only-one-format-being-watched case
	if !mds.watching {
		mds.source.Watch(mds.emit)
		mds.watching = true
	}

	return nil
}

func (mds *MarshaledDataSource) emit(data interface{}) bool {
	if !mds.watching {
		return false
	}
	any := false
	for _, watcher := range mds.watchers {
		if watcher.emit(data) {
			any = true
		}
	}
	if !any {
		mds.watching = false
	}
	return any
}
<commit_msg>Tweak on marshaledWatcher docs<commit_after>package gwr

import (
	"io"
	"log"
	"strings"
	"text/template"
)

// TODO: punts on any locking concerns
// TODO: .emit(interface{}) vs chan interface{}

// NOTE: This approach is perhaps overfit to the json module's marshalling
// mindset.  A better interface (for performance) would work by passing a
// writer to the specific encoder, rather than a []byte-returning Marshal
// function.  This would be possible perhaps using something like
// io.MultiWriter.

// MarshaledDataSource wraps a format-agnostic data source and provides one or
// more formats for it
type MarshaledDataSource struct {
	source      GenericDataSource
	formats     map[string]GenericDataFormat
	formatNames []string
	watchers    map[string]*marshaledWatcher
	watching    bool
}

// GenericDataWatcher is a type alias for the function signature passed to
// source.Watch.
type GenericDataWatcher func(interface{}) bool

// GenericDataSource is a format-agnostic data source
type GenericDataSource interface {
	// Info returns a description of the data source
	Info() GenericDataSourceInfo

	// Get should return any data available for the data source.  A nil value
	// should  result in a ErrNotGetable.  If a generic data source wants a
	// marshaled null value, its Get must return a non-nil interface value.
	Get() interface{}

	// GetInit should return any inital data to send to a new watch stream.
	// Similarly to Get a nil value will not be marshaled, but no error will be
	// returned to the Watch request.
	GetInit() interface{}

	// Watch sets the current (singular!) watcher.  Implementations must call
	// the passed watcher until it returns false, or until a new watcher is
	// passed by a future call of Watch.
	Watch(GenericDataWatcher)
}

// GenericDataSourceInfo describes a format-agnostic data source
type GenericDataSourceInfo struct {
	Name         string
	Attrs        map[string]interface{}
	TextTemplate *template.Template
}

// GenericDataFormat provides both a data marshaling protocol and a framing
// protocol for the watch stream.  Any marshaling or framing error should cause
// a break in any watch streams subscribed to this format.
type GenericDataFormat interface {
	// Marshal serializes the passed data from GenericDataSource.Get.
	MarshalGet(interface{}) ([]byte, error)

	// Marshal serializes the passed data from GenericDataSource.GetInit.
	MarshalInit(interface{}) ([]byte, error)

	// Marshal serializes data passed to a GenericDataWatcher.
	MarshalItem(interface{}) ([]byte, error)

	// FrameItem wraps a MarshalItem-ed byte buffer for a watch stream.
	FrameItem([]byte) ([]byte, error)
}

// marshaledWatcher manages all of the low level io.Writers for a given format.
// Instances are created once for each MarshaledDataSource.
//
// MarshaledDataSource then manages calling marshaledWatcher.emit for each data
// item as long as there is one valid io.Writer for a given format.  Once the
// last marshaledWatcher goes idle, the underlying GenericDataSource watch is
// ended.
type marshaledWatcher struct {
	source  GenericDataSource
	format  GenericDataFormat
	writers []io.Writer
}

func newMarshaledWatcher(source GenericDataSource, format GenericDataFormat) *marshaledWatcher {
	gw := &marshaledWatcher{source: source, format: format}
	return gw
}

func (gw *marshaledWatcher) init(w io.Writer) error {
	if data := gw.source.GetInit(); data != nil {
		format := gw.format
		buf, err := format.MarshalInit(data)
		if err != nil {
			log.Printf("inital marshaling error %v", err)
			return err
		}
		buf, err = format.FrameItem(buf)
		if err != nil {
			log.Printf("inital framing error %v", err)
			return err
		}
		_, err = w.Write(buf)
		if err != nil {
			return err
		}
	}
	gw.writers = append(gw.writers, w)
	return nil
}

func (gw *marshaledWatcher) emit(data interface{}) bool {
	if len(gw.writers) == 0 {
		return false
	}
	buf, err := gw.format.MarshalItem(data)
	if err != nil {
		log.Printf("item marshaling error %v", err)
		return false
	}
	buf, err = gw.format.FrameItem(buf)
	if err != nil {
		log.Printf("item framing error %v", err)
		return false
	}

	// TODO: avoid blocking fan out, parallelize; error back-propagation then
	// needs to happen over another channel

	var failed []int // TODO: could carry this rather than allocate on failure
	for i, w := range gw.writers {
		if _, err := w.Write(buf); err != nil {
			if failed == nil {
				failed = make([]int, 0, len(gw.writers))
			}
			failed = append(failed, i)
		}
	}
	if len(failed) == 0 {
		return true
	}

	var (
		okay []io.Writer
		remain = len(gw.writers) - len(failed)
	)
	if remain > 0 {
		okay = make([]io.Writer, 0, remain)
	}
	for i, w := range gw.writers {
		if i != failed[0] {
			okay = append(okay, w)
		}
		if i >= failed[0] {
			failed = failed[1:]
			if len(failed) == 0 {
				if j := i + 1; j < len(gw.writers) {
					okay = append(okay, gw.writers[j:]...)
				}
				break
			}
		}
	}
	gw.writers = okay

	return len(gw.writers) != 0
}

// NewMarshaledDataSource creates a MarshaledDataSource for a given
// format-agnostic data source and a map of marshalers
func NewMarshaledDataSource(
	source GenericDataSource,
	formats map[string]GenericDataFormat,
) *MarshaledDataSource {
	var formatNames []string

	// we need room for json and text defaults plus any specified
	n := len(formats)
	if formats["json"] == nil {
		n++
	}
	if formats["text"] == nil {
		// may over estimate by one if source has no TextTemplate; probably not
		// a big deal
		n++
	}
	watchers := make(map[string]*marshaledWatcher, n)

	// standard json protocol
	if formats["json"] == nil {
		formatNames = append(formatNames, "json")
		watchers["json"] = newMarshaledWatcher(source, LDJSONMarshal)
	}

	// convenience templated text protocol
	if tt := source.Info().TextTemplate; tt != nil && formats["text"] == nil {
		formatNames = append(formatNames, "text")
		watchers["text"] = newMarshaledWatcher(source, NewTemplatedMarshal(tt))
	}

	// TODO: source should be able to declare some formats in addition to any
	// integratgor

	for name, format := range formats {
		formatNames = append(formatNames, name)
		watchers[name] = newMarshaledWatcher(source, format)
	}

	return &MarshaledDataSource{
		source:      source,
		formats:     formats,
		formatNames: formatNames,
		watchers:    watchers,
	}
}

// Info returns the generic data source description, plus any format specific
// description
func (mds *MarshaledDataSource) Info() DataSourceInfo {
	info := mds.source.Info()
	// TODO: any need for per-format Attrs?
	return DataSourceInfo{
		Name:    info.Name,
		Formats: mds.formatNames,
		Attrs:   info.Attrs,
	}
}

// Get marshals data source's Get data to the writer
func (mds *MarshaledDataSource) Get(formatName string, w io.Writer) error {
	format, ok := mds.formats[strings.ToLower(formatName)]
	if !ok {
		return ErrUnsupportedFormat
	}
	data := mds.source.Get()
	if data == nil {
		return ErrNotGetable
	}
	buf, err := format.MarshalGet(data)
	if err != nil {
		log.Printf("get marshaling error %v", err)
		return err
	}
	_, err = w.Write(buf)
	return err
}

// Watch marshals any data source GetInit data to the writer, and then
// retains a reference to the writer so that any future agnostic data source
// Watch(emit)'ed data gets marshaled to it as well
func (mds *MarshaledDataSource) Watch(formatName string, w io.Writer) error {
	watcher, ok := mds.watchers[strings.ToLower(formatName)]
	if !ok {
		return ErrUnsupportedFormat
	}

	if err := watcher.init(w); err != nil {
		return err
	}

	// TODO: we could optimize the only-one-format-being-watched case
	if !mds.watching {
		mds.source.Watch(mds.emit)
		mds.watching = true
	}

	return nil
}

func (mds *MarshaledDataSource) emit(data interface{}) bool {
	if !mds.watching {
		return false
	}
	any := false
	for _, watcher := range mds.watchers {
		if watcher.emit(data) {
			any = true
		}
	}
	if !any {
		mds.watching = false
	}
	return any
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"strconv"
	"sync"
	"time"

	"github.com/brotherlogic/goserver"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"

	pbd "github.com/brotherlogic/discovery/proto"
	pbgh "github.com/brotherlogic/githubcard/proto"
	pb "github.com/brotherlogic/gobuildmaster/proto"
	pbs "github.com/brotherlogic/gobuildslave/proto"
	pbg "github.com/brotherlogic/goserver/proto"
	"github.com/brotherlogic/goserver/utils"
)

const (
	intentWait = time.Second
)

// Server the main server type
type Server struct {
	*goserver.GoServer
	config            *pb.Config
	serving           bool
	LastIntent        time.Time
	LastMaster        time.Time
	worldMutex        *sync.Mutex
	world             map[string]map[string]struct{}
	slaveMap          map[string][]string
	getter            getter
	mapString         string
	lastWorldRun      int64
	lastMasterSatisfy map[string]time.Time
	serverMap         map[string]time.Time
	lastSeen          map[string]time.Time
	timeChange        time.Duration
	registerAttempts  int64
	lastMasterRunTime time.Duration
	lastJob           string
	lastTrack         string
	accessPoints      map[string]time.Time
	accessPointsMutex *sync.Mutex
	testing           bool
	decisions         map[string]string
	claimed           string
}

func (s *Server) alertOnMissingJob(ctx context.Context) error {
	for _, nin := range s.config.Nintents {
		_, err := utils.ResolveV3(nin.Job.Name)
		if err != nil && !nin.GetNoMaster() {
			if _, ok := s.lastSeen[nin.Job.Name]; !ok {
				s.lastSeen[nin.Job.Name] = time.Now()
			}

			if time.Now().Sub(s.lastSeen[nin.Job.Name]) > time.Hour*2 {
				if nin.Job.Name == "githubcard" {
					fmt.Printf("Unable to locate githubcard\n")
					os.Exit(1)
				}

				// Discovery does not show up in discovery
				if nin.Job.Name != "discovery" {
					s.RaiseIssue("Missing Job", fmt.Sprintf("%v is missing - last seen %v (%v)", nin.Job.Name, time.Now().Sub(s.lastSeen[nin.Job.Name]), err))
				}
			}
		} else {
			s.lastSeen[nin.Job.Name] = time.Now()
		}
	}

	return nil
}

type prodGetter struct {
	dial func(entry string) (*grpc.ClientConn, error)
}

func (g *prodGetter) getJobs(ctx context.Context, server *pbd.RegistryEntry) ([]*pbs.JobAssignment, error) {
	conn, err := g.dial(fmt.Sprintf("%v:%v", server.GetIdentifier(), server.GetPort()))
	if err != nil {
		return nil, err
	}
	defer conn.Close()

	slave := pbs.NewBuildSlaveClient(conn)

	// Set a tighter rpc deadline for listing jobs.
	ctx, cancel := utils.ManualContext("getJobs", time.Minute)
	defer cancel()
	r, err := slave.ListJobs(ctx, &pbs.ListRequest{})
	if err != nil {
		return nil, err
	}
	return r.Jobs, err
}

func (g *prodGetter) getConfig(ctx context.Context, server *pbd.RegistryEntry) ([]*pbs.Requirement, error) {
	conn, err := g.dial(fmt.Sprintf("%v:%v", server.GetIdentifier(), server.GetPort()))
	if err != nil {
		return nil, err
	}
	defer conn.Close()

	slave := pbs.NewBuildSlaveClient(conn)
	r, err := slave.SlaveConfig(ctx, &pbs.ConfigRequest{})
	if err != nil {
		return nil, err
	}
	return r.Config.Requirements, err
}

func (g *prodGetter) getSlaves(ctx context.Context) (*pbd.ServiceList, error) {
	ret := &pbd.ServiceList{}

	conn, err := g.dial("127.0.0.1:" + strconv.Itoa(utils.RegistryPort))
	if err != nil {
		return ret, err
	}
	defer conn.Close()

	registry := pbd.NewDiscoveryServiceV2Client(conn)
	r, err := registry.Get(ctx, &pbd.GetRequest{Job: "gobuildslave"})
	if err != nil {
		return ret, err
	}

	for _, s := range r.GetServices() {
		if s.GetName() == "gobuildslave" {
			ret.Services = append(ret.Services, s)
		}
	}

	return ret, nil
}

type mainChecker struct {
	prev      []string
	logger    func(string)
	dial      func(server, host string) (*grpc.ClientConn, error)
	dialEntry func(*pbd.RegistryEntry) (*grpc.ClientConn, error)
}

func (t *mainChecker) getprev() []string {
	return t.prev
}
func (t *mainChecker) setprev(v []string) {
	t.prev = v
}

func (t *mainChecker) assess(ctx context.Context, server string) (*pbs.JobList, *pbs.Config) {
	conn, err := t.dial("gobuildslave", server)
	if err != nil {
		return nil, nil
	}
	defer conn.Close()

	slave := pbs.NewGoBuildSlaveClient(conn)
	r, err := slave.List(ctx, &pbs.Empty{})
	if err != nil {
		return nil, nil
	}

	r2, err := slave.GetConfig(ctx, &pbs.Empty{})
	if err != nil {
		return nil, nil
	}

	return r, r2
}

func (s *Server) runJob(ctx context.Context, job *pbs.Job, localSlave *pbd.RegistryEntry, bits int) error {
	if s.testing {
		return nil
	}
	conn, err := s.FDial(fmt.Sprintf("%v:%v", localSlave.GetIdentifier(), localSlave.GetPort()))
	if err == nil {
		defer conn.Close()

		slave := pbs.NewBuildSlaveClient(conn)
		_, err = slave.RunJob(ctx, &pbs.RunRequest{Job: job, Bits: int32(bits)})
	}
	return err
}

func (t *mainChecker) discover() *pbd.ServiceList {
	ret := &pbd.ServiceList{}

	conn, _ := grpc.Dial(utils.RegistryIP+":"+strconv.Itoa(utils.RegistryPort), grpc.WithInsecure())
	defer conn.Close()

	registry := pbd.NewDiscoveryServiceClient(conn)
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	r, err := registry.ListAllServices(ctx, &pbd.ListRequest{})
	if err == nil {
		ret.Services = append(ret.GetServices(), r.GetServices().Services...)
	}

	return ret
}

// DoRegister Registers this server
func (s Server) DoRegister(server *grpc.Server) {
	pb.RegisterGoBuildMasterServer(server, &s)
}

// ReportHealth determines if the server is healthy
func (s Server) ReportHealth() bool {
	return true
}

// Shutdown does the shutdown
func (s Server) Shutdown(ctx context.Context) error {
	return nil
}

//GetState gets the state of the server
func (s Server) GetState() []*pbg.State {
	return []*pbg.State{}
}

//Compare compares current state to desired state
func (s Server) Compare(ctx context.Context, in *pb.Empty) (*pb.CompareResponse, error) {
	resp := &pb.CompareResponse{}
	list, _ := getFleetStatus(ctx, &mainChecker{logger: s.Log, dial: s.DialServer, dialEntry: s.DoDial})
	cc := &pb.Config{}
	for _, jlist := range list {
		for _, job := range jlist.GetDetails() {
			cc.Intents = append(cc.Intents, &pb.Intent{Spec: job.GetSpec()})
		}
	}
	resp.Current = cc
	resp.Desired = s.config

	return resp, nil
}

func (s *Server) GetDecisions(ctx context.Context, _ *pb.GetDecisionsRequest) (*pb.GetDecisionsResponse, error) {
	resp := &pb.GetDecisionsResponse{}
	for job, dec := range s.decisions {
		resp.Decisions = append(resp.Decisions, &pb.Decision{
			JobName: job,
			Running: len(dec) == 0,
			Reason:  dec,
		})
	}
	return resp, nil
}

//Init builds up the server
func Init(config *pb.Config) *Server {
	s := &Server{
		&goserver.GoServer{},
		config,
		true,
		time.Now(),
		time.Now(),
		&sync.Mutex{},
		make(map[string]map[string]struct{}),
		make(map[string][]string),
		&prodGetter{},
		"",
		0,
		make(map[string]time.Time),
		make(map[string]time.Time),
		make(map[string]time.Time),
		time.Hour, // time.Change
		int64(0),
		0,
		"",
		"",
		make(map[string]time.Time),
		&sync.Mutex{},
		false,
		make(map[string]string),
		"",
	}
	s.getter = &prodGetter{s.FDial}

	return s
}

func (s *Server) registerJob(ctx context.Context, int *pb.NIntent) error {
	conn, err := s.FDialServer(ctx, "githubcard")
	if err != nil {
		return nil
	}
	defer conn.Close()

	client := pbgh.NewGithubClient(conn)
	_, err = client.RegisterJob(ctx, &pbgh.RegisterRequest{Job: int.GetJob().GetName()})

	return err
}

func main() {
	config, err := loadConfig()
	if err != nil {
		log.Fatalf("Fatal loading of config: %v", err)
	}

	s := Init(config)

	var quiet = flag.Bool("quiet", false, "Show all output")
	flag.Parse()

	if *quiet {
		log.SetFlags(0)
		log.SetOutput(ioutil.Discard)
	}

	s.Register = s
	s.PrepServer("gobuildmaster")

	err = s.RegisterServerV2(false)
	if err != nil {
		if c := status.Convert(err); c.Code() == codes.FailedPrecondition || c.Code() == codes.Unavailable {
			// this is expected if disc is not ready
			return
		}
		log.Fatalf("Unable to register: %v", err)
	}

	//We need to register ourselves
	go func() {
		for {
			time.Sleep(time.Minute)

			ctx, cancel := utils.ManualContext("gbm-register", time.Minute)
			defer cancel()

			conn, err := s.FDialServer(ctx, "githubcard")
			if err != nil {
				continue
			}

			client := pbgh.NewGithubClient(conn)
			client.RegisterJob(ctx, &pbgh.RegisterRequest{Job: "gobuildmaster"})
			break
		}
	}()

	go func() {
		for !s.LameDuck {
			t1 := time.Now()
			ctx, cancel := utils.ManualContext("gobuildmaster", time.Minute*10)
			err = s.adjustWorld(ctx)
			cancel()
			rebuildTime.Set(float64(time.Since(t1).Seconds()))
			time.Sleep(time.Minute * 10)
		}
	}()
	err = s.Serve()
	if err != nil {
		log.Fatalf("Serve error: %v", err)
	}
}
<commit_msg>Log on adjust<commit_after>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"strconv"
	"sync"
	"time"

	"github.com/brotherlogic/goserver"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"

	pbd "github.com/brotherlogic/discovery/proto"
	pbgh "github.com/brotherlogic/githubcard/proto"
	pb "github.com/brotherlogic/gobuildmaster/proto"
	pbs "github.com/brotherlogic/gobuildslave/proto"
	pbg "github.com/brotherlogic/goserver/proto"
	"github.com/brotherlogic/goserver/utils"
)

const (
	intentWait = time.Second
)

// Server the main server type
type Server struct {
	*goserver.GoServer
	config            *pb.Config
	serving           bool
	LastIntent        time.Time
	LastMaster        time.Time
	worldMutex        *sync.Mutex
	world             map[string]map[string]struct{}
	slaveMap          map[string][]string
	getter            getter
	mapString         string
	lastWorldRun      int64
	lastMasterSatisfy map[string]time.Time
	serverMap         map[string]time.Time
	lastSeen          map[string]time.Time
	timeChange        time.Duration
	registerAttempts  int64
	lastMasterRunTime time.Duration
	lastJob           string
	lastTrack         string
	accessPoints      map[string]time.Time
	accessPointsMutex *sync.Mutex
	testing           bool
	decisions         map[string]string
	claimed           string
}

func (s *Server) alertOnMissingJob(ctx context.Context) error {
	for _, nin := range s.config.Nintents {
		_, err := utils.ResolveV3(nin.Job.Name)
		if err != nil && !nin.GetNoMaster() {
			if _, ok := s.lastSeen[nin.Job.Name]; !ok {
				s.lastSeen[nin.Job.Name] = time.Now()
			}

			if time.Now().Sub(s.lastSeen[nin.Job.Name]) > time.Hour*2 {
				if nin.Job.Name == "githubcard" {
					fmt.Printf("Unable to locate githubcard\n")
					os.Exit(1)
				}

				// Discovery does not show up in discovery
				if nin.Job.Name != "discovery" {
					s.RaiseIssue("Missing Job", fmt.Sprintf("%v is missing - last seen %v (%v)", nin.Job.Name, time.Now().Sub(s.lastSeen[nin.Job.Name]), err))
				}
			}
		} else {
			s.lastSeen[nin.Job.Name] = time.Now()
		}
	}

	return nil
}

type prodGetter struct {
	dial func(entry string) (*grpc.ClientConn, error)
}

func (g *prodGetter) getJobs(ctx context.Context, server *pbd.RegistryEntry) ([]*pbs.JobAssignment, error) {
	conn, err := g.dial(fmt.Sprintf("%v:%v", server.GetIdentifier(), server.GetPort()))
	if err != nil {
		return nil, err
	}
	defer conn.Close()

	slave := pbs.NewBuildSlaveClient(conn)

	// Set a tighter rpc deadline for listing jobs.
	ctx, cancel := utils.ManualContext("getJobs", time.Minute)
	defer cancel()
	r, err := slave.ListJobs(ctx, &pbs.ListRequest{})
	if err != nil {
		return nil, err
	}
	return r.Jobs, err
}

func (g *prodGetter) getConfig(ctx context.Context, server *pbd.RegistryEntry) ([]*pbs.Requirement, error) {
	conn, err := g.dial(fmt.Sprintf("%v:%v", server.GetIdentifier(), server.GetPort()))
	if err != nil {
		return nil, err
	}
	defer conn.Close()

	slave := pbs.NewBuildSlaveClient(conn)
	r, err := slave.SlaveConfig(ctx, &pbs.ConfigRequest{})
	if err != nil {
		return nil, err
	}
	return r.Config.Requirements, err
}

func (g *prodGetter) getSlaves(ctx context.Context) (*pbd.ServiceList, error) {
	ret := &pbd.ServiceList{}

	conn, err := g.dial("127.0.0.1:" + strconv.Itoa(utils.RegistryPort))
	if err != nil {
		return ret, err
	}
	defer conn.Close()

	registry := pbd.NewDiscoveryServiceV2Client(conn)
	r, err := registry.Get(ctx, &pbd.GetRequest{Job: "gobuildslave"})
	if err != nil {
		return ret, err
	}

	for _, s := range r.GetServices() {
		if s.GetName() == "gobuildslave" {
			ret.Services = append(ret.Services, s)
		}
	}

	return ret, nil
}

type mainChecker struct {
	prev      []string
	logger    func(string)
	dial      func(server, host string) (*grpc.ClientConn, error)
	dialEntry func(*pbd.RegistryEntry) (*grpc.ClientConn, error)
}

func (t *mainChecker) getprev() []string {
	return t.prev
}
func (t *mainChecker) setprev(v []string) {
	t.prev = v
}

func (t *mainChecker) assess(ctx context.Context, server string) (*pbs.JobList, *pbs.Config) {
	conn, err := t.dial("gobuildslave", server)
	if err != nil {
		return nil, nil
	}
	defer conn.Close()

	slave := pbs.NewGoBuildSlaveClient(conn)
	r, err := slave.List(ctx, &pbs.Empty{})
	if err != nil {
		return nil, nil
	}

	r2, err := slave.GetConfig(ctx, &pbs.Empty{})
	if err != nil {
		return nil, nil
	}

	return r, r2
}

func (s *Server) runJob(ctx context.Context, job *pbs.Job, localSlave *pbd.RegistryEntry, bits int) error {
	if s.testing {
		return nil
	}
	conn, err := s.FDial(fmt.Sprintf("%v:%v", localSlave.GetIdentifier(), localSlave.GetPort()))
	if err == nil {
		defer conn.Close()

		slave := pbs.NewBuildSlaveClient(conn)
		_, err = slave.RunJob(ctx, &pbs.RunRequest{Job: job, Bits: int32(bits)})
	}
	return err
}

func (t *mainChecker) discover() *pbd.ServiceList {
	ret := &pbd.ServiceList{}

	conn, _ := grpc.Dial(utils.RegistryIP+":"+strconv.Itoa(utils.RegistryPort), grpc.WithInsecure())
	defer conn.Close()

	registry := pbd.NewDiscoveryServiceClient(conn)
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	r, err := registry.ListAllServices(ctx, &pbd.ListRequest{})
	if err == nil {
		ret.Services = append(ret.GetServices(), r.GetServices().Services...)
	}

	return ret
}

// DoRegister Registers this server
func (s Server) DoRegister(server *grpc.Server) {
	pb.RegisterGoBuildMasterServer(server, &s)
}

// ReportHealth determines if the server is healthy
func (s Server) ReportHealth() bool {
	return true
}

// Shutdown does the shutdown
func (s Server) Shutdown(ctx context.Context) error {
	return nil
}

//GetState gets the state of the server
func (s Server) GetState() []*pbg.State {
	return []*pbg.State{}
}

//Compare compares current state to desired state
func (s Server) Compare(ctx context.Context, in *pb.Empty) (*pb.CompareResponse, error) {
	resp := &pb.CompareResponse{}
	list, _ := getFleetStatus(ctx, &mainChecker{logger: s.Log, dial: s.DialServer, dialEntry: s.DoDial})
	cc := &pb.Config{}
	for _, jlist := range list {
		for _, job := range jlist.GetDetails() {
			cc.Intents = append(cc.Intents, &pb.Intent{Spec: job.GetSpec()})
		}
	}
	resp.Current = cc
	resp.Desired = s.config

	return resp, nil
}

func (s *Server) GetDecisions(ctx context.Context, _ *pb.GetDecisionsRequest) (*pb.GetDecisionsResponse, error) {
	resp := &pb.GetDecisionsResponse{}
	for job, dec := range s.decisions {
		resp.Decisions = append(resp.Decisions, &pb.Decision{
			JobName: job,
			Running: len(dec) == 0,
			Reason:  dec,
		})
	}
	return resp, nil
}

//Init builds up the server
func Init(config *pb.Config) *Server {
	s := &Server{
		&goserver.GoServer{},
		config,
		true,
		time.Now(),
		time.Now(),
		&sync.Mutex{},
		make(map[string]map[string]struct{}),
		make(map[string][]string),
		&prodGetter{},
		"",
		0,
		make(map[string]time.Time),
		make(map[string]time.Time),
		make(map[string]time.Time),
		time.Hour, // time.Change
		int64(0),
		0,
		"",
		"",
		make(map[string]time.Time),
		&sync.Mutex{},
		false,
		make(map[string]string),
		"",
	}
	s.getter = &prodGetter{s.FDial}

	return s
}

func (s *Server) registerJob(ctx context.Context, int *pb.NIntent) error {
	conn, err := s.FDialServer(ctx, "githubcard")
	if err != nil {
		return nil
	}
	defer conn.Close()

	client := pbgh.NewGithubClient(conn)
	_, err = client.RegisterJob(ctx, &pbgh.RegisterRequest{Job: int.GetJob().GetName()})

	return err
}

func main() {
	config, err := loadConfig()
	if err != nil {
		log.Fatalf("Fatal loading of config: %v", err)
	}

	s := Init(config)

	var quiet = flag.Bool("quiet", false, "Show all output")
	flag.Parse()

	if *quiet {
		log.SetFlags(0)
		log.SetOutput(ioutil.Discard)
	}

	s.Register = s
	s.PrepServer("gobuildmaster")

	err = s.RegisterServerV2(false)
	if err != nil {
		if c := status.Convert(err); c.Code() == codes.FailedPrecondition || c.Code() == codes.Unavailable {
			// this is expected if disc is not ready
			return
		}
		log.Fatalf("Unable to register: %v", err)
	}

	//We need to register ourselves
	go func() {
		for {
			time.Sleep(time.Minute)

			ctx, cancel := utils.ManualContext("gbm-register", time.Minute)
			defer cancel()

			conn, err := s.FDialServer(ctx, "githubcard")
			if err != nil {
				continue
			}

			client := pbgh.NewGithubClient(conn)
			client.RegisterJob(ctx, &pbgh.RegisterRequest{Job: "gobuildmaster"})
			break
		}
	}()

	go func() {
		for !s.LameDuck {
			t1 := time.Now()
			ctx, cancel := utils.ManualContext("gobuildmaster", time.Minute*10)
			err = s.adjustWorld(ctx)
			s.CtxLog(ctx, fmt.Sprintf("Adjusted world: %v", err))
			cancel()
			rebuildTime.Set(float64(time.Since(t1).Seconds()))
			time.Sleep(time.Minute * 10)
		}
	}()
	err = s.Serve()
	if err != nil {
		log.Fatalf("Serve error: %v", err)
	}
}
<|endoftext|>


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

package geom

import (
	"testing"
)

func TestMat4Id(t *testing.T) {
	m := Mat4{
		{11, 12, 13, 14},
		{21, 22, 23, 24},
		{31, 32, 33, 34},
		{41, 42, 43, 44},
	}
	id := Mat4{
		{1, 0, 0, 0},
		{0, 1, 0, 0},
		{0, 0, 1, 0},
		{0, 0, 0, 1},
	}
	mp := m.Id()
	if m != id {
		t.Errorf("m.Id() does not set m to be the identity matrix, got instead: %v", m)
	}
	if mp != &m {
		t.Errorf("m.Id() does not return the pointer to m")
	}
}

func TestMat4Ortho(t *testing.T) {
	tests := []struct {
		l, r, b, t, n, f float32
		want             Mat4
	}{
		{-1, 1, -1, 1, 1, -1, Mat4{
			{1, 0, 0, 0},
			{0, 1, 0, 0},
			{0, 0, 1, 0},
			{0, 0, 0, 1},
		}},
		{-2, 2, -2, 2, 2, -2, Mat4{
			{0.5, 0, 0, 0},
			{0, 0.5, 0, 0},
			{0, 0, 0.5, 0},
			{0, 0, 0, 1.0},
		}},
		{1, 2, 3, 4, 5, 6, Mat4{
			{2, 0, 0, 0},
			{0, 2, 0, 0},
			{0, 0, -2, 0},
			{-3, -7, -11, 1},
		}},
	}
	var m Mat4
	for _, tt := range tests {
		m.Ortho(tt.l, tt.r, tt.b, tt.t, tt.n, tt.f)
		if m != tt.want {
			t.Errorf("m.Ortho(%g, %g, %g, %g, %g, %g) = %v, want %v",
				tt.l, tt.r, tt.b, tt.t, tt.n, tt.f, m, tt.want)
		}
	}
}

func TestMat4Frustum(t *testing.T) {
	tests := []struct {
		l, r, b, t, n, f float32
		want             Mat4
	}{
		{-1, 1, -1, 1, 1, -1, Mat4{
			{1, 0, 0, 0},
			{0, 1, 0, 0},
			{0, 0, 0, -1},
			{0, 0, -1, 0},
		}},
		{-2, 2, -2, 2, 2, -2, Mat4{
			{1, 0, 0, 0},
			{0, 1, 0, 0},
			{0, 0, 0, -1},
			{0, 0, -2, 0},
		}},
		{1, 2, 3, 4, 5, 6, Mat4{
			{10, 0, 0, 0},
			{0, 10, 0, 0},
			{3, 7, -11, -1},
			{0, 0, -60, 0},
		}},
	}
	var m Mat4
	for _, tt := range tests {
		m.Frustum(tt.l, tt.r, tt.b, tt.t, tt.n, tt.f)
		if m != tt.want {
			t.Errorf("m.Frustum(%g, %g, %g, %g, %g, %g) = %v, want %v",
				tt.l, tt.r, tt.b, tt.t, tt.n, tt.f, m, tt.want)
		}
	}
}

func TestMat4Perspective(t *testing.T) {
	tests := []struct {
		fovy, a, n, f float32
		want          Mat4
	}{
	// TODO
	}
	var m Mat4
	for _, tt := range tests {
		m.Perspective(tt.fovy, tt.a, tt.n, tt.f)
		if m != tt.want {
			t.Errorf("m.Perspective(%g, %g, %g, %g) = %v, want %v",
				tt.fovy, tt.a, tt.n, tt.f, m, tt.want)
		}
	}
}

func TestMat4LookAt(t *testing.T) {
	tests := []struct {
		eye, center, up Vec3
		want            Mat4
	}{
		{V3(1, 1, 1), V3(1, 1, 0), V3(0, 1, 0), Mat4{
			{1, 0, 0, 0},
			{0, 1, 0, 0},
			{0, 0, 1, 0},
			{-1, -1, -1, 1},
		}},
		{V3(0, 0, 1), V3(0, 0, -1), V3(0, 1, 0), Mat4{
			{1, 0, 0, 0},
			{0, 1, 0, 0},
			{0, 0, 1, 0},
			{0, 0, -1, 1},
		}},
		{V3(20, 80, 15), V3(15, 0, 12), V3(0, -1, 0), Mat4{
			{-0.5144958, 0.8552243, 0.06233464, 0},
			{0, -0.07269406, 0.99735427, 0},
			{0.857493, 0.5131346, 0.037400786, 0},
			{-2.5724783, -18.985981, -81.596054, 1},
		}},
	}
	var m Mat4
	for _, tt := range tests {
		m.LookAt(tt.eye, tt.center, tt.up)
		if m != tt.want {
			t.Errorf("m.LookAt(%s, %s, %s) = %v, want %v",
				tt.eye, tt.center, tt.up, m, tt.want)
		}
	}
}

func TestMat4Floats(t *testing.T) {
	m := Mat4{
		{11, 12, 13, 14},
		{21, 22, 23, 24},
		{31, 32, 33, 34},
		{41, 42, 43, 44},
	}
	want := [16]float32{11, 12, 13, 14, 21, 22, 23, 24, 31, 32, 33, 34, 41, 42, 43, 44}
	f := m.Floats()
	if *f != want {
		t.Errorf("%v.Floats() = %v, want %v", m, *f, want)
	}
	f[6] = 99
	if m[1][2] != 99 {
		t.Errorf("Pointer to float32 array returned by Floats() does not point to matrix data.")
	}
}

func TestMat4Mul(t *testing.T) {
	tests := []struct {
		a, b, want Mat4
	}{
		{Mat4{
			{1, 2, 3, 4},
			{5, 6, 7, 8},
			{1, 2, 3, 4},
			{5, 6, 7, 8},
		}, Mat4{
			{1, 2, 3, 4},
			{5, 6, 7, 8},
			{1, 2, 3, 4},
			{5, 6, 7, 8},
		}, Mat4{
			{34, 44, 54, 64},
			{82, 108, 134, 160},
			{34, 44, 54, 64},
			{82, 108, 134, 160},
		}},

		{Mat4{
			{2.1, 3.2, 1.2, 0},
			{4.6, -5.3, 5.4, 8.4},
			{-9.1, 1, 0.2, -7.3},
			{-1.25, 2.2, 2.3, 6.3},
		}, Mat4{
			{-3, 2.4, 8.4, 3.3},
			{0.2, -5, 2.6, 1.2},
			{5.3, 8.1, 4.4, 2.5},
			{4.9, -1, 0, 4},
		}, Mat4{
			{0.7, -1.24, 31.24, 13.77},
			{54.92, 72.88, 48.62, 55.92},
			{-7.21, -17.92, -72.96, -57.53},
			{47.25, -1.67, 5.34, 29.465},
		}},
	}
	for _, tt := range tests {
		var m Mat4
		mp := m.Mul(&tt.a, &tt.b)
		if !tt.want.nearEq(&m) {
			t.Errorf("%v * %v = %v, want %v", tt.a, tt.b, m, tt.want)
		}
		if mp != &m {
			t.Errorf("m.Mul(...) does not return the pointer to m")
		}
	}
}
<commit_msg>add unit test for (*Mat4).Perspective<commit_after>// Copyright 2013 Frederik Zipp. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package geom

import (
	"testing"
)

func TestMat4Id(t *testing.T) {
	m := Mat4{
		{11, 12, 13, 14},
		{21, 22, 23, 24},
		{31, 32, 33, 34},
		{41, 42, 43, 44},
	}
	id := Mat4{
		{1, 0, 0, 0},
		{0, 1, 0, 0},
		{0, 0, 1, 0},
		{0, 0, 0, 1},
	}
	mp := m.Id()
	if m != id {
		t.Errorf("m.Id() does not set m to be the identity matrix, got instead: %v", m)
	}
	if mp != &m {
		t.Errorf("m.Id() does not return the pointer to m")
	}
}

func TestMat4Ortho(t *testing.T) {
	tests := []struct {
		l, r, b, t, n, f float32
		want             Mat4
	}{
		{-1, 1, -1, 1, 1, -1, Mat4{
			{1, 0, 0, 0},
			{0, 1, 0, 0},
			{0, 0, 1, 0},
			{0, 0, 0, 1},
		}},
		{-2, 2, -2, 2, 2, -2, Mat4{
			{0.5, 0, 0, 0},
			{0, 0.5, 0, 0},
			{0, 0, 0.5, 0},
			{0, 0, 0, 1.0},
		}},
		{1, 2, 3, 4, 5, 6, Mat4{
			{2, 0, 0, 0},
			{0, 2, 0, 0},
			{0, 0, -2, 0},
			{-3, -7, -11, 1},
		}},
	}
	var m Mat4
	for _, tt := range tests {
		m.Ortho(tt.l, tt.r, tt.b, tt.t, tt.n, tt.f)
		if m != tt.want {
			t.Errorf("m.Ortho(%g, %g, %g, %g, %g, %g) = %v, want %v",
				tt.l, tt.r, tt.b, tt.t, tt.n, tt.f, m, tt.want)
		}
	}
}

func TestMat4Frustum(t *testing.T) {
	tests := []struct {
		l, r, b, t, n, f float32
		want             Mat4
	}{
		{-1, 1, -1, 1, 1, -1, Mat4{
			{1, 0, 0, 0},
			{0, 1, 0, 0},
			{0, 0, 0, -1},
			{0, 0, -1, 0},
		}},
		{-2, 2, -2, 2, 2, -2, Mat4{
			{1, 0, 0, 0},
			{0, 1, 0, 0},
			{0, 0, 0, -1},
			{0, 0, -2, 0},
		}},
		{1, 2, 3, 4, 5, 6, Mat4{
			{10, 0, 0, 0},
			{0, 10, 0, 0},
			{3, 7, -11, -1},
			{0, 0, -60, 0},
		}},
	}
	var m Mat4
	for _, tt := range tests {
		m.Frustum(tt.l, tt.r, tt.b, tt.t, tt.n, tt.f)
		if m != tt.want {
			t.Errorf("m.Frustum(%g, %g, %g, %g, %g, %g) = %v, want %v",
				tt.l, tt.r, tt.b, tt.t, tt.n, tt.f, m, tt.want)
		}
	}
}

func TestMat4Perspective(t *testing.T) {
	tests := []struct {
		fovy, a, n, f float32
		want          Mat4
	}{
		{65, 1.5, 5, 10, Mat4{
			{0.35279062, 0, 0, 0},
			{0, 0.52918595, 0, 0},
			{0, 0, -3, -1},
			{0, 0, -20, 0},
		}},
		{70, 1.33, 1, 30, Mat4{
			{1.5868644, 0, 0, 0},
			{0, 2.1105297, 0, 0},
			{0, 0, -1.0689656, -1},
			{0, 0, -2.0689654, 0},
		}},
	}
	var m Mat4
	for _, tt := range tests {
		m.Perspective(tt.fovy, tt.a, tt.n, tt.f)
		if m != tt.want {
			t.Errorf("m.Perspective(%g, %g, %g, %g) = %v, want %v",
				tt.fovy, tt.a, tt.n, tt.f, m, tt.want)
		}
	}
}

func TestMat4LookAt(t *testing.T) {
	tests := []struct {
		eye, center, up Vec3
		want            Mat4
	}{
		{V3(1, 1, 1), V3(1, 1, 0), V3(0, 1, 0), Mat4{
			{1, 0, 0, 0},
			{0, 1, 0, 0},
			{0, 0, 1, 0},
			{-1, -1, -1, 1},
		}},
		{V3(0, 0, 1), V3(0, 0, -1), V3(0, 1, 0), Mat4{
			{1, 0, 0, 0},
			{0, 1, 0, 0},
			{0, 0, 1, 0},
			{0, 0, -1, 1},
		}},
		{V3(20, 80, 15), V3(15, 0, 12), V3(0, -1, 0), Mat4{
			{-0.5144958, 0.8552243, 0.06233464, 0},
			{0, -0.07269406, 0.99735427, 0},
			{0.857493, 0.5131346, 0.037400786, 0},
			{-2.5724783, -18.985981, -81.596054, 1},
		}},
	}
	var m Mat4
	for _, tt := range tests {
		m.LookAt(tt.eye, tt.center, tt.up)
		if m != tt.want {
			t.Errorf("m.LookAt(%s, %s, %s) = %v, want %v",
				tt.eye, tt.center, tt.up, m, tt.want)
		}
	}
}

func TestMat4Floats(t *testing.T) {
	m := Mat4{
		{11, 12, 13, 14},
		{21, 22, 23, 24},
		{31, 32, 33, 34},
		{41, 42, 43, 44},
	}
	want := [16]float32{11, 12, 13, 14, 21, 22, 23, 24, 31, 32, 33, 34, 41, 42, 43, 44}
	f := m.Floats()
	if *f != want {
		t.Errorf("%v.Floats() = %v, want %v", m, *f, want)
	}
	f[6] = 99
	if m[1][2] != 99 {
		t.Errorf("Pointer to float32 array returned by Floats() does not point to matrix data.")
	}
}

func TestMat4Mul(t *testing.T) {
	tests := []struct {
		a, b, want Mat4
	}{
		{Mat4{
			{1, 2, 3, 4},
			{5, 6, 7, 8},
			{1, 2, 3, 4},
			{5, 6, 7, 8},
		}, Mat4{
			{1, 2, 3, 4},
			{5, 6, 7, 8},
			{1, 2, 3, 4},
			{5, 6, 7, 8},
		}, Mat4{
			{34, 44, 54, 64},
			{82, 108, 134, 160},
			{34, 44, 54, 64},
			{82, 108, 134, 160},
		}},

		{Mat4{
			{2.1, 3.2, 1.2, 0},
			{4.6, -5.3, 5.4, 8.4},
			{-9.1, 1, 0.2, -7.3},
			{-1.25, 2.2, 2.3, 6.3},
		}, Mat4{
			{-3, 2.4, 8.4, 3.3},
			{0.2, -5, 2.6, 1.2},
			{5.3, 8.1, 4.4, 2.5},
			{4.9, -1, 0, 4},
		}, Mat4{
			{0.7, -1.24, 31.24, 13.77},
			{54.92, 72.88, 48.62, 55.92},
			{-7.21, -17.92, -72.96, -57.53},
			{47.25, -1.67, 5.34, 29.465},
		}},
	}
	for _, tt := range tests {
		var m Mat4
		mp := m.Mul(&tt.a, &tt.b)
		if !tt.want.nearEq(&m) {
			t.Errorf("%v * %v = %v, want %v", tt.a, tt.b, m, tt.want)
		}
		if mp != &m {
			t.Errorf("m.Mul(...) does not return the pointer to m")
		}
	}
}
<|endoftext|>