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

package jsonrpc2

import (
	"context"
	"io"
	"sync"
	"time"

	errors "golang.org/x/xerrors"
)

// Listener is implemented by protocols to accept new inbound connections.
type Listener interface {
	// Accept an inbound connection to a server.
	// It must block until an inbound connection is made, or the listener is
	// shut down.
	Accept(context.Context) (io.ReadWriteCloser, error)

	// Close is used to ask a listener to stop accepting new connections.
	Close() error

	// Dialer returns a dialer that can be used to connect to this listener
	// locally.
	// If a listener does not implement this it will return a nil.
	Dialer() Dialer
}

// Dialer is used by clients to dial a server.
type Dialer interface {
	// Dial returns a new communication byte stream to a listening server.
	Dial(ctx context.Context) (io.ReadWriteCloser, error)
}

// Server is a running server that is accepting incoming connections.
type Server struct {
	listener Listener
	binder   Binder
	async    async
}

// Dial uses the dialer to make a new connection, wraps the returned
// reader and writer using the framer to make a stream, and then builds
// a connection on top of that stream using the binder.
func Dial(ctx context.Context, dialer Dialer, binder Binder) (*Connection, error) {
	// dial a server
	rwc, err := dialer.Dial(ctx)
	if err != nil {
		return nil, err
	}
	return newConnection(ctx, rwc, binder)
}

// Serve starts a new server listening for incoming connections and returns
// it.
// This returns a fully running and connected server, it does not block on
// the listener.
// You can call Wait to block on the server, or Shutdown to get the sever to
// terminate gracefully.
// To notice incoming connections, use an intercepting Binder.
func Serve(ctx context.Context, listener Listener, binder Binder) (*Server, error) {
	server := &Server{
		listener: listener,
		binder:   binder,
	}
	server.async.init()
	go server.run(ctx)
	return server, nil
}

// Wait returns only when the server has shut down.
func (s *Server) Wait() error {
	return s.async.wait()
}

// run accepts incoming connections from the listener,
// If IdleTimeout is non-zero, run exits after there are no clients for this
// duration, otherwise it exits only on error.
func (s *Server) run(ctx context.Context) {
	defer s.async.done()
	var activeConns []*Connection
	for {
		// we never close the accepted connection, we rely on the other end
		// closing or the socket closing itself naturally
		rwc, err := s.listener.Accept(ctx)
		if err != nil {
			if !isClosingError(err) {
				s.async.setError(err)
			}
			// we are done generating new connections for good
			break
		}

		// see if any connections were closed while we were waiting
		activeConns = onlyActive(activeConns)

		// a new inbound connection,
		conn, err := newConnection(ctx, rwc, s.binder)
		if err != nil {
			if !isClosingError(err) {
				s.async.setError(err)
			}
			continue
		}
		activeConns = append(activeConns, conn)
	}

	// wait for all active conns to finish
	for _, c := range activeConns {
		c.Wait()
	}
}

func onlyActive(conns []*Connection) []*Connection {
	i := 0
	for _, c := range conns {
		if !c.async.isDone() {
			conns[i] = c
			i++
		}
	}
	// trim the slice down
	return conns[:i]
}

// isClosingError reports if the error occurs normally during the process of
// closing a network connection. It uses imperfect heuristics that err on the
// side of false negatives, and should not be used for anything critical.
func isClosingError(err error) bool {
	if err == nil {
		return false
	}
	// Fully unwrap the error, so the following tests work.
	for wrapped := err; wrapped != nil; wrapped = errors.Unwrap(err) {
		err = wrapped
	}

	// Was it based on an EOF error?
	if err == io.EOF {
		return true
	}

	// Was it based on a closed pipe?
	if err == io.ErrClosedPipe {
		return true
	}

	// Per https://github.com/golang/go/issues/4373, this error string should not
	// change. This is not ideal, but since the worst that could happen here is
	// some superfluous logging, it is acceptable.
	if err.Error() == "use of closed network connection" {
		return true
	}

	return false
}

// NewIdleListener wraps a listener with an idle timeout.
// When there are no active connections for at least the timeout duration a
// call to accept will fail with ErrIdleTimeout.
func NewIdleListener(timeout time.Duration, wrap Listener) Listener {
	l := &idleListener{
		timeout:    timeout,
		wrapped:    wrap,
		newConns:   make(chan *idleCloser),
		closed:     make(chan struct{}),
		wasTimeout: make(chan struct{}),
	}
	go l.run()
	return l
}

type idleListener struct {
	wrapped    Listener
	timeout    time.Duration
	newConns   chan *idleCloser
	closed     chan struct{}
	wasTimeout chan struct{}
	closeOnce  sync.Once
}

type idleCloser struct {
	wrapped   io.ReadWriteCloser
	closed    chan struct{}
	closeOnce sync.Once
}

func (c *idleCloser) Read(p []byte) (int, error) {
	n, err := c.wrapped.Read(p)
	if err != nil && isClosingError(err) {
		c.closeOnce.Do(func() { close(c.closed) })
	}
	return n, err
}

func (c *idleCloser) Write(p []byte) (int, error) {
	// we do not close on write failure, we rely on the wrapped writer to do that
	// if it is appropriate, which we will detect in the next read.
	return c.wrapped.Write(p)
}

func (c *idleCloser) Close() error {
	// we rely on closing the wrapped stream to signal to the next read that we
	// are closed, rather than triggering the closed signal directly
	return c.wrapped.Close()
}

func (l *idleListener) Accept(ctx context.Context) (io.ReadWriteCloser, error) {
	rwc, err := l.wrapped.Accept(ctx)
	if err != nil {
		if isClosingError(err) {
			// underlying listener was closed
			l.closeOnce.Do(func() { close(l.closed) })
			// was it closed because of the idle timeout?
			select {
			case <-l.wasTimeout:
				err = ErrIdleTimeout
			default:
			}
		}
		return nil, err
	}
	conn := &idleCloser{
		wrapped: rwc,
		closed:  make(chan struct{}),
	}
	l.newConns <- conn
	return conn, err
}

func (l *idleListener) Close() error {
	defer l.closeOnce.Do(func() { close(l.closed) })
	return l.wrapped.Close()
}

func (l *idleListener) Dialer() Dialer {
	return l.wrapped.Dialer()
}

func (l *idleListener) run() {
	var conns []*idleCloser
	for {
		var firstClosed chan struct{} // left at nil if there are no active conns
		var timeout <-chan time.Time  // left at nil if there are  active conns
		if len(conns) > 0 {
			firstClosed = conns[0].closed
		} else {
			timeout = time.After(l.timeout)
		}
		select {
		case <-l.closed:
			// the main listener closed, no need to keep going
			return
		case conn := <-l.newConns:
			// a new conn arrived, add it to the list
			conns = append(conns, conn)
		case <-timeout:
			// we timed out, only happens when there are no active conns
			// close the underlying listener, and allow the normal closing process to happen
			close(l.wasTimeout)
			l.wrapped.Close()
		case <-firstClosed:
			// a conn closed, remove it from the active list
			conns = conns[:copy(conns, conns[1:])]
		}
	}
}
<commit_msg>internal/jsonrpc2_v2: make TestIdleTimeout pass on Plan 9<commit_after>// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package jsonrpc2

import (
	"context"
	"io"
	"runtime"
	"strings"
	"sync"
	"syscall"
	"time"

	errors "golang.org/x/xerrors"
)

// Listener is implemented by protocols to accept new inbound connections.
type Listener interface {
	// Accept an inbound connection to a server.
	// It must block until an inbound connection is made, or the listener is
	// shut down.
	Accept(context.Context) (io.ReadWriteCloser, error)

	// Close is used to ask a listener to stop accepting new connections.
	Close() error

	// Dialer returns a dialer that can be used to connect to this listener
	// locally.
	// If a listener does not implement this it will return a nil.
	Dialer() Dialer
}

// Dialer is used by clients to dial a server.
type Dialer interface {
	// Dial returns a new communication byte stream to a listening server.
	Dial(ctx context.Context) (io.ReadWriteCloser, error)
}

// Server is a running server that is accepting incoming connections.
type Server struct {
	listener Listener
	binder   Binder
	async    async
}

// Dial uses the dialer to make a new connection, wraps the returned
// reader and writer using the framer to make a stream, and then builds
// a connection on top of that stream using the binder.
func Dial(ctx context.Context, dialer Dialer, binder Binder) (*Connection, error) {
	// dial a server
	rwc, err := dialer.Dial(ctx)
	if err != nil {
		return nil, err
	}
	return newConnection(ctx, rwc, binder)
}

// Serve starts a new server listening for incoming connections and returns
// it.
// This returns a fully running and connected server, it does not block on
// the listener.
// You can call Wait to block on the server, or Shutdown to get the sever to
// terminate gracefully.
// To notice incoming connections, use an intercepting Binder.
func Serve(ctx context.Context, listener Listener, binder Binder) (*Server, error) {
	server := &Server{
		listener: listener,
		binder:   binder,
	}
	server.async.init()
	go server.run(ctx)
	return server, nil
}

// Wait returns only when the server has shut down.
func (s *Server) Wait() error {
	return s.async.wait()
}

// run accepts incoming connections from the listener,
// If IdleTimeout is non-zero, run exits after there are no clients for this
// duration, otherwise it exits only on error.
func (s *Server) run(ctx context.Context) {
	defer s.async.done()
	var activeConns []*Connection
	for {
		// we never close the accepted connection, we rely on the other end
		// closing or the socket closing itself naturally
		rwc, err := s.listener.Accept(ctx)
		if err != nil {
			if !isClosingError(err) {
				s.async.setError(err)
			}
			// we are done generating new connections for good
			break
		}

		// see if any connections were closed while we were waiting
		activeConns = onlyActive(activeConns)

		// a new inbound connection,
		conn, err := newConnection(ctx, rwc, s.binder)
		if err != nil {
			if !isClosingError(err) {
				s.async.setError(err)
			}
			continue
		}
		activeConns = append(activeConns, conn)
	}

	// wait for all active conns to finish
	for _, c := range activeConns {
		c.Wait()
	}
}

func onlyActive(conns []*Connection) []*Connection {
	i := 0
	for _, c := range conns {
		if !c.async.isDone() {
			conns[i] = c
			i++
		}
	}
	// trim the slice down
	return conns[:i]
}

// isClosingError reports if the error occurs normally during the process of
// closing a network connection. It uses imperfect heuristics that err on the
// side of false negatives, and should not be used for anything critical.
func isClosingError(err error) bool {
	if err == nil {
		return false
	}
	// Fully unwrap the error, so the following tests work.
	for wrapped := err; wrapped != nil; wrapped = errors.Unwrap(err) {
		err = wrapped
	}

	// Was it based on an EOF error?
	if err == io.EOF {
		return true
	}

	// Was it based on a closed pipe?
	if err == io.ErrClosedPipe {
		return true
	}

	// Per https://github.com/golang/go/issues/4373, this error string should not
	// change. This is not ideal, but since the worst that could happen here is
	// some superfluous logging, it is acceptable.
	if err.Error() == "use of closed network connection" {
		return true
	}

	if runtime.GOOS == "plan9" {
		// Error reading from a closed connection.
		if err == syscall.EINVAL {
			return true
		}
		// Error trying to accept a new connection from a closed listener.
		if strings.HasSuffix(err.Error(), " listen hungup") {
			return true
		}
	}
	return false
}

// NewIdleListener wraps a listener with an idle timeout.
// When there are no active connections for at least the timeout duration a
// call to accept will fail with ErrIdleTimeout.
func NewIdleListener(timeout time.Duration, wrap Listener) Listener {
	l := &idleListener{
		timeout:    timeout,
		wrapped:    wrap,
		newConns:   make(chan *idleCloser),
		closed:     make(chan struct{}),
		wasTimeout: make(chan struct{}),
	}
	go l.run()
	return l
}

type idleListener struct {
	wrapped    Listener
	timeout    time.Duration
	newConns   chan *idleCloser
	closed     chan struct{}
	wasTimeout chan struct{}
	closeOnce  sync.Once
}

type idleCloser struct {
	wrapped   io.ReadWriteCloser
	closed    chan struct{}
	closeOnce sync.Once
}

func (c *idleCloser) Read(p []byte) (int, error) {
	n, err := c.wrapped.Read(p)
	if err != nil && isClosingError(err) {
		c.closeOnce.Do(func() { close(c.closed) })
	}
	return n, err
}

func (c *idleCloser) Write(p []byte) (int, error) {
	// we do not close on write failure, we rely on the wrapped writer to do that
	// if it is appropriate, which we will detect in the next read.
	return c.wrapped.Write(p)
}

func (c *idleCloser) Close() error {
	// we rely on closing the wrapped stream to signal to the next read that we
	// are closed, rather than triggering the closed signal directly
	return c.wrapped.Close()
}

func (l *idleListener) Accept(ctx context.Context) (io.ReadWriteCloser, error) {
	rwc, err := l.wrapped.Accept(ctx)
	if err != nil {
		if isClosingError(err) {
			// underlying listener was closed
			l.closeOnce.Do(func() { close(l.closed) })
			// was it closed because of the idle timeout?
			select {
			case <-l.wasTimeout:
				err = ErrIdleTimeout
			default:
			}
		}
		return nil, err
	}
	conn := &idleCloser{
		wrapped: rwc,
		closed:  make(chan struct{}),
	}
	l.newConns <- conn
	return conn, err
}

