<commit_before>/*

	test is a package that is used to run a boardgame/server.StorageManager
	implementation through its paces and verify it does everything correctly.

*/
package test

import (
	"encoding/json"
	"github.com/jkomoros/boardgame"
	"github.com/jkomoros/boardgame/examples/blackjack"
	"github.com/jkomoros/boardgame/examples/tictactoe"
	"github.com/jkomoros/boardgame/server/api/users"
	"github.com/workfit/tester/assert"
	"reflect"
	"testing"
)

type StorageManager interface {
	boardgame.StorageManager

	//CleanUp will be called when a given manager is done and can be dispoed of.
	CleanUp()

	//The methods past this point are the same ones that are included in Server.StorageManager
	Name() string

	Connect(config string) error

	Close()

	ListGames(max int) []*boardgame.GameStorageRecord

	UserIdsForGame(gameId string) []string

	SetPlayerForGame(gameId string, playerIndex boardgame.PlayerIndex, userId string) error

	UpdateUser(user *users.StorageRecord) error

	GetUserById(uid string) *users.StorageRecord

	GetUserByCookie(cookie string) *users.StorageRecord

	ConnectCookieToUser(cookie string, user *users.StorageRecord) error
}

type managerMap map[string]*boardgame.GameManager

func (m managerMap) Get(name string) *boardgame.GameManager {
	return m[name]
}

type StorageManagerFactory func() StorageManager

func Test(factory StorageManagerFactory, testName string, connectConfig string, t *testing.T) {

	BasicTest(factory, testName, connectConfig, t)
	UsersTest(factory, testName, connectConfig, t)

}

func BasicTest(factory StorageManagerFactory, testName string, connectConfig string, t *testing.T) {
	storage := factory()

	defer storage.CleanUp()

	if err := storage.Connect(connectConfig); err != nil {
		t.Fatal("Unexpected error connecting: ", err.Error())
	}

	assert.For(t).ThatActual(storage.Name()).Equals(testName)

	managers := make(managerMap)

	tictactoeManager := tictactoe.NewManager(storage)

	managers[tictactoeManager.Delegate().Name()] = tictactoeManager

	blackjackManager := blackjack.NewManager(storage)

	managers[blackjackManager.Delegate().Name()] = blackjackManager

	tictactoeGame := boardgame.NewGame(tictactoeManager)

	tictactoeGame.SetUp(0)

	move := tictactoeGame.PlayerMoveByName("Place Token")

	if move == nil {
		t.Fatal(testName, "Couldn't find a move")
	}

	if err := <-tictactoeGame.ProposeMove(move, boardgame.AdminPlayerIndex); err != nil {
		t.Fatal(testName, "Couldn't make move", err)
	}

	//OK, now test that the manager and SetUp and everyone did the right thing.

	localGame, err := storage.Game(tictactoeGame.Id())

	if err != nil {
		t.Error(testName, "Unexpected error", err)
	}

	if localGame == nil {
		t.Fatal(testName, "Couldn't get game copy out")
	}

	blob, err := json.MarshalIndent(tictactoeGame.StorageRecord(), "", "  ")

	if err != nil {
		t.Fatal(testName, "couldn't marshal game", err)
	}

	localBlob, err := json.MarshalIndent(localGame, "", "  ")

	if err != nil {
		t.Fatal(testName, "Couldn't marshal localGame", err)
	}

	compareJSONObjects(blob, localBlob, testName+"Comparing game and local game", t)

	//Verify that if the game is stored with wrong name that doesn't match manager it won't load up.

	blackjackGame := boardgame.NewGame(blackjackManager)

	blackjackGame.SetUp(0)

	games := storage.ListGames(10)

	if games == nil {
		t.Error(testName, "ListGames gave back nothing")
	}

	if len(games) != 2 {
		t.Error(testName, "We called listgames with a tictactoe game and a blackjack game, but got", len(games), "back.")
	}

	//TODO: figure out how to test that name is matched when retrieving from store.

}

func UsersTest(factory StorageManagerFactory, testName string, connectConfig string, t *testing.T) {
	storage := factory()

	defer storage.CleanUp()

	if err := storage.Connect(connectConfig); err != nil {
		t.Fatal("Err connecting to storage: ", err)
	}

	manager := tictactoe.NewManager(storage)

	game := boardgame.NewGame(manager)

	game.SetUp(2)

	var nilIds []string

	ids := storage.UserIdsForGame("DEADBEEF")

	assert.For(t).ThatActual(ids).Equals(nilIds)

	ids = storage.UserIdsForGame(game.Id())

	assert.For(t).ThatActual(ids).Equals([]string{"", ""})

	userId := "MYUSERID"

	cookie := "MYCOOKIE"

	fetchedUser := storage.GetUserById(userId)

	var nilUser *users.StorageRecord

	assert.For(t).ThatActual(fetchedUser).Equals(nilUser)

	user := &users.StorageRecord{Id: userId}

	err := storage.UpdateUser(user)

	assert.For(t).ThatActual(err).IsNil()

	fetchedUser = storage.GetUserById(userId)

	assert.For(t).ThatActual(fetchedUser).Equals(user)

	fetchedUser = storage.GetUserByCookie(cookie)

	assert.For(t).ThatActual(fetchedUser).Equals(nilUser)

	err = storage.ConnectCookieToUser(cookie, user)

	assert.For(t).ThatActual(err).IsNil()

	fetchedUser = storage.GetUserByCookie(cookie)

	assert.For(t).ThatActual(fetchedUser).Equals(user)

	err = storage.SetPlayerForGame(game.Id(), 0, userId)

	assert.For(t).ThatActual(err).IsNil()

	ids = storage.UserIdsForGame(game.Id())

	assert.For(t).ThatActual(ids).Equals([]string{userId, ""})

	err = storage.SetPlayerForGame(game.Id(), 0, userId)

	assert.For(t).ThatActual(err).IsNotNil()
}

func compareJSONObjects(in []byte, golden []byte, message string, t *testing.T) {

	//recreated in boardgame/state_test.go

	var deserializedIn interface{}
	var deserializedGolden interface{}

	json.Unmarshal(in, &deserializedIn)
	json.Unmarshal(golden, &deserializedGolden)

	if deserializedIn == nil {
		t.Error("In didn't deserialize", message)
	}

	if deserializedGolden == nil {
		t.Error("Golden didn't deserialize", message)
	}

	if !reflect.DeepEqual(deserializedIn, deserializedGolden) {
		t.Error("Got wrong json.", message, "Got", string(in), "wanted", string(golden))
	}
}
<commit_msg>Close databases at end of test. Part of #255.<commit_after>/*

	test is a package that is used to run a boardgame/server.StorageManager
	implementation through its paces and verify it does everything correctly.

*/
package test

import (
	"encoding/json"
	"github.com/jkomoros/boardgame"
	"github.com/jkomoros/boardgame/examples/blackjack"
	"github.com/jkomoros/boardgame/examples/tictactoe"
	"github.com/jkomoros/boardgame/server/api/users"
	"github.com/workfit/tester/assert"
	"reflect"
	"testing"
)

type StorageManager interface {
	boardgame.StorageManager

	//CleanUp will be called when a given manager is done and can be dispoed of.
	CleanUp()

	//The methods past this point are the same ones that are included in Server.StorageManager
	Name() string

	Connect(config string) error

	Close()

	ListGames(max int) []*boardgame.GameStorageRecord

	UserIdsForGame(gameId string) []string

	SetPlayerForGame(gameId string, playerIndex boardgame.PlayerIndex, userId string) error

	UpdateUser(user *users.StorageRecord) error

	GetUserById(uid string) *users.StorageRecord

	GetUserByCookie(cookie string) *users.StorageRecord

	ConnectCookieToUser(cookie string, user *users.StorageRecord) error
}

type managerMap map[string]*boardgame.GameManager

func (m managerMap) Get(name string) *boardgame.GameManager {
	return m[name]
}

type StorageManagerFactory func() StorageManager

func Test(factory StorageManagerFactory, testName string, connectConfig string, t *testing.T) {

	BasicTest(factory, testName, connectConfig, t)
	UsersTest(factory, testName, connectConfig, t)

}

func BasicTest(factory StorageManagerFactory, testName string, connectConfig string, t *testing.T) {
	storage := factory()

	defer storage.Close()

	defer storage.CleanUp()

	if err := storage.Connect(connectConfig); err != nil {
		t.Fatal("Unexpected error connecting: ", err.Error())
	}

	assert.For(t).ThatActual(storage.Name()).Equals(testName)

	managers := make(managerMap)

	tictactoeManager := tictactoe.NewManager(storage)

	managers[tictactoeManager.Delegate().Name()] = tictactoeManager

	blackjackManager := blackjack.NewManager(storage)

	managers[blackjackManager.Delegate().Name()] = blackjackManager

	tictactoeGame := boardgame.NewGame(tictactoeManager)

	tictactoeGame.SetUp(0)

	move := tictactoeGame.PlayerMoveByName("Place Token")

	if move == nil {
		t.Fatal(testName, "Couldn't find a move")
	}

	if err := <-tictactoeGame.ProposeMove(move, boardgame.AdminPlayerIndex); err != nil {
		t.Fatal(testName, "Couldn't make move", err)
	}

	//OK, now test that the manager and SetUp and everyone did the right thing.

	localGame, err := storage.Game(tictactoeGame.Id())

	if err != nil {
		t.Error(testName, "Unexpected error", err)
	}

	if localGame == nil {
		t.Fatal(testName, "Couldn't get game copy out")
	}

	blob, err := json.MarshalIndent(tictactoeGame.StorageRecord(), "", "  ")

	if err != nil {
		t.Fatal(testName, "couldn't marshal game", err)
	}

	localBlob, err := json.MarshalIndent(localGame, "", "  ")

	if err != nil {
		t.Fatal(testName, "Couldn't marshal localGame", err)
	}

	compareJSONObjects(blob, localBlob, testName+"Comparing game and local game", t)

	//Verify that if the game is stored with wrong name that doesn't match manager it won't load up.

	blackjackGame := boardgame.NewGame(blackjackManager)

	blackjackGame.SetUp(0)

	games := storage.ListGames(10)

	if games == nil {
		t.Error(testName, "ListGames gave back nothing")
	}

	if len(games) != 2 {
		t.Error(testName, "We called listgames with a tictactoe game and a blackjack game, but got", len(games), "back.")
	}

	//TODO: figure out how to test that name is matched when retrieving from store.

}

func UsersTest(factory StorageManagerFactory, testName string, connectConfig string, t *testing.T) {
	storage := factory()

	defer storage.Close()

	defer storage.CleanUp()

	if err := storage.Connect(connectConfig); err != nil {
		t.Fatal("Err connecting to storage: ", err)
	}

	manager := tictactoe.NewManager(storage)

	game := boardgame.NewGame(manager)

	game.SetUp(2)

	var nilIds []string

	ids := storage.UserIdsForGame("DEADBEEF")

	assert.For(t).ThatActual(ids).Equals(nilIds)

	ids = storage.UserIdsForGame(game.Id())

	assert.For(t).ThatActual(ids).Equals([]string{"", ""})

	userId := "MYUSERID"

	cookie := "MYCOOKIE"

	fetchedUser := storage.GetUserById(userId)

	var nilUser *users.StorageRecord

	assert.For(t).ThatActual(fetchedUser).Equals(nilUser)

	user := &users.StorageRecord{Id: userId}

	err := storage.UpdateUser(user)

	assert.For(t).ThatActual(err).IsNil()

	fetchedUser = storage.GetUserById(userId)

	assert.For(t).ThatActual(fetchedUser).Equals(user)

	fetchedUser = storage.GetUserByCookie(cookie)

	assert.For(t).ThatActual(fetchedUser).Equals(nilUser)

	err = storage.ConnectCookieToUser(cookie, user)

	assert.For(t).ThatActual(err).IsNil()

	fetchedUser = storage.GetUserByCookie(cookie)

	assert.For(t).ThatActual(fetchedUser).Equals(user)

	err = storage.SetPlayerForGame(game.Id(), 0, userId)

	assert.For(t).ThatActual(err).IsNil()

	ids = storage.UserIdsForGame(game.Id())

	assert.For(t).ThatActual(ids).Equals([]string{userId, ""})

	err = storage.SetPlayerForGame(game.Id(), 0, userId)

	assert.For(t).ThatActual(err).IsNotNil()
}

