<commit_before>package bmattermost

import (
	"github.com/42wim/matterbridge/bridge/config"
	"github.com/42wim/matterbridge/bridge/helper"
	"github.com/42wim/matterbridge/matterclient"
	matterclient6 "github.com/matterbridge/matterclient"
	"github.com/mattermost/mattermost-server/v5/model"
	model6 "github.com/mattermost/mattermost-server/v6/model"
)

// handleDownloadAvatar downloads the avatar of userid from channel
// sends a EVENT_AVATAR_DOWNLOAD message to the gateway if successful.
// logs an error message if it fails
func (b *Bmattermost) handleDownloadAvatar(userid string, channel string) {
	rmsg := config.Message{
		Username: "system",
		Text:     "avatar",
		Channel:  channel,
		Account:  b.Account,
		UserID:   userid,
		Event:    config.EventAvatarDownload,
		Extra:    make(map[string][]interface{}),
	}
	if _, ok := b.avatarMap[userid]; !ok {
		var (
			data []byte
			err  error
			resp *model.Response
		)
		if b.mc6 != nil {
			data, _, err = b.mc6.Client.GetProfileImage(userid, "")
			if err != nil {
				b.Log.Errorf("ProfileImage download failed for %#v %s", userid, err)
				return
			}
		} else {
			data, resp = b.mc.Client.GetProfileImage(userid, "")
			if resp.Error != nil {
				b.Log.Errorf("ProfileImage download failed for %#v %s", userid, resp.Error)
				return
			}
		}

		err = helper.HandleDownloadSize(b.Log, &rmsg, userid+".png", int64(len(data)), b.General)
		if err != nil {
			b.Log.Error(err)
			return
		}
		helper.HandleDownloadData(b.Log, &rmsg, userid+".png", rmsg.Text, "", &data, b.General)
		b.Remote <- rmsg
	}
}

// handleDownloadFile handles file download
func (b *Bmattermost) handleDownloadFile(rmsg *config.Message, id string) error {
	if b.mc6 != nil {
		return b.handleDownloadFile6(rmsg, id)
	}

	url, _ := b.mc.Client.GetFileLink(id)
	finfo, resp := b.mc.Client.GetFileInfo(id)
	if resp.Error != nil {
		return resp.Error
	}
	err := helper.HandleDownloadSize(b.Log, rmsg, finfo.Name, finfo.Size, b.General)
	if err != nil {
		return err
	}
	data, resp := b.mc.Client.DownloadFile(id, true)
	if resp.Error != nil {
		return resp.Error
	}
	helper.HandleDownloadData(b.Log, rmsg, finfo.Name, rmsg.Text, url, &data, b.General)
	return nil
}

// nolint:wrapcheck
func (b *Bmattermost) handleDownloadFile6(rmsg *config.Message, id string) error {
	url, _, _ := b.mc6.Client.GetFileLink(id)
	finfo, _, err := b.mc6.Client.GetFileInfo(id)
	if err != nil {
		return err
	}
	err = helper.HandleDownloadSize(b.Log, rmsg, finfo.Name, finfo.Size, b.General)
	if err != nil {
		return err
	}
	data, _, err := b.mc6.Client.DownloadFile(id, true)
	if err != nil {
		return err
	}
	helper.HandleDownloadData(b.Log, rmsg, finfo.Name, rmsg.Text, url, &data, b.General)
	return nil
}

func (b *Bmattermost) handleMatter() {
	messages := make(chan *config.Message)
	if b.GetString("WebhookBindAddress") != "" {
		b.Log.Debugf("Choosing webhooks based receiving")
		go b.handleMatterHook(messages)
	} else {
		if b.GetString("Token") != "" {
			b.Log.Debugf("Choosing token based receiving")
		} else {
			b.Log.Debugf("Choosing login/password based receiving")
		}
		// if for some reason we only want to sent stuff to mattermost but not receive, return
		if b.GetString("WebhookBindAddress") == "" && b.GetString("WebhookURL") != "" && b.GetString("Token") == "" && b.GetString("Login") == "" {
			b.Log.Debugf("No WebhookBindAddress specified, only WebhookURL. You will not receive messages from mattermost, only sending is possible.")
		}
		go b.handleMatterClient(messages)
	}
	var ok bool
	for message := range messages {
		message.Avatar = helper.GetAvatar(b.avatarMap, message.UserID, b.General)
		message.Account = b.Account
		message.Text, ok = b.replaceAction(message.Text)
		if ok {
			message.Event = config.EventUserAction
		}
		b.Log.Debugf("<= Sending message from %s on %s to gateway", message.Username, b.Account)
		b.Log.Debugf("<= Message is %#v", message)
		b.Remote <- *message
	}
}

func (b *Bmattermost) handleMatterClient(messages chan *config.Message) {
	if b.mc6 != nil {
		b.Log.Debug("starting matterclient6")
		b.handleMatterClient6(messages)
		return
	}

	for message := range b.mc.MessageChan {
		b.Log.Debugf("%#v", message.Raw.Data)

		if b.skipMessage(message) {
			b.Log.Debugf("Skipped message: %#v", message)
			continue
		}

		// only download avatars if we have a place to upload them (configured mediaserver)
		if b.General.MediaServerUpload != "" || b.General.MediaDownloadPath != "" {
			b.handleDownloadAvatar(message.UserID, message.Channel)
		}

		b.Log.Debugf("== Receiving event %#v", message)

		rmsg := &config.Message{
			Username: message.Username,
			UserID:   message.UserID,
			Channel:  message.Channel,
			Text:     message.Text,
			ID:       message.Post.Id,
			ParentID: message.Post.RootId, // ParentID is obsolete with mattermost
			Extra:    make(map[string][]interface{}),
		}

		// handle mattermost post properties (override username and attachments)
		b.handleProps(rmsg, message)

		// create a text for bridges that don't support native editing
		if message.Raw.Event == model.WEBSOCKET_EVENT_POST_EDITED && !b.GetBool("EditDisable") {
			rmsg.Text = message.Text + b.GetString("EditSuffix")
		}

		if message.Raw.Event == model.WEBSOCKET_EVENT_POST_DELETED {
			rmsg.Event = config.EventMsgDelete
		}

		for _, id := range message.Post.FileIds {
			err := b.handleDownloadFile(rmsg, id)
			if err != nil {
				b.Log.Errorf("download failed: %s", err)
			}
		}

		// Use nickname instead of username if defined
		if nick := b.mc.GetNickName(rmsg.UserID); nick != "" {
			rmsg.Username = nick
		}

		messages <- rmsg
	}
}

// nolint:cyclop
func (b *Bmattermost) handleMatterClient6(messages chan *config.Message) {
	for message := range b.mc6.MessageChan {
		b.Log.Debugf("%#v", message.Raw.GetData())

		if b.skipMessage6(message) {
			b.Log.Debugf("Skipped message: %#v", message)
			continue
		}

		// only download avatars if we have a place to upload them (configured mediaserver)
		if b.General.MediaServerUpload != "" || b.General.MediaDownloadPath != "" {
			b.handleDownloadAvatar(message.UserID, message.Channel)
		}

		b.Log.Debugf("== Receiving event %#v", message)

		rmsg := &config.Message{
			Username: message.Username,
			UserID:   message.UserID,
			Channel:  message.Channel,
			Text:     message.Text,
			ID:       message.Post.Id,
			ParentID: message.Post.RootId, // ParentID is obsolete with mattermost
			Extra:    make(map[string][]interface{}),
		}

		// handle mattermost post properties (override username and attachments)
		b.handleProps6(rmsg, message)

		// create a text for bridges that don't support native editing
		if message.Raw.EventType() == model6.WebsocketEventPostEdited && !b.GetBool("EditDisable") {
			rmsg.Text = message.Text + b.GetString("EditSuffix")
		}

		if message.Raw.EventType() == model6.WebsocketEventPostDeleted {
			rmsg.Event = config.EventMsgDelete
		}

		for _, id := range message.Post.FileIds {
			err := b.handleDownloadFile(rmsg, id)
			if err != nil {
				b.Log.Errorf("download failed: %s", err)
			}
		}

		// Use nickname instead of username if defined
		if nick := b.mc6.GetNickName(rmsg.UserID); nick != "" {
			rmsg.Username = nick
		}

		messages <- rmsg
	}
}

func (b *Bmattermost) handleMatterHook(messages chan *config.Message) {
	for {
		message := b.mh.Receive()
		b.Log.Debugf("Receiving from matterhook %#v", message)
		messages <- &config.Message{
			UserID:   message.UserID,
			Username: message.UserName,
			Text:     message.Text,
			Channel:  message.ChannelName,
		}
	}
}

// handleUploadFile handles native upload of files
func (b *Bmattermost) handleUploadFile(msg *config.Message) (string, error) {
	if b.mc6 != nil {
		return b.handleUploadFile6(msg)
	}

	var err error
	var res, id string
	channelID := b.mc.GetChannelId(msg.Channel, b.TeamID)
	for _, f := range msg.Extra["file"] {
		fi := f.(config.FileInfo)
		id, err = b.mc.UploadFile(*fi.Data, channelID, fi.Name)
		if err != nil {
			return "", err
		}
		msg.Text = fi.Comment
		if b.GetBool("PrefixMessagesWithNick") {
			msg.Text = msg.Username + msg.Text
		}
		res, err = b.mc.PostMessageWithFiles(channelID, msg.Text, msg.ParentID, []string{id})
	}
	return res, err
}

// nolint:forcetypeassert,wrapcheck
func (b *Bmattermost) handleUploadFile6(msg *config.Message) (string, error) {
	var err error
	var res, id string
	channelID := b.mc6.GetChannelID(msg.Channel, b.TeamID)
	for _, f := range msg.Extra["file"] {
		fi := f.(config.FileInfo)
		id, err = b.mc6.UploadFile(*fi.Data, channelID, fi.Name)
		if err != nil {
			return "", err
		}
		msg.Text = fi.Comment
		if b.GetBool("PrefixMessagesWithNick") {
			msg.Text = msg.Username + msg.Text
		}
		res, err = b.mc6.PostMessageWithFiles(channelID, msg.Text, msg.ParentID, []string{id})
	}
	return res, err
}

func (b *Bmattermost) handleProps(rmsg *config.Message, message *matterclient.Message) {
	props := message.Post.Props
	if props == nil {
		return
	}
	if _, ok := props["override_username"].(string); ok {
		rmsg.Username = props["override_username"].(string)
	}
	if _, ok := props["attachments"].([]interface{}); ok {
		rmsg.Extra["attachments"] = props["attachments"].([]interface{})
		if rmsg.Text == "" {
			for _, attachment := range rmsg.Extra["attachments"] {
				attach := attachment.(map[string]interface{})
				if attach["text"].(string) != "" {
					rmsg.Text += attach["text"].(string)
					continue
				}
				if attach["fallback"].(string) != "" {
					rmsg.Text += attach["fallback"].(string)
				}
			}
		}
	}
}