func (l *idleListener) Close() error {
	defer l.closeOnce.Do(func() { close(l.closed) })
	return l.wrapped.Close()
}

func (l *idleListener) Dialer() Dialer {
	return l.wrapped.Dialer()
}

func (l *idleListener) run() {
	var conns []*idleCloser
	for {
		var firstClosed chan struct{} // left at nil if there are no active conns
		var timeout <-chan time.Time  // left at nil if there are  active conns
		if len(conns) > 0 {
			firstClosed = conns[0].closed
		} else {
			timeout = time.After(l.timeout)
		}
		select {
		case <-l.closed:
			// the main listener closed, no need to keep going
			return
		case conn := <-l.newConns:
			// a new conn arrived, add it to the list
			conns = append(conns, conn)
		case <-timeout:
			// we timed out, only happens when there are no active conns
			// close the underlying listener, and allow the normal closing process to happen
			close(l.wasTimeout)
			l.wrapped.Close()
		case <-firstClosed:
			// a conn closed, remove it from the active list
			conns = conns[:copy(conns, conns[1:])]
		}
	}
}
<|endoftext|>


<commit_before>package try

import (
	"bufio"
	"context"
	"fmt"
	"io"
	"os"
	"regexp"
	"sort"
	"strings"

	"github.com/urfave/cli/v2"

	"go.skia.org/infra/go/exec"
	"go.skia.org/infra/go/gerrit"
	"go.skia.org/infra/go/httputils"
	"go.skia.org/infra/go/repo_root"
	"go.skia.org/infra/go/skerr"
	"go.skia.org/infra/task_scheduler/go/specs"
)

var (
	// stdin is an abstraction of os.Stdin which is convenient for testing.
	stdin io.Reader = os.Stdin

	// tryjobs is an instance of tryJobReader which may be replaced for testing.
	tryjobs tryJobReader = &tryJobReaderImpl{}
)

// Command returns a cli.Command instance which represents the "try" command.
func Command() *cli.Command {
	yFlag := "y"
	return &cli.Command{
		Name:        "try",
		Usage:       "try [-y] [job name or regex]...",
		Description: "Run try jobs against the active CL",
		Flags: []cli.Flag{
			&cli.BoolFlag{
				Name:  yFlag,
				Value: false,
				Usage: "Trigger all matching try jobs without asking for confirmation.",
			},
		},
		Action: func(ctx *cli.Context) error {
			if err := fixupIssue(ctx.Context); err != nil {
				return err
			}
			return try(ctx.Context, ctx.Args().Slice(), ctx.Bool(yFlag))
		},
	}
}

// try loads the available try jobs, filters by the given request strings, and
// triggers the try jobs selected by the user.
func try(ctx context.Context, jobRequests []string, triggerWithoutPrompt bool) error {
	// Setup.
	jobs, err := tryjobs.getTryJobs(ctx)
	if err != nil {
		return err
	}

	// Filter by the given requested job names/regexes.
	filteredJobs := jobs
	if len(jobRequests) > 0 {
		jobRegexes := make([]*regexp.Regexp, 0, len(jobRequests))
		for _, jobRequest := range jobRequests {
			jobRegex, err := regexp.Compile(jobRequest)
			if err != nil {
				return err
			}
			jobRegexes = append(jobRegexes, jobRegex)
		}
		filteredJobs = map[string][]string{}
		for bucket, jobList := range jobs {
			for _, job := range jobList {
				for _, jobRegex := range jobRegexes {
					if jobRegex.MatchString(job) {
						filteredJobs[bucket] = append(filteredJobs[bucket], job)
						break
					}
				}
			}
		}
	}

	// Prompt the user before triggering jobs.
	count := 0
	for _, jobList := range filteredJobs {
		count += len(jobList)
		sort.Strings(jobList)
	}
	if count == 0 {
		return skerr.Fmt("Found no jobs matching %v", jobRequests)
	}
	fmt.Println(fmt.Sprintf("Found %d jobs:", count))
	for bucket, jobList := range filteredJobs {
		fmt.Println(fmt.Sprintf("  %s:", bucket))
		for _, job := range jobList {
			fmt.Println(fmt.Sprintf("    %s", job))
		}
	}
	if len(jobRequests) == 0 || count == 0 {
		return nil
	}

	jobsToTrigger := filteredJobs
	if !triggerWithoutPrompt {
		fmt.Printf("Do you want to trigger these jobs? (y/n or i for interactive): ")
		reader := bufio.NewReader(stdin)
		read, err := reader.ReadString('\n')
		if err != nil {
			return err
		}
		read = strings.TrimSpace(read)
		if read != "y" && read != "i" {
			return nil
		}
		if read == "i" {
			jobsToTrigger = map[string][]string{}
			for bucket, jobList := range filteredJobs {
				for _, job := range jobList {
					fmt.Printf("Trigger %s? (y/n): ", job)
					trigger, err := reader.ReadString('\n')
					if err != nil {
						return err
					}
					if strings.TrimSpace(trigger) == "y" {
						jobsToTrigger[bucket] = append(jobsToTrigger[bucket], job)
					}
				}
			}
		}
	}

	// Trigger the try jobs.
	for bucket, jobList := range jobsToTrigger {
		cmd := []string{"git", "cl", "try", "-B", bucket}
		for _, job := range jobList {
			cmd = append(cmd, "-b", job)
		}
		if _, err := exec.RunCwd(ctx, ".", cmd...); err != nil {
			return err
		}
	}
	return nil
}

// fixupIssue ensures that the Gerrit issue is set via "git cl issue",
// regardless of whether "git cl upload" was used to create the change.
func fixupIssue(ctx context.Context) error {
	// If the change was uploaded using Depot Tools, the issue configuration
	// will already be present in the git config.
	output, err := exec.RunCwd(ctx, ".", "git", "branch", "--show-current")
	if err != nil {
		return err
	}
	branch := strings.TrimSpace(output)

	if _, err := exec.RunCwd(ctx, ".", "git", "config", "--local", fmt.Sprintf("branch.%s.gerritissue", branch)); err == nil {
		return nil
	}
	// If the above failed, it's probably because the CL was not uploaded
	// using Depot Tools.  Find the Change-Id line in the most recent commit
	// and obtain the issue number using that.
	output, err = exec.RunCwd(ctx, ".", "git", "log", "n1", branch)
	if err != nil {
		return err
	}
	changeId, err := gerrit.ParseChangeId(output)
	if err != nil {
		return err
	}
	g, err := gerrit.NewGerrit(gerrit.GerritSkiaURL, httputils.DefaultClientConfig().Client())
	if err != nil {
		return err
	}
	ci, err := g.GetChange(ctx, changeId)
	if err != nil {
		return err
	}
	issue := fmt.Sprintf("%d", ci.Issue)
	if _, err := exec.RunCwd(ctx, ".", "git", "cl", "issue", issue); err != nil {
		return err
	}
	return nil
}

// tryJobReader provides an abstraction for reading the available set of try
// jobs to facilitate testing.
type tryJobReader interface {
	// getTryJobs reads tasks.json from the current repo and returns a
	// map[string][]string of Buildbucket bucket names to try job names.
	getTryJobs(context.Context) (map[string][]string, error)
}

// tryJobReaderImpl is the default tryJobReader implementation which reads from
// the tasks.json file in the current repo.
type tryJobReaderImpl struct{}

// GetTryJobs implements tryJobReader.
func (r *tryJobReaderImpl) getTryJobs(ctx context.Context) (map[string][]string, error) {
	repoRoot, err := repo_root.GetLocal()
	if err != nil {
		return nil, err
	}
	tasksCfg, err := specs.ReadTasksCfg(repoRoot)
	if err != nil {
		return nil, err
	}
	jobs := make([]string, 0, len(tasksCfg.Jobs))
	for name := range tasksCfg.Jobs {
		jobs = append(jobs, name)
	}
	// TODO(borenet): This assumes that the current repo is associated with the
	// skia.primary bucket. This will work for most repos but it would be better
	// to look up the correct bucket to use.
	return map[string][]string{
		"skia/skia.primary": jobs,
	}, nil
}
<commit_msg>[sk] Fix "git log n1" in "try" command<commit_after>package try

import (
	"bufio"
	"context"
	"fmt"
	"io"
	"os"
	"regexp"
	"sort"
	"strings"

	"github.com/urfave/cli/v2"

	"go.skia.org/infra/go/exec"
	"go.skia.org/infra/go/gerrit"
	"go.skia.org/infra/go/httputils"
	"go.skia.org/infra/go/repo_root"
	"go.skia.org/infra/go/skerr"
	"go.skia.org/infra/task_scheduler/go/specs"
)

var (
	// stdin is an abstraction of os.Stdin which is convenient for testing.
	stdin io.Reader = os.Stdin

	// tryjobs is an instance of tryJobReader which may be replaced for testing.
	tryjobs tryJobReader = &tryJobReaderImpl{}
)

// Command returns a cli.Command instance which represents the "try" command.
func Command() *cli.Command {
	yFlag := "y"
	return &cli.Command{
		Name:        "try",
		Usage:       "try [-y] [job name or regex]...",
		Description: "Run try jobs against the active CL",
		Flags: []cli.Flag{
			&cli.BoolFlag{
				Name:  yFlag,
				Value: false,
				Usage: "Trigger all matching try jobs without asking for confirmation.",
			},
		},
		Action: func(ctx *cli.Context) error {
			if err := fixupIssue(ctx.Context); err != nil {
				return err
			}
			return try(ctx.Context, ctx.Args().Slice(), ctx.Bool(yFlag))
		},
	}
}

// try loads the available try jobs, filters by the given request strings, and
// triggers the try jobs selected by the user.
func try(ctx context.Context, jobRequests []string, triggerWithoutPrompt bool) error {
	// Setup.
	jobs, err := tryjobs.getTryJobs(ctx)
	if err != nil {
		return err
	}

	// Filter by the given requested job names/regexes.
	filteredJobs := jobs
	if len(jobRequests) > 0 {
		jobRegexes := make([]*regexp.Regexp, 0, len(jobRequests))
		for _, jobRequest := range jobRequests {
			jobRegex, err := regexp.Compile(jobRequest)
			if err != nil {
				return err
			}
			jobRegexes = append(jobRegexes, jobRegex)
		}
		filteredJobs = map[string][]string{}
		for bucket, jobList := range jobs {
			for _, job := range jobList {
				for _, jobRegex := range jobRegexes {
					if jobRegex.MatchString(job) {
						filteredJobs[bucket] = append(filteredJobs[bucket], job)
						break
					}
				}
			}
		}
	}

	// Prompt the user before triggering jobs.
	count := 0
	for _, jobList := range filteredJobs {
		count += len(jobList)
		sort.Strings(jobList)
	}
	if count == 0 {
		return skerr.Fmt("Found no jobs matching %v", jobRequests)
	}
	fmt.Println(fmt.Sprintf("Found %d jobs:", count))
	for bucket, jobList := range filteredJobs {
		fmt.Println(fmt.Sprintf("  %s:", bucket))
		for _, job := range jobList {
			fmt.Println(fmt.Sprintf("    %s", job))
		}
	}
	if len(jobRequests) == 0 || count == 0 {
		return nil
	}

	jobsToTrigger := filteredJobs
	if !triggerWithoutPrompt {
		fmt.Printf("Do you want to trigger these jobs? (y/n or i for interactive): ")
		reader := bufio.NewReader(stdin)
		read, err := reader.ReadString('\n')
		if err != nil {
			return err
		}
		read = strings.TrimSpace(read)
		if read != "y" && read != "i" {
			return nil
		}
		if read == "i" {
			jobsToTrigger = map[string][]string{}
			for bucket, jobList := range filteredJobs {
				for _, job := range jobList {
					fmt.Printf("Trigger %s? (y/n): ", job)
					trigger, err := reader.ReadString('\n')
					if err != nil {
						return err
					}
					if strings.TrimSpace(trigger) == "y" {
						jobsToTrigger[bucket] = append(jobsToTrigger[bucket], job)
					}
				}
			}
		}
	}

	// Trigger the try jobs.
	for bucket, jobList := range jobsToTrigger {
		cmd := []string{"git", "cl", "try", "-B", bucket}
		for _, job := range jobList {
			cmd = append(cmd, "-b", job)
		}
		if _, err := exec.RunCwd(ctx, ".", cmd...); err != nil {
			return err
		}
	}
	return nil
}

// fixupIssue ensures that the Gerrit issue is set via "git cl issue",
// regardless of whether "git cl upload" was used to create the change.
func fixupIssue(ctx context.Context) error {
	// If the change was uploaded using Depot Tools, the issue configuration
	// will already be present in the git config.
	output, err := exec.RunCwd(ctx, ".", "git", "branch", "--show-current")
	if err != nil {
		return err
	}
	branch := strings.TrimSpace(output)

	if _, err := exec.RunCwd(ctx, ".", "git", "config", "--local", fmt.Sprintf("branch.%s.gerritissue", branch)); err == nil {
		return nil
	}
	// If the above failed, it's probably because the CL was not uploaded
	// using Depot Tools.  Find the Change-Id line in the most recent commit
	// and obtain the issue number using that.
	output, err = exec.RunCwd(ctx, ".", "git", "log", "-n1", branch)
	if err != nil {
		return err
	}
	changeId, err := gerrit.ParseChangeId(output)
	if err != nil {
		return err
	}
	g, err := gerrit.NewGerrit(gerrit.GerritSkiaURL, httputils.DefaultClientConfig().Client())
	if err != nil {
		return err
	}
	ci, err := g.GetChange(ctx, changeId)
	if err != nil {
		return err
	}
	issue := fmt.Sprintf("%d", ci.Issue)
	if _, err := exec.RunCwd(ctx, ".", "git", "cl", "issue", issue); err != nil {
		return err
	}
	return nil
}