func compareJSONObjects(in []byte, golden []byte, message string, t *testing.T) {

	//recreated in boardgame/state_test.go

	var deserializedIn interface{}
	var deserializedGolden interface{}

	json.Unmarshal(in, &deserializedIn)
	json.Unmarshal(golden, &deserializedGolden)

	if deserializedIn == nil {
		t.Error("In didn't deserialize", message)
	}

	if deserializedGolden == nil {
		t.Error("Golden didn't deserialize", message)
	}

	if !reflect.DeepEqual(deserializedIn, deserializedGolden) {
		t.Error("Got wrong json.", message, "Got", string(in), "wanted", string(golden))
	}
}
<|endoftext|>


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

package main

import (
	"bytes"
	"context"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"text/template"

	"github.com/google/go-github/v26/github"
	"golang.org/x/oauth2"
	"gopkg.in/alecthomas/kingpin.v2"
)

type githubClient struct {
	clt   *github.Client
	owner string
	repo  string
	pr    int
}

type commentMonitorClient struct {
	ghClient           githubClient
	allArgs            map[string]string
	inputFilePath      string
	outputDirPath      string
	regexString        string
	regex              *regexp.Regexp
	verifyUserDisabled bool
}

func main() {
	log.SetFlags(log.Ltime | log.Lshortfile)
	cmClient := commentMonitorClient{
		allArgs: make(map[string]string),
	}

	app := kingpin.New(filepath.Base(os.Args[0]), `commentMonitor github comment extract
	./commentMonitor -i /path/event.json -o /path "^myregex$"
	Example of comment template environment variable:
	COMMENT_TEMPLATE="The benchmark is starting. Your Github token is {{ index . "SOME_VAR" }}."`)
	app.HelpFlag.Short('h')
	app.Flag("input", "path to event.json").
		Short('i').
		Default("/github/workflow/event.json").
		StringVar(&cmClient.inputFilePath)
	app.Flag("output", "path to write args to").
		Short('o').
		Default("/github/home/commentMonitor").
		StringVar(&cmClient.outputDirPath)
	app.Flag("no-verify-user", "disable verifying user").
		BoolVar(&cmClient.verifyUserDisabled)
	app.Arg("regex", "Regex pattern to match").
		StringVar(&cmClient.regexString)
	kingpin.MustParse(app.Parse(os.Args[1:]))

	err := os.MkdirAll(cmClient.outputDirPath, os.ModePerm)
	if err != nil {
		log.Fatalln(err)
	}
	data, err := ioutil.ReadFile(cmClient.inputFilePath)
	if err != nil {
		log.Fatalln(err)
	}

	// Temporary fix for the new Github actions time format. This makes the time stamps unusable.
	txt := string(data)
	reg := regexp.MustCompile("(.*)\"[0-9]+/[0-9]+/2019 [0-9]+:[0-9]+:[0-9]+ [AP]M(.*)")
	txt = reg.ReplaceAllString(txt, "$1\"2019-06-11T09:26:28Z$2")
	data = []byte(txt)
	log.Println("temp fix active")
	// End of the temporary fix

	// Parsing event.json.
	event, err := github.ParseWebHook("issue_comment", data)
	if err != nil {
		log.Fatalln(err)
	}

	switch e := event.(type) {
	case *github.IssueCommentEvent:

		owner := *e.GetRepo().Owner.Login
		repo := *e.GetRepo().Name
		pr := *e.GetIssue().Number
		author := *e.Sender.Login
		authorAssociation := *e.GetComment().AuthorAssociation
		commentBody := *e.GetComment().Body

		// Setup commentMonitorClient.
		ctx := context.Background()
		cmClient.ghClient = newGithubClient(ctx, owner, repo, pr)

		// Validate comment if regexString provided.
		if cmClient.regexString != "" {
			cmClient.regex = regexp.MustCompile(cmClient.regexString)
			if !cmClient.regex.MatchString(commentBody) {
				log.Fatalf("matching command not found. comment validation failed")
			}
			log.Println("comment validation successful")
		}

		// Verify if user is allowed to perform activity.
		if !cmClient.verifyUserDisabled {
			var allowed bool
			allowedAssociations := []string{"COLLABORATOR", "MEMBER", "OWNER"}
			for _, a := range allowedAssociations {
				if a == authorAssociation {
					allowed = true
				}
			}
			if !allowed {
				log.Printf("author is not a member or collaborator")
				b := fmt.Sprintf("@%s is not a org member nor a collaborator and cannot execute benchmarks.", author)
				if err := cmClient.ghClient.postComment(ctx, b); err != nil {
					log.Fatalf("%v : couldn't post comment", err)
				}
				os.Exit(1)
			}
			log.Println("author is a member or collaborator")
		}

		// Extract args if regexString provided.
		if cmClient.regexString != "" {
			// Add comment arguments.
			commentArgs := cmClient.regex.FindStringSubmatch(commentBody)[1:]
			commentArgsNames := cmClient.regex.SubexpNames()[1:]
			for i, argName := range commentArgsNames {
				if argName == "" {
					log.Fatalln("using named groups is mandatory")
				}
				cmClient.allArgs[argName] = commentArgs[i]
			}

			// Add non-comment arguments if any.
			cmClient.allArgs["PR_NUMBER"] = strconv.Itoa(pr)

			err := cmClient.writeArgs()
			if err != nil {
				log.Fatalf("%v: could not write args to fs", err)
			}
		}

		// Post generated comment to Github pr if COMMENT_TEMPLATE is set.
		if os.Getenv("COMMENT_TEMPLATE") != "" {
			err := cmClient.generateAndPostComment(ctx)
			if err != nil {
				log.Fatalf("%v: could not post comment", err)
			}
		}

		// Set label to Github pr if LABEL_NAME is set.
		if os.Getenv("LABEL_NAME") != "" {
			if err := cmClient.ghClient.createLabel(ctx, os.Getenv("LABEL_NAME")); err != nil {
				log.Fatalf("%v : couldn't set label", err)
			}
			log.Println("label successfully set")
		}

	default:
		log.Fatalln("only issue_comment event is supported")
	}
}

func (c commentMonitorClient) writeArgs() error {
	for filename, content := range c.allArgs {
		data := []byte(content)
		err := ioutil.WriteFile(filepath.Join(c.outputDirPath, filename), data, 0644)
		if err != nil {
			return fmt.Errorf("%v: could not write arg to filesystem", err)
		}
		log.Printf("file added: %v", filepath.Join(c.outputDirPath, filename))
	}
	return nil
}

func (c commentMonitorClient) generateAndPostComment(ctx context.Context) error {
	// Add all env vars to allArgs.
	for _, e := range os.Environ() {
		tmp := strings.Split(e, "=")
		c.allArgs[tmp[0]] = tmp[1]
	}
	// Generate the comment template.
	var buf bytes.Buffer
	commentTemplate := template.Must(template.New("Comment").
		Parse(os.Getenv("COMMENT_TEMPLATE")))
	if err := commentTemplate.Execute(&buf, c.allArgs); err != nil {
		return err
	}
	// Post the comment.
	if err := c.ghClient.postComment(ctx, buf.String()); err != nil {
		return fmt.Errorf("%v : couldn't post generated comment", err)
	}
	log.Println("comment successfully posted")
	return nil
}

// githubClient Methods
func newGithubClient(ctx context.Context, owner, repo string, pr int) githubClient {
	ghToken := os.Getenv("GITHUB_TOKEN")
	ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: ghToken})
	tc := oauth2.NewClient(ctx, ts)
	return githubClient{
		clt:   github.NewClient(tc),
		owner: owner,
		repo:  repo,
		pr:    pr,
	}
}

func (c githubClient) postComment(ctx context.Context, commentBody string) error {
	issueComment := &github.IssueComment{Body: github.String(commentBody)}
	_, _, err := c.clt.Issues.CreateComment(ctx, c.owner, c.repo, c.pr, issueComment)
	return err
}

func (c githubClient) createLabel(ctx context.Context, labelName string) error {
	benchmarkLabel := []string{labelName}
	_, _, err := c.clt.Issues.AddLabelsToIssue(ctx, c.owner, c.repo, c.pr, benchmarkLabel)
	return err
}
<commit_msg>added date check github action fix (#267)<commit_after>// Copyright 2019 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"bytes"
	"context"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"text/template"

	"github.com/google/go-github/v26/github"
	"golang.org/x/oauth2"
	"gopkg.in/alecthomas/kingpin.v2"
)

type githubClient struct {
	clt   *github.Client
	owner string
	repo  string
	pr    int
}

type commentMonitorClient struct {
	ghClient           githubClient
	allArgs            map[string]string
	inputFilePath      string
	outputDirPath      string
	regexString        string
	regex              *regexp.Regexp
	verifyUserDisabled bool
}

func main() {
	log.SetFlags(log.Ltime | log.Lshortfile)
	cmClient := commentMonitorClient{
		allArgs: make(map[string]string),
	}

	app := kingpin.New(filepath.Base(os.Args[0]), `commentMonitor github comment extract
	./commentMonitor -i /path/event.json -o /path "^myregex$"
	Example of comment template environment variable:
	COMMENT_TEMPLATE="The benchmark is starting. Your Github token is {{ index . "SOME_VAR" }}."`)
	app.HelpFlag.Short('h')
	app.Flag("input", "path to event.json").
		Short('i').
		Default("/github/workflow/event.json").
		StringVar(&cmClient.inputFilePath)
	app.Flag("output", "path to write args to").
		Short('o').
		Default("/github/home/commentMonitor").
		StringVar(&cmClient.outputDirPath)
	app.Flag("no-verify-user", "disable verifying user").
		BoolVar(&cmClient.verifyUserDisabled)
	app.Arg("regex", "Regex pattern to match").
		StringVar(&cmClient.regexString)
	kingpin.MustParse(app.Parse(os.Args[1:]))

	err := os.MkdirAll(cmClient.outputDirPath, os.ModePerm)
	if err != nil {
		log.Fatalln(err)
	}
	data, err := ioutil.ReadFile(cmClient.inputFilePath)
	if err != nil {
		log.Fatalln(err)
	}

	// Temporary fix for the new Github actions time format. This makes the time stamps unusable.
	reg := regexp.MustCompile("(.*)\"[0-9]+/[0-9]+/2019 [0-9]+:[0-9]+:[0-9]+ [AP]M(.*)")
	m := reg.FindSubmatch(data)
	if m != nil {
		txt := string(data)
		txt = reg.ReplaceAllString(txt, "$1\"2019-06-11T09:26:28Z$2")
		data = []byte(txt)
		log.Println("temp fix active")
	} else {
		log.Println(`WARNING: Github actions outputs correct date format so can
		remove the workaround fix in the code commentMonitor.
		https://github.com/google/go-github/issues/1254`)
	}
	// End of the temporary fix

	// Parsing event.json.
	event, err := github.ParseWebHook("issue_comment", data)
	if err != nil {
		log.Fatalln(err)
	}

	switch e := event.(type) {
	case *github.IssueCommentEvent:

		owner := *e.GetRepo().Owner.Login
		repo := *e.GetRepo().Name
		pr := *e.GetIssue().Number
		author := *e.Sender.Login
		authorAssociation := *e.GetComment().AuthorAssociation
		commentBody := *e.GetComment().Body

		// Setup commentMonitorClient.
		ctx := context.Background()
		cmClient.ghClient = newGithubClient(ctx, owner, repo, pr)

		// Validate comment if regexString provided.
		if cmClient.regexString != "" {
			cmClient.regex = regexp.MustCompile(cmClient.regexString)
			if !cmClient.regex.MatchString(commentBody) {
				log.Fatalf("matching command not found. comment validation failed")
			}
			log.Println("comment validation successful")
		}

		// Verify if user is allowed to perform activity.
		if !cmClient.verifyUserDisabled {
			var allowed bool
			allowedAssociations := []string{"COLLABORATOR", "MEMBER", "OWNER"}
			for _, a := range allowedAssociations {
				if a == authorAssociation {
					allowed = true
				}
			}
			if !allowed {
				log.Printf("author is not a member or collaborator")
				b := fmt.Sprintf("@%s is not a org member nor a collaborator and cannot execute benchmarks.", author)
				if err := cmClient.ghClient.postComment(ctx, b); err != nil {
					log.Fatalf("%v : couldn't post comment", err)
				}
				os.Exit(1)
			}
			log.Println("author is a member or collaborator")
		}

		// Extract args if regexString provided.
		if cmClient.regexString != "" {
			// Add comment arguments.
			commentArgs := cmClient.regex.FindStringSubmatch(commentBody)[1:]
			commentArgsNames := cmClient.regex.SubexpNames()[1:]
			for i, argName := range commentArgsNames {
				if argName == "" {
					log.Fatalln("using named groups is mandatory")
				}
				cmClient.allArgs[argName] = commentArgs[i]
			}

			// Add non-comment arguments if any.
			cmClient.allArgs["PR_NUMBER"] = strconv.Itoa(pr)

			err := cmClient.writeArgs()
			if err != nil {
				log.Fatalf("%v: could not write args to fs", err)
			}
		}

		// Post generated comment to Github pr if COMMENT_TEMPLATE is set.
		if os.Getenv("COMMENT_TEMPLATE") != "" {
			err := cmClient.generateAndPostComment(ctx)
			if err != nil {
				log.Fatalf("%v: could not post comment", err)
			}
		}

		// Set label to Github pr if LABEL_NAME is set.
		if os.Getenv("LABEL_NAME") != "" {
			if err := cmClient.ghClient.createLabel(ctx, os.Getenv("LABEL_NAME")); err != nil {
				log.Fatalf("%v : couldn't set label", err)
			}
			log.Println("label successfully set")
		}

	default:
		log.Fatalln("only issue_comment event is supported")
	}
}