// nolint:forcetypeassert
func (b *Bmattermost) handleProps6(rmsg *config.Message, message *matterclient6.Message) {
	props := message.Post.Props
	if props == nil {
		return
	}
	if _, ok := props["override_username"].(string); ok {
		rmsg.Username = props["override_username"].(string)
	}
	if _, ok := props["attachments"].([]interface{}); ok {
		rmsg.Extra["attachments"] = props["attachments"].([]interface{})
		if rmsg.Text != "" {
			return
		}

		for _, attachment := range rmsg.Extra["attachments"] {
			attach := attachment.(map[string]interface{})
			if attach["text"].(string) != "" {
				rmsg.Text += attach["text"].(string)
				continue
			}
			if attach["fallback"].(string) != "" {
				rmsg.Text += attach["fallback"].(string)
			}
		}
	}
}
<commit_msg>Log eventtype in debug (mattermost) (#1676)<commit_after>package bmattermost

import (
	"github.com/42wim/matterbridge/bridge/config"
	"github.com/42wim/matterbridge/bridge/helper"
	"github.com/42wim/matterbridge/matterclient"
	matterclient6 "github.com/matterbridge/matterclient"
	"github.com/mattermost/mattermost-server/v5/model"
	model6 "github.com/mattermost/mattermost-server/v6/model"
)

// handleDownloadAvatar downloads the avatar of userid from channel
// sends a EVENT_AVATAR_DOWNLOAD message to the gateway if successful.
// logs an error message if it fails
func (b *Bmattermost) handleDownloadAvatar(userid string, channel string) {
	rmsg := config.Message{
		Username: "system",
		Text:     "avatar",
		Channel:  channel,
		Account:  b.Account,
		UserID:   userid,
		Event:    config.EventAvatarDownload,
		Extra:    make(map[string][]interface{}),
	}
	if _, ok := b.avatarMap[userid]; !ok {
		var (
			data []byte
			err  error
			resp *model.Response
		)
		if b.mc6 != nil {
			data, _, err = b.mc6.Client.GetProfileImage(userid, "")
			if err != nil {
				b.Log.Errorf("ProfileImage download failed for %#v %s", userid, err)
				return
			}
		} else {
			data, resp = b.mc.Client.GetProfileImage(userid, "")
			if resp.Error != nil {
				b.Log.Errorf("ProfileImage download failed for %#v %s", userid, resp.Error)
				return
			}
		}

		err = helper.HandleDownloadSize(b.Log, &rmsg, userid+".png", int64(len(data)), b.General)
		if err != nil {
			b.Log.Error(err)
			return
		}
		helper.HandleDownloadData(b.Log, &rmsg, userid+".png", rmsg.Text, "", &data, b.General)
		b.Remote <- rmsg
	}
}

// handleDownloadFile handles file download
func (b *Bmattermost) handleDownloadFile(rmsg *config.Message, id string) error {
	if b.mc6 != nil {
		return b.handleDownloadFile6(rmsg, id)
	}

	url, _ := b.mc.Client.GetFileLink(id)
	finfo, resp := b.mc.Client.GetFileInfo(id)
	if resp.Error != nil {
		return resp.Error
	}
	err := helper.HandleDownloadSize(b.Log, rmsg, finfo.Name, finfo.Size, b.General)
	if err != nil {
		return err
	}
	data, resp := b.mc.Client.DownloadFile(id, true)
	if resp.Error != nil {
		return resp.Error
	}
	helper.HandleDownloadData(b.Log, rmsg, finfo.Name, rmsg.Text, url, &data, b.General)
	return nil
}

// nolint:wrapcheck
func (b *Bmattermost) handleDownloadFile6(rmsg *config.Message, id string) error {
	url, _, _ := b.mc6.Client.GetFileLink(id)
	finfo, _, err := b.mc6.Client.GetFileInfo(id)
	if err != nil {
		return err
	}
	err = helper.HandleDownloadSize(b.Log, rmsg, finfo.Name, finfo.Size, b.General)
	if err != nil {
		return err
	}
	data, _, err := b.mc6.Client.DownloadFile(id, true)
	if err != nil {
		return err
	}
	helper.HandleDownloadData(b.Log, rmsg, finfo.Name, rmsg.Text, url, &data, b.General)
	return nil
}

func (b *Bmattermost) handleMatter() {
	messages := make(chan *config.Message)
	if b.GetString("WebhookBindAddress") != "" {
		b.Log.Debugf("Choosing webhooks based receiving")
		go b.handleMatterHook(messages)
	} else {
		if b.GetString("Token") != "" {
			b.Log.Debugf("Choosing token based receiving")
		} else {
			b.Log.Debugf("Choosing login/password based receiving")
		}
		// if for some reason we only want to sent stuff to mattermost but not receive, return
		if b.GetString("WebhookBindAddress") == "" && b.GetString("WebhookURL") != "" && b.GetString("Token") == "" && b.GetString("Login") == "" {
			b.Log.Debugf("No WebhookBindAddress specified, only WebhookURL. You will not receive messages from mattermost, only sending is possible.")
		}
		go b.handleMatterClient(messages)
	}
	var ok bool
	for message := range messages {
		message.Avatar = helper.GetAvatar(b.avatarMap, message.UserID, b.General)
		message.Account = b.Account
		message.Text, ok = b.replaceAction(message.Text)
		if ok {
			message.Event = config.EventUserAction
		}
		b.Log.Debugf("<= Sending message from %s on %s to gateway", message.Username, b.Account)
		b.Log.Debugf("<= Message is %#v", message)
		b.Remote <- *message
	}
}

func (b *Bmattermost) handleMatterClient(messages chan *config.Message) {
	if b.mc6 != nil {
		b.Log.Debug("starting matterclient6")
		b.handleMatterClient6(messages)
		return
	}

	for message := range b.mc.MessageChan {
		b.Log.Debugf("%#v", message.Raw.Data)

		if b.skipMessage(message) {
			b.Log.Debugf("Skipped message: %#v", message)
			continue
		}

		// only download avatars if we have a place to upload them (configured mediaserver)
		if b.General.MediaServerUpload != "" || b.General.MediaDownloadPath != "" {
			b.handleDownloadAvatar(message.UserID, message.Channel)
		}

		b.Log.Debugf("== Receiving event %#v", message)

		rmsg := &config.Message{
			Username: message.Username,
			UserID:   message.UserID,
			Channel:  message.Channel,
			Text:     message.Text,
			ID:       message.Post.Id,
			ParentID: message.Post.RootId, // ParentID is obsolete with mattermost
			Extra:    make(map[string][]interface{}),
		}

		// handle mattermost post properties (override username and attachments)
		b.handleProps(rmsg, message)

		// create a text for bridges that don't support native editing
		if message.Raw.Event == model.WEBSOCKET_EVENT_POST_EDITED && !b.GetBool("EditDisable") {
			rmsg.Text = message.Text + b.GetString("EditSuffix")
		}

		if message.Raw.Event == model.WEBSOCKET_EVENT_POST_DELETED {
			rmsg.Event = config.EventMsgDelete
		}

		for _, id := range message.Post.FileIds {
			err := b.handleDownloadFile(rmsg, id)
			if err != nil {
				b.Log.Errorf("download failed: %s", err)
			}
		}

		// Use nickname instead of username if defined
		if nick := b.mc.GetNickName(rmsg.UserID); nick != "" {
			rmsg.Username = nick
		}

		messages <- rmsg
	}
}

// nolint:cyclop
func (b *Bmattermost) handleMatterClient6(messages chan *config.Message) {
	for message := range b.mc6.MessageChan {
		b.Log.Debugf("%#v %#v", message.Raw.GetData(), message.Raw.EventType())

		if b.skipMessage6(message) {
			b.Log.Debugf("Skipped message: %#v", message)
			continue
		}

		// only download avatars if we have a place to upload them (configured mediaserver)
		if b.General.MediaServerUpload != "" || b.General.MediaDownloadPath != "" {
			b.handleDownloadAvatar(message.UserID, message.Channel)
		}

		b.Log.Debugf("== Receiving event %#v", message)

		rmsg := &config.Message{
			Username: message.Username,
			UserID:   message.UserID,
			Channel:  message.Channel,
			Text:     message.Text,
			ID:       message.Post.Id,
			ParentID: message.Post.RootId, // ParentID is obsolete with mattermost
			Extra:    make(map[string][]interface{}),
		}

		// handle mattermost post properties (override username and attachments)
		b.handleProps6(rmsg, message)

		// create a text for bridges that don't support native editing
		if message.Raw.EventType() == model6.WebsocketEventPostEdited && !b.GetBool("EditDisable") {
			rmsg.Text = message.Text + b.GetString("EditSuffix")
		}

		if message.Raw.EventType() == model6.WebsocketEventPostDeleted {
			rmsg.Event = config.EventMsgDelete
		}

		for _, id := range message.Post.FileIds {
			err := b.handleDownloadFile(rmsg, id)
			if err != nil {
				b.Log.Errorf("download failed: %s", err)
			}
		}

		// Use nickname instead of username if defined
		if nick := b.mc6.GetNickName(rmsg.UserID); nick != "" {
			rmsg.Username = nick
		}

		messages <- rmsg
	}
}

func (b *Bmattermost) handleMatterHook(messages chan *config.Message) {
	for {
		message := b.mh.Receive()
		b.Log.Debugf("Receiving from matterhook %#v", message)
		messages <- &config.Message{
			UserID:   message.UserID,
			Username: message.UserName,
			Text:     message.Text,
			Channel:  message.ChannelName,
		}
	}
}

// handleUploadFile handles native upload of files
func (b *Bmattermost) handleUploadFile(msg *config.Message) (string, error) {
	if b.mc6 != nil {
		return b.handleUploadFile6(msg)
	}

	var err error
	var res, id string
	channelID := b.mc.GetChannelId(msg.Channel, b.TeamID)
	for _, f := range msg.Extra["file"] {
		fi := f.(config.FileInfo)
		id, err = b.mc.UploadFile(*fi.Data, channelID, fi.Name)
		if err != nil {
			return "", err
		}
		msg.Text = fi.Comment
		if b.GetBool("PrefixMessagesWithNick") {
			msg.Text = msg.Username + msg.Text
		}
		res, err = b.mc.PostMessageWithFiles(channelID, msg.Text, msg.ParentID, []string{id})
	}
	return res, err
}

// nolint:forcetypeassert,wrapcheck
func (b *Bmattermost) handleUploadFile6(msg *config.Message) (string, error) {
	var err error
	var res, id string
	channelID := b.mc6.GetChannelID(msg.Channel, b.TeamID)
	for _, f := range msg.Extra["file"] {
		fi := f.(config.FileInfo)
		id, err = b.mc6.UploadFile(*fi.Data, channelID, fi.Name)
		if err != nil {
			return "", err
		}
		msg.Text = fi.Comment
		if b.GetBool("PrefixMessagesWithNick") {
			msg.Text = msg.Username + msg.Text
		}
		res, err = b.mc6.PostMessageWithFiles(channelID, msg.Text, msg.ParentID, []string{id})
	}
	return res, err
}

func (b *Bmattermost) handleProps(rmsg *config.Message, message *matterclient.Message) {
	props := message.Post.Props
	if props == nil {
		return
	}
	if _, ok := props["override_username"].(string); ok {
		rmsg.Username = props["override_username"].(string)
	}
	if _, ok := props["attachments"].([]interface{}); ok {
		rmsg.Extra["attachments"] = props["attachments"].([]interface{})
		if rmsg.Text == "" {
			for _, attachment := range rmsg.Extra["attachments"] {
				attach := attachment.(map[string]interface{})
				if attach["text"].(string) != "" {
					rmsg.Text += attach["text"].(string)
					continue
				}
				if attach["fallback"].(string) != "" {
					rmsg.Text += attach["fallback"].(string)
				}
			}
		}
	}
}

// nolint:forcetypeassert
func (b *Bmattermost) handleProps6(rmsg *config.Message, message *matterclient6.Message) {
	props := message.Post.Props
	if props == nil {
		return
	}
	if _, ok := props["override_username"].(string); ok {
		rmsg.Username = props["override_username"].(string)
	}
	if _, ok := props["attachments"].([]interface{}); ok {
		rmsg.Extra["attachments"] = props["attachments"].([]interface{})
		if rmsg.Text != "" {
			return
		}

		for _, attachment := range rmsg.Extra["attachments"] {
			attach := attachment.(map[string]interface{})
			if attach["text"].(string) != "" {
				rmsg.Text += attach["text"].(string)
				continue
			}
			if attach["fallback"].(string) != "" {
				rmsg.Text += attach["fallback"].(string)
			}
		}
	}
}
<|endoftext|>


<commit_before>package workflow

import (
	"fmt"

	"github.com/zmj/sfslack/slack"
)

type sendWorkflow struct {
	*wfBase
}

func newSend(host Host) Workflow {
	return &sendWorkflow{
		wfBase: newBase(host),
	}
}

func (wf *sendWorkflow) Setup() error {
	if wf.err != nil {
		return wf.err
	}
	folder, err := wf.createWorkflowFolder()
	if err != nil {
		wf.err = err
		return err
	}

	// go subscribe
	err = wf.subscribe(folder)
	if err != nil {
		wf.err = err
		return err
	}

	requestShare, err := wf.sf.CreateRequestShare(folder.ID)
	if err != nil {
		wf.err = err
		return err // cancel sub - check done / shutdown called?
	}
	// wait for subscribe
	uploadURL := requestShare.URI
	wf.RedirectOrReply(uploadURL, uploadMessage(uploadURL))

	return nil
}

func (wf *sendWorkflow) Event() {

}

func (wf *sendWorkflow) Listen() error {
	if wf.Err != nil {
		return wf.err
	}
	return nil
}

func uploadMessage(uploadURL string) slack.Message {
	return slack.Message{
		Text: fmt.Sprintf("Upload your files: %v", uploadURL),
	}
}
<commit_msg>workflow error decoration<commit_after>package workflow

import (
	"fmt"

	"github.com/zmj/sfslack/slack"
)

type sendWorkflow struct {
	*wfBase
}

func newSend(host Host) Workflow {
	return &sendWorkflow{
		wfBase: newBase(host),
	}
}

func (wf *sendWorkflow) Setup() error {
	if wf.err != nil {
		return fmt.Errorf("Workflow already error\n%v", wf.err)
	}
	folder, err := wf.createWorkflowFolder()
	if err != nil {
		wf.err = fmt.Errorf("Failed to create workflow folder\n%v", err)
		return wf.err
	}

	// go subscribe
	err = wf.subscribe(folder)
	if err != nil {
		wf.err = fmt.Errorf("Failed to subscribe to workflow folder\n%v", err)
		return wf.err
	}

	requestShare, err := wf.sf.CreateRequestShare(folder.ID)
	if err != nil {
		wf.err = fmt.Errorf("Failed to create request share\n%v", err)
		return wf.err // cancel sub - check done / shutdown called?
	}
	// wait for subscribe
	uploadURL := requestShare.URI
	wf.RedirectOrReply(uploadURL, uploadMessage(uploadURL))

	return nil
}

func (wf *sendWorkflow) Event() {

}

func (wf *sendWorkflow) Listen() error {
	if wf.Err != nil {
		return fmt.Errorf("Workflow already error\n%v", wf.err)
	}
	return nil
}

func uploadMessage(uploadURL string) slack.Message {
	return slack.Message{
		Text: fmt.Sprintf("Upload your files: %v", uploadURL),
	}
}
<|endoftext|>


<commit_before>package main_test

import (
	"time"

	. "github.com/alphagov/govuk_crawler_worker"
	. "github.com/alphagov/govuk_crawler_worker/queue"
	. "github.com/alphagov/govuk_crawler_worker/ttl_hash_set"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"github.com/fzzy/radix/redis"
	"github.com/streadway/amqp"
)

var _ = Describe("Workflow", func() {
	Describe("Acknowledging items", func() {
		exchangeName, queueName := "test-workflow-exchange", "test-workflow-queue"
		prefix := "govuk_mirror_crawler_workflow_test"

		var (
			queueManager    *QueueManager
			queueManagerErr error
			ttlHashSet      *TTLHashSet
			ttlHashSetErr   error
		)

		BeforeEach(func() {
			ttlHashSet, ttlHashSetErr = NewTTLHashSet(prefix, "127.0.0.1:6379")
			Expect(ttlHashSetErr).To(BeNil())

			queueManager, queueManagerErr = NewQueueManager(
				"amqp://guest:guest@localhost:5672/",
				exchangeName,
				queueName)

			Expect(queueManagerErr).To(BeNil())
			Expect(queueManager).ToNot(BeNil())
		})

		AfterEach(func() {
			Expect(ttlHashSet.Close()).To(BeNil())
			Expect(purgeAllKeys(prefix, "127.0.0.1:6379")).To(BeNil())

			deleted, err := queueManager.Consumer.Channel.QueueDelete(queueName, false, false, true)
			Expect(err).To(BeNil())
			Expect(deleted).To(Equal(0))

			err = queueManager.Consumer.Channel.ExchangeDelete(exchangeName, false, true)
			Expect(err).To(BeNil())

			queueManager.Close()
		})

		Describe("AcknowledgeItem", func() {
			It("should read from a channel and add URLs to the hash set", func() {
				url := "https://www.gov.uk/foo"

				exists, err := ttlHashSet.Exists(url)
				Expect(err).To(BeNil())
				Expect(exists).To(BeFalse())

				deliveries, err := queueManager.Consume()
				Expect(err).To(BeNil())

				outbound := make(chan *CrawlerMessageItem, 1)

				err = queueManager.Publish("#", "text/plain", url)
				Expect(err).To(BeNil())

				for item := range deliveries {
					outbound <- NewCrawlerMessageItem(item, "www.gov.uk", []string{})
					break
				}

				Expect(len(outbound)).To(Equal(1))

				go AcknowledgeItem(outbound, ttlHashSet)
				time.Sleep(time.Millisecond)

				Expect(len(outbound)).To(Equal(0))

				exists, err = ttlHashSet.Exists(url)
				Expect(err).To(BeNil())
				Expect(exists).To(BeTrue())

				// Close the channel to stop the goroutine for AcknowledgeItem.
				close(outbound)
			})
		})

		Describe("ExtractURLs", func() {
			It("extracts URLs from the HTML body and adds them to a new channel; acknowledging item", func() {
				url := "https://www.gov.uk/extract-some-urls"
				deliveryItem := &amqp.Delivery{Body: []byte(url)}
				item := NewCrawlerMessageItem(*deliveryItem, "www.gov.uk", []string{})
				item.HTMLBody = []byte(`<a href="https://www.gov.uk/some-url">a link</a>`)

				outbound := make(chan *CrawlerMessageItem, 1)
				publish, acknowledge := ExtractURLs(outbound)

				Expect(len(publish)).To(Equal(0))
				Expect(len(acknowledge)).To(Equal(0))

				outbound <- item

				Expect(<-publish).To(Equal("https://www.gov.uk/some-url"))
				Expect(<-acknowledge).To(Equal(item))

				close(outbound)
			})
		})

		Describe("PublishURLs", func() {
			It("doesn't publish URLs that have already been crawled", func() {
				url := "https://www.gov.uk/government/organisations"

				deliveries, err := queueManager.Consume()
				Expect(err).To(BeNil())
				Expect(len(deliveries)).To(Equal(0))

				_, err = ttlHashSet.Add(url)
				Expect(err).To(BeNil())

				publish := make(chan string, 1)
				outbound := make(chan []byte, 1)

				go func() {
					for item := range deliveries {
						outbound <- item.Body
					}
				}()
				go PublishURLs(ttlHashSet, queueManager, publish)
				time.Sleep(time.Millisecond)

				publish <- url
				time.Sleep(time.Millisecond)

				Expect(len(publish)).To(Equal(0))
				Expect(len(outbound)).To(Equal(0))

				// Close the channel to stop the goroutine for PublishURLs.
				close(publish)
				close(outbound)
			})

			It("publishes URLs that haven't been crawled yet", func() {
				url := "https://www.gov.uk/government/foo"

				deliveries, err := queueManager.Consume()
				Expect(err).To(BeNil())
				Expect(len(deliveries)).To(Equal(0))

				publish := make(chan string, 1)
				outbound := make(chan []byte, 1)

				go func() {
					for item := range deliveries {
						outbound <- item.Body
					}
				}()
				go PublishURLs(ttlHashSet, queueManager, publish)
				time.Sleep(time.Millisecond)

				publish <- url
				time.Sleep(5 * time.Millisecond)

				Expect(len(publish)).To(Equal(0))
				Expect(len(outbound)).To(Equal(1))
				Expect(<-outbound).To(Equal([]byte(url)))

				close(publish)
				close(outbound)
			})
		})

		Describe("ReadFromQueue", func() {
			It("provides a way of converting AMQP bodies to CrawlerMessageItems", func() {
				deliveries, err := queueManager.Consume()
				Expect(err).To(BeNil())

				outbound := ReadFromQueue(deliveries, ttlHashSet)
				Expect(len(outbound)).To(Equal(0))

				url := "https://www.foo.com/bar"
				err = queueManager.Publish("#", "text/plain", url)
				Expect(err).To(BeNil())

				item := <-outbound
				Expect(string(item.Body)).To(Equal(url))

				close(outbound)
			})
		})
	})
})

func purgeAllKeys(prefix string, address string) error {
	client, err := redis.Dial("tcp", address)
	if err != nil {
		return err
	}

	keys, err := client.Cmd("KEYS", prefix+"*").List()
	if err != nil || len(keys) <= 0 {
		return err
	}

	reply := client.Cmd("DEL", keys)
	if reply.Err != nil {
		return reply.Err
	}

	return nil
}
<commit_msg>Move to an async-style test when working with channels we read from<commit_after>package main_test

import (
	"time"

	. "github.com/alphagov/govuk_crawler_worker"
	. "github.com/alphagov/govuk_crawler_worker/queue"
	. "github.com/alphagov/govuk_crawler_worker/ttl_hash_set"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"github.com/fzzy/radix/redis"
	"github.com/streadway/amqp"
)

var _ = Describe("Workflow", func() {
	Describe("Acknowledging items", func() {
		exchangeName, queueName := "test-workflow-exchange", "test-workflow-queue"
		prefix := "govuk_mirror_crawler_workflow_test"

		var (
			queueManager    *QueueManager
			queueManagerErr error
			ttlHashSet      *TTLHashSet
			ttlHashSetErr   error
		)

		BeforeEach(func() {
			ttlHashSet, ttlHashSetErr = NewTTLHashSet(prefix, "127.0.0.1:6379")
			Expect(ttlHashSetErr).To(BeNil())

			queueManager, queueManagerErr = NewQueueManager(
				"amqp://guest:guest@localhost:5672/",
				exchangeName,
				queueName)

			Expect(queueManagerErr).To(BeNil())
			Expect(queueManager).ToNot(BeNil())
		})

		AfterEach(func() {
			Expect(ttlHashSet.Close()).To(BeNil())
			Expect(purgeAllKeys(prefix, "127.0.0.1:6379")).To(BeNil())

			deleted, err := queueManager.Consumer.Channel.QueueDelete(queueName, false, false, true)
			Expect(err).To(BeNil())
			Expect(deleted).To(Equal(0))

			err = queueManager.Consumer.Channel.ExchangeDelete(exchangeName, false, true)
			Expect(err).To(BeNil())

			queueManager.Close()
		})

		Describe("AcknowledgeItem", func() {
			It("should read from a channel and add URLs to the hash set", func() {
				url := "https://www.gov.uk/foo"

				exists, err := ttlHashSet.Exists(url)
				Expect(err).To(BeNil())
				Expect(exists).To(BeFalse())

				deliveries, err := queueManager.Consume()
				Expect(err).To(BeNil())

				outbound := make(chan *CrawlerMessageItem, 1)

				err = queueManager.Publish("#", "text/plain", url)
				Expect(err).To(BeNil())

				for item := range deliveries {
					outbound <- NewCrawlerMessageItem(item, "www.gov.uk", []string{})
					break
				}

				Expect(len(outbound)).To(Equal(1))

				go AcknowledgeItem(outbound, ttlHashSet)
				time.Sleep(time.Millisecond)

				Expect(len(outbound)).To(Equal(0))

				exists, err = ttlHashSet.Exists(url)
				Expect(err).To(BeNil())
				Expect(exists).To(BeTrue())

				// Close the channel to stop the goroutine for AcknowledgeItem.
				close(outbound)
			})
		})

		Describe("ExtractURLs", func() {
			It("extracts URLs from the HTML body and adds them to a new channel; acknowledging item", func() {
				url := "https://www.gov.uk/extract-some-urls"
				deliveryItem := &amqp.Delivery{Body: []byte(url)}
				item := NewCrawlerMessageItem(*deliveryItem, "www.gov.uk", []string{})
				item.HTMLBody = []byte(`<a href="https://www.gov.uk/some-url">a link</a>`)

				outbound := make(chan *CrawlerMessageItem, 1)
				publish, acknowledge := ExtractURLs(outbound)

				Expect(len(publish)).To(Equal(0))
				Expect(len(acknowledge)).To(Equal(0))

				outbound <- item

				Expect(<-publish).To(Equal("https://www.gov.uk/some-url"))
				Expect(<-acknowledge).To(Equal(item))

				close(outbound)
			})
		})

		Describe("PublishURLs", func() {
			It("doesn't publish URLs that have already been crawled", func() {
				url := "https://www.gov.uk/government/organisations"

				deliveries, err := queueManager.Consume()
				Expect(err).To(BeNil())
				Expect(len(deliveries)).To(Equal(0))

				_, err = ttlHashSet.Add(url)
				Expect(err).To(BeNil())

				publish := make(chan string, 1)
				outbound := make(chan []byte, 1)

				go func() {
					for item := range deliveries {
						outbound <- item.Body
					}
				}()
				go PublishURLs(ttlHashSet, queueManager, publish)
				time.Sleep(time.Millisecond)

				publish <- url
				time.Sleep(time.Millisecond)

				Expect(len(publish)).To(Equal(0))
				Expect(len(outbound)).To(Equal(0))

				// Close the channel to stop the goroutine for PublishURLs.
				close(publish)
				close(outbound)
			})

			It("publishes URLs that haven't been crawled yet", func() {
				url := "https://www.gov.uk/government/foo"

				deliveries, err := queueManager.Consume()
				Expect(err).To(BeNil())
				Expect(len(deliveries)).To(Equal(0))

				publish := make(chan string, 1)
				outbound := make(chan []byte, 1)

				go func() {
					for item := range deliveries {
						outbound <- item.Body
					}
				}()
				go PublishURLs(ttlHashSet, queueManager, publish)

				publish <- url

				Expect(<-outbound).To(Equal([]byte(url)))
				Expect(len(publish)).To(Equal(0))

				close(publish)
				close(outbound)
			})
		})

		Describe("ReadFromQueue", func() {
			It("provides a way of converting AMQP bodies to CrawlerMessageItems", func() {
				deliveries, err := queueManager.Consume()
				Expect(err).To(BeNil())

				outbound := ReadFromQueue(deliveries, ttlHashSet)
				Expect(len(outbound)).To(Equal(0))

				url := "https://www.foo.com/bar"
				err = queueManager.Publish("#", "text/plain", url)
				Expect(err).To(BeNil())

				item := <-outbound
				Expect(string(item.Body)).To(Equal(url))

				close(outbound)
			})
		})
	})
})

func purgeAllKeys(prefix string, address string) error {
	client, err := redis.Dial("tcp", address)
	if err != nil {
		return err
	}

	keys, err := client.Cmd("KEYS", prefix+"*").List()
	if err != nil || len(keys) <= 0 {
		return err
	}

	reply := client.Cmd("DEL", keys)
	if reply.Err != nil {
		return reply.Err
	}

	return nil
}
<|endoftext|>


<commit_before>package ui

import (
	"errors"
	"fmt"
	"math/rand"
	"os"
	"os/exec"
	"os/signal"
	"strings"
	"time"

	"github.com/fabiofalci/sconsify/events"
	sp "github.com/op/go-libspotify/spotify"
)

type NoUi struct {
	silent         *bool
	playlistFilter []string
	tracks         []*sp.Track
}

func StartNoUserInterface(events *events.Events, silent *bool, playlistFilter *string, repeatOn *bool) error {
	noui := &NoUi{silent: silent}
	noui.setPlaylistFilter(*playlistFilter)

	playlists := noui.waitForPlaylists(events)
	if playlists == nil {
		return nil
	}

	go listenForKeyboardEvents(events.NextPlay)

	listenForNoCuiTermination(events)

	err := noui.randomTracks(playlists)
	if err != nil {
		return err
	}

	if !*silent {
		noui.printPlaylistInfo()
	}

	nextToPlayIndex := 0
	numberOfTracks := len(noui.tracks)

	for {
		track := noui.tracks[nextToPlayIndex]

		events.ToPlay <- track

		if *silent {
			<-events.WaitForStatus()
		} else {
			fmt.Println(<-events.WaitForStatus())
		}
		<-events.NextPlay

		nextToPlayIndex++
		if nextToPlayIndex >= numberOfTracks {
			if *repeatOn {
				nextToPlayIndex = 0
			} else {
				return nil
			}
		}
	}

	return nil
}

func (noui *NoUi) waitForPlaylists(events *events.Events) map[string]*sp.Playlist {
	select {
	case playlists := <-events.WaitForPlaylists():
		if playlists != nil {
			return playlists
		}
	case <-events.WaitForShutdown():
	}
	return nil
}

func (noui *NoUi) printPlaylistInfo() {
	fmt.Printf("%v track(s) from ", len(noui.tracks))
	if noui.playlistFilter == nil {
		fmt.Printf("all playlist(s)\n")
	} else {
		fmt.Printf("%v playlist(s)\n", len(noui.playlistFilter))
	}
}

func listenForNoCuiTermination(events *events.Events) {
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	go func() {
		for _ = range c {
			shutdownNogui(events)
		}
	}()
}

func shutdownNogui(events *events.Events) {
	events.Shutdown()
	<-events.WaitForShutdown()
	os.Exit(0)
}

func listenForKeyboardEvents(nextPlay chan bool) {
	exec.Command("stty", "-F", "/dev/tty", "cbreak", "min", "1").Run()

	// we could disable echo but I can't enable it back

	// do not display entered characters on the screen
	// exec.Command("stty", "-F", "/dev/tty", "-echo").Run()
	// defer exec.Command("stty", "-F", "/dev/tty", "echo")

	var b []byte = make([]byte, 1)
	for {
		os.Stdin.Read(b)

		key := string(b)
		if key == ">" {
			fmt.Println("")
			nextPlay <- true
		}
	}
}

func (noui *NoUi) randomTracks(playlists map[string]*sp.Playlist) error {
	numberOfTracks := 0
	for _, playlist := range playlists {
		playlist.Wait()
		if noui.isOnFilter(playlist.Name()) {
			numberOfTracks += playlist.Tracks()
		}
	}

	if numberOfTracks == 0 {
		return errors.New("No tracks selected")
	}

	noui.tracks = make([]*sp.Track, numberOfTracks)
	perm := getRandomPermutation(numberOfTracks)
	permIndex := 0

	for _, playlist := range playlists {
		playlist.Wait()
		if noui.isOnFilter(playlist.Name()) {
			for i := 0; i < playlist.Tracks(); i++ {
				track := playlist.Track(i).Track()
				track.Wait()

				noui.tracks[perm[permIndex]] = track
				permIndex++
			}
		}
	}

	return nil
}

func (noui *NoUi) setPlaylistFilter(playlistFilter string) {
	if playlistFilter == "" {
		return
	}
	noui.playlistFilter = strings.Split(playlistFilter, ",")
	for i := range noui.playlistFilter {
		noui.playlistFilter[i] = strings.Trim(noui.playlistFilter[i], " ")
	}
}

func (noui *NoUi) isOnFilter(playlist string) bool {
	if noui.playlistFilter == nil {
		return true
	}
	for _, filter := range noui.playlistFilter {
		if filter == playlist {
			return true
		}
	}
	return false
}

func getRandomPermutation(numberOfTracks int) []int {
	rand.Seed(time.Now().Unix())
	return rand.Perm(numberOfTracks)
}
<commit_msg>Add events to noui struct<commit_after>package ui

import (
	"errors"
	"fmt"
	"math/rand"
	"os"
	"os/exec"
	"os/signal"
	"strings"
	"time"

	"github.com/fabiofalci/sconsify/events"
	sp "github.com/op/go-libspotify/spotify"
)

type NoUi struct {
	silent         *bool
	playlistFilter []string
	tracks         []*sp.Track
	events         *events.Events
}

func StartNoUserInterface(events *events.Events, silent *bool, playlistFilter *string, repeatOn *bool) error {
	noui := &NoUi{silent: silent, events: events}
	noui.setPlaylistFilter(*playlistFilter)

	playlists := noui.waitForPlaylists(events)
	if playlists == nil {
		return nil
	}

	go noui.listenForKeyboardEvents()

	listenForNoCuiTermination(events)

	err := noui.randomTracks(playlists)
	if err != nil {
		return err
	}

	if !*silent {
		noui.printPlaylistInfo()
	}

	nextToPlayIndex := 0
	numberOfTracks := len(noui.tracks)

	for {
		track := noui.tracks[nextToPlayIndex]

		events.ToPlay <- track

		if *silent {
			<-events.WaitForStatus()
		} else {
			fmt.Println(<-events.WaitForStatus())
		}
		<-events.NextPlay

		nextToPlayIndex++
		if nextToPlayIndex >= numberOfTracks {
			if *repeatOn {
				nextToPlayIndex = 0
			} else {
				return nil
			}
		}
	}

	return nil
}

func (noui *NoUi) waitForPlaylists(events *events.Events) map[string]*sp.Playlist {
	select {
	case playlists := <-events.WaitForPlaylists():
		if playlists != nil {
			return playlists
		}
	case <-events.WaitForShutdown():
	}
	return nil
}

func (noui *NoUi) printPlaylistInfo() {
	fmt.Printf("%v track(s) from ", len(noui.tracks))
	if noui.playlistFilter == nil {
		fmt.Printf("all playlist(s)\n")
	} else {
		fmt.Printf("%v playlist(s)\n", len(noui.playlistFilter))
	}
}

func listenForNoCuiTermination(events *events.Events) {
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	go func() {
		for _ = range c {
			shutdownNogui(events)
		}
	}()
}

func shutdownNogui(events *events.Events) {
	events.Shutdown()
	<-events.WaitForShutdown()
	os.Exit(0)
}

func (noui *NoUi) listenForKeyboardEvents() {
	exec.Command("stty", "-F", "/dev/tty", "cbreak", "min", "1").Run()

	// we could disable echo but I can't enable it back

	// do not display entered characters on the screen
	// exec.Command("stty", "-F", "/dev/tty", "-echo").Run()
	// defer exec.Command("stty", "-F", "/dev/tty", "echo")

	var b []byte = make([]byte, 1)
	for {
		os.Stdin.Read(b)

		key := string(b)
		if key == ">" {
			fmt.Println("")
			noui.events.NextPlay <- true
		}
	}
}

func (noui *NoUi) randomTracks(playlists map[string]*sp.Playlist) error {
	numberOfTracks := 0
	for _, playlist := range playlists {
		playlist.Wait()
		if noui.isOnFilter(playlist.Name()) {
			numberOfTracks += playlist.Tracks()
		}
	}

	if numberOfTracks == 0 {
		return errors.New("No tracks selected")
	}

	noui.tracks = make([]*sp.Track, numberOfTracks)
	perm := getRandomPermutation(numberOfTracks)
	permIndex := 0

	for _, playlist := range playlists {
		playlist.Wait()
		if noui.isOnFilter(playlist.Name()) {
			for i := 0; i < playlist.Tracks(); i++ {
				track := playlist.Track(i).Track()
				track.Wait()

				noui.tracks[perm[permIndex]] = track
				permIndex++
			}
		}
	}

	return nil
}

func (noui *NoUi) setPlaylistFilter(playlistFilter string) {
	if playlistFilter == "" {
		return
	}
	noui.playlistFilter = strings.Split(playlistFilter, ",")
	for i := range noui.playlistFilter {
		noui.playlistFilter[i] = strings.Trim(noui.playlistFilter[i], " ")
	}
}

func (noui *NoUi) isOnFilter(playlist string) bool {
	if noui.playlistFilter == nil {
		return true
	}
	for _, filter := range noui.playlistFilter {
		if filter == playlist {
			return true
		}
	}
	return false
}

func getRandomPermutation(numberOfTracks int) []int {
	rand.Seed(time.Now().Unix())
	return rand.Perm(numberOfTracks)
}
<|endoftext|>


<commit_before>/*
 *
 * Author     : Valentin Kuznetsov <vkuznet AT gmail dot com>
 * Description: URL fetch proxy server concurrently fetches data from
 *              provided URL list. It provides a POST HTTP interface
 *              "/getdata" which accepts urls as newline separated encoded
 *              string
 * Created    : Wed Mar 20 13:29:48 EDT 2013
 * License    : MIT
 *
 */
package urlfetch

import (
    "os"
    "fmt"
    "log"
    "strings"
    "net/http"
    "io/ioutil"
    "crypto/tls"
)

/*
 * Return array of certificates
 */
func Certs() (tls_certs []tls.Certificate) {
    uproxy := os.Getenv("X509_USER_PROXY")
    uckey  := os.Getenv("X509_USER_KEY")
    ucert  := os.Getenv("X509_USER_CERT")
    if  len(uproxy) > 0 {
        x509cert, err := tls.LoadX509KeyPair(uproxy, uproxy)
        if  err != nil {
            fmt.Println("Fail to parser proxy X509 certificate", err)
            return
        }
        tls_certs = []tls.Certificate{x509cert}
    } else if len(uckey) > 0 {
        x509cert, err := tls.LoadX509KeyPair(ucert, uckey)
        if  err != nil {
            fmt.Println("Fail to parser user X509 certificate", err)
            return
        }
        tls_certs = []tls.Certificate{x509cert}
    } else {
        return
    }
    return
}

/*
 * HTTP client for urlfetch server
 */
func HttpClient() (client *http.Client) {
    // create HTTP client
    certs := Certs()
    if  len(certs) == 0 {
        client = &http.Client{}
        return
    }
    tr := &http.Transport{
        TLSClientConfig: &tls.Config{tls.Certificates: certs},
    }
    client = &http.Client{Transport: tr}
    return
}

/*
 * Getdata(client *http.Client, url string, ch chan<- []byte)
 * Using given client and chanel fetches data for provided URL.
 * Results are redirected to specified channel
 */
func Getdata(client *http.Client, url string, ch chan<- []byte) {
    msg := ""
    resp, err := client.Get(url)
    if  err != nil {
        msg = "Fail to contact " + url
        ch <- []byte(msg)
        return
    }
    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)
    if  err != nil {
        msg = "Fail to parse reponse body"
        log.Println(msg, err)
        ch <- []byte(msg)
        return
    }
    ch <- body
}

