<commit_before>// Copyright 2019 Google LLC
//
// 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
//
//     https://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 fleetspeak provides administrative functionality for Fleetspeak.
package fleetspeak

import (
	"context"
	"errors"
	"fmt"
	"math/rand"
	"sort"
	"strings"
	"time"

	"github.com/golang/protobuf/ptypes"
	"github.com/google/fleetspeak/fleetspeak/src/common"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"

	log "github.com/golang/glog"
	spb "github.com/google/emitto/source/sensor/proto"
	fspb "github.com/google/fleetspeak/fleetspeak/src/common/proto/fleetspeak"
	fsspb "github.com/google/fleetspeak/fleetspeak/src/server/proto/fleetspeak_server"
)

const (
	// Service name for Fleetspeak messages.
	serviceName = "Emitto"
	dateFmt     = "15:04:05.000 2006.01.02"
)

// Client represents an Fleetspeak admin client.
type Client struct {
	admin fsspb.AdminClient
	conn  *grpc.ClientConn
}

// New creates a new Client.
func New(addr, certFile string) (*Client, error) {
	var (
		conn *grpc.ClientConn
		err  error
	)
	switch {
	case certFile != "":
		cred, err := credentials.NewClientTLSFromFile(certFile, "")
		if err != nil {
			return nil, err
		}
		conn, err = grpc.Dial(addr, grpc.WithTransportCredentials(cred))
	default:
		conn, err = grpc.Dial(addr, grpc.WithInsecure())
	}
	if err != nil {
		return nil, fmt.Errorf("unable to connect to Fleetspeak admin interface [%s]: %v", addr, err)
	}

	return &Client{
		admin: fsspb.NewAdminClient(conn),
		conn:  conn,
	}, nil
}

// Close terminates the Fleetspeak admin connection.
func (c *Client) Close() error {
	return c.conn.Close()
}

// InsertMessage inserts a message into the Fleetspeak system to be delivered to a sensor, where
// the sensor is identified by the id.
func (c *Client) InsertMessage(ctx context.Context, req *spb.SensorRequest, id []byte) error {
	data, err := ptypes.MarshalAny(req)
	if err != nil {
		return fmt.Errorf("failed to marshal operation message (%+v): %v", req, err)
	}
	mid := make([]byte, 16)
	rand.Read(mid)
	m := &fspb.Message{
		SourceMessageId: mid,
		Source: &fspb.Address{
			ServiceName: serviceName,
		},
		Destination: &fspb.Address{
			ServiceName: serviceName,
			ClientId:    id,
		},
		Data:       data,
		Background: true,
	}
	if _, err := c.admin.InsertMessage(ctx, m); err != nil {
		return fmt.Errorf("failed to insert message for client (%X): %v", id, err)
	}
	log.Infof("Sent message (%X) to Fleetspeak", m.GetSourceMessageId())
	return nil
}

// ListClients returns a list of clients from Fleetspeak.
func (c *Client) ListClients(ctx context.Context) ([]*fsspb.Client, error) {
	res, err := c.admin.ListClients(ctx, &fsspb.ListClientsRequest{}, grpc.MaxCallRecvMsgSize(1024*1024*1024))
	if err != nil {
		return nil, fmt.Errorf("failed to list clients: %v", err)
	}
	if len(res.Clients) == 0 {
		return nil, errors.New("no clients found")
	}
	sort.Sort(byContactTime(res.Clients))
	return res.Clients, nil
}

// ParseClients returns human-readable client details.
func ParseClients(clients []*fsspb.Client) []string {
	var res []string
	for _, c := range clients {
		id, err := common.BytesToClientID(c.ClientId)
		if err != nil {
			log.Errorf("Ignoring invalid client (id=%v), %v", c.ClientId, err)
			continue
		}
		var ls []string
		for _, l := range c.Labels {
			ls = append(ls, l.ServiceName+":"+l.Label)
		}
		ts, err := ptypes.Timestamp(c.LastContactTime)
		if err != nil {
			log.Errorf("Unable to parse last contact time for client (id=%v): %v", id, err)
		}
		tag := ""
		if c.Blacklisted {
			tag = " *blacklisted*"
		}
		res = append(res, fmt.Sprintf("%v %v [%v]%s\n", id, ts.Format(dateFmt), strings.Join(ls, ","), tag))
	}
	return res
}

// byContactTime adapts []*fspb.Client for use by sort.Sort.
type byContactTime []*fsspb.Client

func (b byContactTime) Len() int           { return len(b) }
func (b byContactTime) Swap(i, j int)      { b[i], b[j] = b[j], b[i] }
func (b byContactTime) Less(i, j int) bool { return contactTime(b[i]).Before(contactTime(b[j])) }

func contactTime(c *fsspb.Client) time.Time {
	return time.Unix(c.LastContactTime.Seconds, int64(c.LastContactTime.Nanos))
}
<commit_msg>Fixed error ineffassign lint error.<commit_after>// Copyright 2019 Google LLC
//
// 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
//
//     https://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 fleetspeak provides administrative functionality for Fleetspeak.
package fleetspeak

import (
	"context"
	"errors"
	"fmt"
	"math/rand"
	"sort"
	"strings"
	"time"

	"github.com/golang/protobuf/ptypes"
	"github.com/google/fleetspeak/fleetspeak/src/common"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"

	log "github.com/golang/glog"
	spb "github.com/google/emitto/source/sensor/proto"
	fspb "github.com/google/fleetspeak/fleetspeak/src/common/proto/fleetspeak"
	fsspb "github.com/google/fleetspeak/fleetspeak/src/server/proto/fleetspeak_server"
)

const (
	// Service name for Fleetspeak messages.
	serviceName = "Emitto"
	dateFmt     = "15:04:05.000 2006.01.02"
)

// Client represents an Fleetspeak admin client.
type Client struct {
	admin fsspb.AdminClient
	conn  *grpc.ClientConn
}

// New creates a new Client.
func New(addr, certFile string) (*Client, error) {
	var client *Client
	if certFile != "" {
		cred, err := credentials.NewClientTLSFromFile(certFile, "")
		if err != nil {
			return nil, fmt.Errorf("failed to retrieve certificate from (%q): %v", certFile, err)
		}
		conn, err := grpc.Dial(addr, grpc.WithTransportCredentials(cred))
		if err != nil {
			return nil, fmt.Errorf("unable to connect to Fleetspeak admin interface [%s] with credentials (%q): %v", addr, certFile, err)
		}
		client = &Client{
			admin: fsspb.NewAdminClient(conn),
			conn:  conn,
		}
	} else {
		conn, err := grpc.Dial(addr, grpc.WithInsecure())
		if err != nil {
			return nil, fmt.Errorf("unable to connect to Fleetspeak admin interface [%s]: %v", addr, err)
		}
		client = &Client{
			admin: fsspb.NewAdminClient(conn),
			conn:  conn,
		}
	}
	return client, nil
}

// Close terminates the Fleetspeak admin connection.
func (c *Client) Close() error {
	return c.conn.Close()
}

// InsertMessage inserts a message into the Fleetspeak system to be delivered to a sensor, where
// the sensor is identified by the id.
func (c *Client) InsertMessage(ctx context.Context, req *spb.SensorRequest, id []byte) error {
	data, err := ptypes.MarshalAny(req)
	if err != nil {
		return fmt.Errorf("failed to marshal operation message (%+v): %v", req, err)
	}
	mid := make([]byte, 16)
	rand.Read(mid)
	m := &fspb.Message{
		SourceMessageId: mid,
		Source: &fspb.Address{
			ServiceName: serviceName,
		},
		Destination: &fspb.Address{
			ServiceName: serviceName,
			ClientId:    id,
		},
		Data:       data,
		Background: true,
	}
	if _, err := c.admin.InsertMessage(ctx, m); err != nil {
		return fmt.Errorf("failed to insert message for client (%X): %v", id, err)
	}
	log.Infof("Sent message (%X) to Fleetspeak", m.GetSourceMessageId())
	return nil
}

// ListClients returns a list of clients from Fleetspeak.
func (c *Client) ListClients(ctx context.Context) ([]*fsspb.Client, error) {
	res, err := c.admin.ListClients(ctx, &fsspb.ListClientsRequest{}, grpc.MaxCallRecvMsgSize(1024*1024*1024))
	if err != nil {
		return nil, fmt.Errorf("failed to list clients: %v", err)
	}
	if len(res.Clients) == 0 {
		return nil, errors.New("no clients found")
	}
	sort.Sort(byContactTime(res.Clients))
	return res.Clients, nil
}

// ParseClients returns human-readable client details.
func ParseClients(clients []*fsspb.Client) []string {
	var res []string
	for _, c := range clients {
		id, err := common.BytesToClientID(c.ClientId)
		if err != nil {
			log.Errorf("Ignoring invalid client (id=%v), %v", c.ClientId, err)
			continue
		}
		var ls []string
		for _, l := range c.Labels {
			ls = append(ls, l.ServiceName+":"+l.Label)
		}
		ts, err := ptypes.Timestamp(c.LastContactTime)
		if err != nil {
			log.Errorf("Unable to parse last contact time for client (id=%v): %v", id, err)
		}
		tag := ""
		if c.Blacklisted {
			tag = " *blacklisted*"
		}
		res = append(res, fmt.Sprintf("%v %v [%v]%s\n", id, ts.Format(dateFmt), strings.Join(ls, ","), tag))
	}
	return res
}

// byContactTime adapts []*fspb.Client for use by sort.Sort.
type byContactTime []*fsspb.Client

func (b byContactTime) Len() int           { return len(b) }
func (b byContactTime) Swap(i, j int)      { b[i], b[j] = b[j], b[i] }
func (b byContactTime) Less(i, j int) bool { return contactTime(b[i]).Before(contactTime(b[j])) }

func contactTime(c *fsspb.Client) time.Time {
	return time.Unix(c.LastContactTime.Seconds, int64(c.LastContactTime.Nanos))
}
<|endoftext|>


<commit_before>package zclwrite

import (
	"io"

	"github.com/zclconf/go-zcl/zcl/zclsyntax"
)

// TokenGen is an abstract type that can append tokens to a list. It is the
// low-level foundation underlying the zclwrite AST; the AST provides a
// convenient abstraction over raw token sequences to facilitate common tasks,
// but it's also possible to directly manipulate the tree of token generators
// to make changes that the AST API doesn't directly allow.
type TokenGen interface {
	EachToken(TokenCallback)
}

// TokenCallback is used with TokenGen implementations to specify the action
// that is to be taken for each token in the flattened token sequence.
type TokenCallback func(*Token)

// Token is a single sequence of bytes annotated with a type. It is similar
// in purpose to zclsyntax.Token, but discards the source position information
// since that is not useful in code generation.
type Token struct {
	Type  zclsyntax.TokenType
	Bytes []byte

	// We record the number of spaces before each token so that we can
	// reproduce the exact layout of the original file when we're making
	// surgical changes in-place. When _new_ code is created it will always
	// be in the canonical style, but we preserve layout of existing code.
	SpacesBefore int
}

// Tokens is a flat list of tokens.
type Tokens []*Token

// TokenSeq combines zero or more TokenGens together to produce a flat sequence
// of tokens from a tree of TokenGens.
type TokenSeq []TokenGen

func (t *Token) EachToken(cb TokenCallback) {
	cb(t)
}

func (ts Tokens) EachToken(cb TokenCallback) {
	for _, t := range ts {
		cb(t)
	}
}

func (ts *TokenSeq) EachToken(cb TokenCallback) {
	for _, gen := range *ts {
		gen.EachToken(cb)
	}
}

// Tokens returns the flat list of tokens represented by the receiving
// token sequence.
func (ts *TokenSeq) Tokens() Tokens {
	var tokens Tokens
	ts.EachToken(func(token *Token) {
		tokens = append(tokens, token)
	})
	return tokens
}

// WriteTo takes an io.Writer and writes the bytes for each token to it,
// along with the spacing that separates each token. In other words, this
// allows serializing the tokens to a file or other such byte stream.
func (ts *TokenSeq) WriteTo(wr io.Writer) (int, error) {
	// We know we're going to be writing a lot of small chunks of repeated
	// space characters, so we'll prepare a buffer of these that we can
	// easily pass to wr.Write without any further allocation.
	spaces := make([]byte, 40)
	for i := range spaces {
		spaces[i] = ' '
	}

	var n int
	var err error
	ts.EachToken(func(token *Token) {
		if err != nil {
			return
		}

		for spacesBefore := token.SpacesBefore; spacesBefore > 0; spacesBefore -= len(spaces) {
			thisChunk := spacesBefore
			if thisChunk > len(spaces) {
				thisChunk = len(spaces)
			}
			var thisN int
			thisN, err = wr.Write(spaces[:thisChunk])
			n += thisN
			if err != nil {
				return
			}
		}

		var thisN int
		thisN, err = wr.Write(token.Bytes)
		n += thisN
	})

	return n, err
}

// TokenSeqEmpty is a TokenSeq that contains no tokens. It can be used anywhere,
// but its primary purpose is to be assigned as a replacement for a non-empty
// TokenSeq when eliminating a section of an input file.
var TokenSeqEmpty = TokenSeq([]TokenGen(nil))
<commit_msg>zclwrite: Don't crash if EachToken called on a nil TokenSeq.<commit_after>package zclwrite

import (
	"io"

	"github.com/zclconf/go-zcl/zcl/zclsyntax"
)

// TokenGen is an abstract type that can append tokens to a list. It is the
// low-level foundation underlying the zclwrite AST; the AST provides a
// convenient abstraction over raw token sequences to facilitate common tasks,
// but it's also possible to directly manipulate the tree of token generators
// to make changes that the AST API doesn't directly allow.
type TokenGen interface {
	EachToken(TokenCallback)
}

