<commit_before>package plugins

import (
	"net/http"
	"strings"

	"strconv"

	"time"

	"github.com/Seklfreak/Robyul2/cache"
	"github.com/Seklfreak/Robyul2/helpers"
	"github.com/bradfitz/slice"
	"github.com/bwmarrin/discordgo"
	"github.com/sirupsen/logrus"
)

type moveAction func(args []string, in *discordgo.Message, out **discordgo.MessageSend) (next moveAction)

type Move struct {
}

func (m *Move) Commands() []string {
	return []string{
		"move",
		"copy",
	}
}

func (m *Move) Init(session *discordgo.Session) {
}

func (m *Move) Action(command string, content string, msg *discordgo.Message, session *discordgo.Session) {
	defer helpers.Recover()

	if !helpers.ModuleIsAllowed(msg.ChannelID, msg.ID, msg.Author.ID, helpers.ModulePermMod) {
		return
	}

	var result *discordgo.MessageSend
	args := strings.Fields(content)

	var action moveAction
	switch command {
	case "move":
		action = m.actionMove
	case "copy":
		action = m.actionCopy
	}

	for action != nil {
		action = action(args, msg, &result)
	}
}

// [p]move <#target channel or channel id> <message id> [<number of messages, min 1, max 100>]
// moves messages and deletes old messages after that
func (m *Move) actionMove(args []string, in *discordgo.Message, out **discordgo.MessageSend) moveAction {
	if !helpers.IsMod(in) {
		*out = m.newMsg("no_permission")
		return m.actionFinish
	}

	if len(args) < 2 {
		*out = m.newMsg("bot.arguments.too-few")
		return m.actionFinish
	}

	cache.GetSession().MessageReactionAdd(in.ChannelID, in.ID, "🔄")

	sourceChannel, err := helpers.GetChannel(in.ChannelID)
	if err != nil {
		cache.GetSession().MessageReactionRemove(in.ChannelID, in.ID, "🔄", cache.GetSession().State.User.ID)
		helpers.Relax(err)
	}

	targetChannel, err := helpers.GetChannelFromMention(in, args[0])
	if err != nil {
		cache.GetSession().MessageReactionRemove(in.ChannelID, in.ID, "🔄", cache.GetSession().State.User.ID)
		helpers.Relax(err)
	}

	// how many messages shall we move?
	numOfMessagesToMove := 1
	if len(args) >= 3 {
		numOfMessagesToMove, err = strconv.Atoi(args[2])
		if err != nil {
			cache.GetSession().MessageReactionRemove(in.ChannelID, in.ID, "🔄", cache.GetSession().State.User.ID)
			*out = m.newMsg("bot.arguments.invalid")
			return m.actionFinish
		}
	}
	if numOfMessagesToMove < 1 || numOfMessagesToMove > 100 {
		cache.GetSession().MessageReactionRemove(in.ChannelID, in.ID, "🔄", cache.GetSession().State.User.ID)
		*out = m.newMsg("bot.arguments.invalid")
		return m.actionFinish
	}

	err = m.copyMessages(sourceChannel.ID, args[1], numOfMessagesToMove, targetChannel.ID, true)
	if err != nil {
		cache.GetSession().MessageReactionRemove(in.ChannelID, in.ID, "🔄", cache.GetSession().State.User.ID)
		helpers.Relax(err)
	}

	cache.GetSession().MessageReactionRemove(in.ChannelID, in.ID, "🔄", cache.GetSession().State.User.ID)
	cache.GetSession().MessageReactionAdd(in.ChannelID, in.ID, "👌")
	go func() {
		defer helpers.Recover()

		time.Sleep(5 * time.Second)
		cache.GetSession().ChannelMessageDelete(in.ChannelID, in.ID)
	}()
	return nil
}

// [p]copy <#target channel or channel id> <message id> [<number of messages, min 1, max 100>]
// moves messages and keeps the old messages
func (m *Move) actionCopy(args []string, in *discordgo.Message, out **discordgo.MessageSend) moveAction {
	if !helpers.IsMod(in) {
		*out = m.newMsg("no_permission")
		return m.actionFinish
	}

	if len(args) < 2 {
		*out = m.newMsg("bot.arguments.too-few")
		return m.actionFinish
	}

	cache.GetSession().MessageReactionAdd(in.ChannelID, in.ID, "🔄")

	sourceChannel, err := helpers.GetChannel(in.ChannelID)
	if err != nil {
		cache.GetSession().MessageReactionRemove(in.ChannelID, in.ID, "🔄", cache.GetSession().State.User.ID)
		helpers.Relax(err)
	}

	targetChannel, err := helpers.GetChannelFromMention(in, args[0])
	if err != nil {
		cache.GetSession().MessageReactionRemove(in.ChannelID, in.ID, "🔄", cache.GetSession().State.User.ID)
		helpers.Relax(err)
	}

	// how many messages shall we move?
	numOfMessagesToMove := 1
	if len(args) >= 3 {
		numOfMessagesToMove, err = strconv.Atoi(args[2])
		if err != nil {
			cache.GetSession().MessageReactionRemove(in.ChannelID, in.ID, "🔄", cache.GetSession().State.User.ID)
			*out = m.newMsg("bot.arguments.invalid")
			return m.actionFinish
		}
	}
	if numOfMessagesToMove < 1 || numOfMessagesToMove > 100 {
		cache.GetSession().MessageReactionRemove(in.ChannelID, in.ID, "🔄", cache.GetSession().State.User.ID)
		*out = m.newMsg("bot.arguments.invalid")
		return m.actionFinish
	}

	err = m.copyMessages(sourceChannel.ID, args[1], numOfMessagesToMove, targetChannel.ID, false)
	if err != nil {
		cache.GetSession().MessageReactionRemove(in.ChannelID, in.ID, "🔄", cache.GetSession().State.User.ID)
		helpers.Relax(err)
	}

	cache.GetSession().MessageReactionRemove(in.ChannelID, in.ID, "🔄", cache.GetSession().State.User.ID)
	cache.GetSession().MessageReactionAdd(in.ChannelID, in.ID, "👌")
	go func() {
		defer helpers.Recover()

		time.Sleep(5 * time.Second)
		cache.GetSession().ChannelMessageDelete(in.ChannelID, in.ID)
	}()
	return nil
}

func (m *Move) copyMessages(sourceChannelID, sourceMessageID string, numberOfMessages int, targetChannelID string, delete bool) (err error) {
	// debug
	//m.logger().Debugf("requested move from #%s Message #%s n %d to #%s deletion %v",
	//	sourceChannelID, sourceMessageID, numberOfMessages, targetChannelID, delete)
	// gather messages to copy
	messagesToMove := make([]*discordgo.Message, 0)
	requestedMessage, err := cache.GetSession().ChannelMessage(sourceChannelID, sourceMessageID)
	if err != nil {
		return err
	}
	messagesToMove = append(messagesToMove, requestedMessage)
	// gather exact message
	// gather after messages
	if numberOfMessages > 1 {
		messagesLeft := numberOfMessages - 1
		lastAfterID := sourceMessageID
		for {
			messagesToGet := messagesLeft
			if messagesLeft > 100 {
				messagesToGet = 100
			}
			messagesLeft -= messagesToGet

			requestedMessages, err := cache.GetSession().ChannelMessages(sourceChannelID, messagesToGet, "", lastAfterID, "")
			if err != nil {
				return err
			}
			slice.Sort(requestedMessages, func(i, j int) bool {
				return requestedMessages[i].Timestamp < requestedMessages[j].Timestamp
			})
			for _, requestedMessage := range requestedMessages {
				messagesToMove = append(messagesToMove, requestedMessage)
				lastAfterID = requestedMessage.ID
			}

			if messagesLeft <= 0 {
				break
			}
		}
	}
	// get two webhooks for target channel (rotation)
	webhooksToUse := make([]*discordgo.Webhook, 0)
	existingWebhooks, err := cache.GetSession().ChannelWebhooks(targetChannelID)
	if err != nil {
		return err
	}
	if existingWebhooks != nil && len(existingWebhooks) > 0 {
		for _, existingWebhook := range existingWebhooks {
			webhooksToUse = append(webhooksToUse, existingWebhook)
			if len(webhooksToUse) >= 2 {
				break
			}
		}
	}
	for {
		if len(webhooksToUse) >= 2 {
			break
		}
		newWebhook, err := cache.GetSession().WebhookCreate(targetChannelID, "Robyul Webhook", "")
		if err != nil {
			return err
		}
		webhooksToUse = append(webhooksToUse, newWebhook)
	}
	// send new messages
	var lastUserID string
	var nextWebhookIndex int
	var nextContent string
	for _, messageToMove := range messagesToMove {
		nextContent = messageToMove.Content
		// should we switch to a new webhook (rotation)
		if lastUserID != messageToMove.Author.ID {
			nextWebhookIndex++
			if nextWebhookIndex > 1 {
				nextWebhookIndex = 0
			}
		}
		// gather file if attachments on message
		if messageToMove.Attachments != nil && len(messageToMove.Attachments) > 0 {
			data, err := helpers.NetGetUAWithError(messageToMove.Attachments[0].URL, helpers.DEFAULT_UA)
			helpers.RelaxLog(err)
			if err == nil {
				// sniff filetype from first 512 bytes
				contentType := http.DetectContentType(data[0:511])
				// debug
				//m.logger().Debugf("found attached file %s content type %s",
				//	messageToMove.Attachments[0].Filename, contentType)
				// reupload images to imgur
				if strings.HasPrefix(contentType, "image/") {
					newLink, err := helpers.UploadImage(data)
					helpers.RelaxLog(err)
					if err == nil {
						nextContent += "\n" + newLink
					}
				}
			}
		}
		// debug
		//m.logger().Debugf("posting webhook id %s by %s content %s",
		//	webhooksToUse[nextWebhookIndex].ID, messageToMove.Author.ID, messageToMove.Content,
		//)
		// send message
		if nextContent == "" && (messageToMove.Embeds == nil || len(messageToMove.Embeds) < 1) {
			continue
		}
		_, err := helpers.WebhookExecuteWithResult(
			webhooksToUse[nextWebhookIndex].ID,
			webhooksToUse[nextWebhookIndex].Token,
			&discordgo.WebhookParams{
				Content:   nextContent,
				Username:  messageToMove.Author.Username,
				AvatarURL: messageToMove.Author.AvatarURL("512"),
				TTS:       false,
				Embeds:    messageToMove.Embeds,
			},
		)
		if err != nil {
			return err
		}
		lastUserID = messageToMove.Author.ID
	}
	// delete messages if wanted
	if delete {
		bulkDeleteMessageIDs := make([]string, 0)
		for _, messageToDelete := range messagesToMove {
			bulkDeleteMessageIDs = append(bulkDeleteMessageIDs, messageToDelete.ID)
		}
		err := cache.GetSession().ChannelMessagesBulkDelete(sourceChannelID, bulkDeleteMessageIDs)
		if err != nil {
			return err
		}
	}

	return nil
}

func (m *Move) actionFinish(args []string, in *discordgo.Message, out **discordgo.MessageSend) moveAction {
	_, err := helpers.SendComplex(in.ChannelID, *out)
	helpers.Relax(err)

	return nil
}

func (m *Move) newMsg(content string) *discordgo.MessageSend {
	return &discordgo.MessageSend{Content: helpers.GetText(content)}
}

func (m *Move) Relax(err error) {
	if err != nil {
		panic(err)
	}
}

func (m *Move) logger() *logrus.Entry {
	return cache.GetLogger().WithField("module", "move")
}
<commit_msg>[move] fixes mod error string<commit_after>package plugins

import (
	"net/http"
	"strings"

	"strconv"

	"time"

	"github.com/Seklfreak/Robyul2/cache"
	"github.com/Seklfreak/Robyul2/helpers"
	"github.com/bradfitz/slice"
	"github.com/bwmarrin/discordgo"
	"github.com/sirupsen/logrus"
)

type moveAction func(args []string, in *discordgo.Message, out **discordgo.MessageSend) (next moveAction)

type Move struct {
}

func (m *Move) Commands() []string {
	return []string{
		"move",
		"copy",
	}
}

func (m *Move) Init(session *discordgo.Session) {
}

func (m *Move) Action(command string, content string, msg *discordgo.Message, session *discordgo.Session) {
	defer helpers.Recover()

	if !helpers.ModuleIsAllowed(msg.ChannelID, msg.ID, msg.Author.ID, helpers.ModulePermMod) {
		return
	}

	var result *discordgo.MessageSend
	args := strings.Fields(content)

	var action moveAction
	switch command {
	case "move":
		action = m.actionMove
	case "copy":
		action = m.actionCopy
	}

	for action != nil {
		action = action(args, msg, &result)
	}
}

// [p]move <#target channel or channel id> <message id> [<number of messages, min 1, max 100>]
// moves messages and deletes old messages after that
func (m *Move) actionMove(args []string, in *discordgo.Message, out **discordgo.MessageSend) moveAction {
	if !helpers.IsMod(in) {
		*out = m.newMsg("mod.no_permission")
		return m.actionFinish
	}

	if len(args) < 2 {
		*out = m.newMsg("bot.arguments.too-few")
		return m.actionFinish
	}

	cache.GetSession().MessageReactionAdd(in.ChannelID, in.ID, "🔄")

	sourceChannel, err := helpers.GetChannel(in.ChannelID)
	if err != nil {
		cache.GetSession().MessageReactionRemove(in.ChannelID, in.ID, "🔄", cache.GetSession().State.User.ID)
		helpers.Relax(err)
	}

	targetChannel, err := helpers.GetChannelFromMention(in, args[0])
	if err != nil {
		cache.GetSession().MessageReactionRemove(in.ChannelID, in.ID, "🔄", cache.GetSession().State.User.ID)
		helpers.Relax(err)
	}

	// how many messages shall we move?
	numOfMessagesToMove := 1
	if len(args) >= 3 {
		numOfMessagesToMove, err = strconv.Atoi(args[2])
		if err != nil {
			cache.GetSession().MessageReactionRemove(in.ChannelID, in.ID, "🔄", cache.GetSession().State.User.ID)
			*out = m.newMsg("bot.arguments.invalid")
			return m.actionFinish
		}
	}
	if numOfMessagesToMove < 1 || numOfMessagesToMove > 100 {
		cache.GetSession().MessageReactionRemove(in.ChannelID, in.ID, "🔄", cache.GetSession().State.User.ID)
		*out = m.newMsg("bot.arguments.invalid")
		return m.actionFinish
	}

	err = m.copyMessages(sourceChannel.ID, args[1], numOfMessagesToMove, targetChannel.ID, true)
	if err != nil {
		cache.GetSession().MessageReactionRemove(in.ChannelID, in.ID, "🔄", cache.GetSession().State.User.ID)
		helpers.Relax(err)
	}

	cache.GetSession().MessageReactionRemove(in.ChannelID, in.ID, "🔄", cache.GetSession().State.User.ID)
	cache.GetSession().MessageReactionAdd(in.ChannelID, in.ID, "👌")
	go func() {
		defer helpers.Recover()

		time.Sleep(5 * time.Second)
		cache.GetSession().ChannelMessageDelete(in.ChannelID, in.ID)
	}()
	return nil
}