// tryJobReader provides an abstraction for reading the available set of try
// jobs to facilitate testing.
type tryJobReader interface {
	// getTryJobs reads tasks.json from the current repo and returns a
	// map[string][]string of Buildbucket bucket names to try job names.
	getTryJobs(context.Context) (map[string][]string, error)
}

// tryJobReaderImpl is the default tryJobReader implementation which reads from
// the tasks.json file in the current repo.
type tryJobReaderImpl struct{}

// GetTryJobs implements tryJobReader.
func (r *tryJobReaderImpl) getTryJobs(ctx context.Context) (map[string][]string, error) {
	repoRoot, err := repo_root.GetLocal()
	if err != nil {
		return nil, err
	}
	tasksCfg, err := specs.ReadTasksCfg(repoRoot)
	if err != nil {
		return nil, err
	}
	jobs := make([]string, 0, len(tasksCfg.Jobs))
	for name := range tasksCfg.Jobs {
		jobs = append(jobs, name)
	}
	// TODO(borenet): This assumes that the current repo is associated with the
	// skia.primary bucket. This will work for most repos but it would be better
	// to look up the correct bucket to use.
	return map[string][]string{
		"skia/skia.primary": jobs,
	}, nil
}
<|endoftext|>


<commit_before>/*
The Venue package exposes the Avid™ VENUE VNC interface as a programmatic API.
*/
package venue

import (
	"context"
	"fmt"
	"time"

	"github.com/golang/glog"
	"github.com/kward/venue/oscparse"
	"github.com/kward/venue/oscparse/commands"
	"github.com/kward/venue/venue/pages"
	"github.com/kward/venue/venuelib"
	"github.com/kward/venue/vnc"
)

const (
	refresh   = 1000 * time.Millisecond
	numInputs = 48
)

// Venue holds information representing the state of the VENUE backend.
type Venue struct {
	opts *options

	vnc *vnc.VNC

	ui       *UI
	currPage pages.Page
	inputs   [numInputs]*Input
	outputs  map[string]*Output
}

// New returns a populated Venue struct.
func New(opts ...func(*options) error) (*Venue, error) {
	o := &options{}
	o.setInputs(numInputs)
	o.setRefresh(refresh)
	for _, opt := range opts {
		if err := opt(o); err != nil {
			return nil, err
		}
	}
	return &Venue{opts: o}, nil
}

// Close a Venue session.
func (v *Venue) Close() error {
	return v.vnc.Close()
}

// Connect to a VENUE VNC server.
func (v *Venue) Connect(ctx context.Context, h string, p uint, pw string) error {
	// Establish a connection to the VENUE VNC server.
	handle, err := vnc.New(vnc.Host(h), vnc.Port(p), vnc.Password(pw))
	if err != nil {
		return err
	}
	if err := handle.Connect(ctx); err != nil {
		return err
	}
	v.vnc = handle
	return nil
}

// Initialize the in-memory state representation of a VENUE console.
func (v *Venue) Initialize() error {
	if glog.V(3) {
		glog.Info(venuelib.FnName())
	}

	v.ui = NewUI()

	// Initialize inputs.
	if glog.V(2) {
		glog.Info("Initializing inputs.")
	}
	for ch := 0; ch < numInputs; ch++ {
		input := NewInput(v, ch+1, signalChannel)
		v.inputs[ch] = input
	}

	// Choose output before input so that later when the Inputs page is selected,
	// it shows first bank of channels.
	if glog.V(2) {
		glog.Info("Selecting I/O.")
	}
	if err := v.ui.selectOutput(v.vnc, "aux1"); err != nil {
		return err
	}
	if err := v.ui.selectInput(v.vnc, 1); err != nil {
		return err
	}

	if glog.V(2) {
		glog.Infof("Clearing input solo.")
	}
	p, err := v.ui.selectPage(v.vnc, pages.Inputs)
	if err != nil {
		return err
	}
	w, err := p.Widget("solo_clear")
	if err != nil {
		return err
	}
	if err := w.Press(v.vnc); err != nil {
		return err
	}

	return nil
}

// ListenAndHandle connections and incoming requests.
func (v *Venue) ListenAndHandle() {
	go v.vnc.ListenAndHandle()
	go v.vnc.FramebufferRefresh(v.opts.refresh)
}

// Handle incoming packets.
func (v *Venue) Handle(pkt *oscparse.Packet) {
	if glog.V(3) {
		glog.Info(venuelib.FnName())
	}
	if glog.V(2) {
		glog.Infof("Handling incoming %s packet.", pkt.Command)
	}
	if glog.V(4) {
		glog.Infof("packet: %s", pkt)
	}

	// TODO(kward:20170127) Put the commands in struct as function pointers.
	switch pkt.Command {
	case commands.Ping:
		v.Ping()
	case commands.SelectInput:
		v.SelectInput(pkt.Position)
	case commands.SelectOutput:
		v.SelectOutput(pkt.Position)
	default:
		glog.Errorf("%s packet command unimplemented.", pkt.Command)
	}
}

// Ping is deprecated. This should move to the OSC module, passed on a channel.
func (v *Venue) Ping() {
	v.vnc.DebugMetrics()
}

// SelectInput for adjustment.
func (v *Venue) SelectInput(input int) {
	if glog.V(3) {
		glog.Info(venuelib.FnName())
	}
	if err := v.ui.selectInput(v.vnc, uint16(input)); err != nil {
		glog.Errorf("unable to select input; %s", err)
	}
}

// SelectOutput for adjustment.
func (v *Venue) SelectOutput(output int) {
	if glog.V(3) {
		glog.Info(venuelib.FnName())
	}
	name := "aux"
	pos := output
	if output > 16 {
		name = "group"
		pos = output - 10
	}
	if err := v.ui.selectOutput(v.vnc, fmt.Sprintf("%s%d", name, pos)); err != nil {
		glog.Errorf("unable to select output; %s", err)
	}
}
<commit_msg>replaced "aux" and "group" with Widget constants<commit_after>/*
The Venue package exposes the Avid™ VENUE VNC interface as a programmatic API.
*/
package venue

import (
	"context"
	"fmt"
	"time"

	"github.com/golang/glog"
	"github.com/kward/venue/oscparse"
	"github.com/kward/venue/oscparse/commands"
	"github.com/kward/venue/venue/pages"
	"github.com/kward/venue/venuelib"
	"github.com/kward/venue/vnc"
)

const (
	refresh   = 1000 * time.Millisecond
	numInputs = 48
)

// Venue holds information representing the state of the VENUE backend.
type Venue struct {
	opts *options

	vnc *vnc.VNC

	ui       *UI
	currPage pages.Page
	inputs   [numInputs]*Input
	outputs  map[string]*Output
}

// New returns a populated Venue struct.
func New(opts ...func(*options) error) (*Venue, error) {
	o := &options{}
	o.setInputs(numInputs)
	o.setRefresh(refresh)
	for _, opt := range opts {
		if err := opt(o); err != nil {
			return nil, err
		}
	}
	return &Venue{opts: o}, nil
}

// Close a Venue session.
func (v *Venue) Close() error {
	return v.vnc.Close()
}

// Connect to a VENUE VNC server.
func (v *Venue) Connect(ctx context.Context, h string, p uint, pw string) error {
	// Establish a connection to the VENUE VNC server.
	handle, err := vnc.New(vnc.Host(h), vnc.Port(p), vnc.Password(pw))
	if err != nil {
		return err
	}
	if err := handle.Connect(ctx); err != nil {
		return err
	}
	v.vnc = handle
	return nil
}

// Initialize the in-memory state representation of a VENUE console.
func (v *Venue) Initialize() error {
	if glog.V(3) {
		glog.Info(venuelib.FnName())
	}

	v.ui = NewUI()

	// Initialize inputs.
	if glog.V(2) {
		glog.Info("Initializing inputs.")
	}
	for ch := 0; ch < numInputs; ch++ {
		input := NewInput(v, ch+1, signalChannel)
		v.inputs[ch] = input
	}

	// Choose output before input so that later when the Inputs page is selected,
	// it shows first bank of channels.
	if glog.V(2) {
		glog.Info("Selecting I/O.")
	}
	if err := v.ui.selectOutput(v.vnc, "aux1"); err != nil {
		return err
	}
	if err := v.ui.selectInput(v.vnc, 1); err != nil {
		return err
	}

	if glog.V(2) {
		glog.Infof("Clearing input solo.")
	}
	p, err := v.ui.selectPage(v.vnc, pages.Inputs)
	if err != nil {
		return err
	}
	w, err := p.Widget("solo_clear")
	if err != nil {
		return err
	}
	if err := w.Press(v.vnc); err != nil {
		return err
	}

	return nil
}

// ListenAndHandle connections and incoming requests.
func (v *Venue) ListenAndHandle() {
	go v.vnc.ListenAndHandle()
	go v.vnc.FramebufferRefresh(v.opts.refresh)
}

// Handle incoming packets.
func (v *Venue) Handle(pkt *oscparse.Packet) {
	if glog.V(3) {
		glog.Info(venuelib.FnName())
	}
	if glog.V(2) {
		glog.Infof("Handling incoming %s packet.", pkt.Command)
	}
	if glog.V(4) {
		glog.Infof("packet: %s", pkt)
	}

	// TODO(kward:20170127) Put the commands in struct as function pointers.
	switch pkt.Command {
	case commands.Ping:
		v.Ping()
	case commands.SelectInput:
		v.SelectInput(pkt.Position)
	case commands.SelectOutput:
		v.SelectOutput(pkt.Position)
	default:
		glog.Errorf("%s packet command unimplemented.", pkt.Command)
	}
}

// Ping is deprecated. This should move to the OSC module, passed on a channel.
func (v *Venue) Ping() {
	v.vnc.DebugMetrics()
}

// SelectInput for adjustment.
func (v *Venue) SelectInput(input int) {
	if glog.V(3) {
		glog.Info(venuelib.FnName())
	}
	if err := v.ui.selectInput(v.vnc, uint16(input)); err != nil {
		glog.Errorf("unable to select input; %s", err)
	}
}

// SelectOutput for adjustment.
func (v *Venue) SelectOutput(output int) {
	if glog.V(3) {
		glog.Info(venuelib.FnName())
	}
	name := WidgetAux
	pos := output
	if output > 16 {
		name = WidgetGroup
		pos = output - 10
	}
	if err := v.ui.selectOutput(v.vnc, fmt.Sprintf("%s%d", name, pos)); err != nil {
		glog.Errorf("unable to select output; %s", err)
	}
}
<|endoftext|>


<commit_before>// Package btc9 btc9 rest api package
package btc9

import (
	"io/ioutil"
	"math/rand"
	"net/http"
	"strconv"

	"github.com/Akagi201/cryptotrader/model"
	log "github.com/sirupsen/logrus"
	"github.com/tidwall/gjson"
)

const (
	API = "https://btc9.com/Jsons"
)

// Btc9 API data
type Btc9 struct {
	AccessKey string
	SecretKey string
}

// New create new Btc9 API data
func New(accessKey string, secretKey string) *Btc9 {
	return &Btc9{
		AccessKey: accessKey,
		SecretKey: secretKey,
	}
}

// GetTicker 行情
func (bt *Btc9) GetTicker(base string, quote string) (*model.Ticker, error) {
	var url string
	if quote == "pay" {
		url = API + "/trade_43.js?v=" + strconv.FormatFloat(rand.Float64(), 'g', 1, 64)
	} else if quote == "omg" {
		url = API + "/trade_41.js?v=" + strconv.FormatFloat(rand.Float64(), 'g', 1, 64)
	}

	log.Debugf("Request url: %v", url)

	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	log.Debugf("Response body: %v", string(body))

	buy := gjson.GetBytes(body, "depth.1.#.price").Array()[0].Float()
	sell := gjson.GetBytes(body, "depth.2.#.price").Array()[0].Float()
	last := gjson.GetBytes(body, "cmark.new_price").Float()
	low := gjson.GetBytes(body, "cmark.min_price").Float()
	high := gjson.GetBytes(body, "cmark.max_price").Float()
	vol := gjson.GetBytes(body, "cmark.H24_done_num").Float()

	return &model.Ticker{
		Buy:  buy,
		Sell: sell,
		Last: last,
		Low:  low,
		High: high,
		Vol:  vol,
	}, nil
}
<commit_msg>feat(btc9): add panic recover<commit_after>// Package btc9 btc9 rest api package
package btc9

import (
	"io/ioutil"
	"math/rand"
	"net/http"
	"strconv"

	"github.com/Akagi201/cryptotrader/model"
	log "github.com/sirupsen/logrus"
	"github.com/tidwall/gjson"
)

const (
	API = "https://btc9.com/Jsons"
)