/*
 * RequestHandler is used by web server to handle incoming requests
 */
func RequestHandler(w http.ResponseWriter, r *http.Request) {
    // we only accept POST request with urls (this is by design)
    if  r.Method != "POST" {
        w.WriteHeader(http.StatusBadRequest)
        return
    }

    // parse input request parameter, in this case we should pass urls
    r.ParseForm()
    urls := []string{}
    for k, v := range r.Form {
        if k == "urls" {
            urls = strings.Split(v[0], "\n")
        }
    }
    log.Println(urls)

    // create HTTP client
    client := HttpClient()

    // loop concurently over url list and store results into channel
    ch := make(chan []byte)
    n := 0
    for _, url := range urls {
        n++
        go Getdata(client, url, ch)
    }
    // once channels are ready fill out results to response writer
    for i:=0; i<n; i++ {
        w.Write(<-ch)
        w.Write([]byte("\n"))
    }
}

// proxy server. It defines /getdata public interface
func Server(port string) {
    http.HandleFunc("/getdata", RequestHandler)
    err := http.ListenAndServe(":" + port, nil)
    // NOTE: later this can be replaced with secure connection
    // replace ListenAndServe(addr string, handler Handler)
    // with TLS function
    // ListenAndServeTLS(addr string, certFile string, keyFile string, handler
    // Handler)
    if  err != nil {
        log.Fatal("ListenAndServe: ", err)
    }
}