// [p]copy <#target channel or channel id> <message id> [<number of messages, min 1, max 100>]
// moves messages and keeps the old messages
func (m *Move) actionCopy(args []string, in *discordgo.Message, out **discordgo.MessageSend) moveAction {
	if !helpers.IsMod(in) {
		*out = m.newMsg("mod.no_permission")
		return m.actionFinish
	}

	if len(args) < 2 {
		*out = m.newMsg("bot.arguments.too-few")
		return m.actionFinish
	}

	cache.GetSession().MessageReactionAdd(in.ChannelID, in.ID, "🔄")

	sourceChannel, err := helpers.GetChannel(in.ChannelID)
	if err != nil {
		cache.GetSession().MessageReactionRemove(in.ChannelID, in.ID, "🔄", cache.GetSession().State.User.ID)
		helpers.Relax(err)
	}

	targetChannel, err := helpers.GetChannelFromMention(in, args[0])
	if err != nil {
		cache.GetSession().MessageReactionRemove(in.ChannelID, in.ID, "🔄", cache.GetSession().State.User.ID)
		helpers.Relax(err)
	}

	// how many messages shall we move?
	numOfMessagesToMove := 1
	if len(args) >= 3 {
		numOfMessagesToMove, err = strconv.Atoi(args[2])
		if err != nil {
			cache.GetSession().MessageReactionRemove(in.ChannelID, in.ID, "🔄", cache.GetSession().State.User.ID)
			*out = m.newMsg("bot.arguments.invalid")
			return m.actionFinish
		}
	}
	if numOfMessagesToMove < 1 || numOfMessagesToMove > 100 {
		cache.GetSession().MessageReactionRemove(in.ChannelID, in.ID, "🔄", cache.GetSession().State.User.ID)
		*out = m.newMsg("bot.arguments.invalid")
		return m.actionFinish
	}

	err = m.copyMessages(sourceChannel.ID, args[1], numOfMessagesToMove, targetChannel.ID, false)
	if err != nil {
		cache.GetSession().MessageReactionRemove(in.ChannelID, in.ID, "🔄", cache.GetSession().State.User.ID)
		helpers.Relax(err)
	}

	cache.GetSession().MessageReactionRemove(in.ChannelID, in.ID, "🔄", cache.GetSession().State.User.ID)
	cache.GetSession().MessageReactionAdd(in.ChannelID, in.ID, "👌")
	go func() {
		defer helpers.Recover()

		time.Sleep(5 * time.Second)
		cache.GetSession().ChannelMessageDelete(in.ChannelID, in.ID)
	}()
	return nil
}

func (m *Move) copyMessages(sourceChannelID, sourceMessageID string, numberOfMessages int, targetChannelID string, delete bool) (err error) {
	// debug
	//m.logger().Debugf("requested move from #%s Message #%s n %d to #%s deletion %v",
	//	sourceChannelID, sourceMessageID, numberOfMessages, targetChannelID, delete)
	// gather messages to copy
	messagesToMove := make([]*discordgo.Message, 0)
	requestedMessage, err := cache.GetSession().ChannelMessage(sourceChannelID, sourceMessageID)
	if err != nil {
		return err
	}
	messagesToMove = append(messagesToMove, requestedMessage)
	// gather exact message
	// gather after messages
	if numberOfMessages > 1 {
		messagesLeft := numberOfMessages - 1
		lastAfterID := sourceMessageID
		for {
			messagesToGet := messagesLeft
			if messagesLeft > 100 {
				messagesToGet = 100
			}
			messagesLeft -= messagesToGet

			requestedMessages, err := cache.GetSession().ChannelMessages(sourceChannelID, messagesToGet, "", lastAfterID, "")
			if err != nil {
				return err
			}
			slice.Sort(requestedMessages, func(i, j int) bool {
				return requestedMessages[i].Timestamp < requestedMessages[j].Timestamp
			})
			for _, requestedMessage := range requestedMessages {
				messagesToMove = append(messagesToMove, requestedMessage)
				lastAfterID = requestedMessage.ID
			}

			if messagesLeft <= 0 {
				break
			}
		}
	}
	// get two webhooks for target channel (rotation)
	webhooksToUse := make([]*discordgo.Webhook, 0)
	existingWebhooks, err := cache.GetSession().ChannelWebhooks(targetChannelID)
	if err != nil {
		return err
	}
	if existingWebhooks != nil && len(existingWebhooks) > 0 {
		for _, existingWebhook := range existingWebhooks {
			webhooksToUse = append(webhooksToUse, existingWebhook)
			if len(webhooksToUse) >= 2 {
				break
			}
		}
	}
	for {
		if len(webhooksToUse) >= 2 {
			break
		}
		newWebhook, err := cache.GetSession().WebhookCreate(targetChannelID, "Robyul Webhook", "")
		if err != nil {
			return err
		}
		webhooksToUse = append(webhooksToUse, newWebhook)
	}
	// send new messages
	var lastUserID string
	var nextWebhookIndex int
	var nextContent string
	for _, messageToMove := range messagesToMove {
		nextContent = messageToMove.Content
		// should we switch to a new webhook (rotation)
		if lastUserID != messageToMove.Author.ID {
			nextWebhookIndex++
			if nextWebhookIndex > 1 {
				nextWebhookIndex = 0
			}
		}
		// gather file if attachments on message
		if messageToMove.Attachments != nil && len(messageToMove.Attachments) > 0 {
			data, err := helpers.NetGetUAWithError(messageToMove.Attachments[0].URL, helpers.DEFAULT_UA)
			helpers.RelaxLog(err)
			if err == nil {
				// sniff filetype from first 512 bytes
				contentType := http.DetectContentType(data[0:511])
				// debug
				//m.logger().Debugf("found attached file %s content type %s",
				//	messageToMove.Attachments[0].Filename, contentType)
				// reupload images to imgur
				if strings.HasPrefix(contentType, "image/") {
					newLink, err := helpers.UploadImage(data)
					helpers.RelaxLog(err)
					if err == nil {
						nextContent += "\n" + newLink
					}
				}
			}
		}
		// debug
		//m.logger().Debugf("posting webhook id %s by %s content %s",
		//	webhooksToUse[nextWebhookIndex].ID, messageToMove.Author.ID, messageToMove.Content,
		//)
		// send message
		if nextContent == "" && (messageToMove.Embeds == nil || len(messageToMove.Embeds) < 1) {
			continue
		}
		_, err := helpers.WebhookExecuteWithResult(
			webhooksToUse[nextWebhookIndex].ID,
			webhooksToUse[nextWebhookIndex].Token,
			&discordgo.WebhookParams{
				Content:   nextContent,
				Username:  messageToMove.Author.Username,
				AvatarURL: messageToMove.Author.AvatarURL("512"),
				TTS:       false,
				Embeds:    messageToMove.Embeds,
			},
		)
		if err != nil {
			return err
		}
		lastUserID = messageToMove.Author.ID
	}
	// delete messages if wanted
	if delete {
		bulkDeleteMessageIDs := make([]string, 0)
		for _, messageToDelete := range messagesToMove {
			bulkDeleteMessageIDs = append(bulkDeleteMessageIDs, messageToDelete.ID)
		}
		err := cache.GetSession().ChannelMessagesBulkDelete(sourceChannelID, bulkDeleteMessageIDs)
		if err != nil {
			return err
		}
	}

	return nil
}

func (m *Move) actionFinish(args []string, in *discordgo.Message, out **discordgo.MessageSend) moveAction {
	_, err := helpers.SendComplex(in.ChannelID, *out)
	helpers.Relax(err)

	return nil
}

func (m *Move) newMsg(content string) *discordgo.MessageSend {
	return &discordgo.MessageSend{Content: helpers.GetText(content)}
}

func (m *Move) Relax(err error) {
	if err != nil {
		panic(err)
	}
}

func (m *Move) logger() *logrus.Entry {
	return cache.GetLogger().WithField("module", "move")
}
<|endoftext|>


<commit_before>package config

import (
	"context"
	fmt "fmt"
	"testing"
	"time"

	gorums "github.com/relab/gorums"
	"google.golang.org/grpc"
)

type (
	cfgSrv struct {
		name string
	}
	cfgQSpec struct {
		quorum int
	}
)

func (srv cfgSrv) Config(ctx context.Context, req *Request, out func(*Response, error)) {
	out(&Response{
		Name: srv.name,
		Num:  req.GetNum(),
	}, nil)
}

func newQSpec(cfgSize int) *cfgQSpec {
	return &cfgQSpec{quorum: cfgSize/2 + 1}
}

func (q cfgQSpec) ConfigQF(_ *Request, replies map[uint32]*Response) (*Response, bool) {
	if len(replies) < q.quorum {
		return nil, false
	}
	var reply *Response
	for _, r := range replies {
		reply = r
		break
	}
	return reply, true
}

func setup(t *testing.T, mgr *Manager, cfgSize int) (cfg *Configuration, teardown func()) {
	t.Helper()
	srvs := make([]*cfgSrv, cfgSize)
	for i := range srvs {
		srvs[i] = &cfgSrv{}
	}
	addrs, closeServers := gorums.TestSetup(t, cfgSize, func(i int) gorums.ServerIface {
		srv := gorums.NewServer()
		RegisterConfigTestServer(srv, srvs[i])
		return srv
	})
	for i := range srvs {
		srvs[i].name = addrs[i]
	}
	cfg, err := mgr.NewConfiguration(newQSpec(cfgSize), gorums.WithNodeList(addrs))
	if err != nil {
		t.Fatal(err)
	}
	teardown = func() {
		mgr.Close()
		closeServers()
	}
	return cfg, teardown
}

func TestConfig(t *testing.T) {
	f := func(cfg *Configuration) {
		for i := 0; i < 5; i++ {
			resp, err := cfg.Config(context.Background(), &Request{Num: uint64(i)})
			if err != nil {
				t.Fatal(err)
			}
			if resp == nil {
				t.Fatal("Got nil response")
			}
		}
	}
	mgr := NewManager(
		gorums.WithDialTimeout(100*time.Millisecond),
		gorums.WithGrpcDialOptions(grpc.WithBlock(), grpc.WithInsecure()),
	)
	c1, teardown1 := setup(t, mgr, 4)
	defer teardown1()
	fmt.Println("--- c1 ", c1.Nodes())
	f(c1)

	c2, teardown2 := setup(t, mgr, 2)
	defer teardown2()
	fmt.Println("--- c2 ", c2.Nodes())
	f(c2)

	newNodeList := c1.And(c2)
	c3, err := mgr.NewConfiguration(
		newQSpec(c1.Size()+c2.Size()),
		newNodeList,
	)
	if err != nil {
		t.Fatal(err)
	}
	fmt.Println("--- c3 ", c3.Nodes())
	f(c3)

	rmNodeList := c3.Except(c1)
	c4, err := mgr.NewConfiguration(
		newQSpec(c2.Size()),
		rmNodeList,
	)
	if err != nil {
		t.Fatal(err)
	}
	fmt.Println("--- c4 ", c4.Nodes())
	f(c4)
}
<commit_msg>Added comments to explain TestConfig<commit_after>package config

import (
	"context"
	fmt "fmt"
	"testing"
	"time"

	gorums "github.com/relab/gorums"
	"google.golang.org/grpc"
)

type (
	cfgSrv struct {
		name string
	}
	cfgQSpec struct {
		quorum int
	}
)

func (srv cfgSrv) Config(ctx context.Context, req *Request, out func(*Response, error)) {
	out(&Response{
		Name: srv.name,
		Num:  req.GetNum(),
	}, nil)
}

func newQSpec(cfgSize int) *cfgQSpec {
	return &cfgQSpec{quorum: cfgSize/2 + 1}
}

func (q cfgQSpec) ConfigQF(_ *Request, replies map[uint32]*Response) (*Response, bool) {
	if len(replies) < q.quorum {
		return nil, false
	}
	var reply *Response
	for _, r := range replies {
		reply = r
		break
	}
	return reply, true
}

// setup returns a new configuration of cfgSize and a corresponding teardown function.
// Calling setup multiple times will return a different configuration with different
// sets of nodes.
func setup(t *testing.T, mgr *Manager, cfgSize int) (cfg *Configuration, teardown func()) {
	t.Helper()
	srvs := make([]*cfgSrv, cfgSize)
	for i := range srvs {
		srvs[i] = &cfgSrv{}
	}
	addrs, closeServers := gorums.TestSetup(t, cfgSize, func(i int) gorums.ServerIface {
		srv := gorums.NewServer()
		RegisterConfigTestServer(srv, srvs[i])
		return srv
	})
	for i := range srvs {
		srvs[i].name = addrs[i]
	}
	cfg, err := mgr.NewConfiguration(newQSpec(cfgSize), gorums.WithNodeList(addrs))
	if err != nil {
		t.Fatal(err)
	}
	teardown = func() {
		mgr.Close()
		closeServers()
	}
	return cfg, teardown
}