// Btc9 API data
type Btc9 struct {
	AccessKey string
	SecretKey string
}

// New create new Btc9 API data
func New(accessKey string, secretKey string) *Btc9 {
	return &Btc9{
		AccessKey: accessKey,
		SecretKey: secretKey,
	}
}

// GetTicker 行情
func (bt *Btc9) GetTicker(base string, quote string) (ticker *model.Ticker, rerr error) {
	defer func() {
		if err := recover(); err != nil {
			ticker = nil
			rerr = err.(error)
		}
	}()

	var url string
	if quote == "pay" {
		url = API + "/trade_43.js?v=" + strconv.FormatFloat(rand.Float64(), 'g', 1, 64)
	} else if quote == "omg" {
		url = API + "/trade_41.js?v=" + strconv.FormatFloat(rand.Float64(), 'g', 1, 64)
	}

	log.Debugf("Request url: %v", url)

	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	log.Debugf("Response body: %v", string(body))

	buy := gjson.GetBytes(body, "depth.1.#.price").Array()[0].Float()
	sell := gjson.GetBytes(body, "depth.2.#.price").Array()[0].Float()
	last := gjson.GetBytes(body, "cmark.new_price").Float()
	low := gjson.GetBytes(body, "cmark.min_price").Float()
	high := gjson.GetBytes(body, "cmark.max_price").Float()
	vol := gjson.GetBytes(body, "cmark.H24_done_num").Float()

	return &model.Ticker{
		Buy:  buy,
		Sell: sell,
		Last: last,
		Low:  low,
		High: high,
		Vol:  vol,
	}, nil
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package bugs

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/mozilla/OneCRL-Tools/config"
	"net/http"
	"net/url"
)

type AttachmentFlag struct {
	Name      string `json:"name"`
	Status    string `json:"status"`
	Requestee string `json:"requestee"`
	New       bool   `json:"new"`
}

type Attachment struct {
	ApiKey      string           `json:"api_key"`
	Ids         []int            `json:"ids"`
	ContentType string           `json:"content_type"`
	Data        string           `json:"data"`
	Summary     string           `json:"summary"`
	FileName    string           `json:"file_name"`
	Flags       []AttachmentFlag `json:"flags"`
	BugId       int              `json:"bug_id"`
	Comment     string           `json:"comment"`
}

type AttachmentResponse struct {
	Ids []string `json:"ids"`
}

type Bug struct {
	ApiKey      string `json:"api_key"`
	Product     string `json:"product"`
	Component   string `json:"component"`
	Version     string `json:"version"`
	Summary     string `json:"summary"`
	Comment     string `json:"comment"`
	Description string `json:"description"`
	Blocks      []int  `json:"blocks,omitempty"`
}

type BugResponse struct {
	Id int `json:"id"`
}

type Comment struct {
	ApiKey	string	`json:"api_key"`
	Comment struct {
		Body       string `json:"body"`
		IsPrivate  bool   `json:"is_private"`
		IsMarkdown bool   `json:"is_markdown"`
	} `json:"comment"`
}

const getBugDataIncludeFields string = "id,summary"

type SearchResponse struct {
	Bugs []BugData `json:"bugs"`
}

type BugData struct {
	Summary string `json:"summary"`
	Id      int    `json:"id"`
}

func CreateBug(bug Bug, conf *config.OneCRLConfig) (int, error) {
	// POST the bug
	bugNum := -1
	url := conf.BugzillaBase + "/rest/bug"
	marshalled, err := json.Marshal(bug)
	if "yes" == conf.OneCRLVerbose {
		fmt.Printf("POSTing %s to %s\n", marshalled, url)
	}
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(marshalled))
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return bugNum, err
	}
	if "yes" == conf.OneCRLVerbose {
		fmt.Printf("status code is %d\n", resp.StatusCode)
	}
	dec := json.NewDecoder(resp.Body)
	var response BugResponse
	err = dec.Decode(&response)
	if err != nil {
		return bugNum, err
	} else {
		bugNum = response.Id

		fmt.Printf("%s\n", err)
		if "yes" == conf.OneCRLVerbose {
			fmt.Printf("%v\n", response.Id)
		}
	}
	defer resp.Body.Close()

	return bugNum, nil
}

func AttachToBug(bugNum int, apiKey string, attachments []Attachment, conf *config.OneCRLConfig) error {
	// loop over the attachments, add each to the bug
	for _, attachment := range attachments {
		attUrl := fmt.Sprintf("%s/rest/bug/%d/attachment", conf.BugzillaBase, bugNum)
		attachment.Ids = []int{bugNum}
		attachment.ApiKey = apiKey
		attachment.BugId = bugNum
		if "yes" == conf.OneCRLVerbose {
			fmt.Printf("Attempting to marshal %v\n", attachment)
		}
		attMarshalled, err := json.Marshal(attachment)
		if "yes" == conf.OneCRLVerbose {
			fmt.Printf("POSTing %s to %s\n", attMarshalled, attUrl)
		}
		attReq, err := http.NewRequest("POST", attUrl, bytes.NewBuffer(attMarshalled))
		attReq.Header.Set("Content-Type", "application/json")
		attClient := &http.Client{}
		attResp, err := attClient.Do(attReq)
		if err != nil {
			return err
		}
		if "yes" == conf.OneCRLVerbose {
			fmt.Printf("att response %v\n", attResp)
		}
	}
	return nil
}

func AddCommentToBug(bugNum int, conf *config.OneCRLConfig, comment string) error {
	commentUrl := fmt.Sprintf("%s/rest/bug/%d", conf.BugzillaBase, bugNum)
	commentObject := Comment{}
	commentObject.ApiKey = conf.BugzillaAPIKey
	commentObject.Comment.Body = comment
	commentObject.Comment.IsPrivate = false
	commentObject.Comment.IsMarkdown = false

	if "yes" == conf.OneCRLVerbose {
		fmt.Printf("Attempting to marshal %v\n", commentObject)
	}

	commentMarshalled, err := json.Marshal(commentObject)
	if "yes" == conf.OneCRLVerbose {
		fmt.Printf("PUTing %s to %s\n", commentMarshalled, commentUrl)
	}

	commentReq, err := http.NewRequest("PUT", commentUrl, bytes.NewBuffer(commentMarshalled))
	commentReq.Header.Set("Content-Type", "application/json")
	commentClient := &http.Client{}
	commentResp, err := commentClient.Do(commentReq)
	if err != nil {
		return err
	}
	if "yes" == conf.OneCRLVerbose {
		fmt.Printf("comment response %v\n", commentResp)
	}
	return nil
}

func GetBugData(bugNumStrings []string, conf *config.OneCRLConfig) (SearchResponse, error) {
	var response SearchResponse
	bugNumString := ""
	for _, bugNum := range bugNumStrings {
		if 0 != len(bugNumString) {
			bugNumString = fmt.Sprintf("%s,%s", bugNumString, bugNum)
		} else {
			bugNumString = bugNum
		}
	}

	getUrl := fmt.Sprintf(conf.BugzillaBase+"/rest/bug?id=%s&include_fields=%s",
		url.QueryEscape(bugNumString), url.QueryEscape(getBugDataIncludeFields))

	getReq, err := http.NewRequest("GET", getUrl, nil)

	client := &http.Client{}
	resp, err := client.Do(getReq)

	if err != nil {
		return response, err
	}

	dec := json.NewDecoder(resp.Body)
	err = dec.Decode(&response)

	defer resp.Body.Close()

	return response, err
}
<commit_msg>When communicating with Bugzilla, identify the tool in-use.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package bugs

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/mozilla/OneCRL-Tools/config"
	"net/http"
	"net/url"
)

type AttachmentFlag struct {
	Name      string `json:"name"`
	Status    string `json:"status"`
	Requestee string `json:"requestee"`
	New       bool   `json:"new"`
}

type Attachment struct {
	ApiKey      string           `json:"api_key"`
	Ids         []int            `json:"ids"`
	ContentType string           `json:"content_type"`
	Data        string           `json:"data"`
	Summary     string           `json:"summary"`
	FileName    string           `json:"file_name"`
	Flags       []AttachmentFlag `json:"flags"`
	BugId       int              `json:"bug_id"`
	Comment     string           `json:"comment"`
}

type AttachmentResponse struct {
	Ids []string `json:"ids"`
}

type Bug struct {
	ApiKey      string `json:"api_key"`
	Product     string `json:"product"`
	Component   string `json:"component"`
	Version     string `json:"version"`
	Summary     string `json:"summary"`
	Comment     string `json:"comment"`
	Description string `json:"description"`
	Blocks      []int  `json:"blocks,omitempty"`
}

type BugResponse struct {
	Id int `json:"id"`
}

type Comment struct {
	ApiKey	string	`json:"api_key"`
	Comment struct {
		Body       string `json:"body"`
		IsPrivate  bool   `json:"is_private"`
		IsMarkdown bool   `json:"is_markdown"`
	} `json:"comment"`
}

const getBugDataIncludeFields string = "id,summary"

const userAgent string = "github.com/mozilla/OneCRL-Tools automated bug creation"

type SearchResponse struct {
	Bugs []BugData `json:"bugs"`
}

type BugData struct {
	Summary string `json:"summary"`
	Id      int    `json:"id"`
}

func CreateBug(bug Bug, conf *config.OneCRLConfig) (int, error) {
	// POST the bug
	bugNum := -1
	url := conf.BugzillaBase + "/rest/bug"
	marshalled, err := json.Marshal(bug)
	if "yes" == conf.OneCRLVerbose {
		fmt.Printf("POSTing %s to %s\n", marshalled, url)
	}
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(marshalled))
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("User-Agent", userAgent)

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return bugNum, err
	}
	if "yes" == conf.OneCRLVerbose {
		fmt.Printf("status code is %d\n", resp.StatusCode)
	}
	dec := json.NewDecoder(resp.Body)
	var response BugResponse
	err = dec.Decode(&response)
	if err != nil {
		return bugNum, err
	} else {
		bugNum = response.Id

		fmt.Printf("%s\n", err)
		if "yes" == conf.OneCRLVerbose {
			fmt.Printf("%v\n", response.Id)
		}
	}
	defer resp.Body.Close()

	return bugNum, nil
}

func AttachToBug(bugNum int, apiKey string, attachments []Attachment, conf *config.OneCRLConfig) error {
	// loop over the attachments, add each to the bug
	for _, attachment := range attachments {
		attUrl := fmt.Sprintf("%s/rest/bug/%d/attachment", conf.BugzillaBase, bugNum)
		attachment.Ids = []int{bugNum}
		attachment.ApiKey = apiKey
		attachment.BugId = bugNum
		if "yes" == conf.OneCRLVerbose {
			fmt.Printf("Attempting to marshal %v\n", attachment)
		}
		attMarshalled, err := json.Marshal(attachment)
		if "yes" == conf.OneCRLVerbose {
			fmt.Printf("POSTing %s to %s\n", attMarshalled, attUrl)
		}
		attReq, err := http.NewRequest("POST", attUrl, bytes.NewBuffer(attMarshalled))
		attReq.Header.Set("Content-Type", "application/json")
		attReq.Header.Set("User-Agent", userAgent)
		attClient := &http.Client{}
		attResp, err := attClient.Do(attReq)
		if err != nil {
			return err
		}
		if "yes" == conf.OneCRLVerbose {
			fmt.Printf("att response %v\n", attResp)
		}
	}
	return nil
}

func AddCommentToBug(bugNum int, conf *config.OneCRLConfig, comment string) error {
	commentUrl := fmt.Sprintf("%s/rest/bug/%d", conf.BugzillaBase, bugNum)
	commentObject := Comment{}
	commentObject.ApiKey = conf.BugzillaAPIKey
	commentObject.Comment.Body = comment
	commentObject.Comment.IsPrivate = false
	commentObject.Comment.IsMarkdown = false

	if "yes" == conf.OneCRLVerbose {
		fmt.Printf("Attempting to marshal %v\n", commentObject)
	}

	commentMarshalled, err := json.Marshal(commentObject)
	if "yes" == conf.OneCRLVerbose {
		fmt.Printf("PUTing %s to %s\n", commentMarshalled, commentUrl)
	}

	commentReq, err := http.NewRequest("PUT", commentUrl, bytes.NewBuffer(commentMarshalled))
	commentReq.Header.Set("Content-Type", "application/json")
	commentReq.Header.Set("User-Agent", userAgent)
	commentClient := &http.Client{}
	commentResp, err := commentClient.Do(commentReq)
	if err != nil {
		return err
	}
	if "yes" == conf.OneCRLVerbose {
		fmt.Printf("comment response %v\n", commentResp)
	}
	return nil
}

func GetBugData(bugNumStrings []string, conf *config.OneCRLConfig) (SearchResponse, error) {
	var response SearchResponse
	bugNumString := ""
	for _, bugNum := range bugNumStrings {
		if 0 != len(bugNumString) {
			bugNumString = fmt.Sprintf("%s,%s", bugNumString, bugNum)
		} else {
			bugNumString = bugNum
		}
	}

	getUrl := fmt.Sprintf(conf.BugzillaBase+"/rest/bug?id=%s&include_fields=%s",
		url.QueryEscape(bugNumString), url.QueryEscape(getBugDataIncludeFields))

	getReq, err := http.NewRequest("GET", getUrl, nil)
	getReq.Header.Set("User-Agent", userAgent)

	client := &http.Client{}
	resp, err := client.Do(getReq)

	if err != nil {
		return response, err
	}

	dec := json.NewDecoder(resp.Body)
	err = dec.Decode(&response)

	defer resp.Body.Close()

	return response, err
}
<|endoftext|>