// TokenCallback is used with TokenGen implementations to specify the action
// that is to be taken for each token in the flattened token sequence.
type TokenCallback func(*Token)

// Token is a single sequence of bytes annotated with a type. It is similar
// in purpose to zclsyntax.Token, but discards the source position information
// since that is not useful in code generation.
type Token struct {
	Type  zclsyntax.TokenType
	Bytes []byte

	// We record the number of spaces before each token so that we can
	// reproduce the exact layout of the original file when we're making
	// surgical changes in-place. When _new_ code is created it will always
	// be in the canonical style, but we preserve layout of existing code.
	SpacesBefore int
}

// Tokens is a flat list of tokens.
type Tokens []*Token

// TokenSeq combines zero or more TokenGens together to produce a flat sequence
// of tokens from a tree of TokenGens.
type TokenSeq []TokenGen

func (t *Token) EachToken(cb TokenCallback) {
	cb(t)
}

func (ts Tokens) EachToken(cb TokenCallback) {
	for _, t := range ts {
		cb(t)
	}
}

func (ts *TokenSeq) EachToken(cb TokenCallback) {
	if ts == nil {
		return
	}
	for _, gen := range *ts {
		gen.EachToken(cb)
	}
}

// Tokens returns the flat list of tokens represented by the receiving
// token sequence.
func (ts *TokenSeq) Tokens() Tokens {
	var tokens Tokens
	ts.EachToken(func(token *Token) {
		tokens = append(tokens, token)
	})
	return tokens
}

// WriteTo takes an io.Writer and writes the bytes for each token to it,
// along with the spacing that separates each token. In other words, this
// allows serializing the tokens to a file or other such byte stream.
func (ts *TokenSeq) WriteTo(wr io.Writer) (int, error) {
	// We know we're going to be writing a lot of small chunks of repeated
	// space characters, so we'll prepare a buffer of these that we can
	// easily pass to wr.Write without any further allocation.
	spaces := make([]byte, 40)
	for i := range spaces {
		spaces[i] = ' '
	}

	var n int
	var err error
	ts.EachToken(func(token *Token) {
		if err != nil {
			return
		}

		for spacesBefore := token.SpacesBefore; spacesBefore > 0; spacesBefore -= len(spaces) {
			thisChunk := spacesBefore
			if thisChunk > len(spaces) {
				thisChunk = len(spaces)
			}
			var thisN int
			thisN, err = wr.Write(spaces[:thisChunk])
			n += thisN
			if err != nil {
				return
			}
		}

		var thisN int
		thisN, err = wr.Write(token.Bytes)
		n += thisN
	})

	return n, err
}

// TokenSeqEmpty is a TokenSeq that contains no tokens. It can be used anywhere,
// but its primary purpose is to be assigned as a replacement for a non-empty
// TokenSeq when eliminating a section of an input file.
var TokenSeqEmpty = TokenSeq([]TokenGen(nil))
<|endoftext|>


<commit_before>package commands

import (
	"bufio"
	"fmt"
	"github.com/jingweno/gh/cmd"
	"github.com/jingweno/gh/git"
	"github.com/jingweno/gh/github"
	"github.com/jingweno/gh/utils"
	"io"
	"io/ioutil"
	"os"
	"reflect"
	"regexp"
	"strings"
)

var cmdPullRequest = &Command{
	Run:   pullRequest,
	Usage: "pull-request [-f] [-i ISSUE] [-b BASE] [-h HEAD] [-m MESSAGE] [TITLE]",
	Short: "Open a pull request on GitHub",
	Long: `Opens a pull request on GitHub for the project that the "origin" remote
points to. The default head of the pull request is the current branch.
Both base and head of the pull request can be explicitly given in one of
the following formats: "branch", "owner:branch", "owner/repo:branch".
This command will abort operation if it detects that the current topic
branch has local commits that are not yet pushed to its upstream branch
on the remote. To skip this check, use -f.

If TITLE is omitted, a text editor will open in which title and body of
the pull request can be entered in the same manner as git commit message.

If instead of normal TITLE an issue number is given with -i, the pull
request will be attached to an existing GitHub issue. Alternatively, instead
of title you can paste a full URL to an issue on GitHub.
`,
}

var (
	flagPullRequestBase,
	flagPullRequestHead,
	flagPullRequestIssue,
	flagPullRequestMessage,
	flagPullRequestFile string
	flagPullRequestForce bool
)

func init() {
	cmdPullRequest.Flag.StringVar(&flagPullRequestBase, "b", "", "BASE")
	cmdPullRequest.Flag.StringVar(&flagPullRequestHead, "h", "", "HEAD")
	cmdPullRequest.Flag.StringVar(&flagPullRequestIssue, "i", "", "ISSUE")
	cmdPullRequest.Flag.StringVar(&flagPullRequestMessage, "m", "", "MESSAGE")
	cmdPullRequest.Flag.BoolVar(&flagPullRequestForce, "f", false, "FORCE")
	cmdPullRequest.Flag.StringVar(&flagPullRequestFile, "F", "", "FILE")
	cmdPullRequest.Flag.StringVar(&flagPullRequestFile, "file", "", "FILE")
}

/*
  # while on a topic branch called "feature":
  $ gh pull-request
  [ opens text editor to edit title & body for the request ]
  [ opened pull request on GitHub for "YOUR_USER:feature" ]

  # explicit pull base & head:
  $ gh pull-request -b jingweno:master -h jingweno:feature

  $ gh pull-request -m "title\n\nbody"
  [ create pull request with title & body  ]

  $ gh pull-request -i 123
  [ attached pull request to issue #123 ]

  $ gh pull-request https://github.com/jingweno/gh/pull/123
  [ attached pull request to issue #123 ]
*/
func pullRequest(cmd *Command, args *Args) {
	localRepo := github.LocalRepo()

	currentBranch, err := localRepo.CurrentBranch()
	utils.Check(err)

	baseProject, err := localRepo.MainProject()
	utils.Check(err)

	headProject, err := localRepo.CurrentProject()
	utils.Check(err)

	var (
		base, head           string
		force, explicitOwner bool
	)

	force = flagPullRequestForce

	if flagPullRequestBase != "" {
		baseProject, base = parsePullRequestProject(baseProject, flagPullRequestBase)
	}

	if flagPullRequestHead != "" {
		headProject, head = parsePullRequestProject(headProject, flagPullRequestHead)
		explicitOwner = strings.Contains(flagPullRequestHead, ":")
	}

	if args.ParamsSize() == 1 {
		arg := args.RemoveParam(0)
		flagPullRequestIssue = parsePullRequestIssueNumber(arg)
	}

	if base == "" {
		masterBranch, err := localRepo.MasterBranch()
		utils.Check(err)
		base = masterBranch.ShortName()
	}

	trackedBranch, _ := currentBranch.Upstream()
	if head == "" {
		if trackedBranch != nil && trackedBranch.IsRemote() {
			if reflect.DeepEqual(baseProject, headProject) && base == trackedBranch.ShortName() {
				e := fmt.Errorf(`Aborted: head branch is the same as base ("%s")`, base)
				e = fmt.Errorf("%s\n(use `-h <branch>` to specify an explicit pull request head)", e)
				utils.Check(e)
			}
		} else {
			// the current branch tracking another branch
			// pretend there's no upstream at all
			trackedBranch = nil
		}

		if trackedBranch == nil {
			head = currentBranch.ShortName()
		} else {
			head = trackedBranch.ShortName()
		}
	}

	client := github.NewClient(baseProject)

	// when no tracking, assume remote branch is published under active user's fork
	if trackedBranch == nil && !explicitOwner && client.Credentials.User != headProject.Owner {
		headProject = github.NewProject("", headProject.Name, headProject.Host)
	}

	var title, body string

	if flagPullRequestMessage != "" {
		title, body = readMsg(flagPullRequestMessage)
	}

	if flagPullRequestFile != "" {
		var (
			content []byte
			err     error
		)
		if flagPullRequestFile == "-" {
			content, err = ioutil.ReadAll(os.Stdin)
		} else {
			content, err = ioutil.ReadFile(flagPullRequestFile)
		}
		utils.Check(err)
		title, body = readMsg(string(content))
	}

	fullBase := fmt.Sprintf("%s:%s", baseProject.Owner, base)
	fullHead := fmt.Sprintf("%s:%s", headProject.Owner, head)

	commits, _ := git.RefList(base, head)
	if !force && trackedBranch != nil && len(commits) > 0 {
		err = fmt.Errorf("Aborted: %d commits are not yet pushed to %s", len(commits), trackedBranch.LongName())
		err = fmt.Errorf("%s\n(use `-f` to force submit a pull request anyway)", err)
		utils.Check(err)
	}

	if title == "" && flagPullRequestIssue == "" {
		t, b, err := writePullRequestTitleAndBody(base, head, fullBase, fullHead, commits)
		utils.Check(err)
		title = t
		body = b
	}

	if title == "" && flagPullRequestIssue == "" {
		utils.Check(fmt.Errorf("Aborting due to empty pull request title"))
	}

	var pullRequestURL string
	if args.Noop {
		args.Before(fmt.Sprintf("Would request a pull request to %s from %s", fullBase, fullHead), "")
		pullRequestURL = "PULL_REQUEST_URL"
	} else {
		if title != "" {
			pr, err := client.CreatePullRequest(base, fullHead, title, body)
			utils.Check(err)
			pullRequestURL = pr.HTMLURL
		}

		if flagPullRequestIssue != "" {
			pr, err := client.CreatePullRequestForIssue(base, fullHead, flagPullRequestIssue)
			utils.Check(err)
			pullRequestURL = pr.HTMLURL
		}
	}

	args.Replace("echo", "", pullRequestURL)
}

func writePullRequestTitleAndBody(base, head, fullBase, fullHead string, commits []string) (title, body string, err error) {
	messageFile, err := git.PullReqMsgFile()
	if err != nil {
		return
	}
	defer os.Remove(messageFile)

	err = writePullRequestChanges(base, head, fullBase, fullHead, commits, messageFile)
	if err != nil {
		return
	}

	editor, err := git.Editor()
	if err != nil {
		return
	}

	err = editTitleAndBody(editor, messageFile)
	if err != nil {
		err = fmt.Errorf("error using text editor for pull request message")
		return
	}

	title, body, err = readTitleAndBody(messageFile)
	if err != nil {
		return
	}

	return
}

func writePullRequestChanges(base, head, fullBase, fullHead string, commits []string, messageFile string) error {
	var defaultMsg, commitSummary string
	if len(commits) == 1 {
		defaultMsg, err := git.Show(commits[0])
		if err != nil {
			return err
		}
		defaultMsg = fmt.Sprintf("%s\n", defaultMsg)
	} else if len(commits) > 1 {
		commitLogs, err := git.Log(base, head)
		if err != nil {
			return err
		}

		if len(commitLogs) > 0 {
			startRegexp := regexp.MustCompilePOSIX("^")
			endRegexp := regexp.MustCompilePOSIX(" +$")

			commitLogs = strings.TrimSpace(commitLogs)
			commitLogs = startRegexp.ReplaceAllString(commitLogs, "# ")
			commitLogs = endRegexp.ReplaceAllString(commitLogs, "")
			commitSummary = `
#
# Changes:
#
%s`
			commitSummary = fmt.Sprintf(commitSummary, commitLogs)
		}
	}

	message := `%s
# Requesting a pull to %s from %s
#
# Write a message for this pull request. The first block
# of the text is the title and the rest is description.%s
`
	message = fmt.Sprintf(message, defaultMsg, fullBase, fullHead, commitSummary)

	return ioutil.WriteFile(messageFile, []byte(message), 0644)
}

func editTitleAndBody(editor, messageFile string) error {
	editCmd := cmd.New(editor)
	r := regexp.MustCompile("[mg]?vi[m]$")
	if r.MatchString(editor) {
		editCmd.WithArg("-c")
		editCmd.WithArg("set ft=gitcommit tw=0 wrap lbr")
	}
	editCmd.WithArg(messageFile)

	return editCmd.Exec()
}

func readTitleAndBody(messageFile string) (title, body string, err error) {
	f, err := os.Open(messageFile)
	defer f.Close()
	if err != nil {
		return "", "", err
	}

	reader := bufio.NewReader(f)

	return readTitleAndBodyFrom(reader)
}

func readTitleAndBodyFrom(reader *bufio.Reader) (title, body string, err error) {
	r := regexp.MustCompile("\\S")
	var titleParts, bodyParts []string

	line, err := readLine(reader)
	for err == nil {
		if strings.HasPrefix(line, "#") {
			break
		}

		if len(bodyParts) == 0 && r.MatchString(line) {
			titleParts = append(titleParts, line)
		} else {
			bodyParts = append(bodyParts, line)
		}

		line, err = readLine(reader)
	}

	if err == io.EOF {
		err = nil
	}

	title = strings.Join(titleParts, " ")
	title = strings.TrimSpace(title)

	body = strings.Join(bodyParts, "\n")
	body = strings.TrimSpace(body)

	return
}

func readLine(r *bufio.Reader) (string, error) {
	var (
		isPrefix = true
		err      error
		line, ln []byte
	)

	for isPrefix && err == nil {
		line, isPrefix, err = r.ReadLine()
		ln = append(ln, line...)
	}

	return string(ln), err
}

func readMsg(msg string) (title, body string) {
	split := strings.SplitN(msg, "\n\n", 2)
	title = strings.TrimSpace(split[0])
	if len(split) > 1 {
		body = strings.TrimSpace(split[1])
	}

	return
}

func parsePullRequestProject(context *github.Project, s string) (p *github.Project, ref string) {
	p = context
	ref = s

	if strings.Contains(s, ":") {
		split := strings.SplitN(s, ":", 2)
		ref = split[1]
		var name string
		if !strings.Contains(split[0], "/") {
			name = context.Name
		}
		p = github.NewProject(split[0], name, context.Host)
	}

	return
}