// TestConfig creates and combines multiple configurations and invokes the Config RPC
// method on the different configurations created below.
func TestConfig(t *testing.T) {
	callRPC := func(cfg *Configuration) {
		for i := 0; i < 5; i++ {
			resp, err := cfg.Config(context.Background(), &Request{Num: uint64(i)})
			if err != nil {
				t.Fatal(err)
			}
			if resp == nil {
				t.Fatal("Got nil response")
			}
		}
	}
	mgr := NewManager(
		gorums.WithDialTimeout(100*time.Millisecond),
		gorums.WithGrpcDialOptions(grpc.WithBlock(), grpc.WithInsecure()),
	)
	c1, teardown1 := setup(t, mgr, 4)
	defer teardown1()
	fmt.Println("--- c1 ", c1.Nodes())
	callRPC(c1)

	c2, teardown2 := setup(t, mgr, 2)
	defer teardown2()
	fmt.Println("--- c2 ", c2.Nodes())
	callRPC(c2)

	newNodeList := c1.And(c2)
	c3, err := mgr.NewConfiguration(
		newQSpec(c1.Size()+c2.Size()),
		newNodeList,
	)
	if err != nil {
		t.Fatal(err)
	}
	fmt.Println("--- c3 ", c3.Nodes())
	callRPC(c3)

	rmNodeList := c3.Except(c1)
	c4, err := mgr.NewConfiguration(
		newQSpec(c2.Size()),
		rmNodeList,
	)
	if err != nil {
		t.Fatal(err)
	}
	fmt.Println("--- c4 ", c4.Nodes())
	callRPC(c4)
}
<|endoftext|>


<commit_before>package sql

import (
	"fmt"
)

/*Search defines an interface for structs that can be used in sql queries*/
type Search interface {
	ToQuery() string
	Where(string, *SortTerm) string
	Order(string, ...*SortTerm) string
	And(string, string) string
	Limit(string) string
}

type BaseSearch struct{}

type SortTerm struct {
	key   string
	value string
	order int
	like  bool
}

func NewSortTerm(key, value string, order int, like bool) *SortTerm {
	if order != 0 && order != 1 {
		order = -1
	}

	return &SortTerm{
		key:   key,
		value: value,
		order: order,
		like:  like,
	}
}

func (*BaseSearch) Where(q string, sort *SortTerm) string {
	if sort.key == "" || sort.value == "" {
		return q
	}

	if sort.like {
		return fmt.Sprintf("%s WHERE %s LIKE '%s'", q, sort.key, sort.value)
	}

	return fmt.Sprintf("%s WHERE %s=%s", q, sort.key, sort.value)
}

func (b *BaseSearch) Order(q string, sort ...*SortTerm) string {
	s := sort[len(sort)-1]
	if len(sort) > 1 {
		prev := b.Order(q, sort[0:len(sort)-1]...)

		if s.order < 0 {
			return prev
		}
		return fmt.Sprintf("%s, %s %s", prev, s.key, orderString(s.order))
	}

	return fmt.Sprintf("%s ORDER BY %s %s", q, s.key, orderString(s.order))
}

func orderString(i int) string {
	switch i {
	case 0:
		return "ASC"
	case 1:
		return "DESC"
	default:
		return "ASC"
	}
}

func (*BaseSearch) And(q, s string) string {
	return fmt.Sprintf("%s AND %s", q, s)
}

func (*BaseSearch) Limit(q string) string {
	return fmt.Sprintf("%s LIMIT ?,?", q)
}
<commit_msg>Remove recursive Order function<commit_after>package sql

import (
	"fmt"
)

/*Search defines an interface for structs that can be used in sql queries*/
type Search interface {
	ToQuery() string
	Where(string, *SortTerm) string
	Order(string, ...*SortTerm) string
	And(string, string) string
	Limit(string) string
}

type BaseSearch struct{}

type SortTerm struct {
	key   string
	value string
	order int
	like  bool
}

func NewSortTerm(key, value string, order int, like bool) *SortTerm {
	if order != 0 && order != 1 {
		order = -1
	}

	return &SortTerm{
		key:   key,
		value: value,
		order: order,
		like:  like,
	}
}

func (*BaseSearch) Where(q string, sort *SortTerm) string {
	if sort.key == "" || sort.value == "" {
		return q
	}

	if sort.like {
		return fmt.Sprintf("%s WHERE %s LIKE '%s'", q, sort.key, sort.value)
	}

	return fmt.Sprintf("%s WHERE %s=%s", q, sort.key, sort.value)
}

/*Order returns q + "ORDER BY" the sort key and and order based on the int value
  of sort.order.*/
func (b *BaseSearch) Order(q string, sort ...*SortTerm) string {
	prefix := false

	for _, s := range sort {
		if s.order < 0 {
			continue
		}

		if !prefix {
			q = fmt.Sprintf("%s ORDER BY %s %s", q, s.key, orderString(s.order))
			prefix = true
		} else {
			q = fmt.Sprintf("%s, %s %s", q, s.key, orderString(s.order))
		}
	}

	return q
}

func orderString(i int) string {
	switch i {
	case 0:
		return "ASC"
	case 1:
		return "DESC"
	default:
		return "ASC"
	}
}

func (*BaseSearch) And(q, s string) string {
	if s == "" {
		return q
	}

	return fmt.Sprintf("%s AND %s", q, s)
}

func (*BaseSearch) Limit(q string) string {
	return fmt.Sprintf("%s LIMIT ?,?", q)
}
<|endoftext|>


<commit_before>// Tests and benchmarks performed with default settings only.
package defaults

import (
	"bytes"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"runtime"
	"strings"
	"sync"
	"testing"

	"github.com/rfjakob/gocryptfs/tests/test_helpers"
)

func TestMain(m *testing.M) {
	test_helpers.ResetTmpDir(true)
	test_helpers.MountOrExit(test_helpers.DefaultCipherDir, test_helpers.DefaultPlainDir, "-zerokey")
	r := m.Run()
	test_helpers.UnmountPanic(test_helpers.DefaultPlainDir)
	os.Exit(r)
}

// Test that we get the right timestamp when extracting a tarball.
func Test1980Tar(t *testing.T) {
	c := exec.Command("tar", "xzf", "1980.tar.gz", "-C", test_helpers.DefaultPlainDir)
	c.Stderr = os.Stderr
	c.Stdout = os.Stdout
	err := c.Run()
	if err != nil {
		t.Fatal(err)
	}
	fi, err := os.Stat(test_helpers.DefaultPlainDir + "/1980.txt")
	if err != nil {
		t.Fatal(err)
	}
	m := fi.ModTime().Unix()
	if m != 315619323 {
		t.Errorf("Wrong mtime: %d", m)
	}
}

// In gocryptfs before v1.2, the file header was only read once for each
// open. But truncating a file to zero will generate a new random file ID.
// The sequence below caused an I/O error to be returned.
func TestOpenTruncateRead(t *testing.T) {
	fn := test_helpers.DefaultPlainDir + "/TestTruncateWrite"
	// First FD is used for write and truncate.
	writeFd, err := os.Create(fn)
	if err != nil {
		t.Fatal(err)
	}
	defer writeFd.Close()
	abc := []byte("abc")
	_, err = writeFd.WriteAt(abc, 0)
	if err != nil {
		t.Fatal(err)
	}
	// Second FD is just for reading.
	readFd, err := os.Open(fn)
	if err != nil {
		t.Fatal(err)
	}
	defer readFd.Close()
	content := make([]byte, 3)
	_, err = readFd.ReadAt(content, 0)
	if err != nil {
		t.Fatal(err)
	}
	if !bytes.Equal(content, abc) {
		t.Fatalf("wrong content: %s", string(content))
	}
	// Truncate to zero to generate a new file ID and write new content.
	err = writeFd.Truncate(0)
	if err != nil {
		t.Fatal(err)
	}
	xyz := []byte("xyz")
	_, err = writeFd.WriteAt(xyz, 0)
	if err != nil {
		t.Fatal(err)
	}
	// Try to read from the other FD.
	_, err = readFd.ReadAt(content, 0)
	if err != nil {
		t.Fatal(err)
	}
	if !bytes.Equal(content, xyz) {
		t.Fatalf("wrong content: %s", string(content))
	}
}

// TestWORead tries to read from a write-only FD.
func TestWORead(t *testing.T) {
	fn := test_helpers.DefaultPlainDir + "/TestWORead"
	fd, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY, 0600)
	if err != nil {
		t.Fatal(err)
	}
	defer fd.Close()
	buf := make([]byte, 10)
	_, err = fd.Read(buf)
	if err == nil {
		t.Error("Reading from write-only file should fail, but did not")
	}
}

// xfstests generic/124 triggers this warning:
// cipherSize 18 == header size: interrupted write?
// This test reproduces the problem.
func TestXfs124(t *testing.T) {
	// GOMAXPROCS=8 and N=5000 seem to reliably trigger the problem. With N=1000,
	// the test passes sometimes.
	runtime.GOMAXPROCS(8)
	N := 5000

	fn := test_helpers.DefaultPlainDir + "/TestXfs124"
	fd, err := os.Create(fn)
	if err != nil {
		t.Fatal(err)
	}
	defer fd.Close()

	var wg sync.WaitGroup
	wg.Add(2)

	go func() {
		buf := make([]byte, 10)
		var err2 error
		for i := 0; i < N; i++ {
			err2 = fd.Truncate(0)
			if err2 != nil {
				panic(err2)
			}
			_, err2 = fd.WriteAt(buf, 0)
			if err2 != nil {
				panic(err2)
			}
		}
		wg.Done()
	}()

	fd2, err := os.Open(fn)
	if err != nil {
		t.Fatal(err)
	}
	defer fd2.Close()

	go func() {
		buf := make([]byte, 10)
		var err3 error
		for i := 0; i < N; i++ {
			_, err3 = fd2.ReadAt(buf, 0)
			if err3 == io.EOF {
				continue
			}
			if err3 != nil {
				panic(err3)
			}
		}
		wg.Done()
	}()

	wg.Wait()
}

func TestWrite0200File(t *testing.T) {
	fn := test_helpers.DefaultPlainDir + "/TestWrite0200File"
	err := ioutil.WriteFile(fn, nil, 0200)
	if err != nil {
		t.Fatalf("creating empty file failed: %v", err)
	}
	fd, err := os.OpenFile(fn, os.O_WRONLY, 0)
	if err != nil {
		t.Fatal(err)
	}
	fi, err := fd.Stat()
	if err != nil {
		t.Fatal(err)
	}
	perms := fi.Mode().Perm()
	if perms != 0200 {
		t.Fatal("wrong initial permissions")
	}
	defer fd.Close()
	_, err = fd.Write(make([]byte, 10))
	if err != nil {
		t.Fatal(err)
	}
	perms = fi.Mode().Perm()
	if perms != 0200 {
		t.Fatal("wrong restored permissions")
	}
}

// When xattr support was introduced, mv threw warnings like these:
//   mv: preserving permissions for ‘b/x’: Operation not permitted
// because we returned EPERM when it tried to set system.posix_acl_access.
// Now we return EOPNOTSUPP and mv is happy.
func TestMvWarnings(t *testing.T) {
	fn := test_helpers.TmpDir + "/TestMvWarnings"
	err := ioutil.WriteFile(fn, nil, 0600)
	if err != nil {
		t.Fatalf("creating file failed: %v", err)
	}
	cmd := exec.Command("mv", fn, test_helpers.DefaultPlainDir)
	out, err := cmd.CombinedOutput()
	if err != nil {
		t.Log(string(out))
		t.Fatal(err)
	}
	if len(out) != 0 {
		t.Fatalf("Got warnings from mv:\n%s", string(out))
	}
}

// Check for this bug in symlink handling:
// $ ln -s /asd/asdasd/asdasd b/foo
// $ mv b/foo .
// mv: listing attributes of 'b/foo': No such file or directory
// strace shows:
// llistxattr("b/foo", NULL, 0) = -1 ENOENT (No such file or directory)
func TestMvWarningSymlink(t *testing.T) {
	fn := test_helpers.DefaultPlainDir + "/TestMvWarningSymlink"
	err := os.Symlink("/foo/bar/baz", fn)
	if err != nil {
		t.Fatal(err)
	}
	cmd := exec.Command("mv", fn, test_helpers.TmpDir)
	out, err := cmd.CombinedOutput()
	if err != nil {
		t.Log(string(out))
		if runtime.GOOS == "darwin" {
			t.Skip("mv on darwin chokes on broken symlinks, see https://github.com/rfjakob/gocryptfs/issues/349")
		}
		t.Fatal(err)
	}
	if len(out) != 0 {
		t.Log(strings.TrimSpace(string(out)))
		t.Fatal("Got warnings")
	}
}

// See TestCpWarnings.
func TestCpWarnings(t *testing.T) {
	fn := test_helpers.TmpDir + "/TestCpWarnings"
	err := ioutil.WriteFile(fn, []byte("foo"), 0600)
	if err != nil {
		t.Fatalf("creating file failed: %v", err)
	}
	cmd := exec.Command("cp", "-a", fn, test_helpers.DefaultPlainDir)
	out, err := cmd.CombinedOutput()
	if err != nil {
		t.Log(string(out))
		t.Fatal(err)
	}
	if len(out) != 0 {
		t.Fatalf("Got warnings from cp -a:\n%s", string(out))
	}
}
<commit_msg>tests: fix TestCpWarnings comment typo<commit_after>// Tests and benchmarks performed with default settings only.
package defaults

import (
	"bytes"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"runtime"
	"strings"
	"sync"
	"testing"

	"github.com/rfjakob/gocryptfs/tests/test_helpers"
)

func TestMain(m *testing.M) {
	test_helpers.ResetTmpDir(true)
	test_helpers.MountOrExit(test_helpers.DefaultCipherDir, test_helpers.DefaultPlainDir, "-zerokey")
	r := m.Run()
	test_helpers.UnmountPanic(test_helpers.DefaultPlainDir)
	os.Exit(r)
}