<commit_msg>Switch from fmt to log<commit_after>/*
 *
 * Author     : Valentin Kuznetsov <vkuznet AT gmail dot com>
 * Description: URL fetch proxy server concurrently fetches data from
 *              provided URL list. It provides a POST HTTP interface
 *              "/getdata" which accepts urls as newline separated encoded
 *              string
 * Created    : Wed Mar 20 13:29:48 EDT 2013
 * License    : MIT
 *
 */
package urlfetch

import (
    "os"
    "log"
    "strings"
    "net/http"
    "io/ioutil"
    "crypto/tls"
)

/*
 * Return array of certificates
 */
func Certs() (tls_certs []tls.Certificate) {
    uproxy := os.Getenv("X509_USER_PROXY")
    uckey  := os.Getenv("X509_USER_KEY")
    ucert  := os.Getenv("X509_USER_CERT")
    log.Println("X509_USER_PROXY", uproxy)
    log.Println("X509_USER_KEY", uckey)
    log.Println("X509_USER_CERT", ucert)
    if  len(uproxy) > 0 {
        x509cert, err := tls.LoadX509KeyPair(uproxy, uproxy)
        if  err != nil {
            log.Println("Fail to parser proxy X509 certificate", err)
            return
        }
        tls_certs = []tls.Certificate{x509cert}
    } else if len(uckey) > 0 {
        x509cert, err := tls.LoadX509KeyPair(ucert, uckey)
        if  err != nil {
            log.Println("Fail to parser user X509 certificate", err)
            return
        }
        tls_certs = []tls.Certificate{x509cert}
    } else {
        return
    }
    return
}