func parsePullRequestIssueNumber(url string) string {
	u, e := github.ParseURL(url)
	if e != nil {
		return ""
	}

	r := regexp.MustCompile(`^issues\/(\d+)`)
	p := u.ProjectPath()
	if r.MatchString(p) {
		return r.FindStringSubmatch(p)[1]
	}

	return ""
}
<commit_msg>Update man page<commit_after>package commands

import (
	"bufio"
	"fmt"
	"github.com/jingweno/gh/cmd"
	"github.com/jingweno/gh/git"
	"github.com/jingweno/gh/github"
	"github.com/jingweno/gh/utils"
	"io"
	"io/ioutil"
	"os"
	"reflect"
	"regexp"
	"strings"
)

var cmdPullRequest = &Command{
	Run:   pullRequest,
	Usage: "pull-request [-f] [-m <MESSAGE>|-F <FILE>|-i <ISSUE>|<ISSUE-URL>] [-b <BASE>] [-h <HEAD>] ",
	Short: "Open a pull request on GitHub",
	Long: `Opens a pull request on GitHub for the project that the "origin" remote
points to. The default head of the pull request is the current branch.
Both base and head of the pull request can be explicitly given in one of
the following formats: "branch", "owner:branch", "owner/repo:branch".
This command will abort operation if it detects that the current topic
branch has local commits that are not yet pushed to its upstream branch
on the remote. To skip this check, use "-f".

Without <MESSAGE> or <FILE>, a text editor will open in which title and body
of the pull request can be entered in the same manner as git commit message.
Pull request message can also be passed via stdin with "-F -".

If instead of normal <TITLE> an issue number is given with "-i", the pull
request will be attached to an existing GitHub issue. Alternatively, instead
of title you can paste a full URL to an issue on GitHub.
`,
}

var (
	flagPullRequestBase,
	flagPullRequestHead,
	flagPullRequestIssue,
	flagPullRequestMessage,
	flagPullRequestFile string
	flagPullRequestForce bool
)

func init() {
	cmdPullRequest.Flag.StringVar(&flagPullRequestBase, "b", "", "BASE")
	cmdPullRequest.Flag.StringVar(&flagPullRequestHead, "h", "", "HEAD")
	cmdPullRequest.Flag.StringVar(&flagPullRequestIssue, "i", "", "ISSUE")
	cmdPullRequest.Flag.StringVar(&flagPullRequestMessage, "m", "", "MESSAGE")
	cmdPullRequest.Flag.BoolVar(&flagPullRequestForce, "f", false, "FORCE")
	cmdPullRequest.Flag.StringVar(&flagPullRequestFile, "F", "", "FILE")
	cmdPullRequest.Flag.StringVar(&flagPullRequestFile, "file", "", "FILE")
}

/*
  # while on a topic branch called "feature":
  $ gh pull-request
  [ opens text editor to edit title & body for the request ]
  [ opened pull request on GitHub for "YOUR_USER:feature" ]

  # explicit pull base & head:
  $ gh pull-request -b jingweno:master -h jingweno:feature

  $ gh pull-request -m "title\n\nbody"
  [ create pull request with title & body  ]

  $ gh pull-request -i 123
  [ attached pull request to issue #123 ]

  $ gh pull-request https://github.com/jingweno/gh/pull/123
  [ attached pull request to issue #123 ]

  $ gh pull-request -F FILE
  [ create pull request with title & body from FILE ]
*/
func pullRequest(cmd *Command, args *Args) {
	localRepo := github.LocalRepo()

	currentBranch, err := localRepo.CurrentBranch()
	utils.Check(err)

	baseProject, err := localRepo.MainProject()
	utils.Check(err)

	headProject, err := localRepo.CurrentProject()
	utils.Check(err)

	var (
		base, head           string
		force, explicitOwner bool
	)

	force = flagPullRequestForce

	if flagPullRequestBase != "" {
		baseProject, base = parsePullRequestProject(baseProject, flagPullRequestBase)
	}

	if flagPullRequestHead != "" {
		headProject, head = parsePullRequestProject(headProject, flagPullRequestHead)
		explicitOwner = strings.Contains(flagPullRequestHead, ":")
	}

	if args.ParamsSize() == 1 {
		arg := args.RemoveParam(0)
		flagPullRequestIssue = parsePullRequestIssueNumber(arg)
	}

	if base == "" {
		masterBranch, err := localRepo.MasterBranch()
		utils.Check(err)
		base = masterBranch.ShortName()
	}

	trackedBranch, _ := currentBranch.Upstream()
	if head == "" {
		if trackedBranch != nil && trackedBranch.IsRemote() {
			if reflect.DeepEqual(baseProject, headProject) && base == trackedBranch.ShortName() {
				e := fmt.Errorf(`Aborted: head branch is the same as base ("%s")`, base)
				e = fmt.Errorf("%s\n(use `-h <branch>` to specify an explicit pull request head)", e)
				utils.Check(e)
			}
		} else {
			// the current branch tracking another branch
			// pretend there's no upstream at all
			trackedBranch = nil
		}

		if trackedBranch == nil {
			head = currentBranch.ShortName()
		} else {
			head = trackedBranch.ShortName()
		}
	}

	client := github.NewClient(baseProject)

	// when no tracking, assume remote branch is published under active user's fork
	if trackedBranch == nil && !explicitOwner && client.Credentials.User != headProject.Owner {
		headProject = github.NewProject("", headProject.Name, headProject.Host)
	}

	var title, body string

	if flagPullRequestMessage != "" {
		title, body = readMsg(flagPullRequestMessage)
	}

	if flagPullRequestFile != "" {
		var (
			content []byte
			err     error
		)
		if flagPullRequestFile == "-" {
			content, err = ioutil.ReadAll(os.Stdin)
		} else {
			content, err = ioutil.ReadFile(flagPullRequestFile)
		}
		utils.Check(err)
		title, body = readMsg(string(content))
	}

	fullBase := fmt.Sprintf("%s:%s", baseProject.Owner, base)
	fullHead := fmt.Sprintf("%s:%s", headProject.Owner, head)

	commits, _ := git.RefList(base, head)
	if !force && trackedBranch != nil && len(commits) > 0 {
		err = fmt.Errorf("Aborted: %d commits are not yet pushed to %s", len(commits), trackedBranch.LongName())
		err = fmt.Errorf("%s\n(use `-f` to force submit a pull request anyway)", err)
		utils.Check(err)
	}

	if title == "" && flagPullRequestIssue == "" {
		t, b, err := writePullRequestTitleAndBody(base, head, fullBase, fullHead, commits)
		utils.Check(err)
		title = t
		body = b
	}

	if title == "" && flagPullRequestIssue == "" {
		utils.Check(fmt.Errorf("Aborting due to empty pull request title"))
	}

	var pullRequestURL string
	if args.Noop {
		args.Before(fmt.Sprintf("Would request a pull request to %s from %s", fullBase, fullHead), "")
		pullRequestURL = "PULL_REQUEST_URL"
	} else {
		if title != "" {
			pr, err := client.CreatePullRequest(base, fullHead, title, body)
			utils.Check(err)
			pullRequestURL = pr.HTMLURL
		}

		if flagPullRequestIssue != "" {
			pr, err := client.CreatePullRequestForIssue(base, fullHead, flagPullRequestIssue)
			utils.Check(err)
			pullRequestURL = pr.HTMLURL
		}
	}

	args.Replace("echo", "", pullRequestURL)
}

func writePullRequestTitleAndBody(base, head, fullBase, fullHead string, commits []string) (title, body string, err error) {
	messageFile, err := git.PullReqMsgFile()
	if err != nil {
		return
	}
	defer os.Remove(messageFile)

	err = writePullRequestChanges(base, head, fullBase, fullHead, commits, messageFile)
	if err != nil {
		return
	}

	editor, err := git.Editor()
	if err != nil {
		return
	}

	err = editTitleAndBody(editor, messageFile)
	if err != nil {
		err = fmt.Errorf("error using text editor for pull request message")
		return
	}

	title, body, err = readTitleAndBody(messageFile)
	if err != nil {
		return
	}

	return
}

func writePullRequestChanges(base, head, fullBase, fullHead string, commits []string, messageFile string) error {
	var defaultMsg, commitSummary string
	if len(commits) == 1 {
		defaultMsg, err := git.Show(commits[0])
		if err != nil {
			return err
		}
		defaultMsg = fmt.Sprintf("%s\n", defaultMsg)
	} else if len(commits) > 1 {
		commitLogs, err := git.Log(base, head)
		if err != nil {
			return err
		}

		if len(commitLogs) > 0 {
			startRegexp := regexp.MustCompilePOSIX("^")
			endRegexp := regexp.MustCompilePOSIX(" +$")

			commitLogs = strings.TrimSpace(commitLogs)
			commitLogs = startRegexp.ReplaceAllString(commitLogs, "# ")
			commitLogs = endRegexp.ReplaceAllString(commitLogs, "")
			commitSummary = `
#
# Changes:
#
%s`
			commitSummary = fmt.Sprintf(commitSummary, commitLogs)
		}
	}

	message := `%s
# Requesting a pull to %s from %s
#
# Write a message for this pull request. The first block
# of the text is the title and the rest is description.%s
`
	message = fmt.Sprintf(message, defaultMsg, fullBase, fullHead, commitSummary)

	return ioutil.WriteFile(messageFile, []byte(message), 0644)
}

func editTitleAndBody(editor, messageFile string) error {
	editCmd := cmd.New(editor)
	r := regexp.MustCompile("[mg]?vi[m]$")
	if r.MatchString(editor) {
		editCmd.WithArg("-c")
		editCmd.WithArg("set ft=gitcommit tw=0 wrap lbr")
	}
	editCmd.WithArg(messageFile)

	return editCmd.Exec()
}

func readTitleAndBody(messageFile string) (title, body string, err error) {
	f, err := os.Open(messageFile)
	defer f.Close()
	if err != nil {
		return "", "", err
	}

	reader := bufio.NewReader(f)

	return readTitleAndBodyFrom(reader)
}

func readTitleAndBodyFrom(reader *bufio.Reader) (title, body string, err error) {
	r := regexp.MustCompile("\\S")
	var titleParts, bodyParts []string

	line, err := readLine(reader)
	for err == nil {
		if strings.HasPrefix(line, "#") {
			break
		}

		if len(bodyParts) == 0 && r.MatchString(line) {
			titleParts = append(titleParts, line)
		} else {
			bodyParts = append(bodyParts, line)
		}

		line, err = readLine(reader)
	}

	if err == io.EOF {
		err = nil
	}

	title = strings.Join(titleParts, " ")
	title = strings.TrimSpace(title)

	body = strings.Join(bodyParts, "\n")
	body = strings.TrimSpace(body)

	return
}

func readLine(r *bufio.Reader) (string, error) {
	var (
		isPrefix = true
		err      error
		line, ln []byte
	)

	for isPrefix && err == nil {
		line, isPrefix, err = r.ReadLine()
		ln = append(ln, line...)
	}

	return string(ln), err
}

func readMsg(msg string) (title, body string) {
	split := strings.SplitN(msg, "\n\n", 2)
	title = strings.TrimSpace(split[0])
	if len(split) > 1 {
		body = strings.TrimSpace(split[1])
	}

	return
}

func parsePullRequestProject(context *github.Project, s string) (p *github.Project, ref string) {
	p = context
	ref = s

	if strings.Contains(s, ":") {
		split := strings.SplitN(s, ":", 2)
		ref = split[1]
		var name string
		if !strings.Contains(split[0], "/") {
			name = context.Name
		}
		p = github.NewProject(split[0], name, context.Host)
	}

	return
}

func parsePullRequestIssueNumber(url string) string {
	u, e := github.ParseURL(url)
	if e != nil {
		return ""
	}

	r := regexp.MustCompile(`^issues\/(\d+)`)
	p := u.ProjectPath()
	if r.MatchString(p) {
		return r.FindStringSubmatch(p)[1]
	}

	return ""
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/fabioxgn/go-bot/cmd"
	"github.com/fabioxgn/go-bot/irc"
	. "gopkg.in/check.v1"
	"testing"
)

func Test(t *testing.T) { TestingT(t) }

type HelpSuite struct {
	Mock irc.ConnectionMock
}

var (
	_ = Suite(&HelpSuite{})
)

func (s *HelpSuite) SetUpTest(c *C) {
	cmd.Commands = make(map[string]*cmd.CustomCommand)
	s.Mock = irc.ConnectionMock{}
}

func (s *HelpSuite) TestHelpCommandNotFound(c *C) {
	channel := ""
	msg := []string{}
	s.Mock.NoticeFunc = func(target, message string) {
		channel = target
		msg = append(msg, message)
	}

	availableCommand := &cmd.CustomCommand{
		Cmd: "cmd",
	}
	cmd.RegisterCommand(availableCommand)

	command := &cmd.Cmd{
		Nick:   "nick",
		Prefix: "!",
	}
	Help(command, s.Mock)

	c.Check(channel, Equals, command.Nick)
	c.Check(msg, HasLen, 2)
	c.Check(msg[0], Equals, fmt.Sprintf(helpAboutCommand, command.Prefix))
	c.Check(msg[1], Equals, fmt.Sprintf(availableCommands, availableCommand.Cmd))
}
<commit_msg>Changed gocheck to goconvey<commit_after>package main

import (
	"fmt"
	"github.com/fabioxgn/go-bot/cmd"
	"github.com/fabioxgn/go-bot/irc"
	. "github.com/smartystreets/goconvey/convey"
	"testing"
)