// Test that we get the right timestamp when extracting a tarball.
func Test1980Tar(t *testing.T) {
	c := exec.Command("tar", "xzf", "1980.tar.gz", "-C", test_helpers.DefaultPlainDir)
	c.Stderr = os.Stderr
	c.Stdout = os.Stdout
	err := c.Run()
	if err != nil {
		t.Fatal(err)
	}
	fi, err := os.Stat(test_helpers.DefaultPlainDir + "/1980.txt")
	if err != nil {
		t.Fatal(err)
	}
	m := fi.ModTime().Unix()
	if m != 315619323 {
		t.Errorf("Wrong mtime: %d", m)
	}
}

// In gocryptfs before v1.2, the file header was only read once for each
// open. But truncating a file to zero will generate a new random file ID.
// The sequence below caused an I/O error to be returned.
func TestOpenTruncateRead(t *testing.T) {
	fn := test_helpers.DefaultPlainDir + "/TestTruncateWrite"
	// First FD is used for write and truncate.
	writeFd, err := os.Create(fn)
	if err != nil {
		t.Fatal(err)
	}
	defer writeFd.Close()
	abc := []byte("abc")
	_, err = writeFd.WriteAt(abc, 0)
	if err != nil {
		t.Fatal(err)
	}
	// Second FD is just for reading.
	readFd, err := os.Open(fn)
	if err != nil {
		t.Fatal(err)
	}
	defer readFd.Close()
	content := make([]byte, 3)
	_, err = readFd.ReadAt(content, 0)
	if err != nil {
		t.Fatal(err)
	}
	if !bytes.Equal(content, abc) {
		t.Fatalf("wrong content: %s", string(content))
	}
	// Truncate to zero to generate a new file ID and write new content.
	err = writeFd.Truncate(0)
	if err != nil {
		t.Fatal(err)
	}
	xyz := []byte("xyz")
	_, err = writeFd.WriteAt(xyz, 0)
	if err != nil {
		t.Fatal(err)
	}
	// Try to read from the other FD.
	_, err = readFd.ReadAt(content, 0)
	if err != nil {
		t.Fatal(err)
	}
	if !bytes.Equal(content, xyz) {
		t.Fatalf("wrong content: %s", string(content))
	}
}

// TestWORead tries to read from a write-only FD.
func TestWORead(t *testing.T) {
	fn := test_helpers.DefaultPlainDir + "/TestWORead"
	fd, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY, 0600)
	if err != nil {
		t.Fatal(err)
	}
	defer fd.Close()
	buf := make([]byte, 10)
	_, err = fd.Read(buf)
	if err == nil {
		t.Error("Reading from write-only file should fail, but did not")
	}
}

// xfstests generic/124 triggers this warning:
// cipherSize 18 == header size: interrupted write?
// This test reproduces the problem.
func TestXfs124(t *testing.T) {
	// GOMAXPROCS=8 and N=5000 seem to reliably trigger the problem. With N=1000,
	// the test passes sometimes.
	runtime.GOMAXPROCS(8)
	N := 5000

	fn := test_helpers.DefaultPlainDir + "/TestXfs124"
	fd, err := os.Create(fn)
	if err != nil {
		t.Fatal(err)
	}
	defer fd.Close()

	var wg sync.WaitGroup
	wg.Add(2)

	go func() {
		buf := make([]byte, 10)
		var err2 error
		for i := 0; i < N; i++ {
			err2 = fd.Truncate(0)
			if err2 != nil {
				panic(err2)
			}
			_, err2 = fd.WriteAt(buf, 0)
			if err2 != nil {
				panic(err2)
			}
		}
		wg.Done()
	}()

	fd2, err := os.Open(fn)
	if err != nil {
		t.Fatal(err)
	}
	defer fd2.Close()

	go func() {
		buf := make([]byte, 10)
		var err3 error
		for i := 0; i < N; i++ {
			_, err3 = fd2.ReadAt(buf, 0)
			if err3 == io.EOF {
				continue
			}
			if err3 != nil {
				panic(err3)
			}
		}
		wg.Done()
	}()

	wg.Wait()
}

func TestWrite0200File(t *testing.T) {
	fn := test_helpers.DefaultPlainDir + "/TestWrite0200File"
	err := ioutil.WriteFile(fn, nil, 0200)
	if err != nil {
		t.Fatalf("creating empty file failed: %v", err)
	}
	fd, err := os.OpenFile(fn, os.O_WRONLY, 0)
	if err != nil {
		t.Fatal(err)
	}
	fi, err := fd.Stat()
	if err != nil {
		t.Fatal(err)
	}
	perms := fi.Mode().Perm()
	if perms != 0200 {
		t.Fatal("wrong initial permissions")
	}
	defer fd.Close()
	_, err = fd.Write(make([]byte, 10))
	if err != nil {
		t.Fatal(err)
	}
	perms = fi.Mode().Perm()
	if perms != 0200 {
		t.Fatal("wrong restored permissions")
	}
}

// TestMvWarnings:
// When xattr support was introduced, mv threw warnings like these:
//   mv: preserving permissions for ‘b/x’: Operation not permitted
// because we returned EPERM when it tried to set system.posix_acl_access.
// Now we return EOPNOTSUPP and mv is happy.
func TestMvWarnings(t *testing.T) {
	fn := test_helpers.TmpDir + "/TestMvWarnings"
	err := ioutil.WriteFile(fn, nil, 0600)
	if err != nil {
		t.Fatalf("creating file failed: %v", err)
	}
	cmd := exec.Command("mv", fn, test_helpers.DefaultPlainDir)
	out, err := cmd.CombinedOutput()
	if err != nil {
		t.Log(string(out))
		t.Fatal(err)
	}
	if len(out) != 0 {
		t.Fatalf("Got warnings from mv:\n%s", string(out))
	}
}

// Check for this bug in symlink handling:
// $ ln -s /asd/asdasd/asdasd b/foo
// $ mv b/foo .
// mv: listing attributes of 'b/foo': No such file or directory
// strace shows:
// llistxattr("b/foo", NULL, 0) = -1 ENOENT (No such file or directory)
func TestMvWarningSymlink(t *testing.T) {
	fn := test_helpers.DefaultPlainDir + "/TestMvWarningSymlink"
	err := os.Symlink("/foo/bar/baz", fn)
	if err != nil {
		t.Fatal(err)
	}
	cmd := exec.Command("mv", fn, test_helpers.TmpDir)
	out, err := cmd.CombinedOutput()
	if err != nil {
		t.Log(string(out))
		if runtime.GOOS == "darwin" {
			t.Skip("mv on darwin chokes on broken symlinks, see https://github.com/rfjakob/gocryptfs/issues/349")
		}
		t.Fatal(err)
	}
	if len(out) != 0 {
		t.Log(strings.TrimSpace(string(out)))
		t.Fatal("Got warnings")
	}
}

// See TestMvWarnings.
func TestCpWarnings(t *testing.T) {
	fn := test_helpers.TmpDir + "/TestCpWarnings"
	err := ioutil.WriteFile(fn, []byte("foo"), 0600)
	if err != nil {
		t.Fatalf("creating file failed: %v", err)
	}
	cmd := exec.Command("cp", "-a", fn, test_helpers.DefaultPlainDir)
	out, err := cmd.CombinedOutput()
	if err != nil {
		t.Log(string(out))
		t.Fatal(err)
	}
	if len(out) != 0 {
		t.Fatalf("Got warnings from cp -a:\n%s", string(out))
	}
}
<|endoftext|>


<commit_before>package kit

import (
	"bytes"
	"fmt"
	"strings"
	"text/template"

	"go/format"

	"github.com/cihangir/gene/generators/common"
	"github.com/cihangir/schema"
)

///
/// metric - implement dogstatsd
/// throttling - implement one with shared backend
/// logging - leveled - filtered
///
///
///
type Generator struct{}

// Generate generates and writes the errors of the schema
func (g *Generator) Generate(context *common.Context, s *schema.Schema) ([]common.Output, error) {
	outputs, err := GenerateKitWorker(context, s)
	if err != nil {
		return nil, err
	}

	output, err := GenerateInterface(context, s)
	if err != nil {
		return nil, err
	}

	outputs = append(outputs, output...)

	output, err = GenerateTransportHTTPSemiotics(context, s)
	if err != nil {
		return nil, err
	}
	outputs = append(outputs, output...)

	output, err = GenerateTransportHTTPServer(context, s)
	if err != nil {
		return nil, err
	}

	outputs = append(outputs, output...)

	output, err = GenerateTransportHTTPClient(context, s)
	if err != nil {
		return nil, err
	}

	outputs = append(outputs, output...)

	output, err = GenerateService(context, s)
	if err != nil {
		return nil, err
	}
	outputs = append(outputs, output...)

	return outputs, nil
}

func generate(context *common.Context, s *schema.Schema, templ string, sectionName string) ([]common.Output, error) {
	temp := template.New("kit.tmpl").Funcs(context.TemplateFuncs)
	if _, err := temp.Parse(templ); err != nil {
		return nil, err
	}

	moduleName := context.ModuleNameFunc(s.Title)

	outputs := make([]common.Output, 0)

	for _, def := range common.SortedObjectSchemas(s.Definitions) {
		if len(def.Functions) == 0 {
			continue
		}

		data := struct {
			ModuleName string
			Schema     *schema.Schema
		}{
			ModuleName: moduleName,
			Schema:     def,
		}

		var buf bytes.Buffer

		if err := temp.ExecuteTemplate(&buf, "kit.tmpl", data); err != nil {
			return nil, err
		}

		path := fmt.Sprintf(
			"%s/%s/%s.go",
			context.Config.Target,
			strings.ToLower(def.Title),
			sectionName,
		)

		api, err := format.Source(buf.Bytes())
		if err != nil {
			return nil, err
		}

		outputs = append(outputs, common.Output{
			Content: api,
			Path:    path,
		})
	}

	return outputs, nil
}
<commit_msg>generator/kit: make generation of modules configurable<commit_after>package kit

import (
	"bytes"
	"fmt"
	"strings"
	"text/template"

	"go/format"

	"github.com/cihangir/gene/generators/common"
	"github.com/cihangir/schema"
)

///
/// metric - implement dogstatsd
/// throttling - implement one with shared backend
/// logging - leveled - filtered
///
///
///
type Generator struct{}

// Generate generates and writes the errors of the schema
func (g *Generator) Generate(context *common.Context, s *schema.Schema) ([]common.Output, error) {
	if !common.IsIn("kit", context.Config.Generators...) {
		return nil, nil
	}

	outputs, err := GenerateKitWorker(context, s)
	if err != nil {
		return nil, err
	}

	output, err := GenerateInterface(context, s)
	if err != nil {
		return nil, err
	}

	outputs = append(outputs, output...)

	output, err = GenerateTransportHTTPSemiotics(context, s)
	if err != nil {
		return nil, err
	}
	outputs = append(outputs, output...)

	output, err = GenerateTransportHTTPServer(context, s)
	if err != nil {
		return nil, err
	}

	outputs = append(outputs, output...)

	output, err = GenerateTransportHTTPClient(context, s)
	if err != nil {
		return nil, err
	}

	outputs = append(outputs, output...)

	output, err = GenerateService(context, s)
	if err != nil {
		return nil, err
	}
	outputs = append(outputs, output...)

	return outputs, nil
}

func generate(context *common.Context, s *schema.Schema, templ string, sectionName string) ([]common.Output, error) {
	temp := template.New("kit.tmpl").Funcs(context.TemplateFuncs)
	if _, err := temp.Parse(templ); err != nil {
		return nil, err
	}

	moduleName := context.ModuleNameFunc(s.Title)

	outputs := make([]common.Output, 0)

	for _, def := range common.SortedObjectSchemas(s.Definitions) {
		if len(def.Functions) == 0 {
			continue
		}

		data := struct {
			ModuleName string
			Schema     *schema.Schema
		}{
			ModuleName: moduleName,
			Schema:     def,
		}

		var buf bytes.Buffer

		if err := temp.ExecuteTemplate(&buf, "kit.tmpl", data); err != nil {
			return nil, err
		}

		path := fmt.Sprintf(
			"%s/%s/%s.go",
			context.Config.Target,
			strings.ToLower(def.Title),
			sectionName,
		)

		api, err := format.Source(buf.Bytes())
		if err != nil {
			return nil, err
		}

		outputs = append(outputs, common.Output{
			Content: api,
			Path:    path,
		})
	}

	return outputs, nil
}
<|endoftext|>


<commit_before>package resources

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

	"github.com/jaypipes/pcidb"
	"github.com/pkg/errors"
	"golang.org/x/sys/unix"

	"github.com/lxc/lxd/shared/api"
)