/*
 * HTTP client for urlfetch server
 */
func HttpClient() (client *http.Client) {
    // create HTTP client
    certs := Certs()
    if  len(certs) == 0 {
        client = &http.Client{}
        return
    }
    tr := &http.Transport{
        TLSClientConfig: &tls.Config{tls.Certificates: certs},
    }
    client = &http.Client{Transport: tr}
    return
}

/*
 * Getdata(client *http.Client, url string, ch chan<- []byte)
 * Using given client and chanel fetches data for provided URL.
 * Results are redirected to specified channel
 */
func Getdata(client *http.Client, url string, ch chan<- []byte) {
    msg := ""
    resp, err := client.Get(url)
    if  err != nil {
        msg = "Fail to contact " + url
        log.Println(msg, err)
        ch <- []byte(msg)
        return
    }
    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)
    if  err != nil {
        msg = "Fail to parse reponse body"
        log.Println(msg, err)
        ch <- []byte(msg)
        return
    }
    ch <- body
}

/*
 * RequestHandler is used by web server to handle incoming requests
 */
func RequestHandler(w http.ResponseWriter, r *http.Request) {
    // we only accept POST request with urls (this is by design)
    if  r.Method != "POST" {
        w.WriteHeader(http.StatusBadRequest)
        return
    }

    // parse input request parameter, in this case we should pass urls
    r.ParseForm()
    urls := []string{}
    for k, v := range r.Form {
        if k == "urls" {
            urls = strings.Split(v[0], "\n")
        }
    }
//    log.Println(urls)

    // create HTTP client
    client := HttpClient()

    // loop concurently over url list and store results into channel
    ch := make(chan []byte)
    n := 0
    for _, url := range urls {
        n++
        go Getdata(client, url, ch)
    }
    // once channels are ready fill out results to response writer
    for i:=0; i<n; i++ {
        w.Write(<-ch)
        w.Write([]byte("\n"))
    }
}

// proxy server. It defines /getdata public interface
func Server(port string) {
    log.Printf("Start server localhost:%s/getdata", port)
    http.HandleFunc("/getdata", RequestHandler)
    err := http.ListenAndServe(":" + port, nil)
    // NOTE: later this can be replaced with secure connection
    // replace ListenAndServe(addr string, handler Handler)
    // with TLS function
    // ListenAndServeTLS(addr string, certFile string, keyFile string, handler
    // Handler)
    if  err != nil {
        log.Fatal("ListenAndServe: ", err)
    }
}

<|endoftext|>


<commit_before>package mansion

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"runtime"
	"strings"

	"github.com/itchio/butler/comm"
	itchio "github.com/itchio/go-itchio"
)

func (ctx *Context) UpdateBaseURL(variant VersionVariant) string {
	channel := fmt.Sprintf("%s-%s", runtime.GOOS, runtime.GOARCH)
	if variant == VersionVariantHead {
		channel += "-head"
	}
	return fmt.Sprintf("https://broth.itch.ovh/butler/%s", channel)
}

func (ctx *Context) DoVersionCheck() {
	vinfo, err := ctx.QueryLatestVersion(ctx.CurrentVariant())
	if err != nil {
		comm.Logf("Version check failed: %s", err.Error())
		return
	}

	if vinfo.Current == nil || vinfo.Latest == nil {
		return
	}

	if vinfo.Current.Equal(vinfo.Latest) {
		return
	}

	if vinfo.Current != vinfo.Latest {
		comm.Notice("New version available",
			[]string{
				fmt.Sprintf("Current version: %s", vinfo.Current),
				fmt.Sprintf("Latest version:  %s", vinfo.Latest),
				"",
				"Run `butler upgrade` to get it.",
			})
	}
}

type Version struct {
	Name    string
	Variant VersionVariant
}

func (v *Version) String() string {
	return fmt.Sprintf("%s (%s)", v.Name, v.Variant)
}

func (v *Version) Equal(v2 *Version) bool {
	if v == nil {
		return false
	}
	if v2 == nil {
		return false
	}
	if v.Variant != v2.Variant {
		return false
	}
	if v.Name != v2.Name {
		return false
	}
	return true
}

func normalizeVersionName(name string) string {
	return strings.TrimLeft(name, "v")
}

type VersionCheckResult struct {
	Current *Version
	Latest  *Version
}

type VersionVariant string

const (
	VersionVariantStable = "stable"
	VersionVariantHead   = "head"
)

func (ctx *Context) CurrentVersion() *Version {
	variant := ctx.CurrentVariant()
	switch variant {
	case VersionVariantHead:
		return &Version{
			Name:    ctx.Commit,
			Variant: variant,
		}
	default:
		return &Version{
			Name:    normalizeVersionName(ctx.Version),
			Variant: variant,
		}
	}
}

func (ctx *Context) CurrentVariant() VersionVariant {
	switch ctx.Version {
	case "head":
		return VersionVariantHead
	default:
		return VersionVariantStable
	}
}

func (ctx *Context) QueryLatestVersion(variant VersionVariant) (*VersionCheckResult, error) {
	if ctx.Quiet {
		return nil, nil
	}

	res := &VersionCheckResult{
		Current: ctx.CurrentVersion(),
	}

	c := itchio.ClientWithKey("x")

	latestURL := fmt.Sprintf("%s/LATEST", ctx.UpdateBaseURL(variant))
	req, err := http.NewRequest("GET", latestURL, nil)
	if err != nil {
		return nil, err
	}

	latestRes, err := c.Do(req)
	if err != nil {
		return nil, err
	}

	if latestRes.StatusCode != 200 {
		err = fmt.Errorf("HTTP %d: %s", latestRes.StatusCode, latestURL)
		return nil, err
	}

	buf, err := ioutil.ReadAll(latestRes.Body)
	if err != nil {
		return nil, err
	}

	latestVersion := strings.Trim(string(buf), " \r\n")
	res.Latest = &Version{
		Name:    normalizeVersionName(latestVersion),
		Variant: variant,
	}

	return res, nil
}
<commit_msg>Don't check for new versions if butler is homebuilt<commit_after>package mansion

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"runtime"
	"strings"

	"github.com/itchio/butler/comm"
	itchio "github.com/itchio/go-itchio"
)

func (ctx *Context) UpdateBaseURL(variant VersionVariant) string {
	channel := fmt.Sprintf("%s-%s", runtime.GOOS, runtime.GOARCH)
	if variant == VersionVariantHead {
		channel += "-head"
	}
	return fmt.Sprintf("https://broth.itch.ovh/butler/%s", channel)
}

func (ctx *Context) DoVersionCheck() {
	vinfo, err := ctx.QueryLatestVersion(ctx.CurrentVariant())
	if err != nil {
		comm.Logf("Version check failed: %s", err.Error())
		return
	}

	if vinfo.Current == nil || vinfo.Latest == nil {
		return
	}

	if vinfo.Current.Equal(vinfo.Latest) {
		return
	}

	if vinfo.Current.Name == "" {
		// home-built, don't check
		return
	}

	if vinfo.Current != vinfo.Latest {
		comm.Notice("New version available",
			[]string{
				fmt.Sprintf("Current version: %s", vinfo.Current),
				fmt.Sprintf("Latest version:  %s", vinfo.Latest),
				"",
				"Run `butler upgrade` to get it.",
			})
	}
}

type Version struct {
	Name    string
	Variant VersionVariant
}

func (v *Version) String() string {
	return fmt.Sprintf("%s (%s)", v.Name, v.Variant)
}

func (v *Version) Equal(v2 *Version) bool {
	if v == nil {
		return false
	}
	if v2 == nil {
		return false
	}
	if v.Variant != v2.Variant {
		return false
	}
	if v.Name != v2.Name {
		return false
	}
	return true
}

func normalizeVersionName(name string) string {
	return strings.TrimLeft(name, "v")
}

type VersionCheckResult struct {
	Current *Version
	Latest  *Version
}

type VersionVariant string

const (
	VersionVariantStable = "stable"
	VersionVariantHead   = "head"
)

func (ctx *Context) CurrentVersion() *Version {
	variant := ctx.CurrentVariant()
	switch variant {
	case VersionVariantHead:
		return &Version{
			Name:    ctx.Commit,
			Variant: variant,
		}
	default:
		return &Version{
			Name:    normalizeVersionName(ctx.Version),
			Variant: variant,
		}
	}
}

func (ctx *Context) CurrentVariant() VersionVariant {
	switch ctx.Version {
	case "head":
		return VersionVariantHead
	default:
		return VersionVariantStable
	}
}

func (ctx *Context) QueryLatestVersion(variant VersionVariant) (*VersionCheckResult, error) {
	if ctx.Quiet {
		return nil, nil
	}

	res := &VersionCheckResult{
		Current: ctx.CurrentVersion(),
	}

	c := itchio.ClientWithKey("x")

	latestURL := fmt.Sprintf("%s/LATEST", ctx.UpdateBaseURL(variant))
	req, err := http.NewRequest("GET", latestURL, nil)
	if err != nil {
		return nil, err
	}

	latestRes, err := c.Do(req)
	if err != nil {
		return nil, err
	}

	if latestRes.StatusCode != 200 {
		err = fmt.Errorf("HTTP %d: %s", latestRes.StatusCode, latestURL)
		return nil, err
	}

	buf, err := ioutil.ReadAll(latestRes.Body)
	if err != nil {
		return nil, err
	}

	latestVersion := strings.Trim(string(buf), " \r\n")
	res.Latest = &Version{
		Name:    normalizeVersionName(latestVersion),
		Variant: variant,
	}

	return res, nil
}
<|endoftext|>


<commit_before>package container