func (c commentMonitorClient) writeArgs() error {
	for filename, content := range c.allArgs {
		data := []byte(content)
		err := ioutil.WriteFile(filepath.Join(c.outputDirPath, filename), data, 0644)
		if err != nil {
			return fmt.Errorf("%v: could not write arg to filesystem", err)
		}
		log.Printf("file added: %v", filepath.Join(c.outputDirPath, filename))
	}
	return nil
}

func (c commentMonitorClient) generateAndPostComment(ctx context.Context) error {
	// Add all env vars to allArgs.
	for _, e := range os.Environ() {
		tmp := strings.Split(e, "=")
		c.allArgs[tmp[0]] = tmp[1]
	}
	// Generate the comment template.
	var buf bytes.Buffer
	commentTemplate := template.Must(template.New("Comment").
		Parse(os.Getenv("COMMENT_TEMPLATE")))
	if err := commentTemplate.Execute(&buf, c.allArgs); err != nil {
		return err
	}
	// Post the comment.
	if err := c.ghClient.postComment(ctx, buf.String()); err != nil {
		return fmt.Errorf("%v : couldn't post generated comment", err)
	}
	log.Println("comment successfully posted")
	return nil
}

// githubClient Methods
func newGithubClient(ctx context.Context, owner, repo string, pr int) githubClient {
	ghToken := os.Getenv("GITHUB_TOKEN")
	ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: ghToken})
	tc := oauth2.NewClient(ctx, ts)
	return githubClient{
		clt:   github.NewClient(tc),
		owner: owner,
		repo:  repo,
		pr:    pr,
	}
}

func (c githubClient) postComment(ctx context.Context, commentBody string) error {
	issueComment := &github.IssueComment{Body: github.String(commentBody)}
	_, _, err := c.clt.Issues.CreateComment(ctx, c.owner, c.repo, c.pr, issueComment)
	return err
}

func (c githubClient) createLabel(ctx context.Context, labelName string) error {
	benchmarkLabel := []string{labelName}
	_, _, err := c.clt.Issues.AddLabelsToIssue(ctx, c.owner, c.repo, c.pr, benchmarkLabel)
	return err
}
<|endoftext|>


<commit_before>/*

sudokustate manages modifications to a given grid, allowing easy undo/redo,
and keeping track of all moves.

Moves can be made either by setting a number on a given cell or setting
multiple marks on a given cell.

When multiple modifications are logically part of the same move (for the
purposes of undo/redo), a group can be created. Call `model.StartGroup`, make
the changes, then `model.FinishGroupAndExecute` to make all of the changes in
one 'move'.

*/
package sudokustate

import (
	"github.com/jkomoros/sudoku"
)

type Model struct {
	grid                   sudoku.MutableGrid
	currentCommand         *commandList
	commands               *commandList
	inProgressMultiCommand *multiCommand
}

type commandList struct {
	c    command
	next *commandList
	prev *commandList
}

type command interface {
	Apply(m *Model)
	Undo(m *Model)
	ModifiedCells(m *Model) sudoku.CellSlice
}

type baseCommand struct {
	row, col int
}

func (b *baseCommand) ModifiedCells(m *Model) sudoku.CellSlice {
	if m == nil || m.grid == nil {
		return nil
	}
	return sudoku.CellSlice{m.grid.Cell(b.row, b.col)}
}

func (m *multiCommand) ModifiedCells(model *Model) sudoku.CellSlice {
	var result sudoku.CellSlice

	for _, command := range m.commands {
		result = append(result, command.ModifiedCells(model)...)
	}

	return result
}

func (m *multiCommand) AddCommand(c command) {
	m.commands = append(m.commands, c)
}

type markCommand struct {
	baseCommand
	marksToggle map[int]bool
}

type numberCommand struct {
	baseCommand
	number int
	//Necessary so we can undo.
	oldNumber int
}

type multiCommand struct {
	commands []command
}

//Grid returns the underlying Grid managed by this Model. It's a non-mutable
//reference to emphasize that all mutations should be done by the Model
//itself.
func (m *Model) Grid() sudoku.Grid {
	return m.grid
}

func (m *Model) executeCommand(c command) {
	listItem := &commandList{
		c:    c,
		next: nil,
		prev: m.currentCommand,
	}

	m.commands = listItem
	if m.currentCommand != nil {
		m.currentCommand.next = listItem
	}
	m.currentCommand = listItem

	c.Apply(m)
}

func (m *Model) LastModifiedCells() sudoku.CellSlice {
	if m.currentCommand == nil {
		return nil
	}

	return m.currentCommand.c.ModifiedCells(m)
}

//Undo returns true if there was something to undo.
func (m *Model) Undo() bool {
	if m.currentCommand == nil {
		return false
	}

	m.currentCommand.c.Undo(m)

	m.currentCommand = m.currentCommand.prev

	return true
}

//Redo returns true if there was something to redo.
func (m *Model) Redo() bool {

	if m.commands == nil {
		return false
	}

	var commandToApply *commandList

	if m.currentCommand == nil {
		//If there is a non-nil commands, go all the way to the beginning,
		//because we're currently pointing at state 0
		commandToApply = m.commands
		for commandToApply.prev != nil {
			commandToApply = commandToApply.prev
		}
	} else {
		//Normaly operation is just to move to the next command in the list
		//and apply it.
		commandToApply = m.currentCommand.next
	}

	if commandToApply == nil {
		return false
	}

	m.currentCommand = commandToApply

	m.currentCommand.c.Apply(m)

	return true
}

func (m *Model) StartGroup() {
	m.inProgressMultiCommand = &multiCommand{
		nil,
	}
}

func (m *Model) FinishGroupAndExecute() {
	if m.inProgressMultiCommand == nil {
		return
	}
	m.executeCommand(m.inProgressMultiCommand)
	m.inProgressMultiCommand = nil
}

func (m *Model) CancelGroup() {
	m.inProgressMultiCommand = nil
}

func (m *Model) InGroup() bool {
	return m.inProgressMultiCommand != nil
}

//Reset resets the current grid back to its fully unfilled states and discards
//all commands.
func (m *Model) Reset() {
	//TODO: test this.
	m.commands = nil
	m.currentCommand = nil
	if m.grid != nil {
		m.grid.ResetUnlockedCells()
	}
}

func (m *Model) SetGrid(grid sudoku.MutableGrid) {
	m.grid = grid
	m.Reset()
}

func (m *Model) SetMarks(row, col int, marksToggle map[int]bool) {
	//TODO: should this take a cellRef?
	command := m.newMarkCommand(row, col, marksToggle)
	if command == nil {
		return
	}
	if m.InGroup() {
		m.inProgressMultiCommand.AddCommand(command)
	} else {
		m.executeCommand(command)
	}
}

func (m *Model) newMarkCommand(row, col int, marksToggle map[int]bool) *markCommand {
	//Only keep marks in the toggle that won't be a no-op
	newMarksToggle := make(map[int]bool)

	cell := m.grid.Cell(row, col)

	if cell == nil {
		return nil
	}
	for key, value := range marksToggle {
		if cell.Mark(key) != value {
			//Good, keep it
			newMarksToggle[key] = value
		}
	}

	if len(newMarksToggle) == 0 {
		//The command would be a no op!
		return nil
	}

	return &markCommand{baseCommand{row, col}, newMarksToggle}
}

func (m *Model) SetNumber(row, col int, num int) {
	//TODO: should this take CellRef?
	command := m.newNumberCommand(row, col, num)
	if command == nil {
		return
	}
	if m.InGroup() {
		m.inProgressMultiCommand.AddCommand(command)
	} else {
		m.executeCommand(command)
	}
}

func (m *Model) newNumberCommand(row, col int, num int) *numberCommand {
	cell := m.grid.Cell(row, col)

	if cell == nil {
		return nil
	}

	if cell.Number() == num {
		return nil
	}

	return &numberCommand{baseCommand{row, col}, num, cell.Number()}
}

func (m *markCommand) Apply(model *Model) {
	cell := model.grid.MutableCell(m.row, m.col)
	if cell == nil {
		return
	}
	for key, value := range m.marksToggle {
		cell.SetMark(key, value)
	}
}

func (m *markCommand) Undo(model *Model) {
	cell := model.grid.MutableCell(m.row, m.col)
	if cell == nil {
		return
	}
	for key, value := range m.marksToggle {
		//Set the opposite since we're undoing.
		cell.SetMark(key, !value)
	}
}

func (n *numberCommand) Apply(model *Model) {
	cell := model.grid.MutableCell(n.row, n.col)
	if cell == nil {
		return
	}
	cell.SetNumber(n.number)
}

func (n *numberCommand) Undo(model *Model) {
	cell := model.grid.MutableCell(n.row, n.col)
	if cell == nil {
		return
	}
	cell.SetNumber(n.oldNumber)
}

func (m *multiCommand) Apply(model *Model) {
	for _, command := range m.commands {
		command.Apply(model)
	}
}

func (m *multiCommand) Undo(model *Model) {
	for i := len(m.commands) - 1; i >= 0; i-- {
		m.commands[i].Undo(model)
	}
}
<commit_msg>Added a comment about usage of Model.<commit_after>/*

sudokustate manages modifications to a given grid, allowing easy undo/redo,
and keeping track of all moves.

Moves can be made either by setting a number on a given cell or setting
multiple marks on a given cell.

When multiple modifications are logically part of the same move (for the
purposes of undo/redo), a group can be created. Call `model.StartGroup`, make
the changes, then `model.FinishGroupAndExecute` to make all of the changes in
one 'move'.

*/
package sudokustate

import (
	"github.com/jkomoros/sudoku"
)

//Model maintains all of the state and modifications to the grid. The zero-
//state is valid; create a new Model with sudokustate.Model{}.
type Model struct {
	grid                   sudoku.MutableGrid
	currentCommand         *commandList
	commands               *commandList
	inProgressMultiCommand *multiCommand
}

type commandList struct {
	c    command
	next *commandList
	prev *commandList
}

type command interface {
	Apply(m *Model)
	Undo(m *Model)
	ModifiedCells(m *Model) sudoku.CellSlice
}

type baseCommand struct {
	row, col int
}

func (b *baseCommand) ModifiedCells(m *Model) sudoku.CellSlice {
	if m == nil || m.grid == nil {
		return nil
	}
	return sudoku.CellSlice{m.grid.Cell(b.row, b.col)}
}

func (m *multiCommand) ModifiedCells(model *Model) sudoku.CellSlice {
	var result sudoku.CellSlice

	for _, command := range m.commands {
		result = append(result, command.ModifiedCells(model)...)
	}

	return result
}

func (m *multiCommand) AddCommand(c command) {
	m.commands = append(m.commands, c)
}

type markCommand struct {
	baseCommand
	marksToggle map[int]bool
}

type numberCommand struct {
	baseCommand
	number int
	//Necessary so we can undo.
	oldNumber int
}

type multiCommand struct {
	commands []command
}