// GetPCI returns a filled api.ResourcesPCI struct ready for use by LXD
func GetPCI() (*api.ResourcesPCI, error) {
	pci := api.ResourcesPCI{}

	if !sysfsExists(sysBusPci) {
		return &pci, nil
	}

	// Load PCI database
	pciDB, err := pcidb.New()
	if err != nil {
		pciDB = nil
	}

	// Get uname for driver version
	uname := unix.Utsname{}
	err = unix.Uname(&uname)
	if err != nil {
		return nil, errors.Wrap(err, "Failed to get uname")
	}

	// List all PCI devices
	entries, err := ioutil.ReadDir(sysBusPci)
	if err != nil {
		return nil, errors.Wrapf(err, "Failed to list %q", sysBusPci)
	}

	for _, entry := range entries {
		entryName := entry.Name()
		devicePath := filepath.Join(sysBusPci, entryName)
		device := api.ResourcesPCIDevice{}

		// Get driver name
		driverPath := filepath.Join(devicePath, "driver")

		if sysfsExists(driverPath) {
			linkTarget, err := filepath.EvalSymlinks(driverPath)
			if err != nil {
				return nil, errors.Wrapf(err, "Failed to get driver of %q", devicePath)
			}

			device.Driver = filepath.Base(linkTarget)

			// Try to get the version, fallback to kernel version
			out, err := ioutil.ReadFile(filepath.Join(driverPath, "module", "version"))
			if err == nil {
				device.DriverVersion = strings.TrimSpace(string(out))
			} else {
				device.DriverVersion = strings.TrimRight(string(uname.Release[:]), "\x00")
			}
		}

		// Get NUMA node
		if sysfsExists(filepath.Join(devicePath, "numa_node")) {
			numaNode, err := readInt(filepath.Join(devicePath, "numa_node"))
			if err != nil {
				return nil, errors.Wrapf(err, "Failed to read %q", filepath.Join(devicePath, "numa_node"))
			}

			if numaNode > 0 {
				device.NUMANode = uint64(numaNode)
			}
		}

		// Get PCI address
		device.PCIAddress = entryName

		// Get product ID node
		deviceDevicePath := filepath.Join(devicePath, "device")
		if sysfsExists(deviceDevicePath) {
			id, err := ioutil.ReadFile(deviceDevicePath)
			if err != nil {
				return nil, errors.Wrapf(err, "Failed to read %q", deviceDevicePath)
			}

			device.ProductID = strings.TrimPrefix(strings.TrimSpace(string(id)), "0x")
		}

		// Get vendor ID node
		deviceVendorPath := filepath.Join(devicePath, "vendor")
		if sysfsExists(deviceVendorPath) {
			id, err := ioutil.ReadFile(deviceVendorPath)
			if err != nil {
				return nil, errors.Wrapf(err, "Failed to read %q", deviceVendorPath)
			}

			device.VendorID = strings.TrimPrefix(strings.TrimSpace(string(id)), "0x")
		}

		// Fill vendor and product names
		if pciDB != nil {
			vendor, ok := pciDB.Vendors[device.VendorID]
			if ok {
				device.Vendor = vendor.Name

				for _, product := range vendor.Products {
					if product.ID == device.ProductID {
						device.Product = product.Name
						break
					}
				}
			}
		}

		pci.Devices = append(pci.Devices, device)
		pci.Total++
	}

	return &pci, nil
}
<commit_msg>Add IOMMU group value to PCI devices<commit_after>package resources

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/jaypipes/pcidb"
	"github.com/pkg/errors"
	"golang.org/x/sys/unix"

	"github.com/lxc/lxd/shared/api"
)

// GetPCI returns a filled api.ResourcesPCI struct ready for use by LXD
func GetPCI() (*api.ResourcesPCI, error) {
	pci := api.ResourcesPCI{}

	if !sysfsExists(sysBusPci) {
		return &pci, nil
	}

	// Load PCI database
	pciDB, err := pcidb.New()
	if err != nil {
		pciDB = nil
	}

	// Get uname for driver version
	uname := unix.Utsname{}
	err = unix.Uname(&uname)
	if err != nil {
		return nil, errors.Wrap(err, "Failed to get uname")
	}

	// List all PCI devices
	entries, err := ioutil.ReadDir(sysBusPci)
	if err != nil {
		return nil, errors.Wrapf(err, "Failed to list %q", sysBusPci)
	}

	for _, entry := range entries {
		entryName := entry.Name()
		devicePath := filepath.Join(sysBusPci, entryName)
		device := api.ResourcesPCIDevice{}

		// Get driver name
		driverPath := filepath.Join(devicePath, "driver")

		if sysfsExists(driverPath) {
			linkTarget, err := filepath.EvalSymlinks(driverPath)
			if err != nil {
				return nil, errors.Wrapf(err, "Failed to get driver of %q", devicePath)
			}

			device.Driver = filepath.Base(linkTarget)

			// Try to get the version, fallback to kernel version
			out, err := ioutil.ReadFile(filepath.Join(driverPath, "module", "version"))
			if err == nil {
				device.DriverVersion = strings.TrimSpace(string(out))
			} else {
				device.DriverVersion = strings.TrimRight(string(uname.Release[:]), "\x00")
			}
		}

		// Get NUMA node
		if sysfsExists(filepath.Join(devicePath, "numa_node")) {
			numaNode, err := readInt(filepath.Join(devicePath, "numa_node"))
			if err != nil {
				return nil, errors.Wrapf(err, "Failed to read %q", filepath.Join(devicePath, "numa_node"))
			}

			if numaNode > 0 {
				device.NUMANode = uint64(numaNode)
			}
		}

		// Get PCI address
		device.PCIAddress = entryName

		// Get product ID node
		deviceDevicePath := filepath.Join(devicePath, "device")
		if sysfsExists(deviceDevicePath) {
			id, err := ioutil.ReadFile(deviceDevicePath)
			if err != nil {
				return nil, errors.Wrapf(err, "Failed to read %q", deviceDevicePath)
			}

			device.ProductID = strings.TrimPrefix(strings.TrimSpace(string(id)), "0x")
		}

		// Get vendor ID node
		deviceVendorPath := filepath.Join(devicePath, "vendor")
		if sysfsExists(deviceVendorPath) {
			id, err := ioutil.ReadFile(deviceVendorPath)
			if err != nil {
				return nil, errors.Wrapf(err, "Failed to read %q", deviceVendorPath)
			}

			device.VendorID = strings.TrimPrefix(strings.TrimSpace(string(id)), "0x")
		}

		// Fill vendor and product names
		if pciDB != nil {
			vendor, ok := pciDB.Vendors[device.VendorID]
			if ok {
				device.Vendor = vendor.Name

				for _, product := range vendor.Products {
					if product.ID == device.ProductID {
						device.Product = product.Name
						break
					}
				}
			}
		}

		//Get IOMMU Group
		iommuGroupSymPath := filepath.Join(sysBusPci, device.PCIAddress, "iommu_group")
		if sysfsExists(iommuGroupSymPath) {
			iommuGroupPath, err := os.Readlink(iommuGroupSymPath)
			if err != nil {
				return nil, errors.Wrapf(err, "Failed to readlink %q", iommuGroupSymPath)
			}

			iommuGroup := filepath.Base(iommuGroupPath)
			device.IOMMUGroup, err = strconv.ParseUint(iommuGroup, 10, 64)
			if err != nil {
				return nil, errors.Wrapf(err, "Failed to parse %q", iommuGroup)
			}
		} else {
			device.IOMMUGroup = 0
		}

		pci.Devices = append(pci.Devices, device)
		pci.Total++
	}

	return &pci, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"strings"
	"syscall"
	"time"

	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/logger"
)

// Options for filesystem creation
type mkfsOptions struct {
	label string
}

// Export the mount options map since we might find it useful in other parts of
// LXD.
type mountOptions struct {
	capture bool
	flag    uintptr
}

var MountOptions = map[string]mountOptions{
	"async":         {false, syscall.MS_SYNCHRONOUS},
	"atime":         {false, syscall.MS_NOATIME},
	"bind":          {true, syscall.MS_BIND},
	"defaults":      {true, 0},
	"dev":           {false, syscall.MS_NODEV},
	"diratime":      {false, syscall.MS_NODIRATIME},
	"dirsync":       {true, syscall.MS_DIRSYNC},
	"exec":          {false, syscall.MS_NOEXEC},
	"lazytime":      {true, MS_LAZYTIME},
	"mand":          {true, syscall.MS_MANDLOCK},
	"noatime":       {true, syscall.MS_NOATIME},
	"nodev":         {true, syscall.MS_NODEV},
	"nodiratime":    {true, syscall.MS_NODIRATIME},
	"noexec":        {true, syscall.MS_NOEXEC},
	"nomand":        {false, syscall.MS_MANDLOCK},
	"norelatime":    {false, syscall.MS_RELATIME},
	"nostrictatime": {false, syscall.MS_STRICTATIME},
	"nosuid":        {true, syscall.MS_NOSUID},
	"rbind":         {true, syscall.MS_BIND | syscall.MS_REC},
	"relatime":      {true, syscall.MS_RELATIME},
	"remount":       {true, syscall.MS_REMOUNT},
	"ro":            {true, syscall.MS_RDONLY},
	"rw":            {false, syscall.MS_RDONLY},
	"strictatime":   {true, syscall.MS_STRICTATIME},
	"suid":          {false, syscall.MS_NOSUID},
	"sync":          {true, syscall.MS_SYNCHRONOUS},
}

func lxdResolveMountoptions(options string) (uintptr, string) {
	mountFlags := uintptr(0)
	tmp := strings.SplitN(options, ",", -1)
	for i := 0; i < len(tmp); i++ {
		opt := tmp[i]
		do, ok := MountOptions[opt]
		if !ok {
			continue
		}

		if do.capture {
			mountFlags |= do.flag
		} else {
			mountFlags &= ^do.flag
		}

		copy(tmp[i:], tmp[i+1:])
		tmp[len(tmp)-1] = ""
		tmp = tmp[:len(tmp)-1]
		i--
	}

	return mountFlags, strings.Join(tmp, ",")
}

// Useful functions for unreliable backends
func tryMount(src string, dst string, fs string, flags uintptr, options string) error {
	var err error

	for i := 0; i < 20; i++ {
		err = syscall.Mount(src, dst, fs, flags, options)
		if err == nil {
			break
		}

		time.Sleep(500 * time.Millisecond)
	}

	if err != nil {
		return err
	}

	return nil
}

func tryUnmount(path string, flags int) error {
	var err error

	for i := 0; i < 20; i++ {
		err = syscall.Unmount(path, flags)
		if err == nil {
			break
		}

		time.Sleep(500 * time.Millisecond)
	}

	if err != nil {
		return err
	}

	return nil
}

func storageValidName(value string) error {
	if strings.Contains(value, "/") {
		return fmt.Errorf("Invalid storage volume name \"%s\". Storage volumes cannot contain \"/\" in their name", value)
	}

	return nil
}

func storageConfigDiff(oldConfig map[string]string, newConfig map[string]string) ([]string, bool) {
	changedConfig := []string{}
	userOnly := true
	for key := range oldConfig {
		if oldConfig[key] != newConfig[key] {
			if !strings.HasPrefix(key, "user.") {
				userOnly = false
			}

			if !shared.StringInSlice(key, changedConfig) {
				changedConfig = append(changedConfig, key)
			}
		}
	}

	for key := range newConfig {
		if oldConfig[key] != newConfig[key] {
			if !strings.HasPrefix(key, "user.") {
				userOnly = false
			}

			if !shared.StringInSlice(key, changedConfig) {
				changedConfig = append(changedConfig, key)
			}
		}
	}

	// Skip on no change
	if len(changedConfig) == 0 {
		return nil, false
	}

	return changedConfig, userOnly
}

// Default permissions for folders in ${LXD_DIR}
const storagePoolsDirMode os.FileMode = 0711
const containersDirMode os.FileMode = 0711
const customDirMode os.FileMode = 0711
const imagesDirMode os.FileMode = 0700
const snapshotsDirMode os.FileMode = 0700

// Detect whether LXD already uses the given storage pool.
func lxdUsesPool(dbObj *db.Cluster, onDiskPoolName string, driver string, onDiskProperty string) (bool, string, error) {
	pools, err := dbObj.StoragePools()
	if err != nil && err != db.ErrNoSuchObject {
		return false, "", err
	}

	for _, pool := range pools {
		_, pl, err := dbObj.StoragePoolGet(pool)
		if err != nil {
			continue
		}

		if pl.Driver != driver {
			continue
		}

		if pl.Config[onDiskProperty] == onDiskPoolName {
			return true, pl.Name, nil
		}
	}

	return false, "", nil
}

func makeFSType(path string, fsType string, options *mkfsOptions) (string, error) {
	var err error
	var msg string

	fsOptions := options
	if fsOptions == nil {
		fsOptions = &mkfsOptions{}
	}

	cmd := []string{fmt.Sprintf("mkfs.%s", fsType), path}
	if fsOptions.label != "" {
		cmd = append(cmd, "-L", fsOptions.label)
	}

	if fsType == "ext4" {
		cmd = append(cmd, "-E", "nodiscard,lazy_itable_init=0,lazy_journal_init=0")
	}

	msg, err = shared.TryRunCommand(cmd[0], cmd[1:]...)
	if err != nil {
		return msg, err
	}

	return "", nil
}

func fsGenerateNewUUID(fstype string, lvpath string) (string, error) {
	switch fstype {
	case "btrfs":
		return btrfsGenerateNewUUID(lvpath)
	case "xfs":
		return xfsGenerateNewUUID(lvpath)
	}

	return "", nil
}

func xfsGenerateNewUUID(lvpath string) (string, error) {
	msg, err := shared.RunCommand(
		"xfs_admin",
		"-U", "generate",
		lvpath)
	if err != nil {
		return msg, err
	}

	return "", nil
}

func btrfsGenerateNewUUID(lvpath string) (string, error) {
	msg, err := shared.RunCommand(
		"btrfstune",
		"-f",
		"-u",
		lvpath)
	if err != nil {
		return msg, err
	}

	return "", nil
}

func growFileSystem(fsType string, devPath string, mntpoint string) error {
	var msg string
	var err error
	switch fsType {
	case "": // if not specified, default to ext4
		fallthrough
	case "ext4":
		msg, err = shared.TryRunCommand("resize2fs", devPath)
	case "xfs":
		msg, err = shared.TryRunCommand("xfs_growfs", devPath)
	case "btrfs":
		msg, err = shared.TryRunCommand("btrfs", "filesystem", "resize", "max", mntpoint)
	default:
		return fmt.Errorf(`Growing not supported for filesystem type "%s"`, fsType)
	}

	if err != nil {
		errorMsg := fmt.Sprintf(`Could not extend underlying %s filesystem for "%s": %s`, fsType, devPath, msg)
		logger.Errorf(errorMsg)
		return fmt.Errorf(errorMsg)
	}

	logger.Debugf(`extended underlying %s filesystem for "%s"`, fsType, devPath)
	return nil
}

func shrinkFileSystem(fsType string, devPath string, mntpoint string, byteSize int64) error {
	strSize := fmt.Sprintf("%dK", byteSize/1024)

	switch fsType {
	case "": // if not specified, default to ext4
		fallthrough
	case "ext4":
		_, err := shared.TryRunCommand("e2fsck", "-f", "-y", devPath)
		if err != nil {
			return err
		}

		_, err = shared.TryRunCommand("resize2fs", devPath, strSize)
		if err != nil {
			return err
		}
	case "btrfs":
		_, err := shared.TryRunCommand("btrfs", "filesystem", "resize", strSize, mntpoint)
		if err != nil {
			return err
		}
	default:
		return fmt.Errorf(`Shrinking not supported for filesystem type "%s"`, fsType)
	}

	return nil
}