<commit_before>// Copyright 2016 Google Inc.
//
// 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 view

import (
	"html/template"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"

	"github.com/google/shenzhen-go/graph"
)

const browseTemplateSrc = `
	<title>SHENZHEN GO</title>

SHENZHEN GO

{{$.Base}}

		Up | New

			{{range $.Entries -}}

				{{if .IsDir}}<dir>{{end}}
				{{.Name}}

			{{- end}}

`

var browseTemplate = template.Must(template.New("browse").Parse(browseTemplateSrc))

// dirBrowser serves a way of visually navigating the filesystem.
type dirBrowser struct {
	loadedGraphs map[string]*graph.Graph
}

// NewBrowser makes a Handler that can browse the filesystem and also multiple
// graphs stored in the filesystem.
func NewBrowser() http.Handler {
	return &dirBrowser{
		loadedGraphs: make(map[string]*graph.Graph),
	}
}

type entry struct {
	IsDir bool
	Path  string
	Name  string
}

func (b *dirBrowser) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	log.Printf("%s browse: %s", r.Method, r.URL)

	path := r.URL.Path
	if g, ok := b.loadedGraphs[path]; ok {
		Graph(g, w, r)
		return
	}

	base := filepath.Join(".", path)
	f, err := os.Open(base)
	if err != nil {
		log.Printf("Couldn't open: %v", err)
		http.NotFound(w, r)
		return
	}
	defer f.Close()
	fi, err := f.Stat()
	if err != nil {
		log.Printf("Couldn't stat: %v", err)
		http.NotFound(w, r)
		return
	}
	if !fi.IsDir() {
		g, err := graph.LoadJSON(f, base)
		if err != nil {
			log.Printf("Not a directory or a valid JSON-encoded graph: %v", err)
			http.NotFound(w, r)
		}
		b.loadedGraphs[path] = g
		Graph(g, w, r)
		return
	}
	fis, err := f.Readdir(0)
	if err != nil {
		log.Printf("Couldn't readdir: %s", err)
		http.NotFound(w, r)
		return
	}

	var e []entry
	for _, fi := range fis {
		if strings.HasPrefix(fi.Name(), ".") {
			continue
		}
		e = append(e, entry{
			IsDir: fi.IsDir(),
			Name:  fi.Name(),
			Path:  filepath.Join(path, fi.Name()),
		})
	}

	d := &struct {
		Up      string
		Base    string
		Entries []entry
	}{
		Up:      filepath.Dir(base),
		Base:    base,
		Entries: e,
	}
	if err := browseTemplate.Execute(w, d); err != nil {
		log.Printf("Could not execute browser template: %v", err)
		http.Error(w, "Could not execute browser template", http.StatusInternalServerError)
	}
}
<commit_msg>Add a TODO<commit_after>// Copyright 2016 Google Inc.
//
// 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 view

import (
	"html/template"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"

	"github.com/google/shenzhen-go/graph"
)

const browseTemplateSrc = `
	<title>SHENZHEN GO</title>

SHENZHEN GO

{{$.Base}}

		Up | New

			{{range $.Entries -}}

				{{if .IsDir}}<dir>{{end}}
				{{.Name}}

			{{- end}}

`

var browseTemplate = template.Must(template.New("browse").Parse(browseTemplateSrc))

// dirBrowser serves a way of visually navigating the filesystem.
type dirBrowser struct {
	loadedGraphs map[string]*graph.Graph
}

// NewBrowser makes a Handler that can browse the filesystem and also multiple
// graphs stored in the filesystem.
func NewBrowser() http.Handler {
	return &dirBrowser{
		loadedGraphs: make(map[string]*graph.Graph),
	}
}

type entry struct {
	IsDir bool
	Path  string
	Name  string
}

func (b *dirBrowser) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	log.Printf("%s browse: %s", r.Method, r.URL)

	path := r.URL.Path
	if g, ok := b.loadedGraphs[path]; ok {
		Graph(g, w, r)
		return
	}

	base := filepath.Join(".", path)
	f, err := os.Open(base)
	if err != nil {
		// TODO: If query == "?new", then ask to create a new file.
		log.Printf("Couldn't open: %v", err)
		http.NotFound(w, r)
		return
	}
	defer f.Close()
	fi, err := f.Stat()
	if err != nil {
		log.Printf("Couldn't stat: %v", err)
		http.NotFound(w, r)
		return
	}
	if !fi.IsDir() {
		g, err := graph.LoadJSON(f, base)
		if err != nil {
			log.Printf("Not a directory or a valid JSON-encoded graph: %v", err)
			http.NotFound(w, r)
		}
		b.loadedGraphs[path] = g
		Graph(g, w, r)
		return
	}
	fis, err := f.Readdir(0)
	if err != nil {
		log.Printf("Couldn't readdir: %s", err)
		http.NotFound(w, r)
		return
	}

	var e []entry
	for _, fi := range fis {
		if strings.HasPrefix(fi.Name(), ".") {
			continue
		}
		e = append(e, entry{
			IsDir: fi.IsDir(),
			Name:  fi.Name(),
			Path:  filepath.Join(path, fi.Name()),
		})
	}

	d := &struct {
		Up      string
		Base    string
		Entries []entry
	}{
		Up:      filepath.Dir(base),
		Base:    base,
		Entries: e,
	}
	if err := browseTemplate.Execute(w, d); err != nil {
		log.Printf("Could not execute browser template: %v", err)
		http.Error(w, "Could not execute browser template", http.StatusInternalServerError)
	}
}
<|endoftext|>


<commit_before>package slackapi

import (
	"encoding/json"
	"testing"
)

func CheckResponse(t *testing.T, x interface{}, y string) {
	out, err := json.Marshal(x)
	if err != nil {
		t.Fatal("json fromat;", err)
	}
	if string(out) != y {
		t.Fatalf("invalid json response;\n- %s\n+ %s\n", y, out)
	}
}

func TestAPITest(t *testing.T) {
	s := New()
	x := s.APITest()
	y := `{"ok":true}`
	CheckResponse(t, x, y)
}

func TestAppsList(t *testing.T) {
	s := New()
	x := s.AppsList()
	y := `{"ok":false,"error":"not_authed","apps":null,"cache_ts":""}`
	CheckResponse(t, x, y)
}

func TestAuthRevoke(t *testing.T) {
	s := New()
	x := s.AuthRevoke()
	y := `{"ok":false,"error":"not_authed","revoked":false}`
	CheckResponse(t, x, y)
}

func TestAuthTest(t *testing.T) {
	s := New()
	x, err := s.AuthTest()
	if err != nil {
		t.Fatal(err)
	}
	y := `{"ok":false,"error":"not_authed","team":"","team_id":"","url":"","user":"","user_id":""}`
	CheckResponse(t, x, y)
}

func TestBotsInfo(t *testing.T) {
	s := New()
	x := s.BotsInfo("user")
	y := `{"ok":false,"error":"not_authed","bot":{"id":"","deleted":false,"name":"","icons":null}}`
	CheckResponse(t, x, y)
}

func TestChannelsID(t *testing.T) {
	s := New()
	x := s.ChannelsID("channel")
	y := `"channel"`
	CheckResponse(t, x, y)
}

func TestChannelsMyHistory(t *testing.T) {
	s := New()
	x := s.ChannelsMyHistory("channel", "1234567890")
	y := `{"Filtered":0,"Latest":"","Messages":null,"Oldest":"","Total":0,"Username":""}`
	CheckResponse(t, x, y)
}

func TestChannelsPurgeHistory(t *testing.T) {
	s := New()
	x := s.ChannelsPurgeHistory("channel", "1234567890", true)
	y := `{"Deleted":0,"NotDeleted":0,"Messages":null}`
	CheckResponse(t, x, y)
}

func TestChannelsSetRetention(t *testing.T) {
	s := New()
	x := s.ChannelsSetRetention("channel", 1)
	y := `{"ok":false,"error":"not_authed"}`
	CheckResponse(t, x, y)
}

func TestChannelsSuggestions(t *testing.T) {
	s := New()
	x := s.ChannelsSuggestions()
	y := `{"ok":false,"error":"not_authed","status":{"ok":false},"suggestion_types_tried":null}`
	CheckResponse(t, x, y)
}

func TestChatDelete(t *testing.T) {
	s := New()
	x := s.ChatDelete(MessageArgs{})
	y := `{"ok":false,"error":"not_authed","channel":"","text":"","ts":""}`
	CheckResponse(t, x, y)
}

func TestChatMeMessage(t *testing.T) {
	s := New()
	x := s.ChatMeMessage(MessageArgs{})
	y := `{"ok":false,"error":"not_authed","channel":"","text":"","ts":""}`
	CheckResponse(t, x, y)
}

func TestChatPostMessage(t *testing.T) {
	s := New()
	x := s.ChatPostMessage(MessageArgs{})
	y := `{"ok":false,"error":"not_authed","channel":"","ts":"","message":{"display_as_bot":false}}`
	CheckResponse(t, x, y)
}

func TestChatUpdate(t *testing.T) {
	s := New()
	x := s.ChatUpdate(MessageArgs{})
	y := `{"ok":false,"error":"not_authed","channel":"","ts":"","message":{"display_as_bot":false}}`
	CheckResponse(t, x, y)
}

func TestConversationsArchive(t *testing.T) {
	s := New()
	x := s.ConversationsArchive("channel")
	y := `{"ok":false,"error":"not_authed"}`
	CheckResponse(t, x, y)
}
<commit_msg>Remove conversations.archive unit test due to API uncertanties<commit_after>package slackapi

import (
	"encoding/json"
	"testing"
)

func CheckResponse(t *testing.T, x interface{}, y string) {
	out, err := json.Marshal(x)
	if err != nil {
		t.Fatal("json fromat;", err)
	}
	if string(out) != y {
		t.Fatalf("invalid json response;\n- %s\n+ %s\n", y, out)
	}
}

func TestAPITest(t *testing.T) {
	s := New()
	x := s.APITest()
	y := `{"ok":true}`
	CheckResponse(t, x, y)
}

func TestAppsList(t *testing.T) {
	s := New()
	x := s.AppsList()
	y := `{"ok":false,"error":"not_authed","apps":null,"cache_ts":""}`
	CheckResponse(t, x, y)
}

func TestAuthRevoke(t *testing.T) {
	s := New()
	x := s.AuthRevoke()
	y := `{"ok":false,"error":"not_authed","revoked":false}`
	CheckResponse(t, x, y)
}

func TestAuthTest(t *testing.T) {
	s := New()
	x, err := s.AuthTest()
	if err != nil {
		t.Fatal(err)
	}
	y := `{"ok":false,"error":"not_authed","team":"","team_id":"","url":"","user":"","user_id":""}`
	CheckResponse(t, x, y)
}

func TestBotsInfo(t *testing.T) {
	s := New()
	x := s.BotsInfo("user")
	y := `{"ok":false,"error":"not_authed","bot":{"id":"","deleted":false,"name":"","icons":null}}`
	CheckResponse(t, x, y)
}

func TestChannelsID(t *testing.T) {
	s := New()
	x := s.ChannelsID("channel")
	y := `"channel"`
	CheckResponse(t, x, y)
}

func TestChannelsMyHistory(t *testing.T) {
	s := New()
	x := s.ChannelsMyHistory("channel", "1234567890")
	y := `{"Filtered":0,"Latest":"","Messages":null,"Oldest":"","Total":0,"Username":""}`
	CheckResponse(t, x, y)
}

func TestChannelsPurgeHistory(t *testing.T) {
	s := New()
	x := s.ChannelsPurgeHistory("channel", "1234567890", true)
	y := `{"Deleted":0,"NotDeleted":0,"Messages":null}`
	CheckResponse(t, x, y)
}

func TestChannelsSetRetention(t *testing.T) {
	s := New()
	x := s.ChannelsSetRetention("channel", 1)
	y := `{"ok":false,"error":"not_authed"}`
	CheckResponse(t, x, y)
}

func TestChannelsSuggestions(t *testing.T) {
	s := New()
	x := s.ChannelsSuggestions()
	y := `{"ok":false,"error":"not_authed","status":{"ok":false},"suggestion_types_tried":null}`
	CheckResponse(t, x, y)
}

func TestChatDelete(t *testing.T) {
	s := New()
	x := s.ChatDelete(MessageArgs{})
	y := `{"ok":false,"error":"not_authed","channel":"","text":"","ts":""}`
	CheckResponse(t, x, y)
}

func TestChatMeMessage(t *testing.T) {
	s := New()
	x := s.ChatMeMessage(MessageArgs{})
	y := `{"ok":false,"error":"not_authed","channel":"","text":"","ts":""}`
	CheckResponse(t, x, y)
}

func TestChatPostMessage(t *testing.T) {
	s := New()
	x := s.ChatPostMessage(MessageArgs{})
	y := `{"ok":false,"error":"not_authed","channel":"","ts":"","message":{"display_as_bot":false}}`
	CheckResponse(t, x, y)
}