func TestHelp(t *testing.T) {
	cmd.Commands = make(map[string]*cmd.CustomCommand)
	Connection := irc.ConnectionMock{}

	channel := ""
	msg := []string{}
	Connection.NoticeFunc = func(target, message string) {
		channel = target
		msg = append(msg, message)
	}

	command := &cmd.CustomCommand{
		Cmd:         "cmd",
		Description: "Command Description",
		Usage:       "Command Usage",
	}

	availableCommand := &cmd.Cmd{
		Nick:    "unavailable",
		Command: command.Cmd,
		Prefix:  "!",
	}
	unavailableCommand := &cmd.Cmd{
		Nick:    "nick",
		Command: "unavaible",
		Prefix:  "!",
	}
	cmd.RegisterCommand(command)

	Convey("Given a help command", t, func() {
		msg = []string{}

		Convey("when the command is not registered", func() {
			Help(unavailableCommand, Connection)
			Convey("should send a notice to the user with the available commands", func() {
				So(channel, ShouldEqual, unavailableCommand.Nick)
				So(msg, ShouldResemble, []string{
					fmt.Sprintf(helpAboutCommand, unavailableCommand.Prefix),
					fmt.Sprintf(availableCommands, availableCommand.Command),
				})
			})

		})

		Convey("when the command is registered", func() {
			Help(availableCommand, Connection)
			Convey("should send a notice to the user with the command's Description and Usage", func() {
				So(channel, ShouldEqual, availableCommand.Nick)
				So(msg, ShouldResemble, []string{
					fmt.Sprintf(helpDescripton, command.Description),
					fmt.Sprintf(helpUsage, availableCommand.Prefix, command.Cmd, command.Usage),
				})
			})
		})

	})

}
<|endoftext|>


<commit_before>package main_test