import (
	"fmt"
	"io"
	"os"

	"golang.org/x/net/context"

	"github.com/docker/docker/cli"
	"github.com/docker/docker/cli/command"
	"github.com/docker/docker/cli/command/image"
	"github.com/docker/docker/pkg/jsonmessage"
	// FIXME migrate to docker/distribution/reference
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/container"
	networktypes "github.com/docker/docker/api/types/network"
	apiclient "github.com/docker/docker/client"
	"github.com/docker/docker/reference"
	"github.com/docker/docker/registry"
	runconfigopts "github.com/docker/docker/runconfig/opts"
	"github.com/spf13/cobra"
	"github.com/spf13/pflag"
)

type createOptions struct {
	name string
}

// NewCreateCommand creates a new cobra.Command for `docker create`
func NewCreateCommand(dockerCli *command.DockerCli) *cobra.Command {
	var opts createOptions
	var copts *runconfigopts.ContainerOptions

	cmd := &cobra.Command{
		Use:   "create [OPTIONS] IMAGE [COMMAND] [ARG...]",
		Short: "Create a new container",
		Args:  cli.RequiresMinArgs(1),
		RunE: func(cmd *cobra.Command, args []string) error {
			copts.Image = args[0]
			if len(args) > 1 {
				copts.Args = args[1:]
			}
			return runCreate(dockerCli, cmd.Flags(), &opts, copts)
		},
	}

	flags := cmd.Flags()
	flags.SetInterspersed(false)

	flags.StringVar(&opts.name, "name", "", "Assign a name to the container")

	// Add an explicit help that doesn't have a `-h` to prevent the conflict
	// with hostname
	flags.Bool("help", false, "Print usage")

	command.AddTrustedFlags(flags, true)
	copts = runconfigopts.AddFlags(flags)
	return cmd
}

func runCreate(dockerCli *command.DockerCli, flags *pflag.FlagSet, opts *createOptions, copts *runconfigopts.ContainerOptions) error {
	config, hostConfig, networkingConfig, err := runconfigopts.Parse(flags, copts)
	if err != nil {
		reportError(dockerCli.Err(), "create", err.Error(), true)
		return cli.StatusError{StatusCode: 125}
	}
	response, err := createContainer(context.Background(), dockerCli, config, hostConfig, networkingConfig, hostConfig.ContainerIDFile, opts.name)
	if err != nil {
		return err
	}
	fmt.Fprintf(dockerCli.Out(), "%s\n", response.ID)
	return nil
}

func pullImage(ctx context.Context, dockerCli *command.DockerCli, image string, out io.Writer) error {
	ref, err := reference.ParseNamed(image)
	if err != nil {
		return err
	}

	// Resolve the Repository name from fqn to RepositoryInfo
	repoInfo, err := registry.ParseRepositoryInfo(ref)
	if err != nil {
		return err
	}

	authConfig := command.ResolveAuthConfig(ctx, dockerCli, repoInfo.Index)
	encodedAuth, err := command.EncodeAuthToBase64(authConfig)
	if err != nil {
		return err
	}

	options := types.ImageCreateOptions{
		RegistryAuth: encodedAuth,
	}

	responseBody, err := dockerCli.Client().ImageCreate(ctx, image, options)
	if err != nil {
		return err
	}
	defer responseBody.Close()

	return jsonmessage.DisplayJSONMessagesStream(
		responseBody,
		out,
		dockerCli.Out().FD(),
		dockerCli.Out().IsTerminal(),
		nil)
}

type cidFile struct {
	path    string
	file    *os.File
	written bool
}

func (cid *cidFile) Close() error {
	cid.file.Close()

	if !cid.written {
		if err := os.Remove(cid.path); err != nil {
			return fmt.Errorf("failed to remove the CID file '%s': %s \n", cid.path, err)
		}
	}

	return nil
}

func (cid *cidFile) Write(id string) error {
	if _, err := cid.file.Write([]byte(id)); err != nil {
		return fmt.Errorf("Failed to write the container ID to the file: %s", err)
	}
	cid.written = true
	return nil
}

func newCIDFile(path string) (*cidFile, error) {
	if _, err := os.Stat(path); err == nil {
		return nil, fmt.Errorf("Container ID file found, make sure the other container isn't running or delete %s", path)
	}

	f, err := os.Create(path)
	if err != nil {
		return nil, fmt.Errorf("Failed to create the container ID file: %s", err)
	}

	return &cidFile{path: path, file: f}, nil
}

func createContainer(ctx context.Context, dockerCli *command.DockerCli, config *container.Config, hostConfig *container.HostConfig, networkingConfig *networktypes.NetworkingConfig, cidfile, name string) (*types.ContainerCreateResponse, error) {
	stderr := dockerCli.Err()

	var containerIDFile *cidFile
	if cidfile != "" {
		var err error
		if containerIDFile, err = newCIDFile(cidfile); err != nil {
			return nil, err
		}
		defer containerIDFile.Close()
	}

	var trustedRef reference.Canonical
	_, ref, err := reference.ParseIDOrReference(config.Image)
	if err != nil {
		return nil, err
	}
	if ref != nil {
		ref = reference.WithDefaultTag(ref)

		if ref, ok := ref.(reference.NamedTagged); ok && command.IsTrusted() {
			var err error
			trustedRef, err = image.TrustedReference(ctx, dockerCli, ref)
			if err != nil {
				return nil, err
			}
			config.Image = trustedRef.String()
		}
	}

	//create the container
	response, err := dockerCli.Client().ContainerCreate(ctx, config, hostConfig, networkingConfig, name)

	//if image not found try to pull it
	if err != nil {
		if apiclient.IsErrImageNotFound(err) && ref != nil {
			fmt.Fprintf(stderr, "Unable to find image '%s' locally\n", ref.String())

			// we don't want to write to stdout anything apart from container.ID
			if err = pullImage(ctx, dockerCli, config.Image, stderr); err != nil {
				return nil, err
			}
			if ref, ok := ref.(reference.NamedTagged); ok && trustedRef != nil {
				if err := image.TagTrusted(ctx, dockerCli, trustedRef, ref); err != nil {
					return nil, err
				}
			}
			// Retry
			var retryErr error
			response, retryErr = dockerCli.Client().ContainerCreate(ctx, config, hostConfig, networkingConfig, name)
			if retryErr != nil {
				return nil, retryErr
			}
		} else {
			return nil, err
		}
	}

	for _, warning := range response.Warnings {
		fmt.Fprintf(stderr, "WARNING: %s\n", warning)
	}
	if containerIDFile != nil {
		if err = containerIDFile.Write(response.ID); err != nil {
			return nil, err
		}
	}
	return &response, nil
}
<commit_msg>Generate container create response from swagger spec.<commit_after>package container

import (
	"fmt"
	"io"
	"os"

	"golang.org/x/net/context"

	"github.com/docker/docker/cli"
	"github.com/docker/docker/cli/command"
	"github.com/docker/docker/cli/command/image"
	"github.com/docker/docker/pkg/jsonmessage"
	// FIXME migrate to docker/distribution/reference
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/container"
	networktypes "github.com/docker/docker/api/types/network"
	apiclient "github.com/docker/docker/client"
	"github.com/docker/docker/reference"
	"github.com/docker/docker/registry"
	runconfigopts "github.com/docker/docker/runconfig/opts"
	"github.com/spf13/cobra"
	"github.com/spf13/pflag"
)

type createOptions struct {
	name string
}

// NewCreateCommand creates a new cobra.Command for `docker create`
func NewCreateCommand(dockerCli *command.DockerCli) *cobra.Command {
	var opts createOptions
	var copts *runconfigopts.ContainerOptions

	cmd := &cobra.Command{
		Use:   "create [OPTIONS] IMAGE [COMMAND] [ARG...]",
		Short: "Create a new container",
		Args:  cli.RequiresMinArgs(1),
		RunE: func(cmd *cobra.Command, args []string) error {
			copts.Image = args[0]
			if len(args) > 1 {
				copts.Args = args[1:]
			}
			return runCreate(dockerCli, cmd.Flags(), &opts, copts)
		},
	}

	flags := cmd.Flags()
	flags.SetInterspersed(false)

	flags.StringVar(&opts.name, "name", "", "Assign a name to the container")

	// Add an explicit help that doesn't have a `-h` to prevent the conflict
	// with hostname
	flags.Bool("help", false, "Print usage")

	command.AddTrustedFlags(flags, true)
	copts = runconfigopts.AddFlags(flags)
	return cmd
}

func runCreate(dockerCli *command.DockerCli, flags *pflag.FlagSet, opts *createOptions, copts *runconfigopts.ContainerOptions) error {
	config, hostConfig, networkingConfig, err := runconfigopts.Parse(flags, copts)
	if err != nil {
		reportError(dockerCli.Err(), "create", err.Error(), true)
		return cli.StatusError{StatusCode: 125}
	}
	response, err := createContainer(context.Background(), dockerCli, config, hostConfig, networkingConfig, hostConfig.ContainerIDFile, opts.name)
	if err != nil {
		return err
	}
	fmt.Fprintf(dockerCli.Out(), "%s\n", response.ID)
	return nil
}

func pullImage(ctx context.Context, dockerCli *command.DockerCli, image string, out io.Writer) error {
	ref, err := reference.ParseNamed(image)
	if err != nil {
		return err
	}

	// Resolve the Repository name from fqn to RepositoryInfo
	repoInfo, err := registry.ParseRepositoryInfo(ref)
	if err != nil {
		return err
	}

	authConfig := command.ResolveAuthConfig(ctx, dockerCli, repoInfo.Index)
	encodedAuth, err := command.EncodeAuthToBase64(authConfig)
	if err != nil {
		return err
	}

	options := types.ImageCreateOptions{
		RegistryAuth: encodedAuth,
	}

	responseBody, err := dockerCli.Client().ImageCreate(ctx, image, options)
	if err != nil {
		return err
	}
	defer responseBody.Close()

	return jsonmessage.DisplayJSONMessagesStream(
		responseBody,
		out,
		dockerCli.Out().FD(),
		dockerCli.Out().IsTerminal(),
		nil)
}

type cidFile struct {
	path    string
	file    *os.File
	written bool
}

func (cid *cidFile) Close() error {
	cid.file.Close()

	if !cid.written {
		if err := os.Remove(cid.path); err != nil {
			return fmt.Errorf("failed to remove the CID file '%s': %s \n", cid.path, err)
		}
	}

	return nil
}

func (cid *cidFile) Write(id string) error {
	if _, err := cid.file.Write([]byte(id)); err != nil {
		return fmt.Errorf("Failed to write the container ID to the file: %s", err)
	}
	cid.written = true
	return nil
}

func newCIDFile(path string) (*cidFile, error) {
	if _, err := os.Stat(path); err == nil {
		return nil, fmt.Errorf("Container ID file found, make sure the other container isn't running or delete %s", path)
	}

	f, err := os.Create(path)
	if err != nil {
		return nil, fmt.Errorf("Failed to create the container ID file: %s", err)
	}

	return &cidFile{path: path, file: f}, nil
}

func createContainer(ctx context.Context, dockerCli *command.DockerCli, config *container.Config, hostConfig *container.HostConfig, networkingConfig *networktypes.NetworkingConfig, cidfile, name string) (*container.ContainerCreateCreatedBody, error) {
	stderr := dockerCli.Err()

	var containerIDFile *cidFile
	if cidfile != "" {
		var err error
		if containerIDFile, err = newCIDFile(cidfile); err != nil {
			return nil, err
		}
		defer containerIDFile.Close()
	}

	var trustedRef reference.Canonical
	_, ref, err := reference.ParseIDOrReference(config.Image)
	if err != nil {
		return nil, err
	}
	if ref != nil {
		ref = reference.WithDefaultTag(ref)

		if ref, ok := ref.(reference.NamedTagged); ok && command.IsTrusted() {
			var err error
			trustedRef, err = image.TrustedReference(ctx, dockerCli, ref)
			if err != nil {
				return nil, err
			}
			config.Image = trustedRef.String()
		}
	}

	//create the container
	response, err := dockerCli.Client().ContainerCreate(ctx, config, hostConfig, networkingConfig, name)

	//if image not found try to pull it
	if err != nil {
		if apiclient.IsErrImageNotFound(err) && ref != nil {
			fmt.Fprintf(stderr, "Unable to find image '%s' locally\n", ref.String())

			// we don't want to write to stdout anything apart from container.ID
			if err = pullImage(ctx, dockerCli, config.Image, stderr); err != nil {
				return nil, err
			}
			if ref, ok := ref.(reference.NamedTagged); ok && trustedRef != nil {
				if err := image.TagTrusted(ctx, dockerCli, trustedRef, ref); err != nil {
					return nil, err
				}
			}
			// Retry
			var retryErr error
			response, retryErr = dockerCli.Client().ContainerCreate(ctx, config, hostConfig, networkingConfig, name)
			if retryErr != nil {
				return nil, retryErr
			}
		} else {
			return nil, err
		}
	}

	for _, warning := range response.Warnings {
		fmt.Fprintf(stderr, "WARNING: %s\n", warning)
	}
	if containerIDFile != nil {
		if err = containerIDFile.Write(response.ID); err != nil {
			return nil, err
		}
	}
	return &response, nil
}
<|endoftext|>