func TestChatUpdate(t *testing.T) {
	s := New()
	x := s.ChatUpdate(MessageArgs{})
	y := `{"ok":false,"error":"not_authed","channel":"","ts":"","message":{"display_as_bot":false}}`
	CheckResponse(t, x, y)
}
<|endoftext|>


<commit_before>package render

import (
	"bytes"
	"fmt"
	"html/template"
	"io"
	"path/filepath"

	"github.com/vito/booklit"
)

var tmpl *template.Template

func init() {
	tmpl = template.New("engine").Funcs(template.FuncMap{
		"render": renderFunc,
	})

	for _, asset := range AssetNames() {
		info, err := AssetInfo(asset)
		if err != nil {
			panic(err)
		}

		tmpl.New(filepath.Base(info.Name())).Parse(string(MustAsset(asset)))
	}
}

type HTMLRenderingEngine struct {
	template *template.Template
	data     interface{}
}

func NewHTMLRenderingEngine() *HTMLRenderingEngine {
	return &HTMLRenderingEngine{}
}

func (engine *HTMLRenderingEngine) FileExtension() string {
	return "html"
}

func (engine *HTMLRenderingEngine) VisitString(con booklit.String) {
	engine.template = tmpl.Lookup("string.html")
	engine.data = con
}

func (engine *HTMLRenderingEngine) VisitSection(con *booklit.Section) {
	engine.template = tmpl.Lookup("section.html")
	engine.data = con
}

func (engine *HTMLRenderingEngine) VisitSequence(con booklit.Sequence) {
	engine.template = tmpl.Lookup("sequence.html")
	engine.data = con
}

func (engine *HTMLRenderingEngine) VisitParagraph(con booklit.Paragraph) {
	engine.template = tmpl.Lookup("paragraph.html")
	engine.data = con
}

func (engine *HTMLRenderingEngine) Render(out io.Writer) error {
	if engine.template == nil {
		return fmt.Errorf("unknown template for %T", engine.data)
	}

	return engine.template.Execute(out, engine.data)
}

func renderFunc(content booklit.Content) (template.HTML, error) {
	buf := new(bytes.Buffer)

	engine := NewHTMLRenderingEngine()
	content.Visit(engine)
	err := engine.Render(buf)
	if err != nil {
		return "", err
	}

	return template.HTML(buf.String()), nil
}
<commit_msg>err check when loading templates<commit_after>package render

import (
	"bytes"
	"fmt"
	"html/template"
	"io"
	"path/filepath"

	"github.com/vito/booklit"
)

var tmpl *template.Template

func init() {
	tmpl = template.New("engine").Funcs(template.FuncMap{
		"render": renderFunc,
	})

	for _, asset := range AssetNames() {
		info, err := AssetInfo(asset)
		if err != nil {
			panic(err)
		}

		_, err = tmpl.New(filepath.Base(info.Name())).Parse(string(MustAsset(asset)))
		if err != nil {
			panic(err)
		}
	}
}

type HTMLRenderingEngine struct {
	template *template.Template
	data     interface{}
}

func NewHTMLRenderingEngine() *HTMLRenderingEngine {
	return &HTMLRenderingEngine{}
}

func (engine *HTMLRenderingEngine) FileExtension() string {
	return "html"
}

func (engine *HTMLRenderingEngine) VisitString(con booklit.String) {
	engine.template = tmpl.Lookup("string.html")
	engine.data = con
}

func (engine *HTMLRenderingEngine) VisitSection(con *booklit.Section) {
	engine.template = tmpl.Lookup("section.html")
	engine.data = con
}

func (engine *HTMLRenderingEngine) VisitSequence(con booklit.Sequence) {
	engine.template = tmpl.Lookup("sequence.html")
	engine.data = con
}

func (engine *HTMLRenderingEngine) VisitParagraph(con booklit.Paragraph) {
	engine.template = tmpl.Lookup("paragraph.html")
	engine.data = con
}

func (engine *HTMLRenderingEngine) Render(out io.Writer) error {
	if engine.template == nil {
		return fmt.Errorf("unknown template for %T", engine.data)
	}

	return engine.template.Execute(out, engine.data)
}

func renderFunc(content booklit.Content) (template.HTML, error) {
	buf := new(bytes.Buffer)

	engine := NewHTMLRenderingEngine()
	content.Visit(engine)
	err := engine.Render(buf)
	if err != nil {
		return "", err
	}

	return template.HTML(buf.String()), nil
}
<|endoftext|>


<commit_before>package fest
<commit_msg>Delete services.go<commit_after><|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"os"
	"strconv"
	"time"

	"github.com/force12io/force12/api"
	"github.com/force12io/force12/demand"
	"github.com/force12io/force12/docker"
	"github.com/force12io/force12/rng"
	"github.com/force12io/force12/scheduler"
	"github.com/force12io/force12/toy_scheduler"
)

type settings struct {
	demandModelType string
	schedulerType   string
	sendstate       bool
	userID          string
	demandInterval  time.Duration
	demandDelta     int
	maxContainers   int
}

func get_settings() settings {
	var st settings
	st.demandModelType = getEnvOrDefault("F12_DEMAND_MODEL", "RNG")
	st.schedulerType = getEnvOrDefault("F12_SCHEDULER", "DOCKER")
	st.userID = getEnvOrDefault("F12_USER_ID", "5k5gk")
	st.sendstate = (getEnvOrDefault("F12_SEND_STATE_TO_API", "true") == "true")
	st.demandDelta, _ = strconv.Atoi(getEnvOrDefault("F12_DEMAND_DELTA", "3"))
	st.maxContainers, _ = strconv.Atoi(getEnvOrDefault("F12_MAXIMUM_CONTAINERS", "9"))
	demandIntervalMs, _ := strconv.Atoi(getEnvOrDefault("F12_DEMAND_CHANGE_INTERVAL_MS", "3000"))
	st.demandInterval = time.Duration(demandIntervalMs) * time.Millisecond
	return st
}

func get_demand_input(st settings) (demand.Input, error) {
	var di demand.Input

	switch st.demandModelType {
	case "CONSUL":
		return nil, fmt.Errorf("Demand metric from Consul not yet supported")
	case "RNG":
		log.Println("Random demand generation")
		di = rng.NewDemandModel(st.demandDelta, st.maxContainers)
	default:
		return nil, fmt.Errorf("Bad value for F12_DEMAND_MODEL: %s", st.demandModelType)
	}

	return di, nil
}

func get_scheduler(st settings) (scheduler.Scheduler, error) {
	var s scheduler.Scheduler

	switch st.schedulerType {
	case "DOCKER":
		log.Println("Scheduling with Docker remote API")
		s = docker.NewScheduler()
	case "ECS":
		return nil, fmt.Errorf("Scheduling with ECS not yet supported. Tweet with hashtag #F12ECS if you'd like us to add this next!")
	case "KUBERNETES":
		return nil, fmt.Errorf("Scheduling with Kubernetes not yet supported. Tweet with hashtag #F12Kubernetes if you'd like us to add this next!")
	case "MESOS":
		return nil, fmt.Errorf("Scheduling with Mesos / Marathon not yet supported. Tweet with hashtag #F12Mesos if you'd like us to add this next!")
	case "NOMAD":
		return nil, fmt.Errorf("Scheduling with Nomad not yet supported. Tweet with hashtag #F12Nomad if you'd like us to add this next!")
	case "TOY":
		log.Println("Scheduling with toy scheduler")
		s = toy_scheduler.NewScheduler()
	default:
		return nil, fmt.Errorf("Bad value for F12_SCHEDULER: %s", st.schedulerType)
	}

	return s, nil
}

func get_tasks(st settings) map[string]demand.Task {
	var t map[string]demand.Task

	// Get the tasks that have been configured by this user
	t, err := api.GetApps(st.userID)
	if err != nil {
		log.Printf("Error getting tasks: %v", err)
	}

	log.Println(t)
	return t
}

func getEnvOrDefault(name string, defaultValue string) string {
	v := os.Getenv(name)
	if v == "" {
		v = defaultValue
	}

	return v
}
<commit_msg>Option to use the API to generate demand<commit_after>package main

import (
	"fmt"
	"log"
	"os"
	"strconv"
	"time"

	"github.com/force12io/force12/api"
	"github.com/force12io/force12/demand"
	"github.com/force12io/force12/demandapi"
	"github.com/force12io/force12/docker"
	"github.com/force12io/force12/rng"
	"github.com/force12io/force12/scheduler"
	"github.com/force12io/force12/toy_scheduler"
)

type settings struct {
	demandModelType string
	schedulerType   string
	sendstate       bool
	userID          string
	demandInterval  time.Duration
	demandDelta     int
	maxContainers   int
}

func get_settings() settings {
	var st settings
	st.demandModelType = getEnvOrDefault("F12_DEMAND_MODEL", "RNG")
	st.schedulerType = getEnvOrDefault("F12_SCHEDULER", "DOCKER")
	st.userID = getEnvOrDefault("F12_USER_ID", "5k5gk")
	st.sendstate = (getEnvOrDefault("F12_SEND_STATE_TO_API", "true") == "true")
	st.demandDelta, _ = strconv.Atoi(getEnvOrDefault("F12_DEMAND_DELTA", "3"))
	st.maxContainers, _ = strconv.Atoi(getEnvOrDefault("F12_MAXIMUM_CONTAINERS", "9"))
	demandIntervalMs, _ := strconv.Atoi(getEnvOrDefault("F12_DEMAND_CHANGE_INTERVAL_MS", "3000"))
	st.demandInterval = time.Duration(demandIntervalMs) * time.Millisecond
	return st
}

func get_demand_input(st settings) (demand.Input, error) {
	var di demand.Input

	switch st.demandModelType {
	case "CONSUL":
		return nil, fmt.Errorf("Demand metric from Consul not yet supported")
	case "RNG":
		log.Println("Random demand generation")
		di = rng.NewDemandModel(st.demandDelta, st.maxContainers)
	case "API":
		di = demandapi.NewDemandModel(st.userID)
	default:
		return nil, fmt.Errorf("Bad value for F12_DEMAND_MODEL: %s", st.demandModelType)
	}

	return di, nil
}

func get_scheduler(st settings) (scheduler.Scheduler, error) {
	var s scheduler.Scheduler

	switch st.schedulerType {
	case "DOCKER":
		log.Println("Scheduling with Docker remote API")
		s = docker.NewScheduler()
	case "ECS":
		return nil, fmt.Errorf("Scheduling with ECS not yet supported. Tweet with hashtag #F12ECS if you'd like us to add this next!")
	case "KUBERNETES":
		return nil, fmt.Errorf("Scheduling with Kubernetes not yet supported. Tweet with hashtag #F12Kubernetes if you'd like us to add this next!")
	case "MESOS":
		return nil, fmt.Errorf("Scheduling with Mesos / Marathon not yet supported. Tweet with hashtag #F12Mesos if you'd like us to add this next!")
	case "NOMAD":
		return nil, fmt.Errorf("Scheduling with Nomad not yet supported. Tweet with hashtag #F12Nomad if you'd like us to add this next!")
	case "TOY":
		log.Println("Scheduling with toy scheduler")
		s = toy_scheduler.NewScheduler()
	default:
		return nil, fmt.Errorf("Bad value for F12_SCHEDULER: %s", st.schedulerType)
	}

	return s, nil
}

func get_tasks(st settings) map[string]demand.Task {
	var t map[string]demand.Task

	// Get the tasks that have been configured by this user
	t, err := api.GetApps(st.userID)
	if err != nil {
		log.Printf("Error getting tasks: %v", err)
	}

	log.Println(t)
	return t
}

func getEnvOrDefault(name string, defaultValue string) string {
	v := os.Getenv(name)
	if v == "" {
		v = defaultValue
	}

	return v
}
<|endoftext|>


<commit_before>// Simple Syslog Client
//
// changed from https://github.com/Neo23x0/simplesyslog
// License Apache

package simplesyslog

import (
	"fmt"
	"net"
	"os"
	"strings"
	"time"
)

// SyslogAgent defines a syslog server IP and server port
type SyslogAgent struct {
	ServerIP   string
	ServerPort int
	udpConn    *net.UDPConn
	isActive   bool
	hostname   string
	hostip     string
	Rfc3164    bool
	Rfc5424    bool
}

// initialize Syslog connection
func (ss *SyslogAgent) Initialize() (bool, string) {
	// Preset
	ss.isActive = false

	// Server Target
	var serverSocket = fmt.Sprintf("%s:%d", ss.ServerIP, ss.ServerPort)
	ServerAddr, err := net.ResolveUDPAddr("udp", serverSocket)
	if err != nil {
		return false, "Cannot resolve target address"
	}
	// Connect
	var con_err error
	ss.udpConn, con_err = net.DialUDP("udp", nil, ServerAddr)
	if con_err != nil {
		return false, "Cannot connect to target system"
	}

	// Hostname and Local IP address
	var err_hn error
	ss.hostname, err_hn = os.Hostname()
	if err_hn != nil {
		ss.hostname = "unknown"
	}
	ss.hostip = strings.Split(fmt.Sprintf("%v", ss.udpConn.LocalAddr()), ":")[0]

	// Everything went fine
	ss.isActive = true
	return true, "Connection succeeded"
}