//Grid returns the underlying Grid managed by this Model. It's a non-mutable
//reference to emphasize that all mutations should be done by the Model
//itself.
func (m *Model) Grid() sudoku.Grid {
	return m.grid
}

func (m *Model) executeCommand(c command) {
	listItem := &commandList{
		c:    c,
		next: nil,
		prev: m.currentCommand,
	}

	m.commands = listItem
	if m.currentCommand != nil {
		m.currentCommand.next = listItem
	}
	m.currentCommand = listItem

	c.Apply(m)
}

func (m *Model) LastModifiedCells() sudoku.CellSlice {
	if m.currentCommand == nil {
		return nil
	}

	return m.currentCommand.c.ModifiedCells(m)
}

//Undo returns true if there was something to undo.
func (m *Model) Undo() bool {
	if m.currentCommand == nil {
		return false
	}

	m.currentCommand.c.Undo(m)

	m.currentCommand = m.currentCommand.prev

	return true
}

//Redo returns true if there was something to redo.
func (m *Model) Redo() bool {

	if m.commands == nil {
		return false
	}

	var commandToApply *commandList

	if m.currentCommand == nil {
		//If there is a non-nil commands, go all the way to the beginning,
		//because we're currently pointing at state 0
		commandToApply = m.commands
		for commandToApply.prev != nil {
			commandToApply = commandToApply.prev
		}
	} else {
		//Normaly operation is just to move to the next command in the list
		//and apply it.
		commandToApply = m.currentCommand.next
	}

	if commandToApply == nil {
		return false
	}

	m.currentCommand = commandToApply

	m.currentCommand.c.Apply(m)

	return true
}

func (m *Model) StartGroup() {
	m.inProgressMultiCommand = &multiCommand{
		nil,
	}
}

func (m *Model) FinishGroupAndExecute() {
	if m.inProgressMultiCommand == nil {
		return
	}
	m.executeCommand(m.inProgressMultiCommand)
	m.inProgressMultiCommand = nil
}

func (m *Model) CancelGroup() {
	m.inProgressMultiCommand = nil
}

func (m *Model) InGroup() bool {
	return m.inProgressMultiCommand != nil
}

//Reset resets the current grid back to its fully unfilled states and discards
//all commands.
func (m *Model) Reset() {
	//TODO: test this.
	m.commands = nil
	m.currentCommand = nil
	if m.grid != nil {
		m.grid.ResetUnlockedCells()
	}
}

func (m *Model) SetGrid(grid sudoku.MutableGrid) {
	m.grid = grid
	m.Reset()
}

func (m *Model) SetMarks(row, col int, marksToggle map[int]bool) {
	//TODO: should this take a cellRef?
	command := m.newMarkCommand(row, col, marksToggle)
	if command == nil {
		return
	}
	if m.InGroup() {
		m.inProgressMultiCommand.AddCommand(command)
	} else {
		m.executeCommand(command)
	}
}

func (m *Model) newMarkCommand(row, col int, marksToggle map[int]bool) *markCommand {
	//Only keep marks in the toggle that won't be a no-op
	newMarksToggle := make(map[int]bool)

	cell := m.grid.Cell(row, col)

	if cell == nil {
		return nil
	}
	for key, value := range marksToggle {
		if cell.Mark(key) != value {
			//Good, keep it
			newMarksToggle[key] = value
		}
	}

	if len(newMarksToggle) == 0 {
		//The command would be a no op!
		return nil
	}

	return &markCommand{baseCommand{row, col}, newMarksToggle}
}

func (m *Model) SetNumber(row, col int, num int) {
	//TODO: should this take CellRef?
	command := m.newNumberCommand(row, col, num)
	if command == nil {
		return
	}
	if m.InGroup() {
		m.inProgressMultiCommand.AddCommand(command)
	} else {
		m.executeCommand(command)
	}
}

func (m *Model) newNumberCommand(row, col int, num int) *numberCommand {
	cell := m.grid.Cell(row, col)

	if cell == nil {
		return nil
	}

	if cell.Number() == num {
		return nil
	}

	return &numberCommand{baseCommand{row, col}, num, cell.Number()}
}

func (m *markCommand) Apply(model *Model) {
	cell := model.grid.MutableCell(m.row, m.col)
	if cell == nil {
		return
	}
	for key, value := range m.marksToggle {
		cell.SetMark(key, value)
	}
}

func (m *markCommand) Undo(model *Model) {
	cell := model.grid.MutableCell(m.row, m.col)
	if cell == nil {
		return
	}
	for key, value := range m.marksToggle {
		//Set the opposite since we're undoing.
		cell.SetMark(key, !value)
	}
}

func (n *numberCommand) Apply(model *Model) {
	cell := model.grid.MutableCell(n.row, n.col)
	if cell == nil {
		return
	}
	cell.SetNumber(n.number)
}

func (n *numberCommand) Undo(model *Model) {
	cell := model.grid.MutableCell(n.row, n.col)
	if cell == nil {
		return
	}
	cell.SetNumber(n.oldNumber)
}

func (m *multiCommand) Apply(model *Model) {
	for _, command := range m.commands {
		command.Apply(model)
	}
}

func (m *multiCommand) Undo(model *Model) {
	for i := len(m.commands) - 1; i >= 0; i-- {
		m.commands[i].Undo(model)
	}
}
<|endoftext|>


<commit_before>package gitlab

import (
	"fmt"
	"github.com/stretchr/testify/require"
	"net/http"
	"testing"
	"time"
)

func TestSystemHooksService_ListHooks(t *testing.T) {
	mux, server, client := setup(t)
	defer teardown(server)

	mux.HandleFunc("/api/v4/hooks", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, `[{"id":1,"url":"https://gitlab.example.com/hook"}]`)
	})
	hooks, _, err := client.SystemHooks.ListHooks()

	require.NoError(t, err)
	want := []*Hook{{ID: 1, URL: "https://gitlab.example.com/hook"}}
	require.Equal(t, want, hooks)
}

func TestSystemHooksService_AddHook(t *testing.T) {
	mux, server, client := setup(t)
	defer teardown(server)

	mux.HandleFunc("/api/v4/hooks", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprint(w, `{"id": 1, "url": "https://gitlab.example.com/hook"}`)
	})

	opt := &AddHookOptions{
		URL: String("https://gitlab.example.com/hook"),
	}
	h, _, err := client.SystemHooks.AddHook(opt)

	require.NoError(t, err)
	want := &Hook{ID: 1, URL: "https://gitlab.example.com/hook", CreatedAt: (*time.Time)(nil)}
	require.Equal(t, want, h)
}