<commit_before>package imath

import (
	"testing"
)

func TestSqrt(t *testing.T) {
	t.Log(Sqrt(25))
	t.Log(Sqrt(24))
	t.Log(Sqrt(400))
	t.Log(Sqrt(399))
}
<commit_msg>fix use assert instead of log<commit_after>package imath_test

import (
	"testing"

	"github.com/stretchr/testify/assert"

	"github.com/EngoEngine/engo/math/imath"
)

func TestSqrt(t *testing.T) {
	assert.Equal(t, 5, imath.Sqrt(25))
	assert.Equal(t, 4, imath.Sqrt(24))
	assert.Equal(t, 20, imath.Sqrt(400))
	assert.Equal(t, 19, imath.Sqrt(399))
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"

	stdlog "log"
	"runtime"

	log "github.com/sirupsen/logrus"
	"github.com/xshellinc/iotit/device"
	"github.com/xshellinc/iotit/repo"
	"github.com/xshellinc/iotit/workstation"
	"github.com/xshellinc/tools/dialogs"
	"github.com/xshellinc/tools/lib/help"
	"github.com/xshellinc/tools/lib/sudo"
	"gopkg.in/urfave/cli.v1"
)

const progName = "iotit"
const installPath = "/usr/local/bin/"

// version string came from linker
var version = "latest"

// Env string came from linker
var Env = "dev"

var logfile = ""

func init() {
	log.SetLevel(log.WarnLevel)
	if Env == "dev" || runtime.GOOS == "windows" {
		log.SetLevel(log.DebugLevel)
	}
	logfile = fmt.Sprintf(help.GetTempDir()+help.Separator()+"%s.log", progName)

	f, err := os.OpenFile(logfile, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0666)
	if err != nil {
		log.Errorf("error opening file: %v", err)
		return
	}

	log.SetOutput(f)
	stdlog.SetOutput(f)
}

func main() {
	app := cli.NewApp()
	app.Version = version
	app.Name = progName
	app.Usage = "Flashing Tool for IoT devices used by Isaax Cloud"

	app.Action = func(c *cli.Context) error {
		// TODO: launch gui by default
		flasher := device.New(c)
		if flasher == nil {
			return nil
		}
		if err := flasher.Flash(); err != nil {
			fmt.Println("[-] Error: ", err)
			return err
		}
		return nil
	}

	app.Commands = []cli.Command{
		{
			Name:    "flash",
			Aliases: []string{"f"},
			Usage:   "Flash image to the device",
			Flags: []cli.Flag{
				cli.BoolFlag{Name: "quiet, unattended, q", Usage: "Suppress questions and assume default answers"},
				cli.StringFlag{Name: "disk, d", Usage: "External disk or usb device"},
				cli.StringFlag{Name: "port, p", Usage: "Serial port for connected device. " +
					"If set to 'auto' first port will be used."},
			},
			ArgsUsage: "[device image]",
			Action: func(c *cli.Context) error {
				if c.Args().Get(0) == "help" {
					cli.ShowCommandHelp(c, "flash")
					return nil
				}
				flasher := device.New(c)
				if flasher == nil {
					return nil
				}
				if err := flasher.Flash(); err != nil {
					return err
				}
				return nil
			},
		},
		{
			Name:    "configure",
			Aliases: []string{"c"},
			Usage:   "Configure image or device",
			Flags: []cli.Flag{
				cli.StringFlag{Name: "disk, d", Usage: "External disk or usb device"},
				cli.StringFlag{Name: "port, p", Usage: "Serial port for connected device. " +
					"If set to 'auto' first port will be used."},
			},
			ArgsUsage: "[device image]",
			Action: func(c *cli.Context) error {
				flasher := device.New(c)
				if flasher == nil {
					return nil
				}
				if err := flasher.Configure(); err != nil {
					return err
				}
				return nil
			},
		},
		{
			Name:    "write",
			Aliases: []string{"w"},
			Usage:   "Write image to SD or eMMC",
			Flags: []cli.Flag{
				cli.BoolFlag{Name: "flash, f", Usage: "Flash ready image"},
				cli.StringFlag{Name: "image, i", Usage: "Image path"},
				cli.StringFlag{Name: "disk, d", Usage: "External disk or usb device"},
				cli.StringFlag{Name: "port, p", Usage: "Serial port for connected device. " +
					"If set to 'auto' first port will be used."},
			},
			ArgsUsage: "[device image]",
			Action: func(c *cli.Context) error {
				flasher := device.New(c)
				if flasher == nil {
					return nil
				}
				if err := flasher.Write(); err != nil {
					return err
				}
				return nil
			},
		},
		{
			Name:    "list",
			Aliases: []string{"ls"},
			Usage:   "List images, disks, ports",
			Subcommands: []cli.Command{
				{
					Name:  "devices",
					Usage: "List supported devices and images",
					Action: func(c *cli.Context) error {
						list := device.ListMapping()
						fmt.Println("Devices and images listed as \"name (" + dialogs.PrintColored("alias") + ")\"")
						for _, item := range list {
							fmt.Print("Type: " + item.Title)
							if len(item.Alias) > 0 {
								fmt.Print(" (" + dialogs.PrintColored(item.Alias) + ")")
							}
							fmt.Println()
							if len(item.Models) == 0 {
								fmt.Print("\tImages: ")
								for title, alias := range item.Images {
									fmt.Print(title)
									if len(alias) > 0 {
										fmt.Print(" (" + dialogs.PrintColored(alias) + ") ")
									}
								}
								fmt.Println()
							} else {
								for _, sub := range item.Models {
									fmt.Print("\tModel: " + sub.Title)
									if len(sub.Alias) > 0 {
										fmt.Print(" (" + dialogs.PrintColored(sub.Alias) + ")")
									}
									fmt.Println()
									fmt.Print("\t\tImages: ")
									for title, alias := range sub.Images {
										fmt.Print(title)
										if len(alias) > 0 {
											fmt.Print(" (" + dialogs.PrintColored(alias) + ") ")
										}
									}
									fmt.Println()
								}
							}
						}
						fmt.Println(dialogs.PrintColored("Examples"))
						fmt.Println("\tiotit flash raspi lite")
						fmt.Println("\tiotit flash nanopi2 android")
						fmt.Println("\tiotit flash esp32")
						return nil
					},
				},
				{
					Name:  "disks",
					Usage: "List external disks",
					Action: func(c *cli.Context) error {
						w := workstation.NewWorkStation("")
						w.PrintDisks()
						return nil
					},
				},
			},
		},
		{
			Name:    "install",
			Aliases: []string{"i"},
			Usage:   "Install to global app environment",
			Action: func(c *cli.Context) error {
				log.Debug("Checking ", installPath, progName)
				if _, err := os.Stat(installPath + progName); os.IsNotExist(err) {
					p, err := os.Executable()
					if err != nil {
						log.Fatal("[-] ", err)
					}

					fmt.Println("[+] You may need to enter your user password")
					_, eut, err := sudo.Exec(sudo.InputMaskedPassword, nil, "cp", p, installPath+progName)
					fmt.Println("[+] Copying", p, installPath+progName)
					if err != nil {
						fmt.Println("[-] Error: ", string(eut))
						return nil
					}

					fmt.Println("[+] Done")
					return nil
				}

				fmt.Println("[+] Software is already installed")
				return nil
			},
		},
		{
			Name:    "uninstall",
			Aliases: []string{"rm"},
			Usage:   "Uninstall iotit",
			Action: func(c *cli.Context) error {
				log.Debug("Checking ", installPath, progName)
				if _, err := os.Stat(installPath + progName); os.IsNotExist(err) {
					fmt.Println("[+] Software is not installed")
					return nil
				}

				fmt.Println("[+] You may need to enter your user password")
				_, eut, err := sudo.Exec(sudo.InputMaskedPassword, nil, "rm", installPath+progName)
				fmt.Println("[+] Removing", installPath+progName)
				if err != nil {
					fmt.Println("[-] Error: ", string(eut))
					return nil
				}

				fmt.Println("[+] Done")
				return nil
			},
		},
		{
			Name:    "update",
			Aliases: []string{"u"},
			Usage:   "Self-update",
			Action: func(c *cli.Context) error {
				if _, err := os.Stat(installPath + progName); os.IsNotExist(err) {
					fmt.Println("[-] Software is not installed, please install it globally first: `" + progName + " gl`")
					return nil
				}
				fmt.Println("[+] Current os: ", runtime.GOOS, runtime.GOARCH)
				dir, err := repo.DownloadNewVersion(progName, version, help.GetTempDir())

				if err != nil {
					fmt.Println("[-] Error:", err)
					return nil
				}

				if dir == "" {
					fmt.Println("[+] ", progName, " is up to date")
				} else {
					fmt.Println("[+] You may need to enter your user password")
					if _, eut, err := sudo.Exec(sudo.InputMaskedPassword, nil, "mv", dir, installPath+progName); err != nil {
						fmt.Println("[-] Error:", eut)
						return nil
					}
					fmt.Println("[+]", progName, " is updated")
				}

				return nil
			},
		},
		{
			Name:    "log",
			Aliases: []string{"l"},
			Usage:   "Show log file location",
			Action: func(c *cli.Context) error {
				fmt.Println("Log location:", logfile)
				return nil
			},
		},
	}

	if runtime.GOOS == "windows" {
		clean := cli.Command{
			Name:  "clean",
			Usage: "*Windows only* Clean SD card partition table",
			Action: func(c *cli.Context) error {
				w := workstation.NewWorkStation("")
				if err := w.CleanDisk(""); err != nil {
					fmt.Println("[-] Error:", err)
					return nil
				}
				fmt.Println("[+] Disk formatted, now please reconnect the device.")
				return nil
			},
		}
		app.Commands = append(app.Commands, clean)
	}

	app.Run(os.Args)
}
<commit_msg>Disable self-updater until it’s fixed<commit_after>package main

import (
	"fmt"
	"os"

	stdlog "log"
	"runtime"

	log "github.com/sirupsen/logrus"
	"github.com/xshellinc/iotit/device"
	// "github.com/xshellinc/iotit/repo"
	"github.com/xshellinc/iotit/workstation"
	"github.com/xshellinc/tools/dialogs"
	"github.com/xshellinc/tools/lib/help"
	"github.com/xshellinc/tools/lib/sudo"
	"gopkg.in/urfave/cli.v1"
)

const progName = "iotit"
const installPath = "/usr/local/bin/"

// version string came from linker
var version = "latest"

// Env string came from linker
var Env = "dev"

var logfile = ""

func init() {
	log.SetLevel(log.WarnLevel)
	if Env == "dev" || runtime.GOOS == "windows" {
		log.SetLevel(log.DebugLevel)
	}
	logfile = fmt.Sprintf(help.GetTempDir()+help.Separator()+"%s.log", progName)

	f, err := os.OpenFile(logfile, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0666)
	if err != nil {
		log.Errorf("error opening file: %v", err)
		return
	}

	log.SetOutput(f)
	stdlog.SetOutput(f)
}