// Send allows to send a syslog message to the set server and port
func (ss *SyslogAgent) Send(message string, facility string, severity string) {
	var f = facilities[facility]
	var s = severities[severity]

	// Prepare Header
	pri := (f * 8) + s
	timestamp := time.Now().Format("Jan _2 15:04:05")
	hostnameCombi := fmt.Sprintf("%s/%s", ss.hostname, ss.hostip)
	header := fmt.Sprintf("<%d>%s %s", pri, timestamp, hostnameCombi)

	// RFC length reduction
	if ss.Rfc3164 && len(message) > 1024 {
		message = fmt.Sprintf("%s...", message[:1020])
	}
	if ss.Rfc5424 && len(message) > 2048 {
		message = fmt.Sprintf("%s...", message[:2044])
	}

	// Full Message
	full_message := fmt.Sprintf("%s %s", header, message)

	// fmt.Println(full_message)
	fmt.Fprintf(ss.udpConn, full_message)
}

func (ss *SyslogAgent) Forward(message string) (int, error) {
	return ss.udpConn.Write([]byte(message))
}

func (ss *SyslogAgent) Fd() *net.UDPConn {
	return ss.udpConn
}

// Terminate closes the connection gracefully
func (ss *SyslogAgent) Terminate() {
	ss.udpConn.Close()
}

// Syslog severities
// See https://www.ietf.org/rfc/rfc3164.txt
var severities = map[string]int{
	"emergency": 0,
	"alert":     1,
	"critical":  2,
	"error":     3,
	"warning":   4,
	"notice":    5,
	"info":      6,
	"debug":     7,
}

// Syslog facilities
// See https://www.ietf.org/rfc/rfc3164.txt
var facilities = map[string]int{
	"kernel":     0,
	"user-level": 1,
	"mail":       2,
	"system":     3,
	"security":   4,
	"messages":   5,
	"printer":    6,
	"news":       7,
	"uucp":       8,
	"clock":      9,
	"auth":       10,
	"ftp":        11,
	"ntp":        12,
	"audit":      13,
	"log":        14,
	"cron":       15,
	"local0":     16,
	"local1":     17,
	"local2":     18,
	"local3":     19,
	"local4":     20,
	"local5":     21,
	"local6":     22,
	"local7":     23,
}
<commit_msg>string -> []byte<commit_after>// Simple Syslog Client
//
// changed from https://github.com/Neo23x0/simplesyslog
// License Apache

package simplesyslog

import (
	"fmt"
	"net"
	"os"
	"strings"
	"time"
)

// SyslogAgent defines a syslog server IP and server port
type SyslogAgent struct {
	ServerIP   string
	ServerPort int
	udpConn    *net.UDPConn
	isActive   bool
	hostname   string
	hostip     string
	Rfc3164    bool
	Rfc5424    bool
}

// initialize Syslog connection
func (ss *SyslogAgent) Initialize() (bool, string) {
	// Preset
	ss.isActive = false

	// Server Target
	var serverSocket = fmt.Sprintf("%s:%d", ss.ServerIP, ss.ServerPort)
	ServerAddr, err := net.ResolveUDPAddr("udp", serverSocket)
	if err != nil {
		return false, "Cannot resolve target address"
	}
	// Connect
	var con_err error
	ss.udpConn, con_err = net.DialUDP("udp", nil, ServerAddr)
	if con_err != nil {
		return false, "Cannot connect to target system"
	}

	// Hostname and Local IP address
	var err_hn error
	ss.hostname, err_hn = os.Hostname()
	if err_hn != nil {
		ss.hostname = "unknown"
	}
	ss.hostip = strings.Split(fmt.Sprintf("%v", ss.udpConn.LocalAddr()), ":")[0]

	// Everything went fine
	ss.isActive = true
	return true, "Connection succeeded"
}

// Send allows to send a syslog message to the set server and port
func (ss *SyslogAgent) Send(message string, facility string, severity string) {
	var f = facilities[facility]
	var s = severities[severity]

	// Prepare Header
	pri := (f * 8) + s
	timestamp := time.Now().Format("Jan _2 15:04:05")
	hostnameCombi := fmt.Sprintf("%s/%s", ss.hostname, ss.hostip)
	header := fmt.Sprintf("<%d>%s %s", pri, timestamp, hostnameCombi)

	// RFC length reduction
	if ss.Rfc3164 && len(message) > 1024 {
		message = fmt.Sprintf("%s...", message[:1020])
	}
	if ss.Rfc5424 && len(message) > 2048 {
		message = fmt.Sprintf("%s...", message[:2044])
	}

	// Full Message
	full_message := fmt.Sprintf("%s %s", header, message)

	// fmt.Println(full_message)
	fmt.Fprintf(ss.udpConn, full_message)
}

func (ss *SyslogAgent) Forward(message []byte) (int, error) {
	return ss.udpConn.Write(message)
}

func (ss *SyslogAgent) Fd() *net.UDPConn {
	return ss.udpConn
}

// Terminate closes the connection gracefully
func (ss *SyslogAgent) Terminate() {
	ss.udpConn.Close()
}

// Syslog severities
// See https://www.ietf.org/rfc/rfc3164.txt
var severities = map[string]int{
	"emergency": 0,
	"alert":     1,
	"critical":  2,
	"error":     3,
	"warning":   4,
	"notice":    5,
	"info":      6,
	"debug":     7,
}

// Syslog facilities
// See https://www.ietf.org/rfc/rfc3164.txt
var facilities = map[string]int{
	"kernel":     0,
	"user-level": 1,
	"mail":       2,
	"system":     3,
	"security":   4,
	"messages":   5,
	"printer":    6,
	"news":       7,
	"uucp":       8,
	"clock":      9,
	"auth":       10,
	"ftp":        11,
	"ntp":        12,
	"audit":      13,
	"log":        14,
	"cron":       15,
	"local0":     16,
	"local1":     17,
	"local2":     18,
	"local3":     19,
	"local4":     20,
	"local5":     21,
	"local6":     22,
	"local7":     23,
}
<|endoftext|>


<commit_before>package snapshotcommands

import (
	"github.com/rackspace/rack/commandoptions"
	"github.com/rackspace/rack/handler"
	"github.com/rackspace/rack/internal/github.com/codegangsta/cli"
	osSnapshots "github.com/rackspace/rack/internal/github.com/rackspace/gophercloud/openstack/blockstorage/v1/snapshots"
	"github.com/rackspace/rack/util"
)

var create = cli.Command{
	Name:        "create",
	Usage:       util.Usage(commandPrefix, "create", "--volume-id <volumeID>"),
	Description: "Creates a volume",
	Action:      actionCreate,
	Flags:       commandoptions.CommandFlags(flagsCreate, keysCreate),
	BashComplete: func(c *cli.Context) {
		commandoptions.CompleteFlags(commandoptions.CommandFlags(flagsCreate, keysCreate))
	},
}

func flagsCreate() []cli.Flag {
	return []cli.Flag{
		cli.StringFlag{
			Name:  "volume-id",
			Usage: "[required] The volume ID from which to create this snapshot.",
		},
		cli.StringFlag{
			Name:  "name",
			Usage: "[optional] A name for this snapshot.",
		},
		cli.StringFlag{
			Name:  "description",
			Usage: "[optional] A description for this snapshot.",
		},
	}
}

var keysCreate = []string{"ID", "Name", "Description", "Size", "VolumeType", "SnapshotID", "Attachments", "CreatedAt", "Metadata"}

type paramsCreate struct {
	opts *osSnapshots.CreateOpts
}

type commandCreate handler.Command

func actionCreate(c *cli.Context) {
	command := &commandCreate{
		Ctx: &handler.Context{
			CLIContext: c,
		},
	}
	handler.Handle(command)
}

func (command *commandCreate) Context() *handler.Context {
	return command.Ctx
}

func (command *commandCreate) Keys() []string {
	return keysCreate
}

func (command *commandCreate) ServiceClientType() string {
	return serviceClientType
}

func (command *commandCreate) HandleFlags(resource *handler.Resource) error {
	err := command.Ctx.CheckFlagsSet([]string{"volume-id"})
	if err != nil {
		return err
	}

	c := command.Ctx.CLIContext

	opts := &osSnapshots.CreateOpts{
		VolumeID:    c.String("volume-id"),
		Name:        c.String("name"),
		Description: c.String("description"),
	}

	resource.Params = &paramsCreate{
		opts: opts,
	}

	return nil
}

func (command *commandCreate) Execute(resource *handler.Resource) {
	opts := resource.Params.(*paramsCreate).opts
	snapshot, err := osSnapshots.Create(command.Ctx.ServiceClient, opts).Extract()
	if err != nil {
		resource.Err = err
		return
	}
	resource.Result = snapshotSingle(snapshot)
}

func (command *commandCreate) PreCSV(resource *handler.Resource) {
	resource.FlattenMap("Metadata")
}

func (command *commandCreate) PreTable(resource *handler.Resource) {
	command.PreCSV(resource)
}
<commit_msg>add VolumeID to the output<commit_after>package snapshotcommands

import (
	"github.com/rackspace/rack/commandoptions"
	"github.com/rackspace/rack/handler"
	"github.com/rackspace/rack/internal/github.com/codegangsta/cli"
	osSnapshots "github.com/rackspace/rack/internal/github.com/rackspace/gophercloud/openstack/blockstorage/v1/snapshots"
	"github.com/rackspace/rack/util"
)

var create = cli.Command{
	Name:        "create",
	Usage:       util.Usage(commandPrefix, "create", "--volume-id <volumeID>"),
	Description: "Creates a volume",
	Action:      actionCreate,
	Flags:       commandoptions.CommandFlags(flagsCreate, keysCreate),
	BashComplete: func(c *cli.Context) {
		commandoptions.CompleteFlags(commandoptions.CommandFlags(flagsCreate, keysCreate))
	},
}

func flagsCreate() []cli.Flag {
	return []cli.Flag{
		cli.StringFlag{
			Name:  "volume-id",
			Usage: "[required] The volume ID from which to create this snapshot.",
		},
		cli.StringFlag{
			Name:  "name",
			Usage: "[optional] A name for this snapshot.",
		},
		cli.StringFlag{
			Name:  "description",
			Usage: "[optional] A description for this snapshot.",
		},
	}
}

var keysCreate = []string{"ID", "Name", "Description", "Size", "VolumeID", "VolumeType", "SnapshotID", "Attachments", "CreatedAt", "Metadata"}

type paramsCreate struct {
	opts *osSnapshots.CreateOpts
}

type commandCreate handler.Command

func actionCreate(c *cli.Context) {
	command := &commandCreate{
		Ctx: &handler.Context{
			CLIContext: c,
		},
	}
	handler.Handle(command)
}

func (command *commandCreate) Context() *handler.Context {
	return command.Ctx
}

func (command *commandCreate) Keys() []string {
	return keysCreate
}

func (command *commandCreate) ServiceClientType() string {
	return serviceClientType
}

func (command *commandCreate) HandleFlags(resource *handler.Resource) error {
	err := command.Ctx.CheckFlagsSet([]string{"volume-id"})
	if err != nil {
		return err
	}

	c := command.Ctx.CLIContext

	opts := &osSnapshots.CreateOpts{
		VolumeID:    c.String("volume-id"),
		Name:        c.String("name"),
		Description: c.String("description"),
	}

	resource.Params = &paramsCreate{
		opts: opts,
	}

	return nil
}

func (command *commandCreate) Execute(resource *handler.Resource) {
	opts := resource.Params.(*paramsCreate).opts
	snapshot, err := osSnapshots.Create(command.Ctx.ServiceClient, opts).Extract()
	if err != nil {
		resource.Err = err
		return
	}
	resource.Result = snapshotSingle(snapshot)
}

func (command *commandCreate) PreCSV(resource *handler.Resource) {
	resource.FlattenMap("Metadata")
}

func (command *commandCreate) PreTable(resource *handler.Resource) {
	command.PreCSV(resource)
}
<|endoftext|>


<commit_before>//
package vagrant

import (
	"bufio"
	"bytes"
	"fmt"
	"github.com/nanobox-io/nanobox/config"
	"os"
	"os/exec"
	"regexp"
	"strconv"
	"strings"
	"time"
)

//
var err error

// Exists ensure vagrant is installed
func Exists() (exists bool) {
	if err := exec.Command("vagrant", "-v").Run(); err == nil {
		exists = true
	}
	return
}

// run runs a vagrant command
func run(cmd *exec.Cmd) error {

	//
	handleCMDout(cmd)

	// start the command; we need this to 'fire and forget' so that we can manually
	// capture and modify the commands output above
	if err := cmd.Start(); err != nil {
		return err
	}

	// wait for the command to complete/fail and exit, giving us a chance to scan
	// the output
	if err := cmd.Wait(); err != nil {
		return err
	}

	return nil
}

// runInContext runs a command in the context of a Vagrantfile (from the same dir)
func runInContext(cmd *exec.Cmd) error {

	// run the command from ~/.nanobox/apps/<config.App>. Running the command from
	// the directory that contains the Vagratfile ensure that the command can
	// atleast run (especially in cases like 'create' where a VM hadn't been created
	// yet, and a UUID isn't available)
	setContext(config.AppDir)

	//
	handleCMDout(cmd)

	// start the command; we need this to 'fire and forget' so that we can manually
	// capture and modify the commands output above
	if err := cmd.Start(); err != nil {
		return err
	}

	// wait for the command to complete/fail and exit, giving us a chance to scan
	// the output
	if err := cmd.Wait(); err != nil {
		return err
	}

	// switch back to project dir
	setContext(config.CWDir)

	return nil
}