import (
	"bytes"
	"os"

	cli "github.com/heroku/cli"

	"github.com/lunixbochs/vtclean"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("Help", func() {
	var stdout string
	var stderr string
	exit := 9999

	BeforeEach(func() {
		cli.Stdout = new(bytes.Buffer)
		cli.Stderr = new(bytes.Buffer)
		cli.ExitFn = func(code int) { exit = code }
	})

	AfterEach(func() {
		cli.Stdout = os.Stdout
		cli.Stderr = os.Stderr
	})

	JustBeforeEach(func() {
		stdout = vtclean.Clean(cli.Stdout.(*bytes.Buffer).String(), false)
		stderr = vtclean.Clean(cli.Stderr.(*bytes.Buffer).String(), false)
	})

	Context("with no args", func() {
		BeforeEach(func() {
			cli.Start("heroku")
		})

		It("exits with code 0", func() { Expect(exit).To(Equal(0)) })
		It("shows the help", func() {
			Expect(stdout).To(HavePrefix("Usage: heroku COMMAND [--app APP] [command-specific-options]"))
		})
	})

	Context("heroku help", func() {
		BeforeEach(func() {
			cli.Start("heroku", "help")
		})

		It("exits with code 0", func() { Expect(exit).To(Equal(0)) })
		It("shows the help", func() {
			Expect(stdout).To(HavePrefix("Usage: heroku COMMAND [--app APP] [command-specific-options]"))
		})
	})

	Context("heroku hlp", func() {
		BeforeEach(func() {
			cli.Start("heroku", "hlp")
		})

		It("exits with code 2", func() { Expect(exit).To(Equal(2)) })
		It("shows invalid command message", func() {
			Expect(stderr).To(Equal(` !    hlp is not a heroku command.
 !    Perhaps you meant help.
 !    Run heroku help for a list of available commands.
`))
		})
	})

	Context("heroku help version", func() {
		BeforeEach(func() {
			cli.Start("heroku", "help", "version")
		})

		It("exits with code 0", func() { Expect(exit).To(Equal(0)) })
		It("shows help for version command", func() {
			Expect(stdout).To(HavePrefix("Usage: heroku version"))
		})
	})
})
<commit_msg>remove test for no args<commit_after>package main_test

import (
	"bytes"
	"os"

	cli "github.com/heroku/cli"

	"github.com/lunixbochs/vtclean"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("Help", func() {
	var stdout string
	var stderr string
	exit := 9999

	BeforeEach(func() {
		cli.Stdout = new(bytes.Buffer)
		cli.Stderr = new(bytes.Buffer)
		cli.ExitFn = func(code int) { exit = code }
	})

	AfterEach(func() {
		cli.Stdout = os.Stdout
		cli.Stderr = os.Stderr
	})

	JustBeforeEach(func() {
		stdout = vtclean.Clean(cli.Stdout.(*bytes.Buffer).String(), false)
		stderr = vtclean.Clean(cli.Stderr.(*bytes.Buffer).String(), false)
	})

	Context("heroku help", func() {
		BeforeEach(func() {
			cli.Start("heroku", "help")
		})

		It("exits with code 0", func() { Expect(exit).To(Equal(0)) })
		It("shows the help", func() {
			Expect(stdout).To(HavePrefix("Usage: heroku COMMAND [--app APP] [command-specific-options]"))
		})
	})

	Context("heroku hlp", func() {
		BeforeEach(func() {
			cli.Start("heroku", "hlp")
		})

		It("exits with code 2", func() { Expect(exit).To(Equal(2)) })
		It("shows invalid command message", func() {
			Expect(stderr).To(Equal(` !    hlp is not a heroku command.
 !    Perhaps you meant help.
 !    Run heroku help for a list of available commands.
`))
		})
	})

	Context("heroku help version", func() {
		BeforeEach(func() {
			cli.Start("heroku", "help", "version")
		})

		It("exits with code 0", func() { Expect(exit).To(Equal(0)) })
		It("shows help for version command", func() {
			Expect(stdout).To(HavePrefix("Usage: heroku version"))
		})
	})
})
<|endoftext|>


<commit_before>package qemu

import (
	"fmt"
	"github.com/mitchellh/multistep"
	"github.com/mitchellh/packer/packer"
	"log"
	"path/filepath"
	"strings"
)

// stepRun runs the virtual machine
type stepRun struct {
	BootDrive string
	Message   string
}

type qemuArgsTemplateData struct {
	HTTPIP    string
	HTTPPort  uint
	HTTPDir   string
	OutputDir string
	Name      string
}

func (s *stepRun) Run(state multistep.StateBag) multistep.StepAction {
	driver := state.Get("driver").(Driver)
	ui := state.Get("ui").(packer.Ui)

	ui.Say(s.Message)

	command, err := getCommandArgs(s.BootDrive, state)
	if err != nil {
		err := fmt.Errorf("Error processing QemuArggs: %s", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	if err := driver.Qemu(command...); err != nil {
		err := fmt.Errorf("Error launching VM: %s", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	return multistep.ActionContinue
}

func (s *stepRun) Cleanup(state multistep.StateBag) {
	driver := state.Get("driver").(Driver)
	ui := state.Get("ui").(packer.Ui)

	if err := driver.Stop(); err != nil {
		ui.Error(fmt.Sprintf("Error shutting down VM: %s", err))
	}
}

func getCommandArgs(bootDrive string, state multistep.StateBag) ([]string, error) {
	config := state.Get("config").(*config)
	isoPath := state.Get("iso_path").(string)
	vncPort := state.Get("vnc_port").(uint)
	sshHostPort := state.Get("sshHostPort").(uint)
	ui := state.Get("ui").(packer.Ui)

	guiArgument := "sdl"
	vnc := fmt.Sprintf("0.0.0.0:%d", vncPort-5900)
	vmName := config.VMName
	imgPath := filepath.Join(config.OutputDir,
		fmt.Sprintf("%s.%s", vmName, strings.ToLower(config.Format)))

	if config.Headless == true {
		ui.Message("WARNING: The VM will be started in headless mode, as configured.\n" +
			"In headless mode, errors during the boot sequence or OS setup\n" +
			"won't be easily visible. Use at your own discretion.")
		guiArgument = "none"
	}

	defaultArgs := make(map[string]string)
	defaultArgs["-name"] = vmName
	defaultArgs["-machine"] = fmt.Sprintf("type=pc-1.0,accel=%s", config.Accelerator)
	defaultArgs["-display"] = guiArgument
	defaultArgs["-netdev"] = "user,id=user.0"
	defaultArgs["-device"] = fmt.Sprintf("%s,netdev=user.0", config.NetDevice)
	defaultArgs["-drive"] = fmt.Sprintf("file=%s,if=%s", imgPath, config.DiskInterface)
	defaultArgs["-cdrom"] = isoPath
	defaultArgs["-boot"] = bootDrive
	defaultArgs["-m"] = "512m"
	defaultArgs["-redir"] = fmt.Sprintf("tcp:%v::22", sshHostPort)
	defaultArgs["-vnc"] = vnc

	// Determine if we have a floppy disk to attach
	if floppyPathRaw, ok := state.GetOk("floppy_path"); ok {
		defaultArgs["-fda"] = floppyPathRaw.(string)
	} else {
		log.Println("Qemu Builder has no floppy files, not attaching a floppy.")
	}

	inArgs := make(map[string][]string)
	if len(config.QemuArgs) > 0 {
		ui.Say("Overriding defaults Qemu arguments with QemuArgs...")

		httpPort := state.Get("http_port").(uint)
		tplData := qemuArgsTemplateData{
			"10.0.2.2",
			httpPort,
			config.HTTPDir,
			config.OutputDir,
			config.VMName,
		}
		newQemuArgs, err := processArgs(config.QemuArgs, config.tpl, &tplData)
		if err != nil {
			return nil, err
		}

		// because qemu supports multiple appearances of the same
		// switch, just different values, each key in the args hash
		// will have an array of string values
		for _, qemuArgs := range newQemuArgs {
			key := qemuArgs[0]
			val := strings.Join(qemuArgs[1:], "")
			if _, ok := inArgs[key]; !ok {
				inArgs[key] = make([]string, 0)
			}
			if len(val) > 0 {
				inArgs[key] = append(inArgs[key], val)
			}
		}
	}

	// get any remaining missing default args from the default settings
	for key := range defaultArgs {
		if _, ok := inArgs[key]; !ok {
			arg := make([]string, 1)
			arg[0] = defaultArgs[key]
			inArgs[key] = arg
		}
	}

	// Flatten to array of strings
	outArgs := make([]string, 0)
	for key, values := range inArgs {
		if len(values) > 0 {
			for idx := range values {
				outArgs = append(outArgs, key, values[idx])
			}
		} else {
			outArgs = append(outArgs, key)
		}
	}

	return outArgs, nil
}

func processArgs(args [][]string, tpl *packer.ConfigTemplate, tplData *qemuArgsTemplateData) ([][]string, error) {
	var err error

	if args == nil {
		return make([][]string, 0), err
	}

	newArgs := make([][]string, len(args))
	for argsIdx, rowArgs := range args {
		parms := make([]string, len(rowArgs))
		newArgs[argsIdx] = parms
		for i, parm := range rowArgs {
			parms[i], err = tpl.Process(parm, &tplData)
			if err != nil {
				return nil, err
			}
		}
	}

	return newArgs, err
}
<commit_msg>Disabling SDL gui mode<commit_after>package qemu

import (
	"fmt"
	"github.com/mitchellh/multistep"
	"github.com/mitchellh/packer/packer"
	"log"
	"path/filepath"
	"strings"
)

// stepRun runs the virtual machine
type stepRun struct {
	BootDrive string
	Message   string
}

type qemuArgsTemplateData struct {
	HTTPIP    string
	HTTPPort  uint
	HTTPDir   string
	OutputDir string
	Name      string
}

func (s *stepRun) Run(state multistep.StateBag) multistep.StepAction {
	driver := state.Get("driver").(Driver)
	ui := state.Get("ui").(packer.Ui)

	ui.Say(s.Message)

	command, err := getCommandArgs(s.BootDrive, state)
	if err != nil {
		err := fmt.Errorf("Error processing QemuArggs: %s", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	if err := driver.Qemu(command...); err != nil {
		err := fmt.Errorf("Error launching VM: %s", err)
		ui.Error(err.Error())
		return multistep.ActionHalt
	}

	return multistep.ActionContinue
}

func (s *stepRun) Cleanup(state multistep.StateBag) {
	driver := state.Get("driver").(Driver)
	ui := state.Get("ui").(packer.Ui)

	if err := driver.Stop(); err != nil {
		ui.Error(fmt.Sprintf("Error shutting down VM: %s", err))
	}
}

func getCommandArgs(bootDrive string, state multistep.StateBag) ([]string, error) {
	config := state.Get("config").(*config)
	isoPath := state.Get("iso_path").(string)
	vncPort := state.Get("vnc_port").(uint)
	sshHostPort := state.Get("sshHostPort").(uint)
	ui := state.Get("ui").(packer.Ui)

	vnc := fmt.Sprintf("0.0.0.0:%d", vncPort-5900)
	vmName := config.VMName
	imgPath := filepath.Join(config.OutputDir,
		fmt.Sprintf("%s.%s", vmName, strings.ToLower(config.Format)))

	if config.Headless == true {
		ui.Message("WARNING: The VM will be started in headless mode, as configured.\n" +
			"In headless mode, errors during the boot sequence or OS setup\n" +
			"won't be easily visible. Use at your own discretion.")
	}

	defaultArgs := make(map[string]string)
	defaultArgs["-name"] = vmName
	// defaultArgs["-machine"] = fmt.Sprintf("type=pc,accel=%s", config.Accelerator)
	// defaultArgs["-machine"] = "type=pc"
	defaultArgs["-netdev"] = "user,id=user.0"
	defaultArgs["-device"] = fmt.Sprintf("%s,netdev=user.0", config.NetDevice)
	defaultArgs["-drive"] = fmt.Sprintf("file=%s,if=%s", imgPath, config.DiskInterface)
	defaultArgs["-cdrom"] = isoPath
	defaultArgs["-boot"] = bootDrive
	defaultArgs["-m"] = "512m"
	defaultArgs["-redir"] = fmt.Sprintf("tcp:%v::22", sshHostPort)
	defaultArgs["-vnc"] = vnc

	// Determine if we have a floppy disk to attach
	if floppyPathRaw, ok := state.GetOk("floppy_path"); ok {
		defaultArgs["-fda"] = floppyPathRaw.(string)
	} else {
		log.Println("Qemu Builder has no floppy files, not attaching a floppy.")
	}

	inArgs := make(map[string][]string)
	if len(config.QemuArgs) > 0 {
		ui.Say("Overriding defaults Qemu arguments with QemuArgs...")

		httpPort := state.Get("http_port").(uint)
		tplData := qemuArgsTemplateData{
			"10.0.2.2",
			httpPort,
			config.HTTPDir,
			config.OutputDir,
			config.VMName,
		}
		newQemuArgs, err := processArgs(config.QemuArgs, config.tpl, &tplData)
		if err != nil {
			return nil, err
		}

		// because qemu supports multiple appearances of the same
		// switch, just different values, each key in the args hash
		// will have an array of string values
		for _, qemuArgs := range newQemuArgs {
			key := qemuArgs[0]
			val := strings.Join(qemuArgs[1:], "")
			if _, ok := inArgs[key]; !ok {
				inArgs[key] = make([]string, 0)
			}
			if len(val) > 0 {
				inArgs[key] = append(inArgs[key], val)
			}
		}
	}

	// get any remaining missing default args from the default settings
	for key := range defaultArgs {
		if _, ok := inArgs[key]; !ok {
			arg := make([]string, 1)
			arg[0] = defaultArgs[key]
			inArgs[key] = arg
		}
	}

	// Flatten to array of strings
	outArgs := make([]string, 0)
	for key, values := range inArgs {
		if len(values) > 0 {
			for idx := range values {
				outArgs = append(outArgs, key, values[idx])
			}
		} else {
			outArgs = append(outArgs, key)
		}
	}

	return outArgs, nil
}

func processArgs(args [][]string, tpl *packer.ConfigTemplate, tplData *qemuArgsTemplateData) ([][]string, error) {
	var err error

	if args == nil {
		return make([][]string, 0), err
	}

	newArgs := make([][]string, len(args))
	for argsIdx, rowArgs := range args {
		parms := make([]string, len(rowArgs))
		newArgs[argsIdx] = parms
		for i, parm := range rowArgs {
			parms[i], err = tpl.Process(parm, &tplData)
			if err != nil {
				return nil, err
			}
		}
	}

	return newArgs, err
}
<|endoftext|>


<commit_before>// Package edtls provides ed25519 signatures on top of TLS certificates.
//
// There is currently no standard way to use ed25519 or curve25519
// cryptographic algorithms in TLS. See drafts at
// http://ietfreport.isoc.org/idref/draft-josefsson-tls-curve25519/
// and http://ietfreport.isoc.org/idref/draft-josefsson-eddsa-ed25519/
// for standardization attempts.
//
// The way the TLS protocol is designed, it relies on centralized
// registries of algorithms. We cannot easily plug in a new kind of a
// certificate. Instead, we abuse the extension mechanism to transmit
// an extra, custom, certificate.
package edtls
<commit_msg>util/edtls: Document the crypto better<commit_after>// Package edtls provides ed25519 signatures on top of TLS certificates.
//
// There is currently no standard way to use ed25519 or curve25519
// cryptographic algorithms in TLS. See drafts at
// http://ietfreport.isoc.org/idref/draft-josefsson-tls-curve25519/
// and http://ietfreport.isoc.org/idref/draft-josefsson-eddsa-ed25519/
// for standardization attempts.
//
// The way the TLS protocol is designed, it relies on centralized
// registries of algorithms. We cannot easily plug in a new kind of a
// certificate. Instead, we abuse the extension mechanism to transmit
// an extra, custom, certificate.
//
// Client connecting to servers are expected to already know the
// ed25519 public key of the server. Clients will announce their
// public key, and the server-side logic can use that for
// authentication and access control.
//
// In both directions a "vouch" is transmitted as a TLS extension. It
// contains an ed25519 public key and a signature of the certificate
// expiry time and the DER-encoded TLS public key.
//
// If a vouch packet opens without errors, and contents match the TLS
// public key of the sender, the receiver knows that the sender
// actually owns the ed25519 public key and the TLS public key.
//
// Vouches cryptographically verify the expiry time of the TLS
// certificate, to make sure that an attacker did not manage to just
// steal the TLS private key, but also holds the ed25519 private key.
// As the TLS private key lives in the same memory space as the
// ed25519 private keys, an attack may be able to steal both, but
// off-the-shelf attacks will typically only target the TLS key.
//
// There is currently no mechanism to rotate the ed25519 keys.
package edtls
<|endoftext|>


<commit_before>//
// Copyright 2016 Authors of Cilium
//
// 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 types

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"net"
	"os"
	"sort"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/cilium/cilium/bpf/policymap"
	"github.com/cilium/cilium/common"
	"github.com/cilium/cilium/common/addressing"
)

// EPPortMap is the port mapping representation for a particular endpoint.
type EPPortMap struct {
	From  uint16 `json:"from"`
	To    uint16 `json:"to"`
	Proto uint8  `json:"proto"`
}

const (
	OptionAllowToHost         = "AllowToHost"
	OptionAllowToWorld        = "AllowToWorld"
	OptionConntrackAccounting = "ConntrackAccounting"
	OptionConntrack           = "Conntrack"
	OptionDebug               = "Debug"
	OptionDropNotify          = "DropNotification"
	OptionLearnTraffic        = "LearnTraffic"
	OptionNAT46               = "NAT46"
	OptionPolicy              = "Policy"

	maxLogs = 256
)

var (
	OptionSpecAllowToHost = Option{
		Define:      "ALLOW_TO_HOST",
		Immutable:   true,
		Description: "Allow all traffic to local host",
	}

	OptionSpecAllowToWorld = Option{
		Define:      "ALLOW_TO_WORLD",
		Immutable:   true,
		Description: "Allow all traffic to outside world",
	}

	OptionSpecConntrackAccounting = Option{
		Define:      "CONNTRACK_ACCOUNTING",
		Description: "Enable per flow (conntrack) statistics",
	}

	OptionSpecConntrack = Option{
		Define:      "CONNTRACK",
		Description: "Enable stateful connection tracking",
	}

	OptionSpecDebug = Option{
		Define:      "DEBUG",
		Description: "Enable debugging trace statements",
	}

	OptionSpecDropNotify = Option{
		Define:      "DROP_NOTIFY",
		Description: "Enable drop notifications",
	}

	OptionSpecLearnTraffic = Option{
		Define:      "LEARN_TRAFFIC",
		Description: "Learn and add labels to the list of allowed labels",
	}

	OptionSpecNAT46 = Option{
		Define:      "ENABLE_NAT46",
		Description: "Enable automatic NAT46 translation",
	}

	OptionSpecPolicy = Option{
		Define:      "POLICY_ENFORCEMENT",
		Description: "Enable policy enforcement",
	}

	EndpointMutableOptionLibrary = OptionLibrary{
		OptionConntrackAccounting: &OptionSpecConntrackAccounting,
		OptionConntrack:           &OptionSpecConntrack,
		OptionDebug:               &OptionSpecDebug,
		OptionDropNotify:          &OptionSpecDropNotify,
		OptionLearnTraffic:        &OptionSpecLearnTraffic,
		OptionNAT46:               &OptionSpecNAT46,
		OptionPolicy:              &OptionSpecPolicy,
	}

	EndpointOptionLibrary = OptionLibrary{
		OptionAllowToHost:  &OptionSpecAllowToHost,
		OptionAllowToWorld: &OptionSpecAllowToWorld,
	}
)

func init() {
	for k, v := range EndpointMutableOptionLibrary {
		EndpointOptionLibrary[k] = v
	}
}

// Endpoint contains all the details for a particular LXC and the host interface to where
// is connected to.
type Endpoint struct {
	ID               uint16                `json:"id"`                 // Endpoint ID.
	DockerID         string                `json:"docker-id"`          // Docker ID.
	DockerNetworkID  string                `json:"docker-network-id"`  // Docker network ID.
	DockerEndpointID string                `json:"docker-endpoint-id"` // Docker endpoint ID.
	IfName           string                `json:"interface-name"`     // Container's interface name.
	LXCMAC           MAC                   `json:"lxc-mac"`            // Container MAC address.
	IPv6             addressing.CiliumIPv6 `json:"ipv6"`               // Container IPv6 address.
	IPv4             addressing.CiliumIPv4 `json:"ipv4"`               // Container IPv4 address.
	IfIndex          int                   `json:"interface-index"`    // Host's interface index.
	NodeMAC          MAC                   `json:"node-mac"`           // Node MAC address.
	NodeIP           net.IP                `json:"node-ip"`            // Node IPv6 address.
	SecLabel         *SecCtxLabel          `json:"security-label"`     // Security Label  set to this endpoint.
	PortMap          []EPPortMap           `json:"port-mapping"`       // Port mapping used for this endpoint.
	Consumable       *Consumable           `json:"consumable"`
	PolicyMap        *policymap.PolicyMap  `json:"-"`
	Opts             *BoolOptions          `json:"options"` // Endpoint bpf options.
	Status           *EndpointStatus       `json:"status,omitempty"`
}

type statusLog struct {
	Status    Status    `json:"status"`
	Timestamp time.Time `json:"timestamp"`
}

type EndpointStatus struct {
	Log     [maxLogs]*statusLog `json:"log,omitempty"`
	Index   int                 `json:"index"`
	indexMU sync.RWMutex
}

func (e *EndpointStatus) String() string {
	e.indexMU.RLock()
	defer e.indexMU.RUnlock()
	lastLog := e.Log[uint8(e.Index-1)]
	if lastLog != nil {
		return fmt.Sprintf("%s", lastLog.Status.Code)
	}
	return OK.String()
}

func (e *EndpointStatus) DumpLog() string {
	e.indexMU.RLock()
	defer e.indexMU.RUnlock()
	logs := []string{}
	for i := e.Index - 1; ; i-- {
		if i < 0 {
			i = maxLogs - 1
		}
		if e.Log[i] != nil {
			logs = append(logs, fmt.Sprintf("%s - %s",
				e.Log[i].Timestamp.Format(time.RFC3339), e.Log[i].Status))
		}
		if i == e.Index {
			break
		}
	}
	if len(logs) == 0 {
		return OK.String()
	}
	return strings.Join(logs, "\n")
}

func (es *EndpointStatus) DeepCopy() *EndpointStatus {
	cpy := &EndpointStatus{}
	es.indexMU.RLock()
	defer es.indexMU.RUnlock()
	cpy.Index = es.Index
	cpy.Log = [maxLogs]*statusLog{}
	for k, v := range es.Log {
		cpy.Log[k] = v
	}
	return cpy
}

func (e *Endpoint) DeepCopy() *Endpoint {
	cpy := &Endpoint{
		ID:               e.ID,
		DockerID:         e.DockerID,
		DockerNetworkID:  e.DockerNetworkID,
		DockerEndpointID: e.DockerEndpointID,
		IfName:           e.IfName,
		LXCMAC:           make(MAC, len(e.LXCMAC)),
		IPv6:             make(addressing.CiliumIPv6, len(e.IPv6)),
		IfIndex:          e.IfIndex,
		NodeMAC:          make(MAC, len(e.NodeMAC)),
		NodeIP:           make(net.IP, len(e.NodeIP)),
		PortMap:          make([]EPPortMap, len(e.PortMap)),
	}
	copy(cpy.LXCMAC, e.LXCMAC)
	copy(cpy.IPv6, e.IPv6)
	copy(cpy.NodeMAC, e.NodeMAC)
	copy(cpy.NodeIP, e.NodeIP)
	copy(cpy.PortMap, e.PortMap)

	if e.IPv4 != nil {
		cpy.IPv4 = make(addressing.CiliumIPv4, len(e.IPv4))
		copy(cpy.IPv4, e.IPv4)
	}
	if e.SecLabel != nil {
		cpy.SecLabel = e.SecLabel.DeepCopy()
	}
	if e.Consumable != nil {
		cpy.Consumable = e.Consumable.DeepCopy()
	}
	if e.PolicyMap != nil {
		cpy.PolicyMap = e.PolicyMap.DeepCopy()
	}
	if e.Opts != nil {
		cpy.Opts = e.Opts.DeepCopy()
	}
	cpy.Status = e.Status.DeepCopy()

	return cpy
}

// SetID sets the endpoint's host local unique ID.
func (e *Endpoint) SetID() {
	e.ID = e.IPv6.EndpointID()
}

func (e *Endpoint) SetSecLabel(labels *SecCtxLabel) {
	e.SecLabel = labels
	e.Consumable = GetConsumable(labels.ID, labels)
}

func (e *Endpoint) Allows(id uint32) bool {
	if e.Consumable != nil {
		return e.Consumable.Allows(id)
	} else {
		return false
	}
}

// String returns endpoint on a JSON format.
func (e Endpoint) String() string {
	b, err := json.MarshalIndent(e, "", "  ")
	if err != nil {
		return err.Error()
	}
	return string(b)
}

func OptionChanged(key string, value bool, data interface{}) {
	e := data.(*Endpoint)
	switch key {
	case OptionConntrack:
		e.InvalidatePolicy()
	}
}

func (e *Endpoint) ApplyOpts(opts OptionMap) bool {
	// We want to be notified if the packets are dropped with the learn traffic
	if val, ok := opts[OptionLearnTraffic]; ok && val {
		opts[OptionDropNotify] = true
	}

	return e.Opts.Apply(opts, OptionChanged, e) > 0
}

func (ep *Endpoint) SetDefaultOpts(opts *BoolOptions) {
	if ep.Opts == nil {
		ep.Opts = NewBoolOptions(&EndpointOptionLibrary)
	}
	if ep.Opts.Library == nil {
		ep.Opts.Library = &EndpointOptionLibrary
	}

	if opts != nil {
		for k := range EndpointMutableOptionLibrary {
			ep.Opts.Set(k, opts.IsEnabled(k))
		}
		// Lets keep this here to prevent users to hurt themselves.
		ep.Opts.SetIfUnset(OptionLearnTraffic, false)
	}
}

type orderEndpoint func(e1, e2 *Endpoint) bool

// OrderEndpointAsc orders the slice of Endpoint in ascending ID order.
func OrderEndpointAsc(eps []Endpoint) {
	ascPriority := func(e1, e2 *Endpoint) bool {
		return e1.ID < e2.ID
	}
	orderEndpoint(ascPriority).sort(eps)
}

func (by orderEndpoint) sort(eps []Endpoint) {
	dS := &epSorter{
		eps: eps,
		by:  by,
	}
	sort.Sort(dS)
}

type epSorter struct {
	eps []Endpoint
	by  func(e1, e2 *Endpoint) bool
}

func (epS *epSorter) Len() int {
	return len(epS.eps)
}

func (epS *epSorter) Swap(i, j int) {
	epS.eps[i], epS.eps[j] = epS.eps[j], epS.eps[i]
}

func (epS *epSorter) Less(i, j int) bool {
	return epS.by(&epS.eps[i], &epS.eps[j])
}

// Base64 returns the endpoint in a base64 format.
func (ep Endpoint) Base64() (string, error) {
	jsonBytes, err := json.Marshal(ep)
	if err != nil {
		return "", err
	}
	return base64.StdEncoding.EncodeToString(jsonBytes), nil
}

// ParseBase64ToEndpoint parses the endpoint stored in the given base64 string.
func ParseBase64ToEndpoint(str string, ep *Endpoint) error {
	jsonBytes, err := base64.StdEncoding.DecodeString(str)
	if err != nil {
		return err
	}
	return json.Unmarshal(jsonBytes, ep)
}

// FilterEPDir returns a list of directories' names that possible belong to an endpoint.
func FilterEPDir(dirFiles []os.FileInfo) []string {
	eptsID := []string{}
	for _, file := range dirFiles {
		if file.IsDir() {
			if _, err := strconv.ParseUint(file.Name(), 10, 16); err == nil {
				eptsID = append(eptsID, file.Name())
			}
		}
	}
	return eptsID
}

// ParseEndpoint parses the given strEp which is in the form of:
// common.CiliumCHeaderPrefix + common.Version + ":" + endpointBase64
func ParseEndpoint(strEp string) (*Endpoint, error) {
	// TODO: Provide a better mechanism to update from old version once we bump
	// TODO: cilium version.
	strEpSlice := strings.Split(strEp, ":")
	if len(strEpSlice) != 2 {
		return nil, fmt.Errorf("invalid format %q. Should contain a single ':'", strEp)
	}
	var ep Endpoint
	if err := ParseBase64ToEndpoint(strEpSlice[1], &ep); err != nil {
		return nil, fmt.Errorf("failed to parse base64toendpoint: %s", err)
	}
	return &ep, nil
}

// IsLibnetwork returns true if the endpoint was created by Libnetwork, false otherwise.
func (e *Endpoint) IsLibnetwork() bool {
	return e.DockerNetworkID != ""
}

// IsCNI returns true if the endpoint was created by CNI, false otherwise.
func (e *Endpoint) IsCNI() bool {
	return e.DockerNetworkID == ""
}

func (e *Endpoint) PolicyMapPath() string {
	return common.PolicyMapPath + strconv.Itoa(int(e.ID))
}

func (e *Endpoint) InvalidatePolicy() {
	if e.Consumable != nil {
		// Resetting to 0 will trigger a regeneration on the next update
		log.Debugf("Invalidated policy for endpoint %s", e.ID)
		e.Consumable.Iteration = 0
	}
}

func (e *Endpoint) LogStatus(code StatusCode, msg string) {
	e.Status.indexMU.Lock()
	defer e.Status.indexMU.Unlock()
	e.Status.Log[e.Status.Index] = &statusLog{
		Status: Status{
			Code: code,
			Msg:  msg,
		},
		Timestamp: time.Now(),
	}
	e.Status.Index++
	if e.Status.Index >= maxLogs {
		e.Status.Index = 0
	}
}

func (e *Endpoint) LogStatusOK(msg string) {
	e.Status.indexMU.Lock()
	defer e.Status.indexMU.Unlock()
	e.Status.Log[e.Status.Index] = &statusLog{
		Status:    NewStatusOK(msg),
		Timestamp: time.Now(),
	}
	e.Status.Index++
	if e.Status.Index >= maxLogs {
		e.Status.Index = 0
	}
}
<commit_msg>types: fixed some possible status index overflow bugs<commit_after>//
// Copyright 2016 Authors of Cilium
//
// 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 types

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"net"
	"os"
	"sort"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/cilium/cilium/bpf/policymap"
	"github.com/cilium/cilium/common"
	"github.com/cilium/cilium/common/addressing"
)

// EPPortMap is the port mapping representation for a particular endpoint.
type EPPortMap struct {
	From  uint16 `json:"from"`
	To    uint16 `json:"to"`
	Proto uint8  `json:"proto"`
}

const (
	OptionAllowToHost         = "AllowToHost"
	OptionAllowToWorld        = "AllowToWorld"
	OptionConntrackAccounting = "ConntrackAccounting"
	OptionConntrack           = "Conntrack"
	OptionDebug               = "Debug"
	OptionDropNotify          = "DropNotification"
	OptionLearnTraffic        = "LearnTraffic"
	OptionNAT46               = "NAT46"
	OptionPolicy              = "Policy"

	maxLogs = 256
)

var (
	OptionSpecAllowToHost = Option{
		Define:      "ALLOW_TO_HOST",
		Immutable:   true,
		Description: "Allow all traffic to local host",
	}

	OptionSpecAllowToWorld = Option{
		Define:      "ALLOW_TO_WORLD",
		Immutable:   true,
		Description: "Allow all traffic to outside world",
	}

	OptionSpecConntrackAccounting = Option{
		Define:      "CONNTRACK_ACCOUNTING",
		Description: "Enable per flow (conntrack) statistics",
	}

	OptionSpecConntrack = Option{
		Define:      "CONNTRACK",
		Description: "Enable stateful connection tracking",
	}

	OptionSpecDebug = Option{
		Define:      "DEBUG",
		Description: "Enable debugging trace statements",
	}

	OptionSpecDropNotify = Option{
		Define:      "DROP_NOTIFY",
		Description: "Enable drop notifications",
	}

	OptionSpecLearnTraffic = Option{
		Define:      "LEARN_TRAFFIC",
		Description: "Learn and add labels to the list of allowed labels",
	}

	OptionSpecNAT46 = Option{
		Define:      "ENABLE_NAT46",
		Description: "Enable automatic NAT46 translation",
	}

	OptionSpecPolicy = Option{
		Define:      "POLICY_ENFORCEMENT",
		Description: "Enable policy enforcement",
	}

	EndpointMutableOptionLibrary = OptionLibrary{
		OptionConntrackAccounting: &OptionSpecConntrackAccounting,
		OptionConntrack:           &OptionSpecConntrack,
		OptionDebug:               &OptionSpecDebug,
		OptionDropNotify:          &OptionSpecDropNotify,
		OptionLearnTraffic:        &OptionSpecLearnTraffic,
		OptionNAT46:               &OptionSpecNAT46,
		OptionPolicy:              &OptionSpecPolicy,
	}

	EndpointOptionLibrary = OptionLibrary{
		OptionAllowToHost:  &OptionSpecAllowToHost,
		OptionAllowToWorld: &OptionSpecAllowToWorld,
	}
)

func init() {
	for k, v := range EndpointMutableOptionLibrary {
		EndpointOptionLibrary[k] = v
	}
}

// Endpoint contains all the details for a particular LXC and the host interface to where
// is connected to.
type Endpoint struct {
	ID               uint16                `json:"id"`                 // Endpoint ID.
	DockerID         string                `json:"docker-id"`          // Docker ID.
	DockerNetworkID  string                `json:"docker-network-id"`  // Docker network ID.
	DockerEndpointID string                `json:"docker-endpoint-id"` // Docker endpoint ID.
	IfName           string                `json:"interface-name"`     // Container's interface name.
	LXCMAC           MAC                   `json:"lxc-mac"`            // Container MAC address.
	IPv6             addressing.CiliumIPv6 `json:"ipv6"`               // Container IPv6 address.
	IPv4             addressing.CiliumIPv4 `json:"ipv4"`               // Container IPv4 address.
	IfIndex          int                   `json:"interface-index"`    // Host's interface index.
	NodeMAC          MAC                   `json:"node-mac"`           // Node MAC address.
	NodeIP           net.IP                `json:"node-ip"`            // Node IPv6 address.
	SecLabel         *SecCtxLabel          `json:"security-label"`     // Security Label  set to this endpoint.
	PortMap          []EPPortMap           `json:"port-mapping"`       // Port mapping used for this endpoint.
	Consumable       *Consumable           `json:"consumable"`
	PolicyMap        *policymap.PolicyMap  `json:"-"`
	Opts             *BoolOptions          `json:"options"` // Endpoint bpf options.
	Status           *EndpointStatus       `json:"status,omitempty"`
}

type statusLog struct {
	Status    Status    `json:"status"`
	Timestamp time.Time `json:"timestamp"`
}

type EndpointStatus struct {
	Log     [maxLogs]*statusLog `json:"log,omitempty"`
	Index   int                 `json:"index"`
	indexMU sync.RWMutex
}

func (e *EndpointStatus) lastIndex() int {
	lastIndex := e.Index - 1
	if lastIndex < 0 {
		return maxLogs - 1
	}
	return lastIndex
}

func (e *EndpointStatus) getAndIncIdx() int {
	idx := e.Index
	e.Index++
	if e.Index >= maxLogs {
		e.Index = 0
	}
	return idx
}

func (e *EndpointStatus) String() string {
	e.indexMU.RLock()
	defer e.indexMU.RUnlock()
	lastLog := e.Log[e.lastIndex()]
	if lastLog != nil {
		return fmt.Sprintf("%s", lastLog.Status.Code)
	}
	return OK.String()
}

func (e *EndpointStatus) DumpLog() string {
	e.indexMU.RLock()
	defer e.indexMU.RUnlock()
	logs := []string{}
	for i := e.lastIndex(); ; i-- {
		if i < 0 {
			i = maxLogs - 1
		}
		if e.Log[i] != nil {
			logs = append(logs, fmt.Sprintf("%s - %s",
				e.Log[i].Timestamp.Format(time.RFC3339), e.Log[i].Status))
		}
		if i == e.Index {
			break
		}
	}
	if len(logs) == 0 {
		return OK.String()
	}
	return strings.Join(logs, "\n")
}

func (es *EndpointStatus) DeepCopy() *EndpointStatus {
	cpy := &EndpointStatus{}
	es.indexMU.RLock()
	defer es.indexMU.RUnlock()
	cpy.Index = es.Index
	cpy.Log = [maxLogs]*statusLog{}
	for k, v := range es.Log {
		cpy.Log[k] = v
	}
	return cpy
}

func (e *Endpoint) DeepCopy() *Endpoint {
	cpy := &Endpoint{
		ID:               e.ID,
		DockerID:         e.DockerID,
		DockerNetworkID:  e.DockerNetworkID,
		DockerEndpointID: e.DockerEndpointID,
		IfName:           e.IfName,
		LXCMAC:           make(MAC, len(e.LXCMAC)),
		IPv6:             make(addressing.CiliumIPv6, len(e.IPv6)),
		IfIndex:          e.IfIndex,
		NodeMAC:          make(MAC, len(e.NodeMAC)),
		NodeIP:           make(net.IP, len(e.NodeIP)),
		PortMap:          make([]EPPortMap, len(e.PortMap)),
	}
	copy(cpy.LXCMAC, e.LXCMAC)
	copy(cpy.IPv6, e.IPv6)
	copy(cpy.NodeMAC, e.NodeMAC)
	copy(cpy.NodeIP, e.NodeIP)
	copy(cpy.PortMap, e.PortMap)

	if e.IPv4 != nil {
		cpy.IPv4 = make(addressing.CiliumIPv4, len(e.IPv4))
		copy(cpy.IPv4, e.IPv4)
	}
	if e.SecLabel != nil {
		cpy.SecLabel = e.SecLabel.DeepCopy()
	}
	if e.Consumable != nil {
		cpy.Consumable = e.Consumable.DeepCopy()
	}
	if e.PolicyMap != nil {
		cpy.PolicyMap = e.PolicyMap.DeepCopy()
	}
	if e.Opts != nil {
		cpy.Opts = e.Opts.DeepCopy()
	}
	cpy.Status = e.Status.DeepCopy()

	return cpy
}

// SetID sets the endpoint's host local unique ID.
func (e *Endpoint) SetID() {
	e.ID = e.IPv6.EndpointID()
}

func (e *Endpoint) SetSecLabel(labels *SecCtxLabel) {
	e.SecLabel = labels
	e.Consumable = GetConsumable(labels.ID, labels)
}

func (e *Endpoint) Allows(id uint32) bool {
	if e.Consumable != nil {
		return e.Consumable.Allows(id)
	} else {
		return false
	}
}

// String returns endpoint on a JSON format.
func (e Endpoint) String() string {
	b, err := json.MarshalIndent(e, "", "  ")
	if err != nil {
		return err.Error()
	}
	return string(b)
}

func OptionChanged(key string, value bool, data interface{}) {
	e := data.(*Endpoint)
	switch key {
	case OptionConntrack:
		e.InvalidatePolicy()
	}
}

func (e *Endpoint) ApplyOpts(opts OptionMap) bool {
	// We want to be notified if the packets are dropped with the learn traffic
	if val, ok := opts[OptionLearnTraffic]; ok && val {
		opts[OptionDropNotify] = true
	}

	return e.Opts.Apply(opts, OptionChanged, e) > 0
}

func (ep *Endpoint) SetDefaultOpts(opts *BoolOptions) {
	if ep.Opts == nil {
		ep.Opts = NewBoolOptions(&EndpointOptionLibrary)
	}
	if ep.Opts.Library == nil {
		ep.Opts.Library = &EndpointOptionLibrary
	}

	if opts != nil {
		for k := range EndpointMutableOptionLibrary {
			ep.Opts.Set(k, opts.IsEnabled(k))
		}
		// Lets keep this here to prevent users to hurt themselves.
		ep.Opts.SetIfUnset(OptionLearnTraffic, false)
	}
}

type orderEndpoint func(e1, e2 *Endpoint) bool

// OrderEndpointAsc orders the slice of Endpoint in ascending ID order.
func OrderEndpointAsc(eps []Endpoint) {
	ascPriority := func(e1, e2 *Endpoint) bool {
		return e1.ID < e2.ID
	}
	orderEndpoint(ascPriority).sort(eps)
}

func (by orderEndpoint) sort(eps []Endpoint) {
	dS := &epSorter{
		eps: eps,
		by:  by,
	}
	sort.Sort(dS)
}

type epSorter struct {
	eps []Endpoint
	by  func(e1, e2 *Endpoint) bool
}

func (epS *epSorter) Len() int {
	return len(epS.eps)
}

func (epS *epSorter) Swap(i, j int) {
	epS.eps[i], epS.eps[j] = epS.eps[j], epS.eps[i]
}

func (epS *epSorter) Less(i, j int) bool {
	return epS.by(&epS.eps[i], &epS.eps[j])
}

// Base64 returns the endpoint in a base64 format.
func (ep Endpoint) Base64() (string, error) {
	jsonBytes, err := json.Marshal(ep)
	if err != nil {
		return "", err
	}
	return base64.StdEncoding.EncodeToString(jsonBytes), nil
}

// ParseBase64ToEndpoint parses the endpoint stored in the given base64 string.
func ParseBase64ToEndpoint(str string, ep *Endpoint) error {
	jsonBytes, err := base64.StdEncoding.DecodeString(str)
	if err != nil {
		return err
	}
	return json.Unmarshal(jsonBytes, ep)
}

// FilterEPDir returns a list of directories' names that possible belong to an endpoint.
func FilterEPDir(dirFiles []os.FileInfo) []string {
	eptsID := []string{}
	for _, file := range dirFiles {
		if file.IsDir() {
			if _, err := strconv.ParseUint(file.Name(), 10, 16); err == nil {
				eptsID = append(eptsID, file.Name())
			}
		}
	}
	return eptsID
}

// ParseEndpoint parses the given strEp which is in the form of:
// common.CiliumCHeaderPrefix + common.Version + ":" + endpointBase64
func ParseEndpoint(strEp string) (*Endpoint, error) {
	// TODO: Provide a better mechanism to update from old version once we bump
	// TODO: cilium version.
	strEpSlice := strings.Split(strEp, ":")
	if len(strEpSlice) != 2 {
		return nil, fmt.Errorf("invalid format %q. Should contain a single ':'", strEp)
	}
	var ep Endpoint
	if err := ParseBase64ToEndpoint(strEpSlice[1], &ep); err != nil {
		return nil, fmt.Errorf("failed to parse base64toendpoint: %s", err)
	}
	return &ep, nil
}

// IsLibnetwork returns true if the endpoint was created by Libnetwork, false otherwise.
func (e *Endpoint) IsLibnetwork() bool {
	return e.DockerNetworkID != ""
}

// IsCNI returns true if the endpoint was created by CNI, false otherwise.
func (e *Endpoint) IsCNI() bool {
	return e.DockerNetworkID == ""
}

func (e *Endpoint) PolicyMapPath() string {
	return common.PolicyMapPath + strconv.Itoa(int(e.ID))
}

func (e *Endpoint) InvalidatePolicy() {
	if e.Consumable != nil {
		// Resetting to 0 will trigger a regeneration on the next update
		log.Debugf("Invalidated policy for endpoint %s", e.ID)
		e.Consumable.Iteration = 0
	}
}

func (e *Endpoint) LogStatus(code StatusCode, msg string) {
	e.Status.indexMU.Lock()
	defer e.Status.indexMU.Unlock()
	e.Status.Log[e.Status.getAndIncIdx()] = &statusLog{
		Status: Status{
			Code: code,
			Msg:  msg,
		},
		Timestamp: time.Now(),
	}
}

func (e *Endpoint) LogStatusOK(msg string) {
	e.Status.indexMU.Lock()
	defer e.Status.indexMU.Unlock()
	e.Status.Log[e.Status.getAndIncIdx()] = &statusLog{
		Status:    NewStatusOK(msg),
		Timestamp: time.Now(),
	}
}
<|endoftext|>


<commit_before>package util

import (
	"encoding/gob"
	"fmt"
	"io"
	"os"
	"path"
	"strconv"
	"strings"

	"github.com/TuftsBCB/fragbag"
	"github.com/TuftsBCB/fragbag/bow"
	"github.com/TuftsBCB/fragbag/bowdb"
	"github.com/TuftsBCB/hhfrag"
	"github.com/TuftsBCB/io/msa"
	"github.com/TuftsBCB/io/pdb"
	"github.com/TuftsBCB/seq"
)

func Library(path string) fragbag.Library {
	lib, err := fragbag.Open(OpenFile(path))
	Assert(err, "Could not open fragment library '%s'", path)
	return lib
}

func StructureLibrary(path string) *fragbag.StructureLibrary {
	lib, ok := Library(path).(*fragbag.StructureLibrary)
	if !ok {
		Fatalf("%s (%T) is not a structure library.", path, lib)
	}
	return lib
}

func SequenceLibrary(path string) *fragbag.SequenceLibrary {
	lib, ok := Library(path).(*fragbag.SequenceLibrary)
	if !ok {
		Fatalf("%s (%T) is not a sequence library.", path, lib)
	}
	return lib
}

func MSA(path string) seq.MSA {
	if strings.HasSuffix(path, "a2m") || strings.HasSuffix(path, "a3m") {
		aligned, err := msa.Read(OpenFile(path))
		Assert(err, "Could not read MSA (a2m/a3m) from '%s'", path)
		return aligned
	}
	aligned, err := msa.ReadFasta(OpenFile(path))
	Assert(err, "Could not read MSA (fasta) from '%s'", path)
	return aligned
}

func OpenBOWDB(path string) *bowdb.DB {
	db, err := bowdb.OpenDB(path)
	Assert(err, "Could not open BOW database '%s'", path)
	return db
}

func PDBRead(path string) *pdb.Entry {
	entry, err := pdb.ReadPDB(path)
	Assert(err, "Could not open PDB file '%s'", path)
	return entry
}

// PDBPath takes a PDB identifier (e.g., "1ctf" or "1ctfA") and returns
// the full path to the PDB file on the file system.
//
// The PDB_PATH environment variable must be set.
func PDBPath(pid string) string {
	if !IsPDBID(pid) && !IsChainID(pid) {
		Fatalf("PDB ids must contain 4 or 5 characters, but '%s' has %d.",
			pid, len(pid))
	}
	pdbPath := os.Getenv("PDB_PATH")
	if len(pdbPath) == 0 || !IsDir(pdbPath) {
		Fatalf("The PDB_PATH environment variable must be set to open " +
			"PDB chains by just their ID.\n" +
			"PDB_PATH should be set to the directory containing a full " +
			"copy of the PDB database.")
	}

	pdbid := strings.ToLower(pid[0:4])
	group := pdbid[1:3]
	basename := fmt.Sprintf("pdb%s.ent.gz", pdbid)
	return path.Join(pdbPath, group, basename)
}

func PDBReadId(pid string) (*pdb.Entry, *pdb.Chain) {
	e := PDBRead(PDBPath(pid))
	if IsChainID(pid) {
		chain := e.Chain(pid[4])
		if chain == nil {
			Fatalf("Could not find chain '%s' in PDB entry '%s'.", pid[4], pid)
		}
		return e, chain
	}
	return e, nil
}

func GetFmap(fpath string) *hhfrag.FragmentMap {
	var fmap *hhfrag.FragmentMap
	var err error

	switch {
	case IsFasta(fpath):
		fmap, err = HHfragConf.MapFromFasta(FlagPdbHhmDB, FlagSeqDB, fpath)
		Assert(err, "Could not generate map from '%s'", fpath)
	case IsFmap(fpath):
		fmap = FmapRead(fpath)
	default:
		Fatalf("File '%s' is not a fasta or fmap file.", fpath)
	}

	return fmap
}

func FmapRead(path string) *hhfrag.FragmentMap {
	var fmap *hhfrag.FragmentMap
	f := OpenFile(path)
	defer f.Close()

	r := gob.NewDecoder(f)
	Assert(r.Decode(&fmap), "Could not GOB decode fragment map '%s'", path)
	return fmap
}

func FmapWrite(w io.Writer, fmap *hhfrag.FragmentMap) {
	encoder := gob.NewEncoder(w)
	Assert(encoder.Encode(fmap), "Could not GOB encode fragment map")
}

func BOWRead(path string) bow.BOW {
	var bow bow.BOW
	f := OpenFile(path)
	defer f.Close()

	r := gob.NewDecoder(f)
	Assert(r.Decode(&bow), "Could not GOB decode BOW '%s'", path)
	return bow
}

func BOWWrite(w io.Writer, bow bow.BOW) {
	encoder := gob.NewEncoder(w)
	Assert(encoder.Encode(bow), "Could not GOB encode BOW")
}

func OpenFile(path string) *os.File {
	f, err := os.Open(path)
	Assert(err, "Could not open file '%s'", path)
	return f
}

func CreateFile(path string) *os.File {
	f, err := os.Create(path)
	Assert(err, "Could not create file '%s'", path)
	return f
}

func ParseInt(str string) int {
	num, err := strconv.ParseInt(str, 10, 32)
	Assert(err, "Could not parse '%s' as an integer", str)
	return int(num)
}

func IsFasta(fpath string) bool {
	suffix := func(ext string) bool {
		return strings.HasSuffix(fpath, ext)
	}
	return suffix(".fasta") || suffix(".fas")
}

func IsFmap(fpath string) bool {
	return strings.HasSuffix(fpath, ".fmap")
}

func IsPDB(fpath string) bool {
	suffix := func(ext string) bool {
		return strings.HasSuffix(fpath, ext)
	}
	return suffix(".ent.gz") || suffix(".pdb") || suffix(".ent")
}

func IsChainID(s string) bool {
	return len(s) == 5
}

func IsPDBID(s string) bool {
	return len(s) == 4
}
<commit_msg>Open FASTA files, even if they are compressed.<commit_after>package util

import (
	"compress/gzip"
	"encoding/gob"
	"fmt"
	"io"
	"os"
	"path"
	"strconv"
	"strings"

	"github.com/TuftsBCB/fragbag"
	"github.com/TuftsBCB/fragbag/bow"
	"github.com/TuftsBCB/fragbag/bowdb"
	"github.com/TuftsBCB/hhfrag"
	"github.com/TuftsBCB/io/msa"
	"github.com/TuftsBCB/io/pdb"
	"github.com/TuftsBCB/seq"
)

func Library(path string) fragbag.Library {
	lib, err := fragbag.Open(OpenFile(path))
	Assert(err, "Could not open fragment library '%s'", path)
	return lib
}

func StructureLibrary(path string) *fragbag.StructureLibrary {
	lib, ok := Library(path).(*fragbag.StructureLibrary)
	if !ok {
		Fatalf("%s (%T) is not a structure library.", path, lib)
	}
	return lib
}

func SequenceLibrary(path string) *fragbag.SequenceLibrary {
	lib, ok := Library(path).(*fragbag.SequenceLibrary)
	if !ok {
		Fatalf("%s (%T) is not a sequence library.", path, lib)
	}
	return lib
}

func MSA(path string) seq.MSA {
	if strings.HasSuffix(path, "a2m") || strings.HasSuffix(path, "a3m") {
		aligned, err := msa.Read(OpenFile(path))
		Assert(err, "Could not read MSA (a2m/a3m) from '%s'", path)
		return aligned
	}
	aligned, err := msa.ReadFasta(OpenFile(path))
	Assert(err, "Could not read MSA (fasta) from '%s'", path)
	return aligned
}

func OpenBOWDB(path string) *bowdb.DB {
	db, err := bowdb.OpenDB(path)
	Assert(err, "Could not open BOW database '%s'", path)
	return db
}

func PDBRead(path string) *pdb.Entry {
	entry, err := pdb.ReadPDB(path)
	Assert(err, "Could not open PDB file '%s'", path)
	return entry
}

// PDBPath takes a PDB identifier (e.g., "1ctf" or "1ctfA") and returns
// the full path to the PDB file on the file system.
//
// The PDB_PATH environment variable must be set.
func PDBPath(pid string) string {
	if !IsPDBID(pid) && !IsChainID(pid) {
		Fatalf("PDB ids must contain 4 or 5 characters, but '%s' has %d.",
			pid, len(pid))
	}
	pdbPath := os.Getenv("PDB_PATH")
	if len(pdbPath) == 0 || !IsDir(pdbPath) {
		Fatalf("The PDB_PATH environment variable must be set to open " +
			"PDB chains by just their ID.\n" +
			"PDB_PATH should be set to the directory containing a full " +
			"copy of the PDB database.")
	}

	pdbid := strings.ToLower(pid[0:4])
	group := pdbid[1:3]
	basename := fmt.Sprintf("pdb%s.ent.gz", pdbid)
	return path.Join(pdbPath, group, basename)
}

func PDBReadId(pid string) (*pdb.Entry, *pdb.Chain) {
	e := PDBRead(PDBPath(pid))
	if IsChainID(pid) {
		chain := e.Chain(pid[4])
		if chain == nil {
			Fatalf("Could not find chain '%s' in PDB entry '%s'.", pid[4], pid)
		}
		return e, chain
	}
	return e, nil
}

func GetFmap(fpath string) *hhfrag.FragmentMap {
	var fmap *hhfrag.FragmentMap
	var err error

	switch {
	case IsFasta(fpath):
		fmap, err = HHfragConf.MapFromFasta(FlagPdbHhmDB, FlagSeqDB, fpath)
		Assert(err, "Could not generate map from '%s'", fpath)
	case IsFmap(fpath):
		fmap = FmapRead(fpath)
	default:
		Fatalf("File '%s' is not a fasta or fmap file.", fpath)
	}

	return fmap
}

func FmapRead(path string) *hhfrag.FragmentMap {
	var fmap *hhfrag.FragmentMap
	f := OpenFile(path)
	defer f.Close()

	r := gob.NewDecoder(f)
	Assert(r.Decode(&fmap), "Could not GOB decode fragment map '%s'", path)
	return fmap
}

func FmapWrite(w io.Writer, fmap *hhfrag.FragmentMap) {
	encoder := gob.NewEncoder(w)
	Assert(encoder.Encode(fmap), "Could not GOB encode fragment map")
}

func BOWRead(path string) bow.BOW {
	var bow bow.BOW
	f := OpenFile(path)
	defer f.Close()

	r := gob.NewDecoder(f)
	Assert(r.Decode(&bow), "Could not GOB decode BOW '%s'", path)
	return bow
}

func BOWWrite(w io.Writer, bow bow.BOW) {
	encoder := gob.NewEncoder(w)
	Assert(encoder.Encode(bow), "Could not GOB encode BOW")
}

func OpenFile(path string) *os.File {
	f, err := os.Open(path)
	Assert(err, "Could not open file '%s'", path)
	return f
}

func CreateFile(path string) *os.File {
	f, err := os.Create(path)
	Assert(err, "Could not create file '%s'", path)
	return f
}

func ParseInt(str string) int {
	num, err := strconv.ParseInt(str, 10, 32)
	Assert(err, "Could not parse '%s' as an integer", str)
	return int(num)
}

func IsFasta(fpath string) bool {
	suffix := func(ext string) bool {
		return strings.HasSuffix(fpath, ext)
	}
	return suffix(".fasta") || suffix(".fas") ||
		suffix(".fasta.gz") || suffix(".fas.gz")
}

func OpenFasta(fpath string) io.Reader {
	if strings.HasSuffix(fpath, ".gz") {
		r, err := gzip.NewReader(OpenFile(fpath))
		Assert(err, "Could not open '%s'", fpath)
		return r
	}
	return OpenFile(fpath)
}

func IsFmap(fpath string) bool {
	return strings.HasSuffix(fpath, ".fmap")
}

func IsPDB(fpath string) bool {
	suffix := func(ext string) bool {
		return strings.HasSuffix(fpath, ext)
	}
	return suffix(".ent.gz") || suffix(".pdb") || suffix(".ent")
}

func IsChainID(s string) bool {
	return len(s) == 5
}

func IsPDBID(s string) bool {
	return len(s) == 4
}
<|endoftext|>


<commit_before>package pwr

import (
	"bytes"
	"io"
	"testing"

	"github.com/itchio/savior"
	"github.com/itchio/savior/seeksource"
	"github.com/itchio/wharf/wire"
	"github.com/stretchr/testify/assert"
)

type fakeCompressor struct {
	called  bool
	quality int32
}

var _ Compressor = (*fakeCompressor)(nil)

func (fc *fakeCompressor) Apply(writer io.Writer, quality int32) (io.Writer, error) {
	fc.called = true
	fc.quality = quality
	return writer, nil
}

type fakeDecompressor struct {
	called bool
}

var _ Decompressor = (*fakeDecompressor)(nil)

func (fd *fakeDecompressor) Apply(source savior.Source) (savior.Source, error) {
	fd.called = true
	return source, nil
}

func Test_Compression(t *testing.T) {
	fc := &fakeCompressor{}
	RegisterCompressor(CompressionAlgorithm_GZIP, fc)

	fd := &fakeDecompressor{}
	RegisterDecompressor(CompressionAlgorithm_GZIP, fd)

	assert.EqualValues(t, false, fc.called)

	buf := new(bytes.Buffer)
	wc := wire.NewWriteContext(buf)
	_, err := CompressWire(wc, &CompressionSettings{
		Algorithm: CompressionAlgorithm_BROTLI,
		Quality:   3,
	})
	assert.NotNil(t, err)

	cwc, err := CompressWire(wc, &CompressionSettings{
		Algorithm: CompressionAlgorithm_GZIP,
		Quality:   3,
	})
	assert.NoError(t, err)

	assert.EqualValues(t, true, fc.called)
	assert.EqualValues(t, 3, fc.quality)

	assert.NoError(t, cwc.WriteMessage(&SyncHeader{
		FileIndex: 672,
	}))

	ss := seeksource.FromBytes(buf.Bytes())
	_, err = ss.Resume(nil)
	assert.NoError(t, err)

	rc := wire.NewReadContext(ss)

	sh := &SyncHeader{}
	assert.NoError(t, rc.ReadMessage(sh))

	assert.EqualValues(t, 672, sh.FileIndex)
	assert.NotNil(t, rc.ReadMessage(sh))
}
<commit_msg>Fix compression test<commit_after>package pwr

import (
	"bytes"
	"io"
	"testing"

	"github.com/itchio/savior"
	"github.com/itchio/savior/seeksource"
	"github.com/itchio/wharf/wire"
	"github.com/stretchr/testify/assert"
)

type fakeCompressor struct {
	called  bool
	quality int32
}

var _ Compressor = (*fakeCompressor)(nil)

func (fc *fakeCompressor) Apply(writer io.Writer, quality int32) (io.Writer, error) {
	fc.called = true
	fc.quality = quality
	return writer, nil
}

type fakeDecompressor struct {
	called bool
}

var _ Decompressor = (*fakeDecompressor)(nil)

func (fd *fakeDecompressor) Apply(source savior.Source) (savior.Source, error) {
	fd.called = true
	return source, nil
}

func Test_Compression(t *testing.T) {
	fc := &fakeCompressor{}
	RegisterCompressor(CompressionAlgorithm_GZIP, fc)

	fd := &fakeDecompressor{}
	RegisterDecompressor(CompressionAlgorithm_GZIP, fd)

	assert.EqualValues(t, false, fc.called)

	buf := new(bytes.Buffer)
	wc := wire.NewWriteContext(buf)
	_, err := CompressWire(wc, &CompressionSettings{
		Algorithm: CompressionAlgorithm(64),
		Quality:   3,
	})
	assert.NotNil(t, err)

	cwc, err := CompressWire(wc, &CompressionSettings{
		Algorithm: CompressionAlgorithm_GZIP,
		Quality:   3,
	})
	assert.NoError(t, err)

	assert.EqualValues(t, true, fc.called)
	assert.EqualValues(t, 3, fc.quality)

	assert.NoError(t, cwc.WriteMessage(&SyncHeader{
		FileIndex: 672,
	}))

	ss := seeksource.FromBytes(buf.Bytes())
	_, err = ss.Resume(nil)
	assert.NoError(t, err)

	rc := wire.NewReadContext(ss)

	sh := &SyncHeader{}
	assert.NoError(t, rc.ReadMessage(sh))

	assert.EqualValues(t, 672, sh.FileIndex)
	assert.NotNil(t, rc.ReadMessage(sh))
}
<|endoftext|>


<commit_before>/* Copyright (C) 2013 Matthias S. Benkmann
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this file (originally named timestamp.go) and associated documentation files
 * (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is furnished
 * to do so, subject to the following conditions:
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

// Various re-usable utility functions.
package util

import "time"

// Converts t into a timestamp appropriate for use in siserver messages.
// The timestamp loses the time zone information of t. No time zone
// conversion will be performed. IOW "12:00 UTC" and "12:00 PDT" will
// both result in a timestamp that says "yyyymmdd1200ss".
func MakeTimestamp(t time.Time) string {
  return t.Format("20060102150405")
}

// Converts a timestamp as used in siserver messages into a time.Time.
// The returned time will be the time at which the server clock's current
// time converted with MakeTimestamp() is ts. The computation is based on
// the assumption that the server's time zone does not change EXCEPT for
// daylight savings time. IOW on a server running on local time in Berlin
// ParseTimestamp("20140101120000") gives 12:00 CET (winter time) and
// ParseTimestamp("20140601120000") gives 12:00 CEST (summer time).
//
// ParseTimestamp() returns time.Unix(0,0) if the timestamp is invalid.
func ParseTimestamp(ts string) time.Time {
  t, err := time.Parse("20060102150405", ts)
  if err != nil {
    Log(0, "ERROR! Illegal timestamp: %v (%v)", ts, err)
    return time.Unix(0,0)
  }

  // The timestamp doesn't contain information about the time zone it
  // is in, so time.Parse() has interpreted it as UTC. In order to
  // properly get a time in local time, we need to first determine
  // the code of the time zone active at the requested time. Then
  // we reparse with that time zone code appended.
  zone,_ := t.Local().Zone()
  ts += zone
  t, err = time.Parse("20060102150405MST", ts)
  if err != nil {
    Log(0, "ERROR! Could not parse with time zone: %v (%v)", ts, err)
    return time.Unix(0,0)
  }
  return t
}
<commit_msg>new function: util.AddTimestamp() to add e.g. "2_seconds" to "20170102130000"<commit_after>/* Copyright (C) 2013 Matthias S. Benkmann
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this file (originally named timestamp.go) and associated documentation files
 * (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is furnished
 * to do so, subject to the following conditions:
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

// Various re-usable utility functions.
package util

import "time"
import "fmt"
import "strings"
import "strconv"

// Converts t into a timestamp appropriate for use in siserver messages.
// The timestamp loses the time zone information of t. No time zone
// conversion will be performed. IOW "12:00 UTC" and "12:00 PDT" will
// both result in a timestamp that says "yyyymmdd1200ss".
func MakeTimestamp(t time.Time) string {
  return t.Format("20060102150405")
}

// Converts a timestamp as used in siserver messages into a time.Time.
// The returned time will be the time at which the server clock's current
// time converted with MakeTimestamp() is ts. The computation is based on
// the assumption that the server's time zone does not change EXCEPT for
// daylight savings time. IOW on a server running on local time in Berlin
// ParseTimestamp("20140101120000") gives 12:00 CET (winter time) and
// ParseTimestamp("20140601120000") gives 12:00 CEST (summer time).
//
// ParseTimestamp() returns time.Unix(0,0) if the timestamp is invalid.
func ParseTimestamp(ts string) time.Time {
  t, err := time.Parse("20060102150405", ts)
  if err != nil {
    Log(0, "ERROR! Illegal timestamp: %v (%v)", ts, err)
    return time.Unix(0,0)
  }

  // The timestamp doesn't contain information about the time zone it
  // is in, so time.Parse() has interpreted it as UTC. In order to
  // properly get a time in local time, we need to first determine
  // the code of the time zone active at the requested time. Then
  // we reparse with that time zone code appended.
  zone,_ := t.Local().Zone()
  ts += zone
  t, err = time.Parse("20060102150405MST", ts)
  if err != nil {
    Log(0, "ERROR! Could not parse with time zone: %v (%v)", ts, err)
    return time.Unix(0,0)
  }
  return t
}

// Takes a timestamp ts in the format produced by MakeTimestamp and
// adds to it an offset adder in the following
// format:
//   <integer>_seconds
//   <integer>_minutes
//   <integer>_hours
//   <integer>_days
//   <integer>_weeks
//   <integer>_months
//   <integer>_years
//
// where <integer> is any integer (may be negative).
// Example: AddTimestamp("20170201120000", "-3_seconds")
//          returns "20170201115957"
//
// If ts is invalid, time.Unix(0,0) is assumed and an error
// is logged (behavior of ParseTimestamp()).
// If adder is invalid, the timestamp is returned unchanged
// together with an error.
func AddTimestamp(ts string, adder string) (string, error) {
  t := ParseTimestamp(ts)
  p := strings.Split(adder, "_")
  if len(p) != 2 {
    return ts, fmt.Errorf("Invalid timestamp offset: \"%v\"", adder)
  }
  offset, err := strconv.ParseInt(p[0], 10, 64)
  if err != nil {
    return ts, fmt.Errorf("Invalid timestamp offset: \"%v\": %v", adder, err)
  }

  switch p[1] {
    case "seconds": t = t.Add(time.Duration(offset) * time.Second)
    case "minutes": t = t.Add(time.Duration(offset) * time.Minute)
    case "hours":   t = t.Add(time.Duration(offset) * time.Hour)
    case "days":    t = t.AddDate(0,0,int(offset))
    case "weeks":   t = t.AddDate(0,0,int(offset*7))
    case "months":  t = t.AddDate(0,int(offset),0)
    case "years":   t = t.AddDate(int(offset),0,0)
    default:
         return ts, fmt.Errorf("Unknown timestamp offset unit: \"%v\"", p[1])
  }

  return MakeTimestamp(t), nil
}
<|endoftext|>