func shrinkVolumeFilesystem(s storage, volumeType int, fsType string, devPath string, mntpoint string, byteSize int64, data interface{}) (func() (bool, error), error) {
	var cleanupFunc func() (bool, error)
	switch fsType {
	case "xfs":
		logger.Errorf("XFS filesystems cannot be shrunk: dump, mkfs, and restore are required")
		return nil, fmt.Errorf("xfs filesystems cannot be shrunk: dump, mkfs, and restore are required")
	case "btrfs":
		fallthrough
	case "": // if not specified, default to ext4
		fallthrough
	case "ext4":
		switch volumeType {
		case storagePoolVolumeTypeContainer:
			c := data.(container)
			ourMount, err := c.StorageStop()
			if err != nil {
				return nil, err
			}
			if !ourMount {
				cleanupFunc = c.StorageStart
			}
		case storagePoolVolumeTypeCustom:
			ourMount, err := s.StoragePoolVolumeUmount()
			if err != nil {
				return nil, err
			}
			if !ourMount {
				cleanupFunc = s.StoragePoolVolumeMount
			}
		default:
			return nil, fmt.Errorf(`Resizing not implemented for storage volume type %d`, volumeType)
		}

	default:
		return nil, fmt.Errorf(`Shrinking not supported for filesystem type "%s"`, fsType)
	}

	err := shrinkFileSystem(fsType, devPath, mntpoint, byteSize)
	return cleanupFunc, err
}

func storageResource(path string) (*api.ResourcesStoragePool, error) {
	st, err := shared.Statvfs(path)
	if err != nil {
		return nil, err
	}

	res := api.ResourcesStoragePool{}
	res.Space.Total = st.Blocks * uint64(st.Bsize)
	res.Space.Used = (st.Blocks - st.Bfree) * uint64(st.Bsize)

	// Some filesystems don't report inodes since they allocate them
	// dynamically e.g. btrfs.
	if st.Files > 0 {
		res.Inodes.Total = st.Files
		res.Inodes.Used = st.Files - st.Ffree
	}

	return &res, nil
}
<commit_msg>lxd/storage: Handle XFS with leftover journal entries<commit_after>package main

import (
	"fmt"
	"os"
	"strings"
	"syscall"
	"time"

	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/logger"
)

// Options for filesystem creation
type mkfsOptions struct {
	label string
}

// Export the mount options map since we might find it useful in other parts of
// LXD.
type mountOptions struct {
	capture bool
	flag    uintptr
}

var MountOptions = map[string]mountOptions{
	"async":         {false, syscall.MS_SYNCHRONOUS},
	"atime":         {false, syscall.MS_NOATIME},
	"bind":          {true, syscall.MS_BIND},
	"defaults":      {true, 0},
	"dev":           {false, syscall.MS_NODEV},
	"diratime":      {false, syscall.MS_NODIRATIME},
	"dirsync":       {true, syscall.MS_DIRSYNC},
	"exec":          {false, syscall.MS_NOEXEC},
	"lazytime":      {true, MS_LAZYTIME},
	"mand":          {true, syscall.MS_MANDLOCK},
	"noatime":       {true, syscall.MS_NOATIME},
	"nodev":         {true, syscall.MS_NODEV},
	"nodiratime":    {true, syscall.MS_NODIRATIME},
	"noexec":        {true, syscall.MS_NOEXEC},
	"nomand":        {false, syscall.MS_MANDLOCK},
	"norelatime":    {false, syscall.MS_RELATIME},
	"nostrictatime": {false, syscall.MS_STRICTATIME},
	"nosuid":        {true, syscall.MS_NOSUID},
	"rbind":         {true, syscall.MS_BIND | syscall.MS_REC},
	"relatime":      {true, syscall.MS_RELATIME},
	"remount":       {true, syscall.MS_REMOUNT},
	"ro":            {true, syscall.MS_RDONLY},
	"rw":            {false, syscall.MS_RDONLY},
	"strictatime":   {true, syscall.MS_STRICTATIME},
	"suid":          {false, syscall.MS_NOSUID},
	"sync":          {true, syscall.MS_SYNCHRONOUS},
}

func lxdResolveMountoptions(options string) (uintptr, string) {
	mountFlags := uintptr(0)
	tmp := strings.SplitN(options, ",", -1)
	for i := 0; i < len(tmp); i++ {
		opt := tmp[i]
		do, ok := MountOptions[opt]
		if !ok {
			continue
		}

		if do.capture {
			mountFlags |= do.flag
		} else {
			mountFlags &= ^do.flag
		}

		copy(tmp[i:], tmp[i+1:])
		tmp[len(tmp)-1] = ""
		tmp = tmp[:len(tmp)-1]
		i--
	}

	return mountFlags, strings.Join(tmp, ",")
}

// Useful functions for unreliable backends
func tryMount(src string, dst string, fs string, flags uintptr, options string) error {
	var err error

	for i := 0; i < 20; i++ {
		err = syscall.Mount(src, dst, fs, flags, options)
		if err == nil {
			break
		}

		time.Sleep(500 * time.Millisecond)
	}

	if err != nil {
		return err
	}

	return nil
}

func tryUnmount(path string, flags int) error {
	var err error

	for i := 0; i < 20; i++ {
		err = syscall.Unmount(path, flags)
		if err == nil {
			break
		}

		time.Sleep(500 * time.Millisecond)
	}

	if err != nil {
		return err
	}

	return nil
}

func storageValidName(value string) error {
	if strings.Contains(value, "/") {
		return fmt.Errorf("Invalid storage volume name \"%s\". Storage volumes cannot contain \"/\" in their name", value)
	}

	return nil
}

func storageConfigDiff(oldConfig map[string]string, newConfig map[string]string) ([]string, bool) {
	changedConfig := []string{}
	userOnly := true
	for key := range oldConfig {
		if oldConfig[key] != newConfig[key] {
			if !strings.HasPrefix(key, "user.") {
				userOnly = false
			}

			if !shared.StringInSlice(key, changedConfig) {
				changedConfig = append(changedConfig, key)
			}
		}
	}

	for key := range newConfig {
		if oldConfig[key] != newConfig[key] {
			if !strings.HasPrefix(key, "user.") {
				userOnly = false
			}

			if !shared.StringInSlice(key, changedConfig) {
				changedConfig = append(changedConfig, key)
			}
		}
	}

	// Skip on no change
	if len(changedConfig) == 0 {
		return nil, false
	}

	return changedConfig, userOnly
}

// Default permissions for folders in ${LXD_DIR}
const storagePoolsDirMode os.FileMode = 0711
const containersDirMode os.FileMode = 0711
const customDirMode os.FileMode = 0711
const imagesDirMode os.FileMode = 0700
const snapshotsDirMode os.FileMode = 0700

// Detect whether LXD already uses the given storage pool.
func lxdUsesPool(dbObj *db.Cluster, onDiskPoolName string, driver string, onDiskProperty string) (bool, string, error) {
	pools, err := dbObj.StoragePools()
	if err != nil && err != db.ErrNoSuchObject {
		return false, "", err
	}

	for _, pool := range pools {
		_, pl, err := dbObj.StoragePoolGet(pool)
		if err != nil {
			continue
		}

		if pl.Driver != driver {
			continue
		}

		if pl.Config[onDiskProperty] == onDiskPoolName {
			return true, pl.Name, nil
		}
	}

	return false, "", nil
}

func makeFSType(path string, fsType string, options *mkfsOptions) (string, error) {
	var err error
	var msg string

	fsOptions := options
	if fsOptions == nil {
		fsOptions = &mkfsOptions{}
	}

	cmd := []string{fmt.Sprintf("mkfs.%s", fsType), path}
	if fsOptions.label != "" {
		cmd = append(cmd, "-L", fsOptions.label)
	}

	if fsType == "ext4" {
		cmd = append(cmd, "-E", "nodiscard,lazy_itable_init=0,lazy_journal_init=0")
	}

	msg, err = shared.TryRunCommand(cmd[0], cmd[1:]...)
	if err != nil {
		return msg, err
	}

	return "", nil
}

func fsGenerateNewUUID(fstype string, lvpath string) (string, error) {
	switch fstype {
	case "btrfs":
		return btrfsGenerateNewUUID(lvpath)
	case "xfs":
		return xfsGenerateNewUUID(lvpath)
	}

	return "", nil
}

func xfsGenerateNewUUID(devPath string) (string, error) {
	// Attempt to generate a new UUID
	msg, err := shared.RunCommand("xfs_admin", "-U", "generate", devPath)
	if err != nil {
		return msg, err
	}

	if msg != "" {
		// Exit 0 with a msg usually means some log entry getting in the way
		msg, err = shared.RunCommand("xfs_repair", "-o", "force_geometry", "-L", devPath)
		if err != nil {
			return msg, err
		}

		// Attempt to generate a new UUID again
		msg, err = shared.RunCommand("xfs_admin", "-U", "generate", devPath)
		if err != nil {
			return msg, err
		}
	}

	return msg, nil
}

func btrfsGenerateNewUUID(lvpath string) (string, error) {
	msg, err := shared.RunCommand(
		"btrfstune",
		"-f",
		"-u",
		lvpath)
	if err != nil {
		return msg, err
	}

	return msg, nil
}

func growFileSystem(fsType string, devPath string, mntpoint string) error {
	var msg string
	var err error
	switch fsType {
	case "": // if not specified, default to ext4
		fallthrough
	case "ext4":
		msg, err = shared.TryRunCommand("resize2fs", devPath)
	case "xfs":
		msg, err = shared.TryRunCommand("xfs_growfs", devPath)
	case "btrfs":
		msg, err = shared.TryRunCommand("btrfs", "filesystem", "resize", "max", mntpoint)
	default:
		return fmt.Errorf(`Growing not supported for filesystem type "%s"`, fsType)
	}

	if err != nil {
		errorMsg := fmt.Sprintf(`Could not extend underlying %s filesystem for "%s": %s`, fsType, devPath, msg)
		logger.Errorf(errorMsg)
		return fmt.Errorf(errorMsg)
	}

	logger.Debugf(`extended underlying %s filesystem for "%s"`, fsType, devPath)
	return nil
}

func shrinkFileSystem(fsType string, devPath string, mntpoint string, byteSize int64) error {
	strSize := fmt.Sprintf("%dK", byteSize/1024)

	switch fsType {
	case "": // if not specified, default to ext4
		fallthrough
	case "ext4":
		_, err := shared.TryRunCommand("e2fsck", "-f", "-y", devPath)
		if err != nil {
			return err
		}

		_, err = shared.TryRunCommand("resize2fs", devPath, strSize)
		if err != nil {
			return err
		}
	case "btrfs":
		_, err := shared.TryRunCommand("btrfs", "filesystem", "resize", strSize, mntpoint)
		if err != nil {
			return err
		}
	default:
		return fmt.Errorf(`Shrinking not supported for filesystem type "%s"`, fsType)
	}

	return nil
}

func shrinkVolumeFilesystem(s storage, volumeType int, fsType string, devPath string, mntpoint string, byteSize int64, data interface{}) (func() (bool, error), error) {
	var cleanupFunc func() (bool, error)
	switch fsType {
	case "xfs":
		logger.Errorf("XFS filesystems cannot be shrunk: dump, mkfs, and restore are required")
		return nil, fmt.Errorf("xfs filesystems cannot be shrunk: dump, mkfs, and restore are required")
	case "btrfs":
		fallthrough
	case "": // if not specified, default to ext4
		fallthrough
	case "ext4":
		switch volumeType {
		case storagePoolVolumeTypeContainer:
			c := data.(container)
			ourMount, err := c.StorageStop()
			if err != nil {
				return nil, err
			}
			if !ourMount {
				cleanupFunc = c.StorageStart
			}
		case storagePoolVolumeTypeCustom:
			ourMount, err := s.StoragePoolVolumeUmount()
			if err != nil {
				return nil, err
			}
			if !ourMount {
				cleanupFunc = s.StoragePoolVolumeMount
			}
		default:
			return nil, fmt.Errorf(`Resizing not implemented for storage volume type %d`, volumeType)
		}

	default:
		return nil, fmt.Errorf(`Shrinking not supported for filesystem type "%s"`, fsType)
	}

	err := shrinkFileSystem(fsType, devPath, mntpoint, byteSize)
	return cleanupFunc, err
}

func storageResource(path string) (*api.ResourcesStoragePool, error) {
	st, err := shared.Statvfs(path)
	if err != nil {
		return nil, err
	}

	res := api.ResourcesStoragePool{}
	res.Space.Total = st.Blocks * uint64(st.Bsize)
	res.Space.Used = (st.Blocks - st.Bfree) * uint64(st.Bsize)

	// Some filesystems don't report inodes since they allocate them
	// dynamically e.g. btrfs.
	if st.Files > 0 {
		res.Inodes.Total = st.Files
		res.Inodes.Used = st.Files - st.Ffree
	}

	return &res, nil
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/Shopify/sarama"
	"github.com/cihub/seelog"
)

type PartitionManager struct {
	*StartStopControl
	Topic     string
	Partition int32

	// parent
	manager *CallbackManager

	// config
	config *CallbackItemConfig

	// partition consumer
	partitionConsumer *PartitionConsumer

	// arbiter
	arbiter Arbiter

	// transporter
	transporter       []Transporter
	transporterRunner *ServiceRunner
}

func NewPartitionManager() *PartitionManager {
	return &PartitionManager{
		StartStopControl: &StartStopControl{},
	}
}