func main() {
	app := cli.NewApp()
	app.Version = version
	app.Name = progName
	app.Usage = "Flashing Tool for IoT devices used by Isaax Cloud"

	app.Action = func(c *cli.Context) error {
		// TODO: launch gui by default
		flasher := device.New(c)
		if flasher == nil {
			return nil
		}
		if err := flasher.Flash(); err != nil {
			fmt.Println("[-] Error: ", err)
			return err
		}
		return nil
	}

	app.Commands = []cli.Command{
		{
			Name:    "flash",
			Aliases: []string{"f"},
			Usage:   "Flash image to the device",
			Flags: []cli.Flag{
				cli.BoolFlag{Name: "quiet, unattended, q", Usage: "Suppress questions and assume default answers"},
				cli.StringFlag{Name: "disk, d", Usage: "External disk or usb device"},
				cli.StringFlag{Name: "port, p", Usage: "Serial port for connected device. " +
					"If set to 'auto' first port will be used."},
			},
			ArgsUsage: "[device image]",
			Action: func(c *cli.Context) error {
				if c.Args().Get(0) == "help" {
					cli.ShowCommandHelp(c, "flash")
					return nil
				}
				flasher := device.New(c)
				if flasher == nil {
					return nil
				}
				if err := flasher.Flash(); err != nil {
					return err
				}
				return nil
			},
		},
		{
			Name:    "configure",
			Aliases: []string{"c"},
			Usage:   "Configure image or device",
			Flags: []cli.Flag{
				cli.StringFlag{Name: "disk, d", Usage: "External disk or usb device"},
				cli.StringFlag{Name: "port, p", Usage: "Serial port for connected device. " +
					"If set to 'auto' first port will be used."},
			},
			ArgsUsage: "[device image]",
			Action: func(c *cli.Context) error {
				flasher := device.New(c)
				if flasher == nil {
					return nil
				}
				if err := flasher.Configure(); err != nil {
					return err
				}
				return nil
			},
		},
		{
			Name:    "write",
			Aliases: []string{"w"},
			Usage:   "Write image to SD or eMMC",
			Flags: []cli.Flag{
				cli.BoolFlag{Name: "flash, f", Usage: "Flash ready image"},
				cli.StringFlag{Name: "image, i", Usage: "Image path"},
				cli.StringFlag{Name: "disk, d", Usage: "External disk or usb device"},
				cli.StringFlag{Name: "port, p", Usage: "Serial port for connected device. " +
					"If set to 'auto' first port will be used."},
			},
			ArgsUsage: "[device image]",
			Action: func(c *cli.Context) error {
				flasher := device.New(c)
				if flasher == nil {
					return nil
				}
				if err := flasher.Write(); err != nil {
					return err
				}
				return nil
			},
		},
		{
			Name:    "list",
			Aliases: []string{"ls"},
			Usage:   "List images, disks, ports",
			Subcommands: []cli.Command{
				{
					Name:  "devices",
					Usage: "List supported devices and images",
					Action: func(c *cli.Context) error {
						list := device.ListMapping()
						fmt.Println("Devices and images listed as \"name (" + dialogs.PrintColored("alias") + ")\"")
						for _, item := range list {
							fmt.Print("Type: " + item.Title)
							if len(item.Alias) > 0 {
								fmt.Print(" (" + dialogs.PrintColored(item.Alias) + ")")
							}
							fmt.Println()
							if len(item.Models) == 0 {
								fmt.Print("\tImages: ")
								for title, alias := range item.Images {
									fmt.Print(title)
									if len(alias) > 0 {
										fmt.Print(" (" + dialogs.PrintColored(alias) + ") ")
									}
								}
								fmt.Println()
							} else {
								for _, sub := range item.Models {
									fmt.Print("\tModel: " + sub.Title)
									if len(sub.Alias) > 0 {
										fmt.Print(" (" + dialogs.PrintColored(sub.Alias) + ")")
									}
									fmt.Println()
									fmt.Print("\t\tImages: ")
									for title, alias := range sub.Images {
										fmt.Print(title)
										if len(alias) > 0 {
											fmt.Print(" (" + dialogs.PrintColored(alias) + ") ")
										}
									}
									fmt.Println()
								}
							}
						}
						fmt.Println(dialogs.PrintColored("Examples"))
						fmt.Println("\tiotit flash raspi lite")
						fmt.Println("\tiotit flash nanopi2 android")
						fmt.Println("\tiotit flash esp32")
						return nil
					},
				},
				{
					Name:  "disks",
					Usage: "List external disks",
					Action: func(c *cli.Context) error {
						w := workstation.NewWorkStation("")
						w.PrintDisks()
						return nil
					},
				},
			},
		},
		{
			Name:    "install",
			Aliases: []string{"i"},
			Usage:   "Install to global app environment",
			Action: func(c *cli.Context) error {
				log.Debug("Checking ", installPath, progName)
				if _, err := os.Stat(installPath + progName); os.IsNotExist(err) {
					p, err := os.Executable()
					if err != nil {
						log.Fatal("[-] ", err)
					}

					fmt.Println("[+] You may need to enter your user password")
					_, eut, err := sudo.Exec(sudo.InputMaskedPassword, nil, "cp", p, installPath+progName)
					fmt.Println("[+] Copying", p, installPath+progName)
					if err != nil {
						fmt.Println("[-] Error: ", string(eut))
						return nil
					}

					fmt.Println("[+] Done")
					return nil
				}

				fmt.Println("[+] Software is already installed")
				return nil
			},
		},
		{
			Name:    "uninstall",
			Aliases: []string{"rm"},
			Usage:   "Uninstall iotit",
			Action: func(c *cli.Context) error {
				log.Debug("Checking ", installPath, progName)
				if _, err := os.Stat(installPath + progName); os.IsNotExist(err) {
					fmt.Println("[+] Software is not installed")
					return nil
				}

				fmt.Println("[+] You may need to enter your user password")
				_, eut, err := sudo.Exec(sudo.InputMaskedPassword, nil, "rm", installPath+progName)
				fmt.Println("[+] Removing", installPath+progName)
				if err != nil {
					fmt.Println("[-] Error: ", string(eut))
					return nil
				}

				fmt.Println("[+] Done")
				return nil
			},
		},
		/*
			{
				Name:    "update",
				Aliases: []string{"u"},
				Usage:   "Self-update",
				Action: func(c *cli.Context) error {
					if _, err := os.Stat(installPath + progName); os.IsNotExist(err) {
						fmt.Println("[-] Software is not installed, please install it globally first: `" + progName + " gl`")
						return nil
					}
					fmt.Println("[+] Current os: ", runtime.GOOS, runtime.GOARCH)
					dir, err := repo.DownloadNewVersion(progName, version, help.GetTempDir())

					if err != nil {
						fmt.Println("[-] Error:", err)
						return nil
					}

					if dir == "" {
						fmt.Println("[+] ", progName, " is up to date")
					} else {
						fmt.Println("[+] You may need to enter your user password")
						if _, eut, err := sudo.Exec(sudo.InputMaskedPassword, nil, "mv", dir, installPath+progName); err != nil {
							fmt.Println("[-] Error:", eut)
							return nil
						}
						fmt.Println("[+]", progName, " is updated")
					}

					return nil
				},
			},
		*/
		{
			Name:    "log",
			Aliases: []string{"l"},
			Usage:   "Show log file location",
			Action: func(c *cli.Context) error {
				fmt.Println("Log location:", logfile)
				return nil
			},
		},
	}

	if runtime.GOOS == "windows" {
		clean := cli.Command{
			Name:  "clean",
			Usage: "*Windows only* Clean SD card partition table",
			Action: func(c *cli.Context) error {
				w := workstation.NewWorkStation("")
				if err := w.CleanDisk(""); err != nil {
					fmt.Println("[-] Error:", err)
					return nil
				}
				fmt.Println("[+] Disk formatted, now please reconnect the device.")
				return nil
			},
		}
		app.Commands = append(app.Commands, clean)
	}

	app.Run(os.Args)
}
<|endoftext|>


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

package util

import (
	"os"
	"strings"

	"github.com/Masterminds/semver/v3"
	"sigs.k8s.io/yaml"

	"istio.io/pkg/log"
	"istio.io/release-builder/pkg/model"
)

func StandardEnv(manifest model.Manifest) []string {
	env := os.Environ()
	env = append(env,
		"GOPATH="+manifest.WorkDir(),
		"TAG="+manifest.Version,
		"VERSION="+manifest.Version,
		"HUB="+manifest.Docker,
		"BUILD_WITH_CONTAINER=0", // Build should already run in container, having multiple layers of docker causes issues
		"IGNORE_DIRTY_TREE=1",
		"INCLUDE_UNTAGGED_DEFAULT=true",
	)
	return env
}

func removeEnvKey(s []string, key string) []string {
	for i, v := range s {
		if strings.HasPrefix(v, key+"=") {
			return append(s[:i], s[i+1:]...)
		}
	}
	return s
}

// RunMake runs a make command for the repo, with standard environment variables set
func RunMake(manifest model.Manifest, repo string, env []string, c ...string) error {
	cmd := VerboseCommand("make", c...)
	cmd.Env = StandardEnv(manifest)
	// Unset the environment variables that are set in a container which cause `make` artifacts
	// to build in the container directories. release-builder expects all `make` artifacts to be
	// created in the manifest specified directory.
	cmd.Env = removeEnvKey(cmd.Env, "TARGET_OUT")
	cmd.Env = removeEnvKey(cmd.Env, "TARGET_OUT_LINUX")
	cmd.Env = removeEnvKey(cmd.Env, "CONTAINER_TARGET_OUT")
	cmd.Env = removeEnvKey(cmd.Env, "CONTAINER_TARGET_OUT_LINUX")
	cmd.Env = removeEnvKey(cmd.Env, "TARGET_OS")
	cmd.Env = removeEnvKey(cmd.Env, "TARGET_ARCH")
	cmd.Env = append(cmd.Env, env...)
	cmd.Stderr = os.Stderr
	cmd.Stdout = os.Stdout
	cmd.Dir = manifest.RepoDir(repo)
	log.Infof("Running make %v with env=%v wd=%v", strings.Join(c, " "), strings.Join(env, " "), cmd.Dir)
	return cmd.Run()
}

// YamlLog logs a object as yaml
func YamlLog(prefix string, i interface{}) {
	manifestYaml, _ := yaml.Marshal(i)
	log.Infof("%s: %v", prefix, string(manifestYaml))
}

// IsValidSemver checks if the string is a valid semver
// Mirror https://github.com/helm/helm/blob/9fafb4ad6811afb017cc464b630be2ff8390ac63/pkg/chart/metadata.go#L144
func IsValidSemver(v string) bool {
	_, err := semver.NewVersion(v)
	return err == nil
}
<commit_msg>Unset FOR_BUILD_CONTAINER so make happens in manifest directories (#1014)<commit_after>// Copyright Istio Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package util

import (
	"os"
	"strings"

	"github.com/Masterminds/semver/v3"
	"sigs.k8s.io/yaml"

	"istio.io/pkg/log"
	"istio.io/release-builder/pkg/model"
)

func StandardEnv(manifest model.Manifest) []string {
	env := os.Environ()
	env = append(env,
		"GOPATH="+manifest.WorkDir(),
		"TAG="+manifest.Version,
		"VERSION="+manifest.Version,
		"HUB="+manifest.Docker,
		"BUILD_WITH_CONTAINER=0", // Build should already run in container, having multiple layers of docker causes issues
		"IGNORE_DIRTY_TREE=1",
		"INCLUDE_UNTAGGED_DEFAULT=true",
	)
	return env
}

func removeEnvKey(s []string, key string) []string {
	for i, v := range s {
		if strings.HasPrefix(v, key+"=") {
			return append(s[:i], s[i+1:]...)
		}
	}
	return s
}

// RunMake runs a make command for the repo, with standard environment variables set
func RunMake(manifest model.Manifest, repo string, env []string, c ...string) error {
	cmd := VerboseCommand("make", c...)
	cmd.Env = StandardEnv(manifest)
	// Unset the environment variables that are set in a container which cause `make` artifacts
	// to build in the container directories. release-builder expects all `make` artifacts to be
	// created in the manifest specified directory.
	cmd.Env = removeEnvKey(cmd.Env, "TARGET_OUT")
	cmd.Env = removeEnvKey(cmd.Env, "TARGET_OUT_LINUX")
	cmd.Env = removeEnvKey(cmd.Env, "CONTAINER_TARGET_OUT")
	cmd.Env = removeEnvKey(cmd.Env, "CONTAINER_TARGET_OUT_LINUX")
	cmd.Env = removeEnvKey(cmd.Env, "TARGET_OS")
	cmd.Env = removeEnvKey(cmd.Env, "TARGET_ARCH")
	cmd.Env = removeEnvKey(cmd.Env, "FOR_BUILD_CONTAINER")
	cmd.Env = append(cmd.Env, env...)
	cmd.Stderr = os.Stderr
	cmd.Stdout = os.Stdout
	cmd.Dir = manifest.RepoDir(repo)
	log.Infof("Running make %v with env=%v wd=%v", strings.Join(c, " "), strings.Join(env, " "), cmd.Dir)
	return cmd.Run()
}

// YamlLog logs a object as yaml
func YamlLog(prefix string, i interface{}) {
	manifestYaml, _ := yaml.Marshal(i)
	log.Infof("%s: %v", prefix, string(manifestYaml))
}

// IsValidSemver checks if the string is a valid semver
// Mirror https://github.com/helm/helm/blob/9fafb4ad6811afb017cc464b630be2ff8390ac63/pkg/chart/metadata.go#L144
func IsValidSemver(v string) bool {
	_, err := semver.NewVersion(v)
	return err == nil
}
<|endoftext|>