func TestSystemHooksService_TestHook(t *testing.T) {
	mux, server, client := setup(t)
	defer teardown(server)

	mux.HandleFunc("/api/v4/hooks/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, `{"project_id" : 1,"owner_email" : "example@gitlabhq.com","owner_name" : "Someone",
				"name" : "Ruby","path" : "ruby","event_name" : "project_create"}`)
	})

	he, _, err := client.SystemHooks.TestHook(1)

	require.NoError(t, err)
	want := &HookEvent{
		EventName:  "project_create",
		Name:       "Ruby",
		Path:       "ruby",
		ProjectID:  1,
		OwnerName:  "Someone",
		OwnerEmail: "example@gitlabhq.com",
	}
	require.Equal(t, want, he)
}

func TestSystemHooksService_DeleteHook(t *testing.T) {
	mux, server, client := setup(t)
	defer teardown(server)

	mux.HandleFunc("/api/v4/hooks/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
	})

	_, err := client.SystemHooks.DeleteHook(1)
	require.NoError(t, err)
}

func TestHook_String(t *testing.T) {
	h := &Hook{
		ID:        1,
		URL:       "https://github.com/",
		CreatedAt: nil,
	}
	hs := h.String()
	want := "gitlab.Hook{ID:1, URL:\"https://github.com/\"}"
	require.Equal(t,want , hs)
}

func TestHookEvent_String(t *testing.T) {
	he := &HookEvent{
		EventName:  "event",
		Name:       "name",
		Path:       "path",
		ProjectID:  1,
		OwnerName:  "owner",
		OwnerEmail: "owner_email",
	}

	hes := he.String()
	want := "gitlab.HookEvent{EventName:\"event\", Name:\"name\", Path:\"path\", ProjectID:1, OwnerName:\"owner\", OwnerEmail:\"owner_email\"}"
	require.Equal(t, want, hes)
}
<commit_msg>Update system_hooks_test.go<commit_after>package gitlab

import (
	"fmt"
	"github.com/stretchr/testify/require"
	"net/http"
	"testing"
	"time"
)

func TestSystemHooksService_ListHooks(t *testing.T) {
	mux, server, client := setup(t)
	defer teardown(server)

	mux.HandleFunc("/api/v4/hooks", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, `[{"id":1,"url":"https://gitlab.example.com/hook"}]`)
	})

	hooks, _, err := client.SystemHooks.ListHooks()
	require.NoError(t, err)

	want := []*Hook{{ID: 1, URL: "https://gitlab.example.com/hook"}}
	require.Equal(t, want, hooks)
}

func TestSystemHooksService_AddHook(t *testing.T) {
	mux, server, client := setup(t)
	defer teardown(server)

	mux.HandleFunc("/api/v4/hooks", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodPost)
		fmt.Fprint(w, `{"id": 1, "url": "https://gitlab.example.com/hook"}`)
	})

	opt := &AddHookOptions{
		URL: String("https://gitlab.example.com/hook"),
	}
	h, _, err := client.SystemHooks.AddHook(opt)

	require.NoError(t, err)
	want := &Hook{ID: 1, URL: "https://gitlab.example.com/hook", CreatedAt: (*time.Time)(nil)}
	require.Equal(t, want, h)
}

func TestSystemHooksService_TestHook(t *testing.T) {
	mux, server, client := setup(t)
	defer teardown(server)

	mux.HandleFunc("/api/v4/hooks/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprint(w, `{"project_id" : 1,"owner_email" : "example@gitlabhq.com","owner_name" : "Someone",
				"name" : "Ruby","path" : "ruby","event_name" : "project_create"}`)
	})

	he, _, err := client.SystemHooks.TestHook(1)

	require.NoError(t, err)
	want := &HookEvent{
		EventName:  "project_create",
		Name:       "Ruby",
		Path:       "ruby",
		ProjectID:  1,
		OwnerName:  "Someone",
		OwnerEmail: "example@gitlabhq.com",
	}
	require.Equal(t, want, he)
}

func TestSystemHooksService_DeleteHook(t *testing.T) {
	mux, server, client := setup(t)
	defer teardown(server)

	mux.HandleFunc("/api/v4/hooks/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodDelete)
	})

	_, err := client.SystemHooks.DeleteHook(1)
	require.NoError(t, err)
}

func TestHook_String(t *testing.T) {
	h := &Hook{
		ID:        1,
		URL:       "https://github.com/",
		CreatedAt: nil,
	}
	hs := h.String()
	want := "gitlab.Hook{ID:1, URL:\"https://github.com/\"}"
	require.Equal(t,want , hs)
}

func TestHookEvent_String(t *testing.T) {
	he := &HookEvent{
		EventName:  "event",
		Name:       "name",
		Path:       "path",
		ProjectID:  1,
		OwnerName:  "owner",
		OwnerEmail: "owner_email",
	}

	hes := he.String()
	want := "gitlab.HookEvent{EventName:\"event\", Name:\"name\", Path:\"path\", ProjectID:1, OwnerName:\"owner\", OwnerEmail:\"owner_email\"}"
	require.Equal(t, want, hes)
}
<|endoftext|>


<commit_before>// Package inbound implements dns server for inbound connection.
package inbound

import (
	"encoding/json"
	"io"
	"net"
	"net/http"
	"os"
	"strconv"
	"strings"
	"sync"

	"github.com/miekg/dns"
	log "github.com/sirupsen/logrus"

	"github.com/shawn1m/overture/core/outbound"
)

type Server struct {
	bindAddress      string
	debugHttpAddress string
	dispatcher       outbound.Dispatcher
	rejectQType      []uint16
}

func NewServer(bindAddress string, debugHTTPAddress string, dispatcher outbound.Dispatcher, rejectQType []uint16) *Server {
	return &Server{
		bindAddress:      bindAddress,
		debugHttpAddress: debugHTTPAddress,
		dispatcher:       dispatcher,
		rejectQType:      rejectQType,
	}
}

func (s *Server) DumpCache(w http.ResponseWriter, req *http.Request) {
	if s.dispatcher.Cache == nil {
		io.WriteString(w, "error: cache not enabled")
		return
	}

	type answer struct {
		Name  string `json:"name"`
		TTL   int    `json:"ttl"`
		Type  string `json:"type"`
		Rdata string `json:"rdata"`
	}

	type response struct {
		Length   int                  `json:"length"`
		Capacity int                  `json:"capacity"`
		Body     map[string][]*answer `json:"body"`
	}

	query := req.URL.Query()
	nobody := true
	if t := query.Get("nobody"); strings.ToLower(t) == "false" {
		nobody = false
	}

	rs, l := s.dispatcher.Cache.Dump(nobody)
	body := make(map[string][]*answer)

	for k, es := range rs {
		var answers []*answer
		for _, e := range es {
			ts := strings.Split(e, "\t")
			ttl, _ := strconv.Atoi(ts[1])
			r := &answer{
				Name:  ts[0],
				TTL:   ttl,
				Type:  ts[3],
				Rdata: ts[4],
			}
			answers = append(answers, r)
		}
		body[strings.TrimSpace(k)] = answers
	}

	res := response{
		Body:     body,
		Length:   l,
		Capacity: s.dispatcher.Cache.Capacity(),
	}

	responseBytes, err := json.Marshal(&res)
	if err != nil {
		io.WriteString(w, err.Error())
		return
	}

	io.WriteString(w, string(responseBytes))
}

func (s *Server) Run() {

	mux := dns.NewServeMux()
	mux.Handle(".", s)

	wg := new(sync.WaitGroup)
	wg.Add(2)

	log.Infof("Overture is listening on %s", s.bindAddress)

	for _, p := range [2]string{"tcp", "udp"} {
		go func(p string) {
			err := dns.ListenAndServe(s.bindAddress, p, mux)
			if err != nil {
				log.Fatalf("Listening on port %s failed: %s", p, err)
				os.Exit(1)
			}
		}(p)
	}

	if s.debugHttpAddress != "" {
		http.HandleFunc("/cache", s.DumpCache)
		wg.Add(1)
		go http.ListenAndServe(s.debugHttpAddress, nil)
	}

	wg.Wait()
}

func (s *Server) ServeDNS(w dns.ResponseWriter, q *dns.Msg) {
	inboundIP, _, _ := net.SplitHostPort(w.RemoteAddr().String())

	log.Debugf("Question from %s: %s", inboundIP, q.Question[0].String())

	for _, qt := range s.rejectQType {
		if isQuestionType(q, qt) {
			return
		}
	}

	responseMessage := s.dispatcher.Exchange(q, inboundIP)

	if responseMessage == nil {
		dns.HandleFailed(w, q)
		return
	}

	err := w.WriteMsg(responseMessage)
	if err != nil {
		log.Warnf("Write message failed, message: %s, error: %s", responseMessage, err)
		return
	}
}

func isQuestionType(q *dns.Msg, qt uint16) bool { return q.Question[0].Qtype == qt }
<commit_msg>Fix: respond if rejected, or the client hangs for the response (#184)<commit_after>// Package inbound implements dns server for inbound connection.
package inbound

import (
	"encoding/json"
	"io"
	"net"
	"net/http"
	"os"
	"strconv"
	"strings"
	"sync"

	"github.com/miekg/dns"
	log "github.com/sirupsen/logrus"

	"github.com/shawn1m/overture/core/outbound"
)

type Server struct {
	bindAddress      string
	debugHttpAddress string
	dispatcher       outbound.Dispatcher
	rejectQType      []uint16
}

func NewServer(bindAddress string, debugHTTPAddress string, dispatcher outbound.Dispatcher, rejectQType []uint16) *Server {
	return &Server{
		bindAddress:      bindAddress,
		debugHttpAddress: debugHTTPAddress,
		dispatcher:       dispatcher,
		rejectQType:      rejectQType,
	}
}

func (s *Server) DumpCache(w http.ResponseWriter, req *http.Request) {
	if s.dispatcher.Cache == nil {
		io.WriteString(w, "error: cache not enabled")
		return
	}

	type answer struct {
		Name  string `json:"name"`
		TTL   int    `json:"ttl"`
		Type  string `json:"type"`
		Rdata string `json:"rdata"`
	}

	type response struct {
		Length   int                  `json:"length"`
		Capacity int                  `json:"capacity"`
		Body     map[string][]*answer `json:"body"`
	}

	query := req.URL.Query()
	nobody := true
	if t := query.Get("nobody"); strings.ToLower(t) == "false" {
		nobody = false
	}

	rs, l := s.dispatcher.Cache.Dump(nobody)
	body := make(map[string][]*answer)

	for k, es := range rs {
		var answers []*answer
		for _, e := range es {
			ts := strings.Split(e, "\t")
			ttl, _ := strconv.Atoi(ts[1])
			r := &answer{
				Name:  ts[0],
				TTL:   ttl,
				Type:  ts[3],
				Rdata: ts[4],
			}
			answers = append(answers, r)
		}
		body[strings.TrimSpace(k)] = answers
	}

	res := response{
		Body:     body,
		Length:   l,
		Capacity: s.dispatcher.Cache.Capacity(),
	}

	responseBytes, err := json.Marshal(&res)
	if err != nil {
		io.WriteString(w, err.Error())
		return
	}

	io.WriteString(w, string(responseBytes))
}

func (s *Server) Run() {

	mux := dns.NewServeMux()
	mux.Handle(".", s)

	wg := new(sync.WaitGroup)
	wg.Add(2)

	log.Infof("Overture is listening on %s", s.bindAddress)

	for _, p := range [2]string{"tcp", "udp"} {
		go func(p string) {
			err := dns.ListenAndServe(s.bindAddress, p, mux)
			if err != nil {
				log.Fatalf("Listening on port %s failed: %s", p, err)
				os.Exit(1)
			}
		}(p)
	}

	if s.debugHttpAddress != "" {
		http.HandleFunc("/cache", s.DumpCache)
		wg.Add(1)
		go http.ListenAndServe(s.debugHttpAddress, nil)
	}

	wg.Wait()
}

func (s *Server) ServeDNS(w dns.ResponseWriter, q *dns.Msg) {
	inboundIP, _, _ := net.SplitHostPort(w.RemoteAddr().String())

	log.Debugf("Question from %s: %s", inboundIP, q.Question[0].String())

	for _, qt := range s.rejectQType {
		if isQuestionType(q, qt) {
			log.Debugf("Reject %s: %s", inboundIP, q.Question[0].String())
			dns.HandleFailed(w, q)
			return
		}
	}

	responseMessage := s.dispatcher.Exchange(q, inboundIP)

	if responseMessage == nil {
		dns.HandleFailed(w, q)
		return
	}

	err := w.WriteMsg(responseMessage)
	if err != nil {
		log.Warnf("Write message failed, message: %s, error: %s", responseMessage, err)
		return
	}
}

func isQuestionType(q *dns.Msg, qt uint16) bool { return q.Question[0].Qtype == qt }
<|endoftext|>


<commit_before>// Copyright 2014-2017 Amazon.com, Inc. or its affiliates. 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. A copy of the
// License is located at
//
//	http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file 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 dockerclient

type LoggingDriver string

const (
	JSONFileDriver   LoggingDriver = "json-file"
	SyslogDriver     LoggingDriver = "syslog"
	JournaldDriver   LoggingDriver = "journald"
	GelfDriver       LoggingDriver = "gelf"
	FluentdDriver    LoggingDriver = "fluentd"
	AWSLogsDriver    LoggingDriver = "awslogs"
	SplunklogsDriver LoggingDriver = "splunk"
	LogentriesDriver LoggingDriver = "logentries"
	SumoLogicDriver  LoggingDriver = "sumologic"
	NoneDriver       LoggingDriver = "none"
)

var LoggingDriverMinimumVersion = map[LoggingDriver]DockerVersion{
	JSONFileDriver:   Version_1_18,
	SyslogDriver:     Version_1_18,
	JournaldDriver:   Version_1_19,
	GelfDriver:       Version_1_20,
	FluentdDriver:    Version_1_20,
	AWSLogsDriver:    Version_1_21,
	SplunklogsDriver: Version_1_22,
	LogentriesDriver: Version_1_25,
	SumoLogicDriver:  Version_1_29,
	NoneDriver:       Version_1_18,
}
<commit_msg>change min docker version to for none log driver<commit_after>// Copyright 2014-2017 Amazon.com, Inc. or its affiliates. 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. A copy of the
// License is located at
//
//	http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file 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 dockerclient

type LoggingDriver string

const (
	JSONFileDriver   LoggingDriver = "json-file"
	SyslogDriver     LoggingDriver = "syslog"
	JournaldDriver   LoggingDriver = "journald"
	GelfDriver       LoggingDriver = "gelf"
	FluentdDriver    LoggingDriver = "fluentd"
	AWSLogsDriver    LoggingDriver = "awslogs"
	SplunklogsDriver LoggingDriver = "splunk"
	LogentriesDriver LoggingDriver = "logentries"
	SumoLogicDriver  LoggingDriver = "sumologic"
	NoneDriver       LoggingDriver = "none"
)

var LoggingDriverMinimumVersion = map[LoggingDriver]DockerVersion{
	JSONFileDriver:   Version_1_18,
	SyslogDriver:     Version_1_18,
	JournaldDriver:   Version_1_19,
	GelfDriver:       Version_1_20,
	FluentdDriver:    Version_1_20,
	AWSLogsDriver:    Version_1_21,
	SplunklogsDriver: Version_1_22,
	LogentriesDriver: Version_1_25,
	SumoLogicDriver:  Version_1_29,
	NoneDriver:       Version_1_19,
}
<|endoftext|>


<commit_before>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package provisioner_test

import (
	"io/ioutil"
	"net"
	"path/filepath"

	gitjujutesting "github.com/juju/testing"
	jc "github.com/juju/testing/checkers"
	"github.com/juju/utils/arch"
	"github.com/juju/version"
	gc "gopkg.in/check.v1"
	"gopkg.in/juju/names.v2"

	"github.com/juju/juju/apiserver/params"
	"github.com/juju/juju/cloudconfig/instancecfg"
	"github.com/juju/juju/constraints"
	"github.com/juju/juju/environs"
	"github.com/juju/juju/instance"
	instancetest "github.com/juju/juju/instance/testing"
	jujutesting "github.com/juju/juju/juju/testing"
	"github.com/juju/juju/network"
	"github.com/juju/juju/status"
	coretesting "github.com/juju/juju/testing"
	coretools "github.com/juju/juju/tools"
	"github.com/juju/juju/worker/provisioner"
)

type fakeAddr struct{ value string }

func (f *fakeAddr) Network() string { return "net" }
func (f *fakeAddr) String() string {
	if f.value != "" {
		return f.value
	}
	return "fakeAddr"
}

var _ net.Addr = (*fakeAddr)(nil)

type fakeAPI struct {
	*gitjujutesting.Stub

	fakeContainerConfig params.ContainerConfig
	fakeInterfaceInfo   network.InterfaceInfo
}

var _ provisioner.APICalls = (*fakeAPI)(nil)

var fakeInterfaceInfo network.InterfaceInfo = network.InterfaceInfo{
	DeviceIndex:    0,
	MACAddress:     "aa:bb:cc:dd:ee:ff",
	CIDR:           "0.1.2.0/24",
	InterfaceName:  "dummy0",
	Address:        network.NewAddress("0.1.2.3"),
	GatewayAddress: network.NewAddress("0.1.2.1"),
}

var fakeContainerConfig = params.ContainerConfig{
	UpdateBehavior:          &params.UpdateBehavior{true, true},
	ProviderType:            "fake",
	AuthorizedKeys:          coretesting.FakeAuthKeys,
	SSLHostnameVerification: true,
}

func NewFakeAPI() *fakeAPI {
	return &fakeAPI{
		Stub:                &gitjujutesting.Stub{},
		fakeContainerConfig: fakeContainerConfig,
		fakeInterfaceInfo:   fakeInterfaceInfo,
	}
}

func (f *fakeAPI) ContainerConfig() (params.ContainerConfig, error) {
	f.MethodCall(f, "ContainerConfig")
	if err := f.NextErr(); err != nil {
		return params.ContainerConfig{}, err
	}
	return f.fakeContainerConfig, nil
}

func (f *fakeAPI) PrepareContainerInterfaceInfo(tag names.MachineTag) ([]network.InterfaceInfo, error) {
	f.MethodCall(f, "PrepareContainerInterfaceInfo", tag)
	if err := f.NextErr(); err != nil {
		return nil, err
	}
	return []network.InterfaceInfo{f.fakeInterfaceInfo}, nil
}

func (f *fakeAPI) GetContainerInterfaceInfo(tag names.MachineTag) ([]network.InterfaceInfo, error) {
	f.MethodCall(f, "GetContainerInterfaceInfo", tag)
	if err := f.NextErr(); err != nil {
		return nil, err
	}
	return []network.InterfaceInfo{f.fakeInterfaceInfo}, nil
}

func (f *fakeAPI) ReleaseContainerAddresses(tag names.MachineTag) error {
	f.MethodCall(f, "ReleaseContainerAddresses", tag)
	if err := f.NextErr(); err != nil {
		return err
	}
	return nil
}

type patcher interface {
	PatchValue(destination, source interface{})
}

func patchResolvConf(s patcher, c *gc.C) {
	const fakeConf = `
nameserver ns1.dummy
search dummy
nameserver ns2.dummy
search invalid
`
	fakeResolvConf := filepath.Join(c.MkDir(), "fakeresolv.conf")
	err := ioutil.WriteFile(fakeResolvConf, []byte(fakeConf), 0644)
	c.Assert(err, jc.ErrorIsNil)
	s.PatchValue(provisioner.ResolvConf, fakeResolvConf)
}

func instancesFromResults(results ...*environs.StartInstanceResult) []instance.Instance {
	instances := make([]instance.Instance, len(results))
	for i := range results {
		instances[i] = results[i].Instance
	}
	return instances
}

func assertInstancesStarted(c *gc.C, broker environs.InstanceBroker, results ...*environs.StartInstanceResult) {
	allInstances, err := broker.AllInstances()
	c.Assert(err, jc.ErrorIsNil)
	instancetest.MatchInstances(c, allInstances, instancesFromResults(results...)...)
}

func makeInstanceConfig(c *gc.C, s patcher, machineId string) *instancecfg.InstanceConfig {
	machineNonce := "fake-nonce"
	// To isolate the tests from the host's architecture, we override it here.
	s.PatchValue(&arch.HostArch, func() string { return arch.AMD64 })
	apiInfo := jujutesting.FakeAPIInfo(machineId)
	instanceConfig, err := instancecfg.NewInstanceConfig(machineId, machineNonce, "released", "quantal", true, apiInfo)
	c.Assert(err, jc.ErrorIsNil)
	return instanceConfig
}

func makePossibleTools() coretools.List {
	return coretools.List{&coretools.Tools{
		Version: version.MustParseBinary("2.3.4-quantal-amd64"),
		URL:     "http://tools.testing.invalid/2.3.4-quantal-amd64.tgz",
	}, {
		// non-host-arch tools should be filtered out by StartInstance
		Version: version.MustParseBinary("2.3.4-quantal-arm64"),
		URL:     "http://tools.testing.invalid/2.3.4-quantal-arm64.tgz",
	}}
}

func makeNoOpStatusCallback() func(settableStatus status.Status, info string, data map[string]interface{}) error {
	return func(_ status.Status, _ string, _ map[string]interface{}) error {
		return nil
	}
}

func callStartInstance(c *gc.C, s patcher, broker environs.InstanceBroker, machineId string) *environs.StartInstanceResult {
	result, err := broker.StartInstance(environs.StartInstanceParams{
		Constraints:    constraints.Value{},
		Tools:          makePossibleTools(),
		InstanceConfig: makeInstanceConfig(c, s, machineId),
		StatusCallback: makeNoOpStatusCallback(),
	})
	c.Assert(err, jc.ErrorIsNil)
	return result
}

func callMaintainInstance(c *gc.C, s patcher, broker environs.InstanceBroker, machineId string) {
	err := broker.MaintainInstance(environs.StartInstanceParams{
		Constraints:    constraints.Value{},
		Tools:          makePossibleTools(),
		InstanceConfig: makeInstanceConfig(c, s, machineId),
		StatusCallback: makeNoOpStatusCallback(),
	})
	c.Assert(err, jc.ErrorIsNil)
}
<commit_msg>worker/provisioner: Use a correctly formatted fakeResolvConf<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package provisioner_test

import (
	"io/ioutil"
	"net"
	"path/filepath"

	gitjujutesting "github.com/juju/testing"
	jc "github.com/juju/testing/checkers"
	"github.com/juju/utils/arch"
	"github.com/juju/version"
	gc "gopkg.in/check.v1"
	"gopkg.in/juju/names.v2"

	"github.com/juju/juju/apiserver/params"
	"github.com/juju/juju/cloudconfig/instancecfg"
	"github.com/juju/juju/constraints"
	"github.com/juju/juju/environs"
	"github.com/juju/juju/instance"
	instancetest "github.com/juju/juju/instance/testing"
	jujutesting "github.com/juju/juju/juju/testing"
	"github.com/juju/juju/network"
	"github.com/juju/juju/status"
	coretesting "github.com/juju/juju/testing"
	coretools "github.com/juju/juju/tools"
	"github.com/juju/juju/worker/provisioner"
)

type fakeAddr struct{ value string }

func (f *fakeAddr) Network() string { return "net" }
func (f *fakeAddr) String() string {
	if f.value != "" {
		return f.value
	}
	return "fakeAddr"
}

var _ net.Addr = (*fakeAddr)(nil)

type fakeAPI struct {
	*gitjujutesting.Stub

	fakeContainerConfig params.ContainerConfig
	fakeInterfaceInfo   network.InterfaceInfo
}

var _ provisioner.APICalls = (*fakeAPI)(nil)

var fakeInterfaceInfo network.InterfaceInfo = network.InterfaceInfo{
	DeviceIndex:    0,
	MACAddress:     "aa:bb:cc:dd:ee:ff",
	CIDR:           "0.1.2.0/24",
	InterfaceName:  "dummy0",
	Address:        network.NewAddress("0.1.2.3"),
	GatewayAddress: network.NewAddress("0.1.2.1"),
}

var fakeContainerConfig = params.ContainerConfig{
	UpdateBehavior:          &params.UpdateBehavior{true, true},
	ProviderType:            "fake",
	AuthorizedKeys:          coretesting.FakeAuthKeys,
	SSLHostnameVerification: true,
}

func NewFakeAPI() *fakeAPI {
	return &fakeAPI{
		Stub:                &gitjujutesting.Stub{},
		fakeContainerConfig: fakeContainerConfig,
		fakeInterfaceInfo:   fakeInterfaceInfo,
	}
}

func (f *fakeAPI) ContainerConfig() (params.ContainerConfig, error) {
	f.MethodCall(f, "ContainerConfig")
	if err := f.NextErr(); err != nil {
		return params.ContainerConfig{}, err
	}
	return f.fakeContainerConfig, nil
}

func (f *fakeAPI) PrepareContainerInterfaceInfo(tag names.MachineTag) ([]network.InterfaceInfo, error) {
	f.MethodCall(f, "PrepareContainerInterfaceInfo", tag)
	if err := f.NextErr(); err != nil {
		return nil, err
	}
	return []network.InterfaceInfo{f.fakeInterfaceInfo}, nil
}

func (f *fakeAPI) GetContainerInterfaceInfo(tag names.MachineTag) ([]network.InterfaceInfo, error) {
	f.MethodCall(f, "GetContainerInterfaceInfo", tag)
	if err := f.NextErr(); err != nil {
		return nil, err
	}
	return []network.InterfaceInfo{f.fakeInterfaceInfo}, nil
}

func (f *fakeAPI) ReleaseContainerAddresses(tag names.MachineTag) error {
	f.MethodCall(f, "ReleaseContainerAddresses", tag)
	if err := f.NextErr(); err != nil {
		return err
	}
	return nil
}

type patcher interface {
	PatchValue(destination, source interface{})
}

func patchResolvConf(s patcher, c *gc.C) {
	const fakeConf = `
nameserver ns1.dummy
search dummy invalid
nameserver ns2.dummy
`
	fakeResolvConf := filepath.Join(c.MkDir(), "fakeresolv.conf")
	err := ioutil.WriteFile(fakeResolvConf, []byte(fakeConf), 0644)
	c.Assert(err, jc.ErrorIsNil)
	s.PatchValue(provisioner.ResolvConf, fakeResolvConf)
}

func instancesFromResults(results ...*environs.StartInstanceResult) []instance.Instance {
	instances := make([]instance.Instance, len(results))
	for i := range results {
		instances[i] = results[i].Instance
	}
	return instances
}

func assertInstancesStarted(c *gc.C, broker environs.InstanceBroker, results ...*environs.StartInstanceResult) {
	allInstances, err := broker.AllInstances()
	c.Assert(err, jc.ErrorIsNil)
	instancetest.MatchInstances(c, allInstances, instancesFromResults(results...)...)
}

func makeInstanceConfig(c *gc.C, s patcher, machineId string) *instancecfg.InstanceConfig {
	machineNonce := "fake-nonce"
	// To isolate the tests from the host's architecture, we override it here.
	s.PatchValue(&arch.HostArch, func() string { return arch.AMD64 })
	apiInfo := jujutesting.FakeAPIInfo(machineId)
	instanceConfig, err := instancecfg.NewInstanceConfig(machineId, machineNonce, "released", "quantal", true, apiInfo)
	c.Assert(err, jc.ErrorIsNil)
	return instanceConfig
}

func makePossibleTools() coretools.List {
	return coretools.List{&coretools.Tools{
		Version: version.MustParseBinary("2.3.4-quantal-amd64"),
		URL:     "http://tools.testing.invalid/2.3.4-quantal-amd64.tgz",
	}, {
		// non-host-arch tools should be filtered out by StartInstance
		Version: version.MustParseBinary("2.3.4-quantal-arm64"),
		URL:     "http://tools.testing.invalid/2.3.4-quantal-arm64.tgz",
	}}
}

func makeNoOpStatusCallback() func(settableStatus status.Status, info string, data map[string]interface{}) error {
	return func(_ status.Status, _ string, _ map[string]interface{}) error {
		return nil
	}
}

func callStartInstance(c *gc.C, s patcher, broker environs.InstanceBroker, machineId string) *environs.StartInstanceResult {
	result, err := broker.StartInstance(environs.StartInstanceParams{
		Constraints:    constraints.Value{},
		Tools:          makePossibleTools(),
		InstanceConfig: makeInstanceConfig(c, s, machineId),
		StatusCallback: makeNoOpStatusCallback(),
	})
	c.Assert(err, jc.ErrorIsNil)
	return result
}

func callMaintainInstance(c *gc.C, s patcher, broker environs.InstanceBroker, machineId string) {
	err := broker.MaintainInstance(environs.StartInstanceParams{
		Constraints:    constraints.Value{},
		Tools:          makePossibleTools(),
		InstanceConfig: makeInstanceConfig(c, s, machineId),
		StatusCallback: makeNoOpStatusCallback(),
	})
	c.Assert(err, jc.ErrorIsNil)
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"testing"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/dax"
	"github.com/hashicorp/terraform/helper/acctest"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
)

func TestAccAwsDaxParameterGroup_basic(t *testing.T) {
	rName := acctest.RandomWithPrefix("tf-acc-test")
	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAwsDaxParameterGroupDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccDaxParameterGroupConfig(rName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAwsDaxParameterGroupExists("aws_dax_parameter_group.test"),
					resource.TestCheckResourceAttr("aws_dax_parameter_group.test", "parameters.#", "2"),
				),
			},
			{
				Config: testAccDaxParameterGroupConfig_parameters(rName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAwsDaxParameterGroupExists("aws_dax_parameter_group.test"),
					resource.TestCheckResourceAttr("aws_dax_parameter_group.test", "parameters.#", "2"),
				),
			},
		},
	})
}

func TestAccAwsDaxParameterGroup_import(t *testing.T) {
	rName := acctest.RandomWithPrefix("tf-acc-test")
	resourceName := "aws_dax_parameter_group.test"

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAwsDaxParameterGroupDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccDaxParameterGroupConfig(rName),
			},

			{
				ResourceName:      resourceName,
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}

func testAccCheckAwsDaxParameterGroupDestroy(s *terraform.State) error {
	conn := testAccProvider.Meta().(*AWSClient).daxconn

	for _, rs := range s.RootModule().Resources {
		if rs.Type != "aws_dax_parameter_group" {
			continue
		}

		_, err := conn.DescribeParameterGroups(&dax.DescribeParameterGroupsInput{
			ParameterGroupNames: []*string{aws.String(rs.Primary.ID)},
		})
		if err != nil {
			if isAWSErr(err, dax.ErrCodeParameterGroupNotFoundFault, "") {
				return nil
			}
			return err
		}
	}
	return nil
}

func testAccCheckAwsDaxParameterGroupExists(name string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[name]
		if !ok {
			return fmt.Errorf("Not found: %s", name)
		}

		conn := testAccProvider.Meta().(*AWSClient).daxconn

		_, err := conn.DescribeParameterGroups(&dax.DescribeParameterGroupsInput{
			ParameterGroupNames: []*string{aws.String(rs.Primary.ID)},
		})
		if err != nil {
			return err
		}

		return nil
	}
}

func testAccDaxParameterGroupConfig(rName string) string {
	return fmt.Sprintf(`
resource "aws_dax_parameter_group" "test" {
  name = "%s"
}
`, rName)
}

func testAccDaxParameterGroupConfig_parameters(rName string) string {
	return fmt.Sprintf(`
resource "aws_dax_parameter_group" "test" {
  name = "%s"
  parameters {
    name = "query-ttl-millis"
    value = "100000"
  }
  parameters {
    name = "record-ttl-millis"
    value = "100000"
  }
}
`, rName)
}
<commit_msg>tests/resource/aws_dax_parameter_group: Fix gosimple linting issues<commit_after>package aws

import (
	"fmt"
	"testing"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/dax"
	"github.com/hashicorp/terraform/helper/acctest"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
)

func TestAccAwsDaxParameterGroup_basic(t *testing.T) {
	rName := acctest.RandomWithPrefix("tf-acc-test")
	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAwsDaxParameterGroupDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccDaxParameterGroupConfig(rName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAwsDaxParameterGroupExists("aws_dax_parameter_group.test"),
					resource.TestCheckResourceAttr("aws_dax_parameter_group.test", "parameters.#", "2"),
				),
			},
			{
				Config: testAccDaxParameterGroupConfig_parameters(rName),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAwsDaxParameterGroupExists("aws_dax_parameter_group.test"),
					resource.TestCheckResourceAttr("aws_dax_parameter_group.test", "parameters.#", "2"),
				),
			},
		},
	})
}

func TestAccAwsDaxParameterGroup_import(t *testing.T) {
	rName := acctest.RandomWithPrefix("tf-acc-test")
	resourceName := "aws_dax_parameter_group.test"

	resource.ParallelTest(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAwsDaxParameterGroupDestroy,
		Steps: []resource.TestStep{
			{
				Config: testAccDaxParameterGroupConfig(rName),
			},

			{
				ResourceName:      resourceName,
				ImportState:       true,
				ImportStateVerify: true,
			},
		},
	})
}

func testAccCheckAwsDaxParameterGroupDestroy(s *terraform.State) error {
	conn := testAccProvider.Meta().(*AWSClient).daxconn

	for _, rs := range s.RootModule().Resources {
		if rs.Type != "aws_dax_parameter_group" {
			continue
		}

		_, err := conn.DescribeParameterGroups(&dax.DescribeParameterGroupsInput{
			ParameterGroupNames: []*string{aws.String(rs.Primary.ID)},
		})
		if err != nil {
			if isAWSErr(err, dax.ErrCodeParameterGroupNotFoundFault, "") {
				return nil
			}
			return err
		}
	}
	return nil
}

func testAccCheckAwsDaxParameterGroupExists(name string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[name]
		if !ok {
			return fmt.Errorf("Not found: %s", name)
		}

		conn := testAccProvider.Meta().(*AWSClient).daxconn

		_, err := conn.DescribeParameterGroups(&dax.DescribeParameterGroupsInput{
			ParameterGroupNames: []*string{aws.String(rs.Primary.ID)},
		})

		return err
	}
}

func testAccDaxParameterGroupConfig(rName string) string {
	return fmt.Sprintf(`
resource "aws_dax_parameter_group" "test" {
  name = "%s"
}
`, rName)
}

func testAccDaxParameterGroupConfig_parameters(rName string) string {
	return fmt.Sprintf(`
resource "aws_dax_parameter_group" "test" {
  name = "%s"
  parameters {
    name = "query-ttl-millis"
    value = "100000"
  }
  parameters {
    name = "record-ttl-millis"
    value = "100000"
  }
}
`, rName)
}
<|endoftext|>


<commit_before>package converter

import (
	"log"
)

type IngDiBa struct {
	indexOfDate int
	indexOfPayee int
	indexOfMemo int
	indexOfAmount int
	comma rune
}

func NewIngDiBa() IngDiBa {
	return IngDiBa{
		indexOfDate:   0,
		indexOfPayee:  2,
		indexOfMemo:   5,
		indexOfAmount: 8,
		comma:         ';',
	}
}

func (i IngDiBa) Comma() rune {
	return i.comma
}

func (i IngDiBa) IsTransaction(record []string) bool {
	return !(len(record) != 9 || record[0] == "Buchung")
}

func (i IngDiBa) Convert(record []string) []string {
	result := make([]string, 6)
	var err error

	// Date
	result[0], err = convertDateFrom("02.01.2006", record[0])
	if err != nil {
		log.Fatal(err)
	}

	// Payee
	result[1] = record[2]

	// Memo
	result[3] = record[4]

	// Amount
	amount := convertThousandAndCommaSeparator(record[7])
	if isNegative(amount) {
		result[4] = abs(amount)
	} else {
		result[5] = amount
	}

	return result
}
<commit_msg>Call record fields by introduced indices<commit_after>package converter

import (
	"log"
)

type IngDiBa struct {
	indexOfDate   int
	indexOfPayee  int
	indexOfMemo   int
	indexOfAmount int
	comma         rune
}

func NewIngDiBa() IngDiBa {
	return IngDiBa{
		indexOfDate:   0,
		indexOfPayee:  2,
		indexOfMemo:   5,
		indexOfAmount: 8,
		comma:         ';',
	}
}

func (i IngDiBa) Comma() rune {
	return i.comma
}

func (i IngDiBa) IsTransaction(record []string) bool {
	return !(len(record) != 9 || record[0] == "Buchung")
}

func (i IngDiBa) Convert(record []string) []string {
	result := make([]string, 6)
	var err error

	// Date
	result[0], err = convertDateFrom("02.01.2006", record[i.indexOfDate])
	if err != nil {
		log.Fatal(err)
	}

	// Payee
	result[1] = record[i.indexOfPayee]

	// Memo
	result[3] = record[i.indexOfMemo]

	// Amount
	amount := convertThousandAndCommaSeparator(record[i.indexOfAmount])
	if isNegative(amount) {
		result[4] = abs(amount)
	} else {
		result[5] = amount
	}

	return result
}
<|endoftext|>


<commit_before>package adminsock

import (
	"net"
	"testing"
)

// function readConn() is defined in test 02.

func TestOneShot(t *testing.T) {
	d := make(Dispatch) // create Dispatch
	d["echo"] = echo    // and put a function in it
	//instantiate an adminsocket which will spawn connections that
	//close after one response
	as, err := New("test05", d, -1, All)
	if err != nil {
		t.Errorf("Couldn't create socket: %v", err)
	}
	// launch oneshotclient.
	go oneshotclient(as.s, t)
	msg := <-as.Msgr
	if msg.Err != nil {
		t.Errorf("connection creation returned error: %v", msg.Err)
	}
	if msg.Txt != "adminsock conn 1 opened" {
		t.Errorf("unexpected msg.Txt: %v", msg.Txt)
	}
	// wait for disconnect Msg
	<-as.Msgr // discard cmd dispatch message
	msg = <-as.Msgr
	if msg.Err != nil {
		t.Errorf("connection drop should be nil, but got %v", err)
	}
	if msg.Txt != "adminsock conn 1 closing (one-shot)" {
		t.Errorf("unexpected msg.Txt: %v", msg.Txt)
	}
	// shut down adminsocket
	as.Quit()
}

// this time our (less) fake client will send a string over the
// connection and (hopefully) get it echoed back.
func oneshotclient(sn string, t *testing.T) {
	conn, err := net.Dial("unix", sn)
	defer conn.Close()
	if err != nil {
		t.Errorf("Couldn't connect to %v: %v", sn, err)
	}
	conn.Write([]byte("echo it works!"))
	res, err := readConn(conn)
	if string(res) != "it works!" {
		t.Errorf("Expected 'it works!' but got '%v'", string(res))
	}
	// now try sending a second request
	_, err = conn.Write([]byte("foo bar"))
	if err == nil {
		t.Error("conn should be closed by one-shot server, but Write() succeeded")
	}
	res, err = readConn(conn)
	if err == nil {
		t.Errorf("Read should have failed byt got: %v", res)
	}
}

<commit_msg>test05 passing again<commit_after>package adminsock

import (
	"net"
	"testing"
)

// function readConn() is defined in test 02.

func TestOneShot(t *testing.T) {
	d := make(Dispatch) // create Dispatch
	d["echo"] = echo    // and put a function in it
	//instantiate an adminsocket which will spawn connections that
	//close after one response
	as, err := New("test05", d, -1, All)
	if err != nil {
		t.Errorf("Couldn't create socket: %v", err)
	}
	// launch oneshotclient.
	go oneshotclient(as.s, t)
	msg := <-as.Msgr
	if msg.Err != nil {
		t.Errorf("connection creation returned error: %v", msg.Err)
	}
	if msg.Txt != "client connected" {
		t.Errorf("unexpected msg.Txt: %v", msg.Txt)
	}
	// wait for disconnect Msg
	<-as.Msgr // discard cmd dispatch message
	msg = <-as.Msgr
	if msg.Err != nil {
		t.Errorf("connection drop should be nil, but got %v", err)
	}
	if msg.Txt != "closing one-shot" {
		t.Errorf("unexpected msg.Txt: %v", msg.Txt)
	}
	// shut down adminsocket
	as.Quit()
}

// this time our (less) fake client will send a string over the
// connection and (hopefully) get it echoed back.
func oneshotclient(sn string, t *testing.T) {
	conn, err := net.Dial("unix", sn)
	defer conn.Close()
	if err != nil {
		t.Errorf("Couldn't connect to %v: %v", sn, err)
	}
	conn.Write([]byte("echo it works!"))
	res, err := readConn(conn)
	if string(res) != "it works!" {
		t.Errorf("Expected 'it works!' but got '%v'", string(res))
	}
	// now try sending a second request
	_, err = conn.Write([]byte("foo bar"))
	if err == nil {
		t.Error("conn should be closed by one-shot server, but Write() succeeded")
	}
	res, err = readConn(conn)
	if err == nil {
		t.Errorf("Read should have failed byt got: %v", res)
	}
}

<|endoftext|>


<commit_before>package chrootarchive

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/containers/storage/pkg/mount"
	rsystem "github.com/opencontainers/runc/libcontainer/system"
	"golang.org/x/sys/unix"
)

// chroot on linux uses pivot_root instead of chroot
// pivot_root takes a new root and an old root.
// Old root must be a sub-dir of new root, it is where the current rootfs will reside after the call to pivot_root.
// New root is where the new rootfs is set to.
// Old root is removed after the call to pivot_root so it is no longer available under the new root.
// This is similar to how libcontainer sets up a container's rootfs
func chroot(path string) (err error) {
	// if the engine is running in a user namespace we need to use actual chroot
	if rsystem.RunningInUserNS() {
		return realChroot(path)
	}
	if err := unix.Unshare(unix.CLONE_NEWNS); err != nil {
		return fmt.Errorf("Error creating mount namespace before pivot: %v", err)
	}

	// make everything in new ns private
	if err := mount.MakeRPrivate("/"); err != nil {
		return err
	}

	if mounted, _ := mount.Mounted(path); !mounted {
		if err := mount.Mount(path, path, "bind", "rbind,rw"); err != nil {
			return realChroot(path)
		}
	}

	// setup oldRoot for pivot_root
	pivotDir, err := ioutil.TempDir(path, ".pivot_root")
	if err != nil {
		return fmt.Errorf("Error setting up pivot dir: %v", err)
	}

	var mounted bool
	defer func() {
		if mounted {
			// make sure pivotDir is not mounted before we try to remove it
			if errCleanup := unix.Unmount(pivotDir, unix.MNT_DETACH); errCleanup != nil {
				if err == nil {
					err = errCleanup
				}
				return
			}
		}

		errCleanup := os.Remove(pivotDir)
		// pivotDir doesn't exist if pivot_root failed and chroot+chdir was successful
		// because we already cleaned it up on failed pivot_root
		if errCleanup != nil && !os.IsNotExist(errCleanup) {
			errCleanup = fmt.Errorf("Error cleaning up after pivot: %v", errCleanup)
			if err == nil {
				err = errCleanup
			}
		}
	}()

	if err := unix.PivotRoot(path, pivotDir); err != nil {
		// If pivot fails, fall back to the normal chroot after cleaning up temp dir
		if err := os.Remove(pivotDir); err != nil {
			return fmt.Errorf("Error cleaning up after failed pivot: %v", err)
		}
		return realChroot(path)
	}
	mounted = true

	// This is the new path for where the old root (prior to the pivot) has been moved to
	// This dir contains the rootfs of the caller, which we need to remove so it is not visible during extraction
	pivotDir = filepath.Join("/", filepath.Base(pivotDir))

	if err := unix.Chdir("/"); err != nil {
		return fmt.Errorf("Error changing to new root: %v", err)
	}

	// Make the pivotDir (where the old root lives) private so it can be unmounted without propagating to the host
	if err := unix.Mount("", pivotDir, "", unix.MS_PRIVATE|unix.MS_REC, ""); err != nil {
		return fmt.Errorf("Error making old root private after pivot: %v", err)
	}

	// Now unmount the old root so it's no longer visible from the new root
	if err := unix.Unmount(pivotDir, unix.MNT_DETACH); err != nil {
		return fmt.Errorf("Error while unmounting old root after pivot: %v", err)
	}
	mounted = false

	return nil
}

func realChroot(path string) error {
	if err := unix.Chroot(path); err != nil {
		return fmt.Errorf("Error after fallback to chroot: %v", err)
	}
	if err := unix.Chdir("/"); err != nil {
		return fmt.Errorf("Error changing to new root after chroot: %v", err)
	}
	return nil
}
<commit_msg>Decide how to chroot based on capabilities, not uid_map contents<commit_after>package chrootarchive

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/containers/storage/pkg/mount"
	"github.com/syndtr/gocapability/capability"
	"golang.org/x/sys/unix"
)

// chroot on linux uses pivot_root instead of chroot
// pivot_root takes a new root and an old root.
// Old root must be a sub-dir of new root, it is where the current rootfs will reside after the call to pivot_root.
// New root is where the new rootfs is set to.
// Old root is removed after the call to pivot_root so it is no longer available under the new root.
// This is similar to how libcontainer sets up a container's rootfs
func chroot(path string) (err error) {
	caps, err := capability.NewPid(0)
	if err != nil {
		return err
	}

	// if the process doesn't have CAP_SYS_ADMIN, but does have CAP_SYS_CHROOT, we need to use the actual chroot
	if !caps.Get(capability.EFFECTIVE, capability.CAP_SYS_ADMIN) && caps.Get(capability.EFFECTIVE, capability.CAP_SYS_CHROOT) {
		return realChroot(path)
	}

	if err := unix.Unshare(unix.CLONE_NEWNS); err != nil {
		return fmt.Errorf("Error creating mount namespace before pivot: %v", err)
	}

	// make everything in new ns private
	if err := mount.MakeRPrivate("/"); err != nil {
		return err
	}

	if mounted, _ := mount.Mounted(path); !mounted {
		if err := mount.Mount(path, path, "bind", "rbind,rw"); err != nil {
			return realChroot(path)
		}
	}

	// setup oldRoot for pivot_root
	pivotDir, err := ioutil.TempDir(path, ".pivot_root")
	if err != nil {
		return fmt.Errorf("Error setting up pivot dir: %v", err)
	}

	var mounted bool
	defer func() {
		if mounted {
			// make sure pivotDir is not mounted before we try to remove it
			if errCleanup := unix.Unmount(pivotDir, unix.MNT_DETACH); errCleanup != nil {
				if err == nil {
					err = errCleanup
				}
				return
			}
		}

		errCleanup := os.Remove(pivotDir)
		// pivotDir doesn't exist if pivot_root failed and chroot+chdir was successful
		// because we already cleaned it up on failed pivot_root
		if errCleanup != nil && !os.IsNotExist(errCleanup) {
			errCleanup = fmt.Errorf("Error cleaning up after pivot: %v", errCleanup)
			if err == nil {
				err = errCleanup
			}
		}
	}()

	if err := unix.PivotRoot(path, pivotDir); err != nil {
		// If pivot fails, fall back to the normal chroot after cleaning up temp dir
		if err := os.Remove(pivotDir); err != nil {
			return fmt.Errorf("Error cleaning up after failed pivot: %v", err)
		}
		return realChroot(path)
	}
	mounted = true

	// This is the new path for where the old root (prior to the pivot) has been moved to
	// This dir contains the rootfs of the caller, which we need to remove so it is not visible during extraction
	pivotDir = filepath.Join("/", filepath.Base(pivotDir))

	if err := unix.Chdir("/"); err != nil {
		return fmt.Errorf("Error changing to new root: %v", err)
	}

	// Make the pivotDir (where the old root lives) private so it can be unmounted without propagating to the host
	if err := unix.Mount("", pivotDir, "", unix.MS_PRIVATE|unix.MS_REC, ""); err != nil {
		return fmt.Errorf("Error making old root private after pivot: %v", err)
	}

	// Now unmount the old root so it's no longer visible from the new root
	if err := unix.Unmount(pivotDir, unix.MNT_DETACH); err != nil {
		return fmt.Errorf("Error while unmounting old root after pivot: %v", err)
	}
	mounted = false

	return nil
}

func realChroot(path string) error {
	if err := unix.Chroot(path); err != nil {
		return fmt.Errorf("Error after fallback to chroot: %v", err)
	}
	if err := unix.Chdir("/"); err != nil {
		return fmt.Errorf("Error changing to new root after chroot: %v", err)
	}
	return nil
}
<|endoftext|>


<commit_before>package logging

import (
	"bufio"
	"context"
	"fmt"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/sirupsen/logrus"

	"github.com/datawire/dlib/dlog"
	"github.com/telepresenceio/telepresence/v2/pkg/client"
	"github.com/telepresenceio/telepresence/v2/pkg/filelocation"
	"github.com/telepresenceio/telepresence/v2/pkg/log"
)

// loggerForTest exposes internals to initcontext_test.go
var loggerForTest *logrus.Logger

// InitContext sets up standard Telepresence logging for a background process
func InitContext(ctx context.Context, name string, strategy RotationStrategy) (context.Context, error) {
	logger := logrus.New()
	loggerForTest = logger

	// Start with DebugLevel so that the config is read using that level
	logger.SetLevel(logrus.DebugLevel)
	logger.ReportCaller = true

	if IsTerminal(int(os.Stdout.Fd())) {
		logger.Formatter = log.NewFormatter("15:04:05.0000")
	} else {
		logger.Formatter = log.NewFormatter("2006-01-02 15:04:05.0000")
		dir, err := filelocation.AppUserLogDir(ctx)
		if err != nil {
			return ctx, err
		}
		maxFiles := uint16(5)

		// TODO: Also make this a configurable setting in config.yml
		if me := os.Getenv("TELEPRESENCE_MAX_LOGFILES"); me != "" {
			if mx, err := strconv.Atoi(me); err == nil && mx >= 0 {
				maxFiles = uint16(mx)
			}
		}
		rf, err := OpenRotatingFile(filepath.Join(dir, name+".log"), "20060102T150405", true, true, 0600, strategy, maxFiles)
		if err != nil {
			return ctx, err
		}
		logger.SetOutput(rf)
	}
	ctx = dlog.WithLogger(ctx, dlog.WrapLogrus(logger))

	// Read the config and set the configured level.
	logLevels := client.GetConfig(ctx).LogLevels
	level := logrus.InfoLevel
	if name == "daemon" {
		level = logLevels.RootDaemon
	} else if name == "connector" {
		level = logLevels.UserDaemon
	}
	log.SetLogrusLevel(logger, level.String())
	ctx = log.WithLevelSetter(ctx, logger)
	return ctx, nil
}

func SummarizeLog(ctx context.Context, name string) (string, error) {
	dir, err := filelocation.AppUserLogDir(ctx)
	if err != nil {
		return "", err
	}

	filename := filepath.Join(dir, name+".log")
	file, err := os.Open(filename)
	if err != nil {
		return "", err
	}
	defer file.Close()
	scanner := bufio.NewScanner(file)

	errorCount := 0
	for scanner.Scan() {
		// XXX: is there a better way to detect error lines?
		parts := strings.Fields(scanner.Text())
		if len(parts) > 2 && parts[2] == "error" {
			errorCount++
		}
	}
	if errorCount == 0 {
		return "", nil
	}
	desc := fmt.Sprintf("%d error", errorCount)
	if errorCount > 1 {
		desc += "s"
	}

	return fmt.Sprintf("See logs for details (%s found): %q", desc, filename), nil
}
<commit_msg>Never log with report caller unless log level is TRACE<commit_after>package logging

import (
	"bufio"
	"context"
	"fmt"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/sirupsen/logrus"

	"github.com/datawire/dlib/dlog"
	"github.com/telepresenceio/telepresence/v2/pkg/client"
	"github.com/telepresenceio/telepresence/v2/pkg/filelocation"
	"github.com/telepresenceio/telepresence/v2/pkg/log"
)

// loggerForTest exposes internals to initcontext_test.go
var loggerForTest *logrus.Logger

// InitContext sets up standard Telepresence logging for a background process
func InitContext(ctx context.Context, name string, strategy RotationStrategy) (context.Context, error) {
	logger := logrus.New()
	loggerForTest = logger

	// Start with DebugLevel so that the config is read using that level
	logger.SetLevel(logrus.DebugLevel)
	logger.ReportCaller = false // turned on when level >= logrus.TraceLevel

	if IsTerminal(int(os.Stdout.Fd())) {
		logger.Formatter = log.NewFormatter("15:04:05.0000")
	} else {
		logger.Formatter = log.NewFormatter("2006-01-02 15:04:05.0000")
		dir, err := filelocation.AppUserLogDir(ctx)
		if err != nil {
			return ctx, err
		}
		maxFiles := uint16(5)

		// TODO: Also make this a configurable setting in config.yml
		if me := os.Getenv("TELEPRESENCE_MAX_LOGFILES"); me != "" {
			if mx, err := strconv.Atoi(me); err == nil && mx >= 0 {
				maxFiles = uint16(mx)
			}
		}
		rf, err := OpenRotatingFile(filepath.Join(dir, name+".log"), "20060102T150405", true, true, 0600, strategy, maxFiles)
		if err != nil {
			return ctx, err
		}
		logger.SetOutput(rf)
	}
	ctx = dlog.WithLogger(ctx, dlog.WrapLogrus(logger))

	// Read the config and set the configured level.
	logLevels := client.GetConfig(ctx).LogLevels
	level := logrus.InfoLevel
	if name == "daemon" {
		level = logLevels.RootDaemon
	} else if name == "connector" {
		level = logLevels.UserDaemon
	}
	log.SetLogrusLevel(logger, level.String())
	ctx = log.WithLevelSetter(ctx, logger)
	return ctx, nil
}

func SummarizeLog(ctx context.Context, name string) (string, error) {
	dir, err := filelocation.AppUserLogDir(ctx)
	if err != nil {
		return "", err
	}

	filename := filepath.Join(dir, name+".log")
	file, err := os.Open(filename)
	if err != nil {
		return "", err
	}
	defer file.Close()
	scanner := bufio.NewScanner(file)

	errorCount := 0
	for scanner.Scan() {
		// XXX: is there a better way to detect error lines?
		parts := strings.Fields(scanner.Text())
		if len(parts) > 2 && parts[2] == "error" {
			errorCount++
		}
	}
	if errorCount == 0 {
		return "", nil
	}
	desc := fmt.Sprintf("%d error", errorCount)
	if errorCount > 1 {
		desc += "s"
	}

	return fmt.Sprintf("See logs for details (%s found): %q", desc, filename), nil
}
<|endoftext|>