func (this *PartitionManager) Init(config *CallbackItemConfig, topic string, partition int32, manager *CallbackManager) error {
	this.Topic = topic
	this.Partition = partition

	// parent
	this.manager = manager

	// config
	this.config = config

	// init partitionConsumer
	this.partitionConsumer = NewPartitionConsumer()
	this.partitionConsumer.Init(config, topic, partition, this)

	// init arbiter
	var err error
	if this.arbiter, err = NewArbiter(config.ArbiterName); err != nil {
		return err
	}
	if err = this.arbiter.Init(config, config.ArbiterConfig, this); err != nil {
		return err
	}

	// init transporter
	this.transporterRunner = NewServiceRunner()

	return nil
}

func (this *PartitionManager) Run() error {
	if err := this.ensureStart(); err != nil {
		return err
	}

	defer this.markStop()

	// start partition consumer
	this.partitionConsumer.Prepare()
	go this.partitionConsumer.Run()
	defer this.partitionConsumer.Close()
	if err := this.partitionConsumer.Ready(); err != nil {
		seelog.Errorf("Partition consumer start failed [err:%s]", err)
		return err
	}

	// start arbiter
	this.arbiter.Prepare()
	go this.arbiter.Run()
	defer this.arbiter.Close()
	if err := this.arbiter.Ready(); err != nil {
		seelog.Errorf("Arbiter start failed [err:%s]", err)
		return err
	}

	// start transporter group
	this.transporter = make([]Transporter, 0)
	for i := 0; i != this.config.WorkerNum; i++ {
		if transporter, err := NewTransporter(this.config.TransporterName); err == nil {
			if err := transporter.Init(this.config, this.config.TransporterConfig, this); err != nil {
				return err
			}
			this.transporter = append(this.transporter, transporter)
		} else {
			return err
		}
	}
	this.transporterRunner.RetryTimes = len(this.transporter) * 3
	this.transporterRunner.Prepare()
	this.transporterRunner.RunAsync(this.transporter)
	defer this.transporterRunner.Close()

	select {
	case <-this.partitionConsumer.WaitForExitChannel():
	case <-this.arbiter.WaitForExitChannel():
	case <-this.transporterRunner.WaitForExitChannel():
	case <-this.WaitForCloseChannel():
	}

	return nil
}

func (this *PartitionManager) GetConsumer() sarama.PartitionConsumer {
	return this.partitionConsumer.GetConsumer()
}

func (this *PartitionManager) GetArbiter() Arbiter {
	return this.arbiter
}

func (this *PartitionManager) GetCallbackManager() *CallbackManager {
	return this.manager
}
<commit_msg>Add failed to start check on transporter<commit_after>package main

import (
	"github.com/Shopify/sarama"
	"github.com/cihub/seelog"
)

type PartitionManager struct {
	*StartStopControl
	Topic     string
	Partition int32

	// parent
	manager *CallbackManager

	// config
	config *CallbackItemConfig

	// partition consumer
	partitionConsumer *PartitionConsumer

	// arbiter
	arbiter Arbiter

	// transporter
	transporter       []Transporter
	transporterRunner *ServiceRunner
}

func NewPartitionManager() *PartitionManager {
	return &PartitionManager{
		StartStopControl: &StartStopControl{},
	}
}

func (this *PartitionManager) Init(config *CallbackItemConfig, topic string, partition int32, manager *CallbackManager) error {
	this.Topic = topic
	this.Partition = partition

	// parent
	this.manager = manager

	// config
	this.config = config

	// init partitionConsumer
	this.partitionConsumer = NewPartitionConsumer()
	this.partitionConsumer.Init(config, topic, partition, this)

	// init arbiter
	var err error
	if this.arbiter, err = NewArbiter(config.ArbiterName); err != nil {
		return err
	}
	if err = this.arbiter.Init(config, config.ArbiterConfig, this); err != nil {
		return err
	}

	// init transporter
	this.transporterRunner = NewServiceRunner()

	return nil
}

func (this *PartitionManager) Run() error {
	if err := this.ensureStart(); err != nil {
		return err
	}

	defer this.markStop()

	// start partition consumer
	this.partitionConsumer.Prepare()
	go this.partitionConsumer.Run()
	defer this.partitionConsumer.Close()
	if err := this.partitionConsumer.Ready(); err != nil {
		seelog.Errorf("Partition consumer start failed [err:%s]", err)
		return err
	}

	// start arbiter
	this.arbiter.Prepare()
	go this.arbiter.Run()
	defer this.arbiter.Close()
	if err := this.arbiter.Ready(); err != nil {
		seelog.Errorf("Arbiter start failed [err:%s]", err)
		return err
	}

	// start transporter group
	this.transporter = make([]Transporter, 0)
	for i := 0; i != this.config.WorkerNum; i++ {
		if transporter, err := NewTransporter(this.config.TransporterName); err == nil {
			if err := transporter.Init(this.config, this.config.TransporterConfig, this); err != nil {
				return err
			}
			this.transporter = append(this.transporter, transporter)
		} else {
			return err
		}
	}
	this.transporterRunner.RetryTimes = len(this.transporter) * 3
	this.transporterRunner.Prepare()
	if _, err := this.transporterRunner.RunAsync(this.transporter); err != nil {
		seelog.Errorf("Transporter start failed [err:%v]", err)
		return err
	}
	defer this.transporterRunner.Close()

	select {
	case <-this.partitionConsumer.WaitForExitChannel():
	case <-this.arbiter.WaitForExitChannel():
	case <-this.transporterRunner.WaitForExitChannel():
	case <-this.WaitForCloseChannel():
	}

	return nil
}

func (this *PartitionManager) GetConsumer() sarama.PartitionConsumer {
	return this.partitionConsumer.GetConsumer()
}

func (this *PartitionManager) GetArbiter() Arbiter {
	return this.arbiter
}

func (this *PartitionManager) GetCallbackManager() *CallbackManager {
	return this.manager
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"github.com/onsi/ginkgo/config"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"regexp"
)

type suite struct {
	path        string
	packageName string
	isGinkgo    bool
}

func newSuite(dir string, files []os.FileInfo) suite {
	return suite{
		path:        dir,
		packageName: packageNameForSuite(dir),
		isGinkgo:    filesHaveGinkgoSuite(dir, files),
	}
}

func packageNameForSuite(dir string) string {
	path, _ := filepath.Abs(dir)
	return filepath.Base(path)
}

func filesHaveGinkgoSuite(dir string, files []os.FileInfo) bool {
	reTestFile := regexp.MustCompile(`_test\.go$`)
	reGinkgo := regexp.MustCompile(`package ginkgo|\/ginkgo"`)
	for _, file := range files {
		if !file.IsDir() && reTestFile.Match([]byte(file.Name())) {
			contents, _ := ioutil.ReadFile(dir + "/" + file.Name())
			if reGinkgo.Match(contents) {
				return true
			}
		}
	}

	return false
}

type testRunner struct {
	numCPU           int
	recurse          bool
	runMagicI        bool
	race             bool
	cover            bool
	executedCommands []*exec.Cmd
	reports          []*bytes.Buffer
}

func newTestRunner(numCPU int, recurse bool, runMagicI bool, race bool, cover bool) *testRunner {
	return &testRunner{
		numCPU:           numCPU,
		recurse:          recurse,
		runMagicI:        runMagicI,
		race:             race,
		cover:            cover,
		executedCommands: []*exec.Cmd{},
		reports:          []*bytes.Buffer{},
	}
}

func (t *testRunner) run() bool {
	t.registerSignalHandler()

	suites := t.findSuitesInDir(".", t.recurse)

	for _, suite := range suites {
		if !t.runSuite(suite) {
			return false
		}
	}

	return true
}

func (t *testRunner) findSuitesInDir(dir string, recurse bool) []suite {
	suites := []suite{}
	files, _ := ioutil.ReadDir(dir)
	re := regexp.MustCompile(`_test\.go$`)
	for _, file := range files {
		if !file.IsDir() && re.Match([]byte(file.Name())) {
			suites = append(suites, newSuite(dir, files))
			break
		}
	}

	if recurse {
		re = regexp.MustCompile(`^\.`)
		for _, file := range files {
			if file.IsDir() && !re.Match([]byte(file.Name())) {
				suites = append(suites, t.findSuitesInDir(dir+"/"+file.Name(), recurse)...)
			}
		}
	}

	return suites
}

func (t *testRunner) runSuite(suite suite) bool {
	if t.runMagicI {
		t.runGoI(suite)
	}

	if suite.isGinkgo {
		if t.numCPU > 1 {
			return t.runParallelGinkgoSuite(suite)
		} else {
			return t.runSerialGinkgoSuite(suite)
		}
	} else {
		return t.runGoTestSuite(suite)
	}
}

func (t *testRunner) runGoI(suite suite) {
	args := []string{"test", "-i"}
	if t.race {
		args = append(args, "-race")
	}
	args = append(args, suite.path)
	cmd := exec.Command("go", args...)
	output, err := cmd.CombinedOutput()
	if err != nil {
		fmt.Printf("go test -i %s failed with:\n\n%s", suite.path, output)
		os.Exit(1)
	}
}

func (t *testRunner) runParallelGinkgoSuite(suite suite) bool {
	completions := make(chan bool)
	for cpu := 0; cpu < t.numCPU; cpu++ {
		config.GinkgoConfig.ParallelNode = cpu + 1
		config.GinkgoConfig.ParallelTotal = t.numCPU

		args := config.BuildFlagArgs("ginkgo", config.GinkgoConfig, config.DefaultReporterConfig)
		args = append(args, t.commonArgs(suite)...)

		buffer := new(bytes.Buffer)
		t.reports = append(t.reports, buffer)

		go t.runCommand(suite.path, args, buffer, completions)
	}

	passed := true

	for cpu := 0; cpu < t.numCPU; cpu++ {
		passed = <-completions && passed
	}

	for _, report := range t.reports {
		fmt.Print(report.String())
	}
	os.Stdout.Sync()

	return passed
}

func (t *testRunner) runSerialGinkgoSuite(suite suite) bool {
	args := config.BuildFlagArgs("ginkgo", config.GinkgoConfig, config.DefaultReporterConfig)
	args = append(args, t.commonArgs(suite)...)
	return t.runCommand(suite.path, args, os.Stdout, nil)
}

func (t *testRunner) runGoTestSuite(suite suite) bool {
	args := t.commonArgs(suite)
	return t.runCommand(suite.path, args, os.Stdout, nil)
}

func (t *testRunner) commonArgs(suite suite) []string {
	args := []string{}
	if t.race {
		args = append(args, "--race")
	}
	if t.cover {
		args = append([]string{"--cover", "--coverprofile=" + suite.packageName + ".out"})
	}
	return args
}

func (t *testRunner) runCommand(path string, args []string, stream io.Writer, completions chan bool) bool {
	args = append([]string{"test", "-v", "-timeout=24h", path}, args...)

	cmd := exec.Command("go", args...)
	t.executedCommands = append(t.executedCommands, cmd)

	doneStreaming := make(chan bool, 2)
	streamPipe := func(pipe io.ReadCloser) {
		io.Copy(stream, pipe)
		doneStreaming <- true
	}

	stdout, _ := cmd.StdoutPipe()
	stderr, _ := cmd.StderrPipe()
	go streamPipe(stdout)
	go streamPipe(stderr)

	err := cmd.Start()
	if err != nil {
		os.Exit(1)
	}

	<-doneStreaming
	<-doneStreaming

	err = cmd.Wait()
	if completions != nil {
		completions <- (err == nil)
	}
	return err == nil
}

func (t *testRunner) registerSignalHandler() {
	go func() {
		c := make(chan os.Signal, 1)
		signal.Notify(c, os.Interrupt, os.Kill)

		select {
		case sig := <-c:
			for _, cmd := range t.executedCommands {
				cmd.Process.Signal(sig)
			}
			os.Exit(1)
		}
	}()
}
<commit_msg>add coverprofile extension when using cover mode<commit_after>package main

import (
	"bytes"
	"fmt"
	"github.com/onsi/ginkgo/config"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"regexp"
)

type suite struct {
	path        string
	packageName string
	isGinkgo    bool
}

func newSuite(dir string, files []os.FileInfo) suite {
	return suite{
		path:        dir,
		packageName: packageNameForSuite(dir),
		isGinkgo:    filesHaveGinkgoSuite(dir, files),
	}
}

func packageNameForSuite(dir string) string {
	path, _ := filepath.Abs(dir)
	return filepath.Base(path)
}

func filesHaveGinkgoSuite(dir string, files []os.FileInfo) bool {
	reTestFile := regexp.MustCompile(`_test\.go$`)
	reGinkgo := regexp.MustCompile(`package ginkgo|\/ginkgo"`)
	for _, file := range files {
		if !file.IsDir() && reTestFile.Match([]byte(file.Name())) {
			contents, _ := ioutil.ReadFile(dir + "/" + file.Name())
			if reGinkgo.Match(contents) {
				return true
			}
		}
	}

	return false
}

type testRunner struct {
	numCPU           int
	recurse          bool
	runMagicI        bool
	race             bool
	cover            bool
	executedCommands []*exec.Cmd
	reports          []*bytes.Buffer
}

func newTestRunner(numCPU int, recurse bool, runMagicI bool, race bool, cover bool) *testRunner {
	return &testRunner{
		numCPU:           numCPU,
		recurse:          recurse,
		runMagicI:        runMagicI,
		race:             race,
		cover:            cover,
		executedCommands: []*exec.Cmd{},
		reports:          []*bytes.Buffer{},
	}
}

func (t *testRunner) run() bool {
	t.registerSignalHandler()

	suites := t.findSuitesInDir(".", t.recurse)

	for _, suite := range suites {
		if !t.runSuite(suite) {
			return false
		}
	}

	return true
}

func (t *testRunner) findSuitesInDir(dir string, recurse bool) []suite {
	suites := []suite{}
	files, _ := ioutil.ReadDir(dir)
	re := regexp.MustCompile(`_test\.go$`)
	for _, file := range files {
		if !file.IsDir() && re.Match([]byte(file.Name())) {
			suites = append(suites, newSuite(dir, files))
			break
		}
	}

	if recurse {
		re = regexp.MustCompile(`^\.`)
		for _, file := range files {
			if file.IsDir() && !re.Match([]byte(file.Name())) {
				suites = append(suites, t.findSuitesInDir(dir+"/"+file.Name(), recurse)...)
			}
		}
	}

	return suites
}