// setContext changes the working directory to the designated context
func setContext(context string) {
	if err := os.Chdir(context); err != nil {
		fmt.Printf("No app found at %s, exiting...\n", config.AppDir)
		os.Exit(1)
	}
}

func customScanner(data []byte, atEOF bool) (advance int, token []byte, err error) {

	//
	if atEOF && len(data) == 0 {
		return 0, nil, nil
	}

	if i := bytes.IndexByte(data, '\n'); i >= 0 {
		// We have a full newline-terminated line.
		return i + 1, dropCR(data[0:i]), nil
	}

	if i := bytes.IndexByte(data, '\r'); i >= 0 {
		return i + 1, dropCR(data[0:i]), nil
	}

	// If we're at EOF, we have a final, non-terminated line. Return it.
	if atEOF {
		return len(data), dropCR(data), nil
	}

	// Request more data.
	return 0, nil, nil
}

// dropCR drops a terminal \r from the data.
func dropCR(data []byte) []byte {
	if len(data) > 0 && data[len(data)-1] == '\r' {
		return data[0 : len(data)-1]
	}
	return data
}

// handleCMDout
func handleCMDout(cmd *exec.Cmd) {

	// start a goroutine that will act as an 'outputer' allowing us to add 'dots'
	// to the end of each line (as these lines are a reduced version of the actual
	// output there will be some delay between output)
	output := make(chan string)
	go func() {

		tick := time.Second

		// block until any one message outputs
		msg, ok := <-output

		// print initial message to 'get the ball rolling' on our 'outputer'
		fmt.Printf("   - %s", msg)

		// begin a loop to read off the channel until it's closed
		for {
			select {

			// print any messages and reset ticker
			case msg, ok = <-output:

				// once the channel closes print the final newline and close the goroutine
				if !ok {
					fmt.Println("")
					return
				}

				fmt.Printf("\n   - %s", msg)

				tick = time.Second

			// after every tick print a '.' until we get another message one the channel
			// (at which point ticker is reset and it starts all over again)
			case <-time.After(tick):
				fmt.Print(".")

				// increase the wait time by half of the total previous time
				tick += tick / 2
			}
		}
	}()

	// create a stderr pipe that will write any error messages to the log
	stderr, err := cmd.StderrPipe()
	if err != nil {
		Fatal("[util/vagrant/vagrant] cmd.StderrPipe() failed", err.Error())
	}

	// log any command errors to the log
	stderrScanner := bufio.NewScanner(stderr)
	go func() {
		for stderrScanner.Scan() {
			Log.Error(stderrScanner.Text())
		}
	}()

	// create a stdout pipe that will allow for scanning the output line-by-line;
	// if needed a stderr pipe could also be created at some point
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		Fatal("[util/vagrant/vagrant] cmd.StdoutPipe() failed", err.Error())
	}

	// scan the command output intercepting only 'important' lines of vagrant output'
	// and tailoring their message so as to not flood the output.
	// styled according to: http://nanodocs.gopagoda.io/engines/style-guide
	stdoutScanner := bufio.NewScanner(stdout)
	stdoutScanner.Split(customScanner)
	go func() {
		for stdoutScanner.Scan() {

			txt := strings.TrimSpace(stdoutScanner.Text())
			app := config.Nanofile.Name

			// handle generic cases
			switch {

			// show the progress bar when trying to download nanobox/boot2docker
			case strings.Contains(txt, "box: Progress:"):
				subMatch := regexp.MustCompile(`box: Progress: (\d{1,3})% \(Rate: (.*), Estimated time remaining: (\d*:\d*:\d*)`).FindStringSubmatch(txt)

				// ensure we have all the submatches needed before using them
				if len(subMatch) >= 4 {
					i, err := strconv.Atoi(subMatch[1])
					if err != nil {

					}

					// show download progress: [*** progress *** 0.0%] 00:00:00 remaining
					fmt.Printf("\r   [%-41s %s%%] %s (%s remaining)", strings.Repeat("*", int(float64(i)/2.5)), subMatch[1], subMatch[2], subMatch[3])
				}
			}

			// handle specific cases
			switch txt {

			// nanobox vm has not yet been created
			case fmt.Sprintf("==> %v: VM not created. Moving on...", app):
				output <- "Nanobox not yet created, use 'nanobox dev' or 'nanobox run' to create it."

			// nanobox is already running
			case fmt.Sprintf("==> %v: VirtualBox VM is already running.", app):
				continue

			case fmt.Sprintf("==> %v: Importing base box 'nanobox/boot2docker'...", app):
				output <- "Importing nanobox base image"
			case fmt.Sprintf("==> %v: Booting VM...", app):
				output <- "Booting virtual machine"
			case fmt.Sprintf("==> %v: Configuring and enabling network interfaces...", app):
				output <- "Configuring virtual network"
			case fmt.Sprintf("==> %v: Mounting shared folders...", app):
				output <- fmt.Sprintf("Mounting source code (%s)", config.CWDir)
			case fmt.Sprintf("==> %v: Waiting for nanobox server...", app):
				output <- "Starting nanobox server"
			case fmt.Sprintf("==> %v: Attempting graceful shutdown of VM...", app):
				output <- "Shutting down virtual machine"
			case fmt.Sprintf("==> %v: Destroying VM and associated drives...", app):
				// output <- "Destroying virtual machine"
			case fmt.Sprintf("==> %v: Forcing shutdown of VM...", app):
				output <- "Shutting down virtual machine"
			case fmt.Sprintf("==> %v: Saving VM state and suspending execution...", app):
				output <- "Saving virtual machine"
			case fmt.Sprintf("==> %v: Resuming suspended VM...", app):
				// output <- "Resuming virtual machine"
			}
		}

		// close the output channel once all lines of command output have been read
		close(output)
	}()
}
<commit_msg>log all vagrant output<commit_after>//
package vagrant

import (
	"bufio"
	"bytes"
	"fmt"
	"github.com/nanobox-io/nanobox/config"
	"os"
	"os/exec"
	"regexp"
	"strconv"
	"strings"
	"time"
)

//
var err error

// Exists ensure vagrant is installed
func Exists() (exists bool) {
	if err := exec.Command("vagrant", "-v").Run(); err == nil {
		exists = true
	}
	return
}

// run runs a vagrant command
func run(cmd *exec.Cmd) error {

	//
	handleCMDout(cmd)

	// start the command; we need this to 'fire and forget' so that we can manually
	// capture and modify the commands output above
	if err := cmd.Start(); err != nil {
		return err
	}

	// wait for the command to complete/fail and exit, giving us a chance to scan
	// the output
	if err := cmd.Wait(); err != nil {
		return err
	}

	return nil
}

// runInContext runs a command in the context of a Vagrantfile (from the same dir)
func runInContext(cmd *exec.Cmd) error {

	// run the command from ~/.nanobox/apps/<config.App>. Running the command from
	// the directory that contains the Vagratfile ensure that the command can
	// atleast run (especially in cases like 'create' where a VM hadn't been created
	// yet, and a UUID isn't available)
	setContext(config.AppDir)

	//
	handleCMDout(cmd)

	// start the command; we need this to 'fire and forget' so that we can manually
	// capture and modify the commands output above
	if err := cmd.Start(); err != nil {
		return err
	}

	// wait for the command to complete/fail and exit, giving us a chance to scan
	// the output
	if err := cmd.Wait(); err != nil {
		return err
	}

	// switch back to project dir
	setContext(config.CWDir)

	return nil
}

// setContext changes the working directory to the designated context
func setContext(context string) {
	if err := os.Chdir(context); err != nil {
		fmt.Printf("No app found at %s, exiting...\n", config.AppDir)
		os.Exit(1)
	}
}

func customScanner(data []byte, atEOF bool) (advance int, token []byte, err error) {

	//
	if atEOF && len(data) == 0 {
		return 0, nil, nil
	}

	if i := bytes.IndexByte(data, '\n'); i >= 0 {
		// We have a full newline-terminated line.
		return i + 1, dropCR(data[0:i]), nil
	}

	if i := bytes.IndexByte(data, '\r'); i >= 0 {
		return i + 1, dropCR(data[0:i]), nil
	}

	// If we're at EOF, we have a final, non-terminated line. Return it.
	if atEOF {
		return len(data), dropCR(data), nil
	}

	// Request more data.
	return 0, nil, nil
}

// dropCR drops a terminal \r from the data.
func dropCR(data []byte) []byte {
	if len(data) > 0 && data[len(data)-1] == '\r' {
		return data[0 : len(data)-1]
	}
	return data
}

// handleCMDout
func handleCMDout(cmd *exec.Cmd) {

	// start a goroutine that will act as an 'outputer' allowing us to add 'dots'
	// to the end of each line (as these lines are a reduced version of the actual
	// output there will be some delay between output)
	output := make(chan string)
	go func() {

		tick := time.Second

		// block until any one message outputs
		msg, ok := <-output

		// print initial message to 'get the ball rolling' on our 'outputer'
		fmt.Printf("   - %s", msg)

		// begin a loop to read off the channel until it's closed
		for {
			select {

			// print any messages and reset ticker
			case msg, ok = <-output:

				// once the channel closes print the final newline and close the goroutine
				if !ok {
					fmt.Println("")
					return
				}

				fmt.Printf("\n   - %s", msg)

				tick = time.Second

			// after every tick print a '.' until we get another message one the channel
			// (at which point ticker is reset and it starts all over again)
			case <-time.After(tick):
				fmt.Print(".")

				// increase the wait time by half of the total previous time
				tick += tick / 2
			}
		}
	}()

	// create a stderr pipe that will write any error messages to the log
	stderr, err := cmd.StderrPipe()
	if err != nil {
		Fatal("[util/vagrant/vagrant] cmd.StderrPipe() failed", err.Error())
	}

	// log any command errors to the log
	stderrScanner := bufio.NewScanner(stderr)
	go func() {
		for stderrScanner.Scan() {
			Error("A vagrant error occured", stderrScanner.Text())
		}
	}()

	// create a stdout pipe that will allow for scanning the output line-by-line;
	// if needed a stderr pipe could also be created at some point
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		Fatal("[util/vagrant/vagrant] cmd.StdoutPipe() failed", err.Error())
	}

	// scan the command output intercepting only 'important' lines of vagrant output'
	// and tailoring their message so as to not flood the output.
	// styled according to: http://nanodocs.gopagoda.io/engines/style-guide
	stdoutScanner := bufio.NewScanner(stdout)
	stdoutScanner.Split(customScanner)
	go func() {
		for stdoutScanner.Scan() {

			txt := strings.TrimSpace(stdoutScanner.Text())
			app := config.Nanofile.Name

			// log all vagrant output (might as well)
			Log.Info(txt)

			// handle generic cases
			switch {

			// show the progress bar when trying to download nanobox/boot2docker
			case strings.Contains(txt, "box: Progress:"):
				subMatch := regexp.MustCompile(`box: Progress: (\d{1,3})% \(Rate: (.*), Estimated time remaining: (\d*:\d*:\d*)`).FindStringSubmatch(txt)

				// ensure we have all the submatches needed before using them
				if len(subMatch) >= 4 {
					i, err := strconv.Atoi(subMatch[1])
					if err != nil {

					}

					// show download progress: [*** progress *** 0.0%] 00:00:00 remaining
					fmt.Printf("\r   [%-41s %s%%] %s (%s remaining)", strings.Repeat("*", int(float64(i)/2.5)), subMatch[1], subMatch[2], subMatch[3])
				}
			}

			// handle specific cases
			switch txt {

			// nanobox vm has not yet been created
			case fmt.Sprintf("==> %v: VM not created. Moving on...", app):
				output <- "Nanobox not yet created, use 'nanobox dev' or 'nanobox run' to create it."

			// nanobox is already running
			case fmt.Sprintf("==> %v: VirtualBox VM is already running.", app):
				continue

			case fmt.Sprintf("==> %v: Importing base box 'nanobox/boot2docker'...", app):
				output <- "Importing nanobox base image"
			case fmt.Sprintf("==> %v: Booting VM...", app):
				output <- "Booting virtual machine"
			case fmt.Sprintf("==> %v: Configuring and enabling network interfaces...", app):
				output <- "Configuring virtual network"
			case fmt.Sprintf("==> %v: Mounting shared folders...", app):
				output <- fmt.Sprintf("Mounting source code (%s)", config.CWDir)
			case fmt.Sprintf("==> %v: Waiting for nanobox server...", app):
				output <- "Starting nanobox server"
			case fmt.Sprintf("==> %v: Attempting graceful shutdown of VM...", app):
				output <- "Shutting down virtual machine"
			case fmt.Sprintf("==> %v: Destroying VM and associated drives...", app):
				// output <- "Destroying virtual machine"
			case fmt.Sprintf("==> %v: Forcing shutdown of VM...", app):
				output <- "Shutting down virtual machine"
			case fmt.Sprintf("==> %v: Saving VM state and suspending execution...", app):
				output <- "Saving virtual machine"
			case fmt.Sprintf("==> %v: Resuming suspended VM...", app):
				// output <- "Resuming virtual machine"
			}
		}

		// close the output channel once all lines of command output have been read
		close(output)
	}()
}
<|endoftext|>