func (t *testRunner) runSuite(suite suite) bool {
	if t.runMagicI {
		t.runGoI(suite)
	}

	if suite.isGinkgo {
		if t.numCPU > 1 {
			return t.runParallelGinkgoSuite(suite)
		} else {
			return t.runSerialGinkgoSuite(suite)
		}
	} else {
		return t.runGoTestSuite(suite)
	}
}

func (t *testRunner) runGoI(suite suite) {
	args := []string{"test", "-i"}
	if t.race {
		args = append(args, "-race")
	}
	args = append(args, suite.path)
	cmd := exec.Command("go", args...)
	output, err := cmd.CombinedOutput()
	if err != nil {
		fmt.Printf("go test -i %s failed with:\n\n%s", suite.path, output)
		os.Exit(1)
	}
}

func (t *testRunner) runParallelGinkgoSuite(suite suite) bool {
	completions := make(chan bool)
	for cpu := 0; cpu < t.numCPU; cpu++ {
		config.GinkgoConfig.ParallelNode = cpu + 1
		config.GinkgoConfig.ParallelTotal = t.numCPU

		args := config.BuildFlagArgs("ginkgo", config.GinkgoConfig, config.DefaultReporterConfig)
		args = append(args, t.commonArgs(suite)...)

		buffer := new(bytes.Buffer)
		t.reports = append(t.reports, buffer)

		go t.runCommand(suite.path, args, buffer, completions)
	}

	passed := true

	for cpu := 0; cpu < t.numCPU; cpu++ {
		passed = <-completions && passed
	}

	for _, report := range t.reports {
		fmt.Print(report.String())
	}
	os.Stdout.Sync()

	return passed
}

func (t *testRunner) runSerialGinkgoSuite(suite suite) bool {
	args := config.BuildFlagArgs("ginkgo", config.GinkgoConfig, config.DefaultReporterConfig)
	args = append(args, t.commonArgs(suite)...)
	return t.runCommand(suite.path, args, os.Stdout, nil)
}

func (t *testRunner) runGoTestSuite(suite suite) bool {
	args := t.commonArgs(suite)
	return t.runCommand(suite.path, args, os.Stdout, nil)
}

func (t *testRunner) commonArgs(suite suite) []string {
	args := []string{}
	if t.race {
		args = append(args, "--race")
	}
	if t.cover {
		args = append([]string{"--cover", "--coverprofile=" + suite.packageName + ".coverprofile"})
	}
	return args
}

func (t *testRunner) runCommand(path string, args []string, stream io.Writer, completions chan bool) bool {
	args = append([]string{"test", "-v", "-timeout=24h", path}, args...)

	cmd := exec.Command("go", args...)
	t.executedCommands = append(t.executedCommands, cmd)

	doneStreaming := make(chan bool, 2)
	streamPipe := func(pipe io.ReadCloser) {
		io.Copy(stream, pipe)
		doneStreaming <- true
	}

	stdout, _ := cmd.StdoutPipe()
	stderr, _ := cmd.StderrPipe()
	go streamPipe(stdout)
	go streamPipe(stderr)

	err := cmd.Start()
	if err != nil {
		os.Exit(1)
	}

	<-doneStreaming
	<-doneStreaming

	err = cmd.Wait()
	if completions != nil {
		completions <- (err == nil)
	}
	return err == nil
}

func (t *testRunner) registerSignalHandler() {
	go func() {
		c := make(chan os.Signal, 1)
		signal.Notify(c, os.Interrupt, os.Kill)

		select {
		case sig := <-c:
			for _, cmd := range t.executedCommands {
				cmd.Process.Signal(sig)
			}
			os.Exit(1)
		}
	}()
}
<|endoftext|>


<commit_before><commit_msg>Add parametrize tests<commit_after><|endoftext|>


<commit_before><commit_msg>removed unused functions in Framework interface<commit_after><|endoftext|>


<commit_before>package gitManip

import (
	"fmt"

	"github.com/fatih/color"

	git "gopkg.in/libgit2/git2go.v24"
)

/*Map to match the RepositoryState enum type with a string
 */
var repositoryStateToString = map[git.RepositoryState]string{
	git.RepositoryStateNone:                 "None",
	git.RepositoryStateMerge:                "Merge",
	git.RepositoryStateRevert:               "Revert",
	git.RepositoryStateCherrypick:           "Cherrypick",
	git.RepositoryStateBisect:               "Bisect",
	git.RepositoryStateRebase:               "Rebase",
	git.RepositoryStateRebaseInteractive:    "Rebase Interactive",
	git.RepositoryStateRebaseMerge:          "Rebase Merge",
	git.RepositoryStateApplyMailbox:         "Apply Mailbox",
	git.RepositoryStateApplyMailboxOrRebase: "Apply Mailbox or Rebase",
}

var fileStateToString = map[git.Status]string{
	// git.StatusCurrent:         "Current",
	git.StatusIndexNew: "You forgot to commit a new file!",
	// git.StatusIndexModified:   "You forgot to commit a modified file!",
	// git.StatusIndexDeleted:    "You forgot to commit that you deleted a file!",
	// git.StatusIndexRenamed:    "You forgot to commit the renaming of a file!",
	// git.StatusIndexTypeChange: "You forget to commit a type change!",
	git.StatusIgnored:      "Ignored",
	git.StatusConflicted:   "Conflicted",
	git.StatusWtNew:        "New file in your working tree!",
	git.StatusWtModified:   "Modified file in your working tree!",
	git.StatusWtDeleted:    "Deleted file in your working tree!",
	git.StatusWtTypeChange: "Type change detected in your working tree!",
	git.StatusWtRenamed:    "Renamed file in your working tree!",
}

/*Global variable to set the StatusOption parameter, in order to list each file status
 */
var statusOption = git.StatusOptions{
	Show:     git.StatusShowIndexAndWorkdir,
	Flags:    git.StatusOptIncludeUntracked,
	Pathspec: []string{},
}

/*GitObject contains informations about the current git repository
 *
 *The structure is:
 *  accessible:
 *		Is the repository still exists in the hard drive?
 *	path:
 *		The path file.
 *	repository:
 *		The object repository.
 */
type GitObject struct {
	accessible error
	path       string
	repository git.Repository
}

/*New is a constructor for GitObject
 *
 * It neeeds:
 *	path:
 *		The path of the current repository.
 */
func New(path string) *GitObject {
	r, err := git.OpenRepository(path)
	return &GitObject{accessible: err, path: path, repository: *r}
}

/*isAccesible returns the information that is the current git repository is existing or not.
 *This method returns a boolean value: true if the git repository is still accesible (still exists), or false if not.
 */
func (g *GitObject) isAccessible() bool {
	return g.accessible == nil
}

/*Status prints the current status of the repository, accessible via the structure path field.
 *This method works only if the repository is accessible.
 */
func (g *GitObject) Status() {
	if g.isAccessible() {
		g.getUntrackedFiles()
		// g.getDiffWithWT()
	}
}

// func (g *GitObject) getDiffWithWT() {
// 	currentIndex, err := git.OpenIndex(path.Join(g.path, ".git/"))
// 	if err != nil {
// 		fmt.Println("Error using last index: %s", err)
// 	}
// 	diff, err := g.repository.DiffIndexToWorkdir(*currentIndex)
// }

func (g *GitObject) getUntrackedFiles() {
	fmt.Printf("[%s]...", g.path)
	if untrackedFields, err := g.repository.StatusList(&statusOption); err == nil {
		count, _ := untrackedFields.EntryCount()
		if count == 0 {
			fmt.Println(color.GreenString("\tOK!"))
			return
		}
		fmt.Printf(color.RedString("\t%d untracked files!\n", count))
		for i := 0; i < count; i++ {
			statusEntry, _ := untrackedFields.ByIndex(i)
			fmt.Printf("\t%s\n", fileStateToString[statusEntry.Status])
		}
	}
}

/*List lists the path and the accessibility of a list of git repositories
 */
func List(repositories *[]GitObject) {
	for _, object := range *repositories {
		fmt.Printf("* %s ", object.path)
		if object.isAccessible() {
			fmt.Println(color.GreenString(" [accessible]"))
		} else {
			fmt.Println(color.RedString(" [not accessible]"))
		}
	}
}
<commit_msg>Add new code to print repository changes<commit_after>package gitManip

import (
	"fmt"

	"github.com/fatih/color"

	git "gopkg.in/libgit2/git2go.v24"
)

/*Map to match the RepositoryState enum type with a string
 */
var repositoryStateToString = map[git.RepositoryState]string{
	git.RepositoryStateNone:                 "None",
	git.RepositoryStateMerge:                "Merge",
	git.RepositoryStateRevert:               "Revert",
	git.RepositoryStateCherrypick:           "Cherrypick",
	git.RepositoryStateBisect:               "Bisect",
	git.RepositoryStateRebase:               "Rebase",
	git.RepositoryStateRebaseInteractive:    "Rebase Interactive",
	git.RepositoryStateRebaseMerge:          "Rebase Merge",
	git.RepositoryStateApplyMailbox:         "Apply Mailbox",
	git.RepositoryStateApplyMailboxOrRebase: "Apply Mailbox or Rebase",
}

var fileStateToString = map[git.Status]string{
	git.StatusIndexNew:     "You forgot to commit a new file!",
	git.StatusIgnored:      "Ignored",
	git.StatusConflicted:   "Conflicted",
	git.StatusWtNew:        "New file in your working tree!",
	git.StatusWtModified:   "Modified file in your working tree!",
	git.StatusWtDeleted:    "Deleted file in your working tree!",
	git.StatusWtTypeChange: "Type change detected in your working tree!",
	git.StatusWtRenamed:    "Renamed file in your working tree!",
}

// TODO: Update this map enum to string in map enum to function!
// Each of those enums must correspond to a function.
var deltaFileStateToString = map[git.Delta]string{
	git.DeltaAdded:      "Just added!",
	git.DeltaDeleted:    "Just deleted!",
	git.DeltaModified:   "Just modified!",
	git.DeltaRenamed:    "Just renamed!",
	git.DeltaUntracked:  "Untracked!",
	git.DeltaTypeChange: "Type file has been changed!",
}

/*Global variable to set the StatusOption parameter, in order to list each file status
 */
var statusOption = git.StatusOptions{
	Show:     git.StatusShowIndexAndWorkdir,
	Flags:    git.StatusOptIncludeUntracked,
	Pathspec: []string{},
}

/*GitObject contains informations about the current git repository
 *
 *The structure is:
 *  accessible:
 *		Is the repository still exists in the hard drive?
 *	path:
 *		The path file.
 *	repository:
 *		The object repository.
 */
type GitObject struct {
	accessible error
	path       string
	repository git.Repository
}

/*New is a constructor for GitObject
 *
 * It neeeds:
 *	path:
 *		The path of the current repository.
 */
func New(path string) *GitObject {
	r, err := git.OpenRepository(path)
	return &GitObject{accessible: err, path: path, repository: *r}
}

/*isAccesible returns the information that is the current git repository is existing or not.
 *This method returns a boolean value: true if the git repository is still accesible (still exists), or false if not.
 */
func (g *GitObject) isAccessible() bool {
	return g.accessible == nil
}

/*Status prints the current status of the repository, accessible via the structure path field.
 *This method works only if the repository is accessible.
 */
func (g *GitObject) Status() {
	if g.isAccessible() {
		if err := g.printChanges(); err != nil {
			color.RedString("Impossible to get stats from %s, due to error %s", g.path, err)
		}
	}
}

/*getDiffWithWT returns the difference between the working tree and the index, for the current git repository.
 *If there is an error processing the request, it returns an error.
 */
func (g *GitObject) getDiffWithWT() (*git.Diff, error) {
	// Get the index of the repository
	currentIndex, err := g.repository.Index()
	if err != nil {
		return nil, err
	}
	// Get the default diff options
	defaultDiffOptions, err := git.DefaultDiffOptions()
	if err != nil {
		return nil, err
	}
	// Check the difference between the working directory and the index
	diff, err := g.repository.DiffIndexToWorkdir(currentIndex, &defaultDiffOptions)
	if err != nil {
		return nil, err
	}
	return diff, nil
}

/*printChanges prints out all changes for the current git repository.
 *If there is an error processing the request, it returns this one.
 */
func (g *GitObject) printChanges() error {
	diff, err := g.getDiffWithWT()
	if err != nil {
		return err
	}
	numDeltas, err := diff.NumDeltas()
	if err != nil {
		return err
	}
	if numDeltas > 0 {
		for i := 0; i < numDeltas; i++ {
			delta, _ := diff.GetDelta(i)
			currentStatus := delta.Status
			newFile := delta.NewFile.Path
			fmt.Printf("%s [%s]\n\t%s: %s\n",
				color.RedString("/!\\"),
				g.path,
				newFile,
				deltaFileStateToString[currentStatus])
		}
	}
	return nil
}

/*getUntrackedFiles is an old version of a tracking function for Goyave.
 *It is already deprecated.
 */
func (g *GitObject) getUntrackedFiles() {
	fmt.Printf("[%s]...", g.path)
	if untrackedFields, err := g.repository.StatusList(&statusOption); err == nil {
		count, _ := untrackedFields.EntryCount()
		if count == 0 {
			fmt.Println(color.GreenString("\tOK!"))
			return
		}
		fmt.Printf(color.RedString("\t%d untracked files!\n", count))
		for i := 0; i < count; i++ {
			statusEntry, _ := untrackedFields.ByIndex(i)
			fmt.Printf("\t%s\n", fileStateToString[statusEntry.Status])
		}
	}
}

/*List lists the path and the accessibility of a list of git repositories
 */
func List(repositories *[]GitObject) {
	for _, object := range *repositories {
		fmt.Printf("* %s ", object.path)
		if object.isAccessible() {
			fmt.Println(color.GreenString(" [accessible]"))
		} else {
			fmt.Println(color.RedString(" [not accessible]"))
		}
	}
}
<|endoftext|>