<commit_before>package golf

import (
	"bytes"
	"testing"
	"regexp"
)

func assertContains(t *testing.T, content string, query string) {
	re := regexp.MustCompile(query)
	if re.FindString(content) == "" {
		t.Errorf("Not contain: %v in %v", query, content)
	}
}

func TestLogger(t *testing.T) {
	buffer := new(bytes.Buffer)
	app := New()
	app.Use(LoggingMiddleware(buffer))
	app.Get("/example", func(c *Context) {})
	app.Post("/example", func(c *Context) {})
	app.Put("/example", func(c *Context) {})
	app.Delete("/example", func(c *Context) {})
	app.Patch("/example", func(c *Context) {})
	app.Head("/example", func(c *Context) {})
	app.Options("/example", func(c *Context) {})

	_, _, r, w := makeTestContext("GET", "/example")
	app.ServeHTTP(w, r)
	assertContains(t, buffer.String(), "200")
	assertContains(t, buffer.String(), "GET")
	assertContains(t, buffer.String(), "/example")

	// I wrote these first (extending the above) but then realized they are more
	// like integration tests because they test the whole logging process rather
	// than individual functions.  Im not sure where these should go.

	_, _, r, w = makeTestContext("POST", "/example")
	app.ServeHTTP(w, r)
	assertContains(t, buffer.String(), "200")
	assertContains(t, buffer.String(), "POST")
	assertContains(t, buffer.String(), "/example")

	_, _, r, w = makeTestContext("PUT", "/example")
	app.ServeHTTP(w, r)
	assertContains(t, buffer.String(), "200")
	assertContains(t, buffer.String(), "PUT")
	assertContains(t, buffer.String(), "/example")

	_, _, r, w = makeTestContext("DELETE", "/example")
	app.ServeHTTP(w, r)
	assertContains(t, buffer.String(), "200")
	assertContains(t, buffer.String(), "DELETE")
	assertContains(t, buffer.String(), "/example")

	_, _, r, w = makeTestContext("PATCH", "/example")
	app.ServeHTTP(w, r)
	assertContains(t, buffer.String(), "200")
	assertContains(t, buffer.String(), "PATCH")
	assertContains(t, buffer.String(), "/example")

	_, _, r, w = makeTestContext("HEAD", "/example")
	app.ServeHTTP(w, r)
	assertContains(t, buffer.String(), "200")
	assertContains(t, buffer.String(), "HEAD")
	assertContains(t, buffer.String(), "/example")

	_, _, r, w = makeTestContext("OPTIONS", "/example")
	app.ServeHTTP(w, r)
	assertContains(t, buffer.String(), "200")
	assertContains(t, buffer.String(), "OPTIONS")
	assertContains(t, buffer.String(), "/example")

	_, _, r, w = makeTestContext("GET", "/notfound")
	app.ServeHTTP(w, r)
	assertContains(t, buffer.String(), "404")
	assertContains(t, buffer.String(), "GET")
	assertContains(t, buffer.String(), "/notfound")
}
<commit_msg>[refactor] Improve coding format<commit_after>package golf

import (
	"bytes"
	"regexp"
	"testing"
)

func assertContains(t *testing.T, content string, query string) {
	re := regexp.MustCompile(query)
	if re.FindString(content) == "" {
		t.Errorf("Not contain: %v in %v", query, content)
	}
}

func TestLogger(t *testing.T) {
	buffer := new(bytes.Buffer)
	app := New()
	app.Use(LoggingMiddleware(buffer))
	app.Get("/example", func(c *Context) {})
	app.Post("/example", func(c *Context) {})
	app.Put("/example", func(c *Context) {})
	app.Delete("/example", func(c *Context) {})
	app.Patch("/example", func(c *Context) {})
	app.Head("/example", func(c *Context) {})
	app.Options("/example", func(c *Context) {})

	_, _, r, w := makeTestContext("GET", "/example")
	app.ServeHTTP(w, r)
	assertContains(t, buffer.String(), "200")
	assertContains(t, buffer.String(), "GET")
	assertContains(t, buffer.String(), "/example")

	// I wrote these first (extending the above) but then realized they are more
	// like integration tests because they test the whole logging process rather
	// than individual functions.  Im not sure where these should go.

	_, _, r, w = makeTestContext("POST", "/example")
	app.ServeHTTP(w, r)
	assertContains(t, buffer.String(), "200")
	assertContains(t, buffer.String(), "POST")
	assertContains(t, buffer.String(), "/example")

	_, _, r, w = makeTestContext("PUT", "/example")
	app.ServeHTTP(w, r)
	assertContains(t, buffer.String(), "200")
	assertContains(t, buffer.String(), "PUT")
	assertContains(t, buffer.String(), "/example")

	_, _, r, w = makeTestContext("DELETE", "/example")
	app.ServeHTTP(w, r)
	assertContains(t, buffer.String(), "200")
	assertContains(t, buffer.String(), "DELETE")
	assertContains(t, buffer.String(), "/example")

	_, _, r, w = makeTestContext("PATCH", "/example")
	app.ServeHTTP(w, r)
	assertContains(t, buffer.String(), "200")
	assertContains(t, buffer.String(), "PATCH")
	assertContains(t, buffer.String(), "/example")

	_, _, r, w = makeTestContext("HEAD", "/example")
	app.ServeHTTP(w, r)
	assertContains(t, buffer.String(), "200")
	assertContains(t, buffer.String(), "HEAD")
	assertContains(t, buffer.String(), "/example")

	_, _, r, w = makeTestContext("OPTIONS", "/example")
	app.ServeHTTP(w, r)
	assertContains(t, buffer.String(), "200")
	assertContains(t, buffer.String(), "OPTIONS")
	assertContains(t, buffer.String(), "/example")

	_, _, r, w = makeTestContext("GET", "/notfound")
	app.ServeHTTP(w, r)
	assertContains(t, buffer.String(), "404")
	assertContains(t, buffer.String(), "GET")
	assertContains(t, buffer.String(), "/notfound")
}
<|endoftext|>


<commit_before>package psdock

import (
	"bytes"
	"code.google.com/p/go.crypto/ssh/terminal"
	"errors"
	"github.com/kr/pty"
	"log"
	"net/http"
	"os"
	"os/exec"
	"os/signal"
	"os/user"
	"strconv"
	"strings"
	"syscall"
	"time"
)

type Process struct {
	Cmd           *exec.Cmd
	Conf          *Config
	Pty           *os.File
	Term          *terminal.Terminal
	Status        int
	StatusChannel chan ProcessStatus
}

//NewProcess creates a new struct of type *Process and returns its address
func NewProcess(conf *Config) *Process {
	var cmd *exec.Cmd
	if len(conf.Args) > 0 {
		cmd = exec.Command(conf.Command, strings.Split(conf.Args, " ")...)
	} else {
		cmd = exec.Command(conf.Command)
	}
	newStatusChannel := make(chan ProcessStatus, 1)
	return &Process{Cmd: cmd, Conf: conf, StatusChannel: newStatusChannel}
}

//SetEnvVars sets the environment variables for the launched process
func (p *Process) SetEnvVars() {
	if len(p.Conf.EnvVars) == 0 {
		return
	}
	for _, envVar := range strings.Split(p.Conf.EnvVars, ",") {
		p.Cmd.Env = append(p.Cmd.Env, envVar)
	}
}

//ManageSignals awaits for incoming signals and triggers a http request when one
//is received. Signals listened to are SIGINT, SIGQUIT, SIGTERM, SIGHUP, SIGALRM and SIGPIPE
func (p *Process) ManageSignals() {
	signalChannel := make(chan os.Signal)
	signal.Notify(signalChannel, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGHUP, syscall.SIGALRM, syscall.SIGPIPE)
	_ = <-signalChannel

	//Terminate the process and notify
	termErr := p.Terminate()
	p.StatusChannel <- ProcessStatus{Status: PROCESS_STOPPED, Err: termErr}
}

//SetUser tries to change the current user to newUsername
func (p *Process) SetUser() error {
	currentUser, err := user.Current()
	if err != nil {
		return errors.New("Can't determine the current user !\n" + err.Error())
	}

	if p.Conf.UserName == currentUser.Username {
		return nil
	}

	newUser, err := user.Lookup(p.Conf.UserName)
	if err != nil {
		return errors.New("Can't find the user" + p.Conf.UserName + "!\n" + err.Error())
	}

	newUserUID, err := strconv.Atoi(newUser.Uid)
	if err != nil {
		return errors.New("Can't determine the new user UID !\n" + err.Error())
	}

	if err := syscall.Setuid(newUserUID); err != nil {
		return errors.New("Can't change the user !\n" + err.Error())
	}

	return nil
}

func (p *Process) Terminate() error {
	var err error
	for i := 0; i < 5; i++ {
		if err = syscall.Kill(p.Cmd.Process.Pid, syscall.SIGTERM); err == nil {
			return nil
		} else {
			log.Print("Error while trying to kill the process" + err.Error())
			log.Print("retrying...")
		}
	}
	log.Print("Tried 5 times. Will now send a SIGKILL signal to child process")
	if err = syscall.Kill(p.Cmd.Process.Pid, syscall.SIGKILL); err != nil {
		return errors.New("Can't send SIGKILL:" + err.Error())
	}
	return nil
}

func (p *Process) isStarted() bool {
	return p.Cmd.Process != nil
}

func (p *Process) isRunning() bool {
	return p.isStarted() && p.hasBoundPort()
}

func (p *Process) hasBoundPort() bool {
	if p.Conf.BindPort == 0 {
		return true
	}

	//We execute netstat -an | grep bindPort to find if bindPort is open
	netstCmd := exec.Command("netstat", "-an")
	grepCmd := exec.Command("grep", string(p.Conf.BindPort))
	netstOut, _ := netstCmd.Output()
	grepCmd.Stdin = bytes.NewBuffer(netstOut)
	grepOut, _ := grepCmd.Output()

	return len(grepOut) > 0
}

func (p *Process) redirectStdin() error {
	//TODO, will need to store stdin old state in the Process struct
	return nil
}

func (p *Process) restoreStdin() error {
	//TODO
	return nil
}

func (p *Process) redirectStdout() error {
	//TODO, get inspired by parklog
	return nil
}

func (p *Process) NotifyStatusChanged() error {
	if p.Conf.WebHook == "" {
		return nil
	}
	statusStr := ""
	if p.Status == PROCESS_STARTED {
		statusStr = "started"
	} else if p.Status == PROCESS_RUNNING {
		statusStr = "running"
	} else {
		statusStr = "stopped"
	}
	body := `{
							"ps":
								{ "status":` + statusStr + `}
						}`

	req, err := http.NewRequest("PUT", p.Conf.WebHook, bytes.NewBufferString(body))
	if err != nil {
		return errors.New("Failed to construct the HTTP request" + err.Error())
	}

	req.Header.Add("Content-Type", "application/json")
	client := &http.Client{}

	resp, err := client.Do(req)
	defer resp.Body.Close()
	if err != nil {
		return errors.New("Was not able to trigger the hook!\n" + err.Error())
	}
	return nil
}

func (p *Process) Start() error {
	//We start the process
	var startErr error
	p.Pty, startErr = pty.Start(p.Cmd)
	if startErr != nil {
		return startErr
	}

	go func() {
		var err error
		if err = p.redirectStdin(); err != nil {
			p.StatusChannel <- ProcessStatus{Status: -1, Err: err}
		}
		if err = p.redirectStdout(); err != nil {
			p.StatusChannel <- ProcessStatus{Status: -1, Err: err}
		}

		for !p.isStarted() {
			time.Sleep(100 * time.Millisecond)
		}
		p.Status = PROCESS_STARTED
		if err = p.NotifyStatusChanged(); err != nil {
			p.StatusChannel <- ProcessStatus{Status: -1, Err: err}
		}
		p.StatusChannel <- ProcessStatus{Status: p.Status, Err: nil}

		for p.isRunning() == false {
			time.Sleep(100 * time.Millisecond)
		}
		p.Status = PROCESS_RUNNING
		if err = p.NotifyStatusChanged(); err != nil {
			p.StatusChannel <- ProcessStatus{Status: -1, Err: err}
		}
		p.StatusChannel <- ProcessStatus{Status: p.Status, Err: nil}

		err = p.Cmd.Wait()
		if err != nil {
			p.restoreStdin()
			panic(err)
		}

		//p has stopped
		p.restoreStdin()
		p.Status = PROCESS_STOPPED
		if err = p.NotifyStatusChanged(); err != nil {
			p.StatusChannel <- ProcessStatus{Status: -1, Err: err}
		}
		p.StatusChannel <- ProcessStatus{Status: p.Status, Err: nil}
	}()

	return nil
}
<commit_msg>Modified error logging<commit_after>package psdock

import (
	"bytes"
	"code.google.com/p/go.crypto/ssh/terminal"
	"errors"
	"github.com/kr/pty"
	"log"
	"net/http"
	"os"
	"os/exec"
	"os/signal"
	"os/user"
	"strconv"
	"strings"
	"syscall"
	"time"
)

type Process struct {
	Cmd           *exec.Cmd
	Conf          *Config
	Pty           *os.File
	Term          *terminal.Terminal
	Status        int
	StatusChannel chan ProcessStatus
}

//NewProcess creates a new struct of type *Process and returns its address
func NewProcess(conf *Config) *Process {
	var cmd *exec.Cmd
	if len(conf.Args) > 0 {
		cmd = exec.Command(conf.Command, strings.Split(conf.Args, " ")...)
	} else {
		cmd = exec.Command(conf.Command)
	}
	newStatusChannel := make(chan ProcessStatus, 1)
	return &Process{Cmd: cmd, Conf: conf, StatusChannel: newStatusChannel}
}

//SetEnvVars sets the environment variables for the launched process
func (p *Process) SetEnvVars() {
	if len(p.Conf.EnvVars) == 0 {
		return
	}
	for _, envVar := range strings.Split(p.Conf.EnvVars, ",") {
		p.Cmd.Env = append(p.Cmd.Env, envVar)
	}
}

//ManageSignals awaits for incoming signals and triggers a http request when one
//is received. Signals listened to are SIGINT, SIGQUIT, SIGTERM, SIGHUP, SIGALRM and SIGPIPE
func (p *Process) ManageSignals() {
	signalChannel := make(chan os.Signal)
	signal.Notify(signalChannel, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGHUP, syscall.SIGALRM, syscall.SIGPIPE)
	_ = <-signalChannel

	//Terminate the process and notify
	termErr := p.Terminate()
	p.StatusChannel <- ProcessStatus{Status: PROCESS_STOPPED, Err: termErr}
}

//SetUser tries to change the current user to newUsername
func (p *Process) SetUser() error {
	currentUser, err := user.Current()
	if err != nil {
		return errors.New("Can't determine the current user !\n" + err.Error())
	}

	if p.Conf.UserName == currentUser.Username {
		return nil
	}

	newUser, err := user.Lookup(p.Conf.UserName)
	if err != nil {
		return errors.New("Can't find the user" + p.Conf.UserName + "!\n" + err.Error())
	}

	newUserUID, err := strconv.Atoi(newUser.Uid)
	if err != nil {
		return errors.New("Can't determine the new user UID !\n" + err.Error())
	}

	if err := syscall.Setuid(newUserUID); err != nil {
		return errors.New("Can't change the user !\n" + err.Error())
	}

	return nil
}

func (p *Process) Terminate() error {
	var err error
	for i := 0; i < 5; i++ {
		if err = syscall.Kill(p.Cmd.Process.Pid, syscall.SIGTERM); err == nil {
			return nil
		} else {
			log.Print("Error while trying to kill the process" + err.Error())
			log.Print("retrying...")
		}
	}
	log.Print("Tried 5 times. Will now send a SIGKILL signal to child process")
	if err = syscall.Kill(p.Cmd.Process.Pid, syscall.SIGKILL); err != nil {
		return errors.New("Can't send SIGKILL:" + err.Error())
	}
	return nil
}

func (p *Process) isStarted() bool {
	return p.Cmd.Process != nil
}

func (p *Process) isRunning() bool {
	return p.isStarted() && p.hasBoundPort()
}

func (p *Process) hasBoundPort() bool {
	if p.Conf.BindPort == 0 {
		return true
	}

	//We execute netstat -an | grep bindPort to find if bindPort is open
	netstCmd := exec.Command("netstat", "-an")
	grepCmd := exec.Command("grep", string(p.Conf.BindPort))
	netstOut, _ := netstCmd.Output()
	grepCmd.Stdin = bytes.NewBuffer(netstOut)
	grepOut, _ := grepCmd.Output()

	return len(grepOut) > 0
}

func (p *Process) redirectStdin() error {
	//TODO, will need to store stdin old state in the Process struct
	return nil
}

func (p *Process) restoreStdin() error {
	//TODO
	return nil
}

func (p *Process) redirectStdout() error {
	//TODO, get inspired by parklog
	return nil
}

func (p *Process) NotifyStatusChanged() error {
	if p.Conf.WebHook == "" {
		return nil
	}
	statusStr := ""
	if p.Status == PROCESS_STARTED {
		statusStr = "started"
	} else if p.Status == PROCESS_RUNNING {
		statusStr = "running"
	} else {
		statusStr = "stopped"
	}
	body := `{
							"ps":
								{ "status":` + statusStr + `}
						}`

	req, err := http.NewRequest("PUT", p.Conf.WebHook, bytes.NewBufferString(body))
	if err != nil {
		return errors.New("Failed to construct the HTTP request" + err.Error())
	}

	req.Header.Add("Content-Type", "application/json")
	client := &http.Client{}

	resp, err := client.Do(req)
	defer resp.Body.Close()
	if err != nil {
		return errors.New("Was not able to trigger the hook!\n" + err.Error())
	}
	return nil
}

func (p *Process) Start() error {
	//We start the process
	var startErr error
	p.Pty, startErr = pty.Start(p.Cmd)
	if startErr != nil {
		return startErr
	}

	go func() {
		var err error
		if err = p.redirectStdin(); err != nil {
			p.StatusChannel <- ProcessStatus{Status: -1, Err: err}
		}
		if err = p.redirectStdout(); err != nil {
			p.StatusChannel <- ProcessStatus{Status: -1, Err: err}
		}

		for !p.isStarted() {
			time.Sleep(100 * time.Millisecond)
		}
		p.Status = PROCESS_STARTED
		if err = p.NotifyStatusChanged(); err != nil {
			log.Println(err)
		}
		p.StatusChannel <- ProcessStatus{Status: p.Status, Err: nil}

		for p.isRunning() == false {
			time.Sleep(100 * time.Millisecond)
		}
		p.Status = PROCESS_RUNNING
		if err = p.NotifyStatusChanged(); err != nil {
			log.Println(err)
		}
		p.StatusChannel <- ProcessStatus{Status: p.Status, Err: nil}

		err = p.Cmd.Wait()
		if err != nil {
			p.restoreStdin()
			panic(err)
		}

		//p has stopped
		p.restoreStdin()
		p.Status = PROCESS_STOPPED
		if err = p.NotifyStatusChanged(); err != nil {
			log.Println(err)
		}
		p.StatusChannel <- ProcessStatus{Status: p.Status, Err: nil}
	}()

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2015 The pmon 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 pmon

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"strings"
	"time"

	sys "golang.org/x/sys/unix"
)

type Process struct {
	W    io.Writer
	Freq time.Duration

	quit chan struct{}

	fc chan func() error
	ec chan error

	cmd  *exec.Cmd
	proc *os.Process

	start func() error
	stop  func() error
}

func New(cmd string, args ...string) *Process {
	c := exec.Command(cmd, args...)
	c.Stdin = os.Stdin
	c.Stdout = os.Stdout
	c.Stderr = os.Stderr

	c.SysProcAttr = &sys.SysProcAttr{
		Ptrace:  true,
		Setpgid: true,
	}

	proc := &Process{
		cmd:  c,
		Freq: 1 * time.Second,
		W:    ioutil.Discard,
		quit: make(chan struct{}),
		fc:   make(chan func() error),
		ec:   make(chan error),

		start: c.Start,
		stop: func() error {
			pgid, err := sys.Getpgid(c.Process.Pid)
			if err != nil {
				return fmt.Errorf("could not get process group of pid=%d: %w", c.Process.Pid, err)
			}
			err = sys.Kill(-pgid, sys.SIGKILL) // note the minus sign
			if err != nil {
				return fmt.Errorf("could not kill process group %d: %w", pgid, err)
			}

			return nil
		},
	}

	go ptraceRun(proc.fc, proc.ec)
	return proc
}

func (p *Process) Run() error {
	switch {
	case p.cmd != nil:
		return p.runCmd()
	default:
		return p.runPID()
	}
}

func (p *Process) runCmd() error {
	err := p.ptraceRun(p.cmd.Start)
	if err != nil {
		return fmt.Errorf("could not start process: %w", err)
	}

	start := time.Now()

	pid := p.cmd.Process.Pid
	collector, err := newCollector(pid)
	if err != nil {
		return fmt.Errorf("could not create collector: %w", err)
	}
	defer collector.Close()

	_, err = fmt.Fprintf(p.W,
		"# pmon: %s\n# freq: %v\n# format: %#v\n# start: %v\n",
		strings.Join(p.cmd.Args, " "),
		p.Freq,
		Infos{},
		start,
	)
	if err != nil {
		return fmt.Errorf("error writing log-file header: %w", err)
	}

	defer func() {
		stop := time.Now()
		delta := time.Since(start)
		_, _ = fmt.Fprintf(p.W,
			"# elapsed: %v\n# stop: %v\n",
			delta,
			stop,
		)
	}()

	err = p.wait(pid, 0)
	if err != nil {
		return fmt.Errorf("waiting for target execve failed: %w", err)
	}

	err = p.ptraceDetach(pid)
	if err != nil {
		return fmt.Errorf("could not ptrace-detach pid=%d: %w", pid, err)
	}

	go p.monitor(collector)

	log.Printf(
		"monitoring... (pid=%d, freq=%v)\n",
		p.cmd.Process.Pid,
		p.Freq,
	)
	err = p.cmd.Wait()
	p.quit <- struct{}{}
	if err != nil {
		return fmt.Errorf("could not wait for pid=%d: %w", pid, err)
	}

	return nil
}

func (p *Process) runPID() error {
	start := time.Now()

	pid := p.proc.Pid
	collector, err := newCollector(pid)
	if err != nil {
		return fmt.Errorf("could not create collector: %w", err)
	}
	defer collector.Close()

	_, err = fmt.Fprintf(p.W,
		"# pmon: %s\n# freq: %v\n# format: %#v\n# start: %v\n",
		p.cmdline(pid),
		p.Freq,
		Infos{},
		start,
	)
	if err != nil {
		return fmt.Errorf("error writing log-file header: %w", err)
	}

	defer func() {
		stop := time.Now()
		delta := time.Since(start)
		_, _ = fmt.Fprintf(p.W,
			"# elapsed: %v\n# stop: %v\n",
			delta,
			stop,
		)
	}()

	go p.monitor(collector)

	log.Printf(
		"monitoring... (pid=%d, freq=%v)\n",
		p.proc.Pid,
		p.Freq,
	)
	<-p.quit
	if err != nil {
		return fmt.Errorf("could not wait for pid=%d: %w", pid, err)
	}

	return nil
}

func (p *Process) Kill() error {
	return p.stop()
}

func (p *Process) monitor(c *collector) {
	p.collect(c)
	tick := time.Tick(p.Freq)
	for {
		select {
		case <-tick:
			p.collect(c)
		case <-p.quit:
			return
		}
	}
}

func (p *Process) collect(c *collector) {

	if p.cmd != nil && p.cmd.ProcessState != nil {
		// process already stopped. nothing to collect.
		return
	}

	infos, err := c.collect()
	if err != nil {
		log.Printf("error collecting: %+v", err)
		return
	}

	fmt.Fprintf(
		p.W, "%f %f %f %d %d %d %d %d %d %d\n",
		milliseconds(infos.CPU), milliseconds(infos.UTime), milliseconds(infos.STime),
		infos.VMem, infos.RSS,
		infos.Threads,
		infos.Rchar, infos.Wchar,
		infos.Rdisk, infos.Wdisk,
	)
}

func milliseconds(t time.Duration) float64 {
	return t.Seconds() * 1e3
}
<commit_msg>pmon: make sure log file is flushed<commit_after>// Copyright 2015 The pmon 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 pmon

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"strings"
	"time"

	sys "golang.org/x/sys/unix"
)

type Process struct {
	W    io.Writer
	Freq time.Duration

	quit chan struct{}

	fc chan func() error
	ec chan error

	cmd  *exec.Cmd
	proc *os.Process

	start func() error
	stop  func() error
}

func New(cmd string, args ...string) *Process {
	c := exec.Command(cmd, args...)
	c.Stdin = os.Stdin
	c.Stdout = os.Stdout
	c.Stderr = os.Stderr

	c.SysProcAttr = &sys.SysProcAttr{
		Ptrace:  true,
		Setpgid: true,
	}

	proc := &Process{
		cmd:  c,
		Freq: 1 * time.Second,
		W:    ioutil.Discard,
		quit: make(chan struct{}),
		fc:   make(chan func() error),
		ec:   make(chan error),

		start: c.Start,
		stop: func() error {
			pgid, err := sys.Getpgid(c.Process.Pid)
			if err != nil {
				return fmt.Errorf("could not get process group of pid=%d: %w", c.Process.Pid, err)
			}
			err = sys.Kill(-pgid, sys.SIGKILL) // note the minus sign
			if err != nil {
				return fmt.Errorf("could not kill process group %d: %w", pgid, err)
			}

			return nil
		},
	}

	go ptraceRun(proc.fc, proc.ec)
	return proc
}

func (p *Process) Run() error {
	switch {
	case p.cmd != nil:
		return p.runCmd()
	default:
		return p.runPID()
	}
}

func (p *Process) runCmd() error {
	defer func() {
		if w, ok := p.W.(interface{ Flush() error }); ok {
			_ = w.Flush()
		}
	}()

	err := p.ptraceRun(p.cmd.Start)
	if err != nil {
		return fmt.Errorf("could not start process: %w", err)
	}

	start := time.Now()

	pid := p.cmd.Process.Pid
	collector, err := newCollector(pid)
	if err != nil {
		return fmt.Errorf("could not create collector: %w", err)
	}
	defer collector.Close()

	_, err = fmt.Fprintf(p.W,
		"# pmon: %s\n# freq: %v\n# format: %#v\n# start: %v\n",
		strings.Join(p.cmd.Args, " "),
		p.Freq,
		Infos{},
		start,
	)
	if err != nil {
		return fmt.Errorf("error writing log-file header: %w", err)
	}

	defer func() {
		stop := time.Now()
		delta := time.Since(start)
		_, _ = fmt.Fprintf(p.W,
			"# elapsed: %v\n# stop: %v\n",
			delta,
			stop,
		)
	}()

	err = p.wait(pid, 0)
	if err != nil {
		return fmt.Errorf("waiting for target execve failed: %w", err)
	}

	err = p.ptraceDetach(pid)
	if err != nil {
		return fmt.Errorf("could not ptrace-detach pid=%d: %w", pid, err)
	}

	go p.monitor(collector)

	log.Printf(
		"monitoring... (pid=%d, freq=%v)\n",
		p.cmd.Process.Pid,
		p.Freq,
	)
	err = p.cmd.Wait()
	p.quit <- struct{}{}
	if err != nil {
		return fmt.Errorf("could not wait for pid=%d: %w", pid, err)
	}

	return nil
}

func (p *Process) runPID() error {
	start := time.Now()

	pid := p.proc.Pid
	collector, err := newCollector(pid)
	if err != nil {
		return fmt.Errorf("could not create collector: %w", err)
	}
	defer collector.Close()

	_, err = fmt.Fprintf(p.W,
		"# pmon: %s\n# freq: %v\n# format: %#v\n# start: %v\n",
		p.cmdline(pid),
		p.Freq,
		Infos{},
		start,
	)
	if err != nil {
		return fmt.Errorf("error writing log-file header: %w", err)
	}

	defer func() {
		stop := time.Now()
		delta := time.Since(start)
		_, _ = fmt.Fprintf(p.W,
			"# elapsed: %v\n# stop: %v\n",
			delta,
			stop,
		)
	}()

	go p.monitor(collector)

	log.Printf(
		"monitoring... (pid=%d, freq=%v)\n",
		p.proc.Pid,
		p.Freq,
	)
	<-p.quit
	if err != nil {
		return fmt.Errorf("could not wait for pid=%d: %w", pid, err)
	}

	return nil
}

func (p *Process) Kill() error {
	return p.stop()
}

func (p *Process) monitor(c *collector) {
	p.collect(c)
	tick := time.Tick(p.Freq)
	for {
		select {
		case <-tick:
			p.collect(c)
		case <-p.quit:
			return
		}
	}
}

func (p *Process) collect(c *collector) {

	if p.cmd != nil && p.cmd.ProcessState != nil {
		// process already stopped. nothing to collect.
		return
	}

	infos, err := c.collect()
	if err != nil {
		log.Printf("error collecting: %+v", err)
		return
	}

	fmt.Fprintf(
		p.W, "%f %f %f %d %d %d %d %d %d %d\n",
		milliseconds(infos.CPU), milliseconds(infos.UTime), milliseconds(infos.STime),
		infos.VMem, infos.RSS,
		infos.Threads,
		infos.Rchar, infos.Wchar,
		infos.Rdisk, infos.Wdisk,
	)
}

func milliseconds(t time.Duration) float64 {
	return t.Seconds() * 1e3
}
<|endoftext|>


<commit_before>package dashboard

import (
	"log"
	"sync"
	"time"
)

var (
	defaultProjectMap map[string]*Project
	defaultProjects   = []*Project{
		newProject("jekyll", "jekyll/jekyll", "master", "jekyll"),
    newProject("jekyll-coffeescript", "jekyll/jekyll-coffeescript", "master", "jekyll-coffeescript"),
    newProject("jekyll-feed", "jekyll/jekyll-feed", "master", "jekyll-feed"),
		newProject("jekyll-gist", "jekyll/jekyll-gist", "master", "jekyll-gist"),
    newProject("github-metadata", "jekyll/github-metadata", "master", "jekyll-github-metadata"),
    newProject("jekyll-mentions", "jekyll/jekyll-mentions", "master", "jekyll-mentions"),
    newProject("jekyll-paginate", "jekyll/jekyll-paginate", "master", "jekyll-paginate"),
    newProject("jekyll-redirect-from", "jekyll/jekyll-redirect-from", "master", "jekyll-redirect-from"),
    newProject("jekyll-sass-converter", "jekyll/jekyll-sass-converter", "master", "jekyll-sass-converter"),
    newProject("jekyll-seo-tag", "jekyll/jekyll-seo-tag", "master", "jekyll-seo-tag"),
    newProject("jekyll-sitemap", "jekyll/jekyll-sitemap", "master", "jekyll-sitemap"),
    newProject("jemoji", "jekyll/jemoji", "master", "jemoji"),
		newProject("minima", "jekyll/minima", "master", "minima"),
    newProject("jekyll-compose", "jekyll/jekyll-compose", "master", "jekyll-compose"),
		newProject("jekyll-import", "jekyll/jekyll-import", "master", "jekyll-import"),
		newProject("jekyll-archives", "jekyll/jekyll-archives", "master", "jekyll-archives"),
    newProject("jekyll-watch", "jekyll/jekyll-watch", "master", "jekyll-watch"),
		newProject("jekyll-opal", "jekyll/jekyll-opal", "master", "jekyll-opal"),
		newProject("jekyll-commonmark", "jekyll/jekyll-commonmark", "master", "jekyll-commonmark"),
		newProject("jekyll-textile-converter", "jekyll/jekyll-textile-converter", "master", "jekyll-textile-converter"),
    newProject("jekyll-admin", "jekyll/jekyll-admin", "master", "jekyll-admin"),
		newProject("classifier-reborn", "jekyll/classifier-reborn", "master", "classifier-reborn"),
		newProject("mercenary", "jekyll/mercenary", "master", "mercenary"),
		newProject("plugins website", "jekyll/plugins", "master", ""),
	}
)

func init() {
	go resetProjectsPeriodically()
}

func resetProjectsPeriodically() {
	for range time.Tick(time.Hour / 2) {
		log.Println("resetting projects' cache")
		resetProjects()
	}
}

func resetProjects() {
	for _, p := range defaultProjects {
		p.reset()
	}
}

type Project struct {
	Name    string `json:"name"`
	Nwo     string `json:"nwo"`
	Branch  string `json:"branch"`
	GemName string `json:"gem_name"`

	Gem     *RubyGem      `json:"gem"`
	Travis  *TravisReport `json:"travis"`
	GitHub  *GitHub       `json:"github"`
	fetched bool
}

func (p *Project) fetch() {
	rubyGemChan := rubygem(p.GemName)
	travisChan := travis(p.Nwo, p.Branch)
	githubChan := github(p.Nwo)

	if p.Gem == nil {
		p.Gem = <-rubyGemChan
	}

	if p.Travis == nil {
		p.Travis = <-travisChan
	}

	if p.GitHub == nil {
		p.GitHub = <-githubChan
	}

	p.fetched = true
}

func (p *Project) reset() {
	p.fetched = false
	p.Gem = nil
	p.Travis = nil
	p.GitHub = nil
}

func buildProjectMap() {
	defaultProjectMap = map[string]*Project{}
	for _, p := range defaultProjects {
		defaultProjectMap[p.Name] = p
	}
}

func newProject(name, nwo, branch, rubygem string) *Project {
	return &Project{
		Name:    name,
		Nwo:     nwo,
		Branch:  branch,
		GemName: rubygem,
	}
}

func getProject(name string) *Project {
	if defaultProjectMap == nil {
		buildProjectMap()
	}

	if p, ok := defaultProjectMap[name]; ok {
		if !p.fetched {
			p.fetch()
		}
		return p
	}

	return nil
}

func getAllProjects() []*Project {
	var wg sync.WaitGroup
	for _, p := range defaultProjects {
		wg.Add(1)
		go func(project *Project) {
			project.fetch()
			wg.Done()
		}(p)
	}
	wg.Wait()
	return defaultProjects
}

func getProjects() []*Project {
	return defaultProjects
}
<commit_msg>Fix indentation<commit_after>package dashboard

import (
	"log"
	"sync"
	"time"
)

var (
	defaultProjectMap map[string]*Project
	defaultProjects   = []*Project{
		newProject("jekyll", "jekyll/jekyll", "master", "jekyll"),
		newProject("jekyll-coffeescript", "jekyll/jekyll-coffeescript", "master", "jekyll-coffeescript"),
		newProject("jekyll-feed", "jekyll/jekyll-feed", "master", "jekyll-feed"),
		newProject("jekyll-gist", "jekyll/jekyll-gist", "master", "jekyll-gist"),
		newProject("github-metadata", "jekyll/github-metadata", "master", "jekyll-github-metadata"),
		newProject("jekyll-mentions", "jekyll/jekyll-mentions", "master", "jekyll-mentions"),
		newProject("jekyll-paginate", "jekyll/jekyll-paginate", "master", "jekyll-paginate"),
		newProject("jekyll-redirect-from", "jekyll/jekyll-redirect-from", "master", "jekyll-redirect-from"),
		newProject("jekyll-sass-converter", "jekyll/jekyll-sass-converter", "master", "jekyll-sass-converter"),
		newProject("jekyll-seo-tag", "jekyll/jekyll-seo-tag", "master", "jekyll-seo-tag"),
		newProject("jekyll-sitemap", "jekyll/jekyll-sitemap", "master", "jekyll-sitemap"),
		newProject("jemoji", "jekyll/jemoji", "master", "jemoji"),
		newProject("minima", "jekyll/minima", "master", "minima"),
		newProject("jekyll-compose", "jekyll/jekyll-compose", "master", "jekyll-compose"),
		newProject("jekyll-import", "jekyll/jekyll-import", "master", "jekyll-import"),
		newProject("jekyll-archives", "jekyll/jekyll-archives", "master", "jekyll-archives"),
		newProject("jekyll-watch", "jekyll/jekyll-watch", "master", "jekyll-watch"),
		newProject("jekyll-opal", "jekyll/jekyll-opal", "master", "jekyll-opal"),
		newProject("jekyll-commonmark", "jekyll/jekyll-commonmark", "master", "jekyll-commonmark"),
		newProject("jekyll-textile-converter", "jekyll/jekyll-textile-converter", "master", "jekyll-textile-converter"),
		newProject("jekyll-admin", "jekyll/jekyll-admin", "master", "jekyll-admin"),
		newProject("classifier-reborn", "jekyll/classifier-reborn", "master", "classifier-reborn"),
		newProject("mercenary", "jekyll/mercenary", "master", "mercenary"),
		newProject("plugins website", "jekyll/plugins", "master", ""),
	}
)

func init() {
	go resetProjectsPeriodically()
}

func resetProjectsPeriodically() {
	for range time.Tick(time.Hour / 2) {
		log.Println("resetting projects' cache")
		resetProjects()
	}
}

func resetProjects() {
	for _, p := range defaultProjects {
		p.reset()
	}
}

type Project struct {
	Name    string `json:"name"`
	Nwo     string `json:"nwo"`
	Branch  string `json:"branch"`
	GemName string `json:"gem_name"`

	Gem     *RubyGem      `json:"gem"`
	Travis  *TravisReport `json:"travis"`
	GitHub  *GitHub       `json:"github"`
	fetched bool
}

func (p *Project) fetch() {
	rubyGemChan := rubygem(p.GemName)
	travisChan := travis(p.Nwo, p.Branch)
	githubChan := github(p.Nwo)

	if p.Gem == nil {
		p.Gem = <-rubyGemChan
	}

	if p.Travis == nil {
		p.Travis = <-travisChan
	}

	if p.GitHub == nil {
		p.GitHub = <-githubChan
	}

	p.fetched = true
}

func (p *Project) reset() {
	p.fetched = false
	p.Gem = nil
	p.Travis = nil
	p.GitHub = nil
}

func buildProjectMap() {
	defaultProjectMap = map[string]*Project{}
	for _, p := range defaultProjects {
		defaultProjectMap[p.Name] = p
	}
}

func newProject(name, nwo, branch, rubygem string) *Project {
	return &Project{
		Name:    name,
		Nwo:     nwo,
		Branch:  branch,
		GemName: rubygem,
	}
}

func getProject(name string) *Project {
	if defaultProjectMap == nil {
		buildProjectMap()
	}

	if p, ok := defaultProjectMap[name]; ok {
		if !p.fetched {
			p.fetch()
		}
		return p
	}

	return nil
}

func getAllProjects() []*Project {
	var wg sync.WaitGroup
	for _, p := range defaultProjects {
		wg.Add(1)
		go func(project *Project) {
			project.fetch()
			wg.Done()
		}(p)
	}
	wg.Wait()
	return defaultProjects
}

func getProjects() []*Project {
	return defaultProjects
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"sort"

	yaml "gopkg.in/yaml.v2"

	"github.com/lxc/lxd/lxc/config"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/gnuflag"
	"github.com/lxc/lxd/shared/i18n"
	"github.com/olekukonko/tablewriter"
)

type clusterCmd struct {
	force bool
}

func (c *clusterCmd) usage() string {
	return i18n.G(
		`Usage: lxc cluster <subcommand> [options]

Manage cluster nodes.

lxc cluster list [<remote>:]
    List all nodes in the cluster.

lxc cluster show [<remote>:]<node>
    Show details of a node.

lxc cluster rename [<remote>:]<node> <new-name>
    Rename a cluster node.

lxc cluster delete [<remote>:]<node> [--force]
    Delete a node from the cluster.`)
}

func (c *clusterCmd) flags() {
	gnuflag.BoolVar(&c.force, "force", false, i18n.G("Force removing a node, even if degraded"))
}

func (c *clusterCmd) showByDefault() bool {
	return true
}

func (c *clusterCmd) run(conf *config.Config, args []string) error {
	if len(args) < 1 {
		return errUsage
	}

	switch args[0] {
	case "list":
		return c.doClusterList(conf, args)
	case "show":
		return c.doClusterNodeShow(conf, args)
	case "rename":
		return c.doClusterNodeRename(conf, args)
	case "delete":
		return c.doClusterNodeDelete(conf, args)
	}

	return nil
}

func (c *clusterCmd) doClusterNodeShow(conf *config.Config, args []string) error {
	if len(args) < 2 {
		return errArgs
	}

	remote, name, err := conf.ParseRemote(args[1])
	if err != nil {
		return err
	}

	client, err := conf.GetContainerServer(remote)
	if err != nil {
		return err
	}

	node, err := client.GetNode(name)
	if err != nil {
		return err
	}

	data, err := yaml.Marshal(&node)
	if err != nil {
		return err
	}

	fmt.Printf("%s", data)

	return nil
}

func (c *clusterCmd) doClusterNodeRename(conf *config.Config, args []string) error {
	if len(args) < 3 {
		return errArgs
	}
	newName := args[2]

	remote, name, err := conf.ParseRemote(args[1])
	if err != nil {
		return err
	}

	client, err := conf.GetContainerServer(remote)
	if err != nil {
		return err
	}

	err = client.RenameNode(name, api.NodePost{Name: newName})
	if err != nil {
		return err
	}

	fmt.Printf(i18n.G("Node %s renamed to %s")+"\n", name, newName)
	return nil
}

func (c *clusterCmd) doClusterNodeDelete(conf *config.Config, args []string) error {
	if len(args) < 2 {
		return errArgs
	}

	remote, name, err := conf.ParseRemote(args[1])
	if err != nil {
		return err
	}

	client, err := conf.GetContainerServer(remote)
	if err != nil {
		return err
	}

	err = client.LeaveCluster(name, c.force)
	if err != nil {
		return err
	}

	fmt.Printf(i18n.G("Node %s removed")+"\n", name)
	return nil
}

func (c *clusterCmd) doClusterList(conf *config.Config, args []string) error {
	remote := conf.DefaultRemote

	if len(args) > 1 {
		var err error
		remote, _, err = conf.ParseRemote(args[1])
		if err != nil {
			return err
		}
	}

	client, err := conf.GetContainerServer(remote)
	if err != nil {
		return err
	}

	nodes, err := client.GetNodes()
	if err != nil {
		return err
	}

	data := [][]string{}
	for _, node := range nodes {
		database := "NO"
		if node.Database {
			database = "YES"
		}
		line := []string{node.Name, node.URL, database, node.State, node.Message}
		data = append(data, line)
	}

	table := tablewriter.NewWriter(os.Stdout)
	table.SetAutoWrapText(false)
	table.SetAlignment(tablewriter.ALIGN_LEFT)
	table.SetRowLine(true)
	table.SetHeader([]string{
		i18n.G("NAME"),
		i18n.G("URL"),
		i18n.G("DATABASE"),
		i18n.G("STATE"),
		i18n.G("MESSAGE"),
	})
	sort.Sort(byName(data))
	table.AppendBulk(data)
	table.Render()

	return nil
}
<commit_msg>Make lxc cluster return an an error if the sub-command is unknown<commit_after>package main

import (
	"fmt"
	"os"
	"sort"

	yaml "gopkg.in/yaml.v2"

	"github.com/lxc/lxd/lxc/config"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/gnuflag"
	"github.com/lxc/lxd/shared/i18n"
	"github.com/olekukonko/tablewriter"
)

type clusterCmd struct {
	force bool
}

func (c *clusterCmd) usage() string {
	return i18n.G(
		`Usage: lxc cluster <subcommand> [options]

Manage cluster nodes.

lxc cluster list [<remote>:]
    List all nodes in the cluster.

lxc cluster show [<remote>:]<node>
    Show details of a node.

lxc cluster rename [<remote>:]<node> <new-name>
    Rename a cluster node.

lxc cluster delete [<remote>:]<node> [--force]
    Delete a node from the cluster.`)
}

func (c *clusterCmd) flags() {
	gnuflag.BoolVar(&c.force, "force", false, i18n.G("Force removing a node, even if degraded"))
}

func (c *clusterCmd) showByDefault() bool {
	return true
}

func (c *clusterCmd) run(conf *config.Config, args []string) error {
	if len(args) < 1 {
		return errUsage
	}

	switch args[0] {
	case "list":
		return c.doClusterList(conf, args)
	case "show":
		return c.doClusterNodeShow(conf, args)
	case "rename":
		return c.doClusterNodeRename(conf, args)
	case "delete":
		return c.doClusterNodeDelete(conf, args)
	default:
		return errArgs
	}
}

func (c *clusterCmd) doClusterNodeShow(conf *config.Config, args []string) error {
	if len(args) < 2 {
		return errArgs
	}

	remote, name, err := conf.ParseRemote(args[1])
	if err != nil {
		return err
	}

	client, err := conf.GetContainerServer(remote)
	if err != nil {
		return err
	}

	node, err := client.GetNode(name)
	if err != nil {
		return err
	}

	data, err := yaml.Marshal(&node)
	if err != nil {
		return err
	}

	fmt.Printf("%s", data)

	return nil
}

func (c *clusterCmd) doClusterNodeRename(conf *config.Config, args []string) error {
	if len(args) < 3 {
		return errArgs
	}
	newName := args[2]

	remote, name, err := conf.ParseRemote(args[1])
	if err != nil {
		return err
	}

	client, err := conf.GetContainerServer(remote)
	if err != nil {
		return err
	}

	err = client.RenameNode(name, api.NodePost{Name: newName})
	if err != nil {
		return err
	}

	fmt.Printf(i18n.G("Node %s renamed to %s")+"\n", name, newName)
	return nil
}

func (c *clusterCmd) doClusterNodeDelete(conf *config.Config, args []string) error {
	if len(args) < 2 {
		return errArgs
	}

	remote, name, err := conf.ParseRemote(args[1])
	if err != nil {
		return err
	}

	client, err := conf.GetContainerServer(remote)
	if err != nil {
		return err
	}

	err = client.LeaveCluster(name, c.force)
	if err != nil {
		return err
	}

	fmt.Printf(i18n.G("Node %s removed")+"\n", name)
	return nil
}

func (c *clusterCmd) doClusterList(conf *config.Config, args []string) error {
	remote := conf.DefaultRemote

	if len(args) > 1 {
		var err error
		remote, _, err = conf.ParseRemote(args[1])
		if err != nil {
			return err
		}
	}

	client, err := conf.GetContainerServer(remote)
	if err != nil {
		return err
	}

	nodes, err := client.GetNodes()
	if err != nil {
		return err
	}

	data := [][]string{}
	for _, node := range nodes {
		database := "NO"
		if node.Database {
			database = "YES"
		}
		line := []string{node.Name, node.URL, database, node.State, node.Message}
		data = append(data, line)
	}

	table := tablewriter.NewWriter(os.Stdout)
	table.SetAutoWrapText(false)
	table.SetAlignment(tablewriter.ALIGN_LEFT)
	table.SetRowLine(true)
	table.SetHeader([]string{
		i18n.G("NAME"),
		i18n.G("URL"),
		i18n.G("DATABASE"),
		i18n.G("STATE"),
		i18n.G("MESSAGE"),
	})
	sort.Sort(byName(data))
	table.AppendBulk(data)
	table.Render()

	return nil
}
<|endoftext|>


<commit_before>package seven5

import (
	"fmt"
	"net/http"
	"os"
	"path/filepath"
)

const (
	WAITING_ON_NON_WS = iota
	WAITING_ON_EOL
)

//generateStringPrinter creates a function suitable for use with a ServeMux's handle func.
func generateStringPrinter(content string, contentType string) func(http.ResponseWriter, *http.Request) {
	return generateBinPrinter([]byte(content), contentType)
}

//generateBinPrinter creates a function suitable for use with a ServeMux's handle func. It writes out the
//content type as a sequence bytes.
func generateBinPrinter(content []byte, contentType string) func(http.ResponseWriter, *http.Request) {
	return func(writer http.ResponseWriter, req *http.Request) {
		writer.Header().Add("Content-type", contentType)
		_, err := writer.Write(content)
		if err != nil {
			fmt.Printf("error writing constant binary string: %s\n", err)
		}
	}
}

//DefaultProjects adds the resources that we expect to be present for a typical
//seven5 project.  The ProjectFinder is used to find things inside the project, notably the
//static web content. Content added here is all fixed by the build of seven5 and the
//underlying filesystem.
func DefaultProjectBindings(projectName string, pf ProjectFinder, dep DeploymentEnvironment) *ServeMux {
	mux := NewServeMux()
	WebContent(mux, projectName, "/", pf, dep.IsTest())
	SetIcon(mux, gopher_ico)
	return mux
}

//SetIcon creates a go handler in h that will return an icon to be displayed in response to /favicon.ico.
//The binaryIcon should be an array of bytes (usually created via 'seven5tool embedfile')
func SetIcon(mux *ServeMux, binaryIcon []byte) {
	mux.HandleFunc("/favicon.ico", generateBinPrinter(binaryIcon, "image/x-icon"))
}

//FileContent cause the generation of files that need refreshing on each restart.  Typically, this
//is code derived from the go structs that the application exposes.
func FileContent(projectName string, pf ProjectFinder, holder TypeHolder, restPrefix string) {
	sourceDir := filepath.Join("web", "packages", projectName, "src")
	p, err := pf.ProjectFind(sourceDir, projectName, DART_FLAVOR)
	if err != nil {
		panic(fmt.Sprintf("can't find the place to put generated dart source! expected %s but got an error: %s",
			sourceDir, err))
	}
	outputPath := filepath.Join(p, fmt.Sprintf("%s.dart", projectName))
	file, err := os.Create(outputPath)
	if err != nil {
		panic(fmt.Sprintf("can't open dart source output path %s, got an error: %s", outputPath, err))
	}
	code := wrappedCodeGen(holder, restPrefix, projectName)
	file.Write(code.Bytes())
	file.Close()
}

//WebContent adds an http handler for the 'web' subdir of a dart app.  The project
//name is the subdir of 'dart' so the content is dart/projectName/web.  The prefix
//can be used if you don't want the static content mounted at '/' (the default if you pass ""
//as the prefix).  If you supply a prefix, it should end with /.
func WebContent(mux *ServeMux, projectName string, prefix string, pf ProjectFinder, isTestMode bool) {
	truePath, err := pf.ProjectFind("web", projectName, DART_FLAVOR)
	if err != nil {
		panic(fmt.Sprintf("can't understand GOPATH or not using default project layout: %s", err))
	}
	_, err = os.Open(truePath)
	if err != nil {
		panic(fmt.Sprintf("unable to open file resources at %s\n\tderived from your GOPATH\n", truePath))
	}
		mux.Handle(prefix, http.StripPrefix(prefix, http.FileServer(http.Dir(truePath))))
}
<commit_msg>created project blob, ready to connect it to hood<commit_after>package seven5

import (
	"fmt"
	"net/http"
	"os"
	"path/filepath"
	"github.com/iansmith/hood"
)

const (
	WAITING_ON_NON_WS = iota
	WAITING_ON_EOL
)

//generateStringPrinter creates a function suitable for use with a ServeMux's handle func.
func generateStringPrinter(content string, contentType string) func(http.ResponseWriter, *http.Request) {
	return generateBinPrinter([]byte(content), contentType)
}

//generateBinPrinter creates a function suitable for use with a ServeMux's handle func. It writes out the
//content type as a sequence bytes.
func generateBinPrinter(content []byte, contentType string) func(http.ResponseWriter, *http.Request) {
	return func(writer http.ResponseWriter, req *http.Request) {
		writer.Header().Add("Content-type", contentType)
		_, err := writer.Write(content)
		if err != nil {
			fmt.Printf("error writing constant binary string: %s\n", err)
		}
	}
}

//DefaultProjects adds the resources that we expect to be present for a typical
//seven5 project.  The ProjectFinder is used to find things inside the project, notably the
//static web content. Content added here is all fixed by the build of seven5 and the
//underlying filesystem.
func DefaultProjectBindings(projectName string, pf ProjectFinder, dep DeploymentEnvironment) *ServeMux {
	mux := NewServeMux()
	WebContent(mux, projectName, "/", pf, dep.IsTest())
	SetIcon(mux, gopher_ico)
	return mux
}

//SetIcon creates a go handler in h that will return an icon to be displayed in response to /favicon.ico.
//The binaryIcon should be an array of bytes (usually created via 'seven5tool embedfile')
func SetIcon(mux *ServeMux, binaryIcon []byte) {
	mux.HandleFunc("/favicon.ico", generateBinPrinter(binaryIcon, "image/x-icon"))
}

//FileContent cause the generation of files that need refreshing on each restart.  Typically, this
//is code derived from the go structs that the application exposes.
func FileContent(projectName string, pf ProjectFinder, holder TypeHolder, restPrefix string) {
	sourceDir := filepath.Join("web", "packages", projectName, "src")
	p, err := pf.ProjectFind(sourceDir, projectName, DART_FLAVOR)
	if err != nil {
		panic(fmt.Sprintf("can't find the place to put generated dart source! expected %s but got an error: %s",
			sourceDir, err))
	}
	outputPath := filepath.Join(p, fmt.Sprintf("%s.dart", projectName))
	file, err := os.Create(outputPath)
	if err != nil {
		panic(fmt.Sprintf("can't open dart source output path %s, got an error: %s", outputPath, err))
	}
	code := wrappedCodeGen(holder, restPrefix, projectName)
	file.Write(code.Bytes())
	file.Close()
}

//WebContent adds an http handler for the 'web' subdir of a dart app.  The project
//name is the subdir of 'dart' so the content is dart/projectName/web.  The prefix
//can be used if you don't want the static content mounted at '/' (the default if you pass ""
//as the prefix).  If you supply a prefix, it should end with /.
func WebContent(mux *ServeMux, projectName string, prefix string, pf ProjectFinder, isTestMode bool) {
	truePath, err := pf.ProjectFind("web", projectName, DART_FLAVOR)
	if err != nil {
		panic(fmt.Sprintf("can't understand GOPATH or not using default project layout: %s", err))
	}
	_, err = os.Open(truePath)
	if err != nil {
		panic(fmt.Sprintf("unable to open file resources at %s\n\tderived from your GOPATH\n", truePath))
	}
		mux.Handle(prefix, http.StripPrefix(prefix, http.FileServer(http.Dir(truePath))))
}

//Project is an amalgamation of differnt types that many applications can use
//to represent all the state needed to create and launch an application.  It
//does not understand how to have multiple named "apps" inside a single project
//and all of its pieces can be created separately if desired.
type Project struct {
	Name string
	RESTMountPoint string
	Hood *hood.Hood
	*EnvironmentVars
	*HerokuDeploy
	*ServeMux
	*BaseDispatcher
}

//NewProject creates a new Project amalgamation with a large number of defaults
//baked in.  Note that after this been created the serve mux and base dispatcher
//are already configured so you must reset them if you want to change their config.
func NewProject(name string) *Project{
	result := &Project{}
	result.Name = name
	result.RESTMountPoint = "/rest/"
	result.EnvironmentVars= NewEnvironmentVars(name)
	result.HerokuDeploy= &HerokuDeploy{name: name, env: result.EnvironmentVars}

	result.BaseDispatcher = NewBaseDispatcher(name, nil)
	result.ServeMux = DefaultProjectBindings(name, self.EnvironmentVars, self.HerokuDeploy)
  result.ServeMux.Dispatch(result.RESTMountPoint, bd)

	return result
}

//GenerateCode is a shorthand to tell the project to generate all the Dart code needed
//for the set of resources currently configured into the base dispatcher.
func (self *Project) GenerateCode() {
	FileContent(self.Name, self.EnvironmentVars, self.BaseDisptacher, self.RESTMountPoint)
}

<|endoftext|>


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

package main

import (
	"errors"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"sort"
	"strings"

	"github.com/tsuru/gnuflag"
	"github.com/tsuru/tsuru/cmd"
	tsuruerrors "github.com/tsuru/tsuru/errors"
)

type projectCreate struct {
	fs       *gnuflag.FlagSet
	name     string
	platform string
	team     string
	plan     string
	envs     commaSeparatedFlag
}

func (*projectCreate) Info() *cmd.Info {
	return &cmd.Info{
		Name: "project-create",
		Desc: "creates a remote project in the tranor server",
	}
}

func (c *projectCreate) Run(ctx *cmd.Context, client *cmd.Client) error {
	if c.name == "" || c.platform == "" {
		return errors.New("please provide the name and the platform")
	}
	config, err := loadConfigFile()
	if err != nil {
		return errors.New("unable to load environments file, please make sure that tranor is properly configured")
	}
	err = c.envs.validate(config.envNames())
	if err != nil {
		return fmt.Errorf("failed to load environments: %s", err)
	}
	envs := c.filterEnvironments(config.Environments, c.envs.Values())
	apps, err := c.createApps(envs, client)
	if err != nil {
		return err
	}
	err = c.setCNames(apps, client)
	if err != nil {
		appNames := make([]string, len(apps))
		for i, app := range apps {
			appNames[i] = app["name"]
		}
		deleteApps(appNames, client)
		return fmt.Errorf("failed to configure project %q: %s", c.name, err)
	}
	fmt.Fprintf(ctx.Stdout, "successfully created the project %q!\n", c.name)
	if gitRepo := apps[0]["repository_url"]; gitRepo != "" {
		fmt.Fprintf(ctx.Stdout, "Git repository: %s\n", gitRepo)
	}
	return nil
}

func (c *projectCreate) Flags() *gnuflag.FlagSet {
	if c.fs == nil {
		c.fs = gnuflag.NewFlagSet("project-create", gnuflag.ExitOnError)
		c.fs.StringVar(&c.name, "name", "", "name of the project")
		c.fs.StringVar(&c.name, "n", "", "name of the project")
		c.fs.StringVar(&c.platform, "platform", "", "platform of the project")
		c.fs.StringVar(&c.platform, "l", "", "platform of the project")
		c.fs.StringVar(&c.team, "team", "", "team that owns the project")
		c.fs.StringVar(&c.team, "t", "", "team that owns the project")
		c.fs.StringVar(&c.plan, "plan", "", "plan to use for the project")
		c.fs.StringVar(&c.plan, "p", "", "plan to use for the project")
		c.fs.Var(&c.envs, "envs", "comma-separated list of environments to use (defaults to dev,qa,stage,production)")
		c.fs.Var(&c.envs, "e", "comma-separated list of environments to use (defaults to env,qa,stage,production)")
		c.envs.Set("dev,qa,stage,production")
	}
	return c.fs
}

func (c *projectCreate) createApps(envs []Environment, client *cmd.Client) ([]map[string]string, error) {
	createdApps := make([]map[string]string, 0, len(envs))
	appNames := make([]string, 0, len(envs))
	for _, env := range envs {
		appName := fmt.Sprintf("%s-%s", c.name, env.Name)
		app, err := createApp(client, createAppOptions{
			name:     appName,
			plan:     c.plan,
			platform: c.platform,
			pool:     env.poolName(),
			team:     c.team,
		})
		if err != nil {
			deleteApps(appNames, client)
			return nil, fmt.Errorf("failed to create the project in env %q: %s", env.Name, err)
		}
		app["name"] = appName
		app["dnsSuffix"] = env.DNSSuffix
		createdApps = append(createdApps, app)
		appNames = append(appNames, appName)
	}
	return createdApps, nil
}

func (c *projectCreate) setCNames(apps []map[string]string, client *cmd.Client) error {
	for _, app := range apps {
		reqURL, err := cmd.GetURL(fmt.Sprintf("/apps/%s/cname", app["name"]))
		if err != nil {
			return err
		}
		cname := fmt.Sprintf("%s.%s", c.name, app["dnsSuffix"])
		v := make(url.Values)
		v.Set("cname", cname)
		req, err := http.NewRequest("POST", reqURL, strings.NewReader(v.Encode()))
		if err != nil {
			return err
		}
		req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
		resp, err := client.Do(req)
		if err != nil {
			return err
		}
		resp.Body.Close()
	}
	return nil
}

func (c *projectCreate) filterEnvironments(envs []Environment, names []string) []Environment {
	var filtered []Environment
	for _, e := range envs {
		for _, name := range names {
			if e.Name == name {
				filtered = append(filtered, e)
				break
			}
		}
	}
	return filtered
}

type commaSeparatedFlag struct {
	values []string
}

func (f *commaSeparatedFlag) Values() []string {
	return f.values
}

func (f *commaSeparatedFlag) String() string {
	return strings.Join(f.values, ",")
}

func (f *commaSeparatedFlag) Set(v string) error {
	f.values = strings.Split(v, ",")
	return nil
}

func (f *commaSeparatedFlag) validate(validValues []string) error {
	var invalidValues []string
	for _, cv := range f.values {
		var found bool
		for _, validValue := range validValues {
			if validValue == cv {
				found = true
				break
			}
		}
		if !found {
			invalidValues = append(invalidValues, cv)
		}
	}
	if len(invalidValues) > 0 {
		return fmt.Errorf("invalid values: %s (valid options are: %s)", strings.Join(invalidValues, ", "), strings.Join(validValues, ", "))
	}
	return nil
}

type projectRemove struct {
	cmd.ConfirmationCommand
	name string
	fs   *gnuflag.FlagSet
}

func (c *projectRemove) Info() *cmd.Info {
	return &cmd.Info{
		Name: "project-remove",
		Desc: "removes the given project",
	}
}

func (c *projectRemove) Run(ctx *cmd.Context, client *cmd.Client) error {
	if c.name == "" {
		return errors.New("please provide the name of the project")
	}
	config, err := loadConfigFile()
	if err != nil {
		return errors.New("unable to load environments file, please make sure that tranor is properly configured")
	}
	if !c.Confirm(ctx, fmt.Sprintf("Are you sure you want to remove the project %q?", c.name)) {
		return nil
	}
	envNames := config.envNames()
	appNames := make([]string, len(envNames))
	for i, envName := range envNames {
		appNames[i] = fmt.Sprintf("%s-%s", c.name, envName)
	}
	errs, err := deleteApps(appNames, client)
	if err != nil {
		return err
	}
	var notFound int
	for _, err := range errs {
		if err != nil {
			if e, ok := err.(*tsuruerrors.HTTP); ok && e.Code == http.StatusNotFound {
				notFound++
			}
		}
	}
	if notFound == len(errs) {
		return errors.New("project not found")
	}
	fmt.Fprintln(ctx.Stdout, "Project successfully removed!")
	return nil
}

func (c *projectRemove) Flags() *gnuflag.FlagSet {
	if c.fs == nil {
		c.fs = c.ConfirmationCommand.Flags()
		c.fs.StringVar(&c.name, "name", "", "name of the project to b remove")
		c.fs.StringVar(&c.name, "n", "", "name of the project to remove")
	}
	return c.fs
}

type projectList struct {
}

func (c *projectList) Info() *cmd.Info {
	return &cmd.Info{
		Name: "project-list",
	}
}

func (c *projectList) Run(ctx *cmd.Context, client *cmd.Client) error {
	config, err := loadConfigFile()
	if err != nil {
		return errors.New("unable to load environments file, please make sure that tranor is properly configured")
	}
	apps, err := listApps(client)
	if err != nil {
		return err
	}
	projects := make(map[string][]app)
	for _, env := range config.Environments {
		for _, app := range apps {
			if len(app.CName) != 1 {
				continue
			}
			partsName := env.nameRegexp().FindStringSubmatch(app.Name)
			partsDNS := env.dnsRegexp().FindStringSubmatch(app.CName[0])
			if len(partsName) == 2 && len(partsDNS) == 2 && partsName[1] == partsDNS[1] {
				projectName := partsDNS[1]
				app.Env = env
				app.Addr = app.CName[0]
				projects[projectName] = append(projects[projectName], app)
			}
		}
	}
	c.render(ctx.Stdout, projects)
	return nil
}

func (c *projectList) render(w io.Writer, projects map[string][]app) {
	var list projectSlice
	for name, apps := range projects {
		list = append(list, struct {
			Name string
			Apps []app
		}{Name: name, Apps: apps})
	}
	sort.Sort(list)
	var table cmd.Table
	table.LineSeparator = true
	table.Headers = cmd.Row{"Project", "Environments", "Address"}
	for _, project := range list {
		var (
			envNames  []string
			addresses []string
		)
		for _, app := range project.Apps {
			envNames = append(envNames, app.Env.Name)
			addresses = append(addresses, app.Addr)
		}
		table.AddRow(cmd.Row{project.Name, strings.Join(envNames, "\n"), strings.Join(addresses, "\n")})
	}
	w.Write(table.Bytes())
}

type projectSlice []struct {
	Name string
	Apps []app
}

func (l projectSlice) Len() int {
	return len(l)
}

func (l projectSlice) Less(i, j int) bool {
	return l[i].Name < l[j].Name
}

func (l projectSlice) Swap(i, j int) {
	l[i], l[j] = l[j], l[i]
}
<commit_msg>Change project formatting<commit_after>// Copyright 2016 EF CTX. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"errors"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"sort"
	"strings"

	"github.com/tsuru/gnuflag"
	"github.com/tsuru/tsuru/cmd"
	tsuruerrors "github.com/tsuru/tsuru/errors"
)

type projectCreate struct {
	fs       *gnuflag.FlagSet
	name     string
	platform string
	team     string
	plan     string
	envs     commaSeparatedFlag
}

func (*projectCreate) Info() *cmd.Info {
	return &cmd.Info{
		Name: "project-create",
		Desc: "creates a remote project in the tranor server",
	}
}

func (c *projectCreate) Run(ctx *cmd.Context, client *cmd.Client) error {
	if c.name == "" || c.platform == "" {
		return errors.New("please provide the name and the platform")
	}
	config, err := loadConfigFile()
	if err != nil {
		return errors.New("unable to load environments file, please make sure that tranor is properly configured")
	}
	err = c.envs.validate(config.envNames())
	if err != nil {
		return fmt.Errorf("failed to load environments: %s", err)
	}
	envs := c.filterEnvironments(config.Environments, c.envs.Values())
	apps, err := c.createApps(envs, client)
	if err != nil {
		return err
	}
	err = c.setCNames(apps, client)
	if err != nil {
		appNames := make([]string, len(apps))
		for i, app := range apps {
			appNames[i] = app["name"]
		}
		deleteApps(appNames, client)
		return fmt.Errorf("failed to configure project %q: %s", c.name, err)
	}
	fmt.Fprintf(ctx.Stdout, "successfully created the project %q!\n", c.name)
	if gitRepo := apps[0]["repository_url"]; gitRepo != "" {
		fmt.Fprintf(ctx.Stdout, "Git repository: %s\n", gitRepo)
	}
	return nil
}

func (c *projectCreate) Flags() *gnuflag.FlagSet {
	if c.fs == nil {
		c.fs = gnuflag.NewFlagSet("project-create", gnuflag.ExitOnError)
		c.fs.StringVar(&c.name, "name", "", "name of the project")
		c.fs.StringVar(&c.name, "n", "", "name of the project")
		c.fs.StringVar(&c.platform, "platform", "", "platform of the project")
		c.fs.StringVar(&c.platform, "l", "", "platform of the project")
		c.fs.StringVar(&c.team, "team", "", "team that owns the project")
		c.fs.StringVar(&c.team, "t", "", "team that owns the project")
		c.fs.StringVar(&c.plan, "plan", "", "plan to use for the project")
		c.fs.StringVar(&c.plan, "p", "", "plan to use for the project")
		c.fs.Var(&c.envs, "envs", "comma-separated list of environments to use (defaults to dev,qa,stage,production)")
		c.fs.Var(&c.envs, "e", "comma-separated list of environments to use (defaults to env,qa,stage,production)")
		c.envs.Set("dev,qa,stage,production")
	}
	return c.fs
}

func (c *projectCreate) createApps(envs []Environment, client *cmd.Client) ([]map[string]string, error) {
	createdApps := make([]map[string]string, 0, len(envs))
	appNames := make([]string, 0, len(envs))
	for _, env := range envs {
		appName := fmt.Sprintf("%s-%s", c.name, env.Name)
		app, err := createApp(client, createAppOptions{
			name:     appName,
			plan:     c.plan,
			platform: c.platform,
			pool:     env.poolName(),
			team:     c.team,
		})
		if err != nil {
			deleteApps(appNames, client)
			return nil, fmt.Errorf("failed to create the project in env %q: %s", env.Name, err)
		}
		app["name"] = appName
		app["dnsSuffix"] = env.DNSSuffix
		createdApps = append(createdApps, app)
		appNames = append(appNames, appName)
	}
	return createdApps, nil
}

func (c *projectCreate) setCNames(apps []map[string]string, client *cmd.Client) error {
	for _, app := range apps {
		reqURL, err := cmd.GetURL(fmt.Sprintf("/apps/%s/cname", app["name"]))
		if err != nil {
			return err
		}
		cname := fmt.Sprintf("%s.%s", c.name, app["dnsSuffix"])
		v := make(url.Values)
		v.Set("cname", cname)
		req, err := http.NewRequest("POST", reqURL, strings.NewReader(v.Encode()))
		if err != nil {
			return err
		}
		req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
		resp, err := client.Do(req)
		if err != nil {
			return err
		}
		resp.Body.Close()
	}
	return nil
}

func (c *projectCreate) filterEnvironments(envs []Environment, names []string) []Environment {
	var filtered []Environment
	for _, e := range envs {
		for _, name := range names {
			if e.Name == name {
				filtered = append(filtered, e)
				break
			}
		}
	}
	return filtered
}

type commaSeparatedFlag struct {
	values []string
}

func (f *commaSeparatedFlag) Values() []string {
	return f.values
}

func (f *commaSeparatedFlag) String() string {
	return strings.Join(f.values, ",")
}

func (f *commaSeparatedFlag) Set(v string) error {
	f.values = strings.Split(v, ",")
	return nil
}

func (f *commaSeparatedFlag) validate(validValues []string) error {
	var invalidValues []string
	for _, cv := range f.values {
		var found bool
		for _, validValue := range validValues {
			if validValue == cv {
				found = true
				break
			}
		}
		if !found {
			invalidValues = append(invalidValues, cv)
		}
	}
	if len(invalidValues) > 0 {
		return fmt.Errorf("invalid values: %s (valid options are: %s)", strings.Join(invalidValues, ", "), strings.Join(validValues, ", "))
	}
	return nil
}

type projectRemove struct {
	cmd.ConfirmationCommand
	name string
	fs   *gnuflag.FlagSet
}

func (c *projectRemove) Info() *cmd.Info {
	return &cmd.Info{
		Name: "project-remove",
		Desc: "removes the given project",
	}
}

func (c *projectRemove) Run(ctx *cmd.Context, client *cmd.Client) error {
	if c.name == "" {
		return errors.New("please provide the name of the project")
	}
	config, err := loadConfigFile()
	if err != nil {
		return errors.New("unable to load environments file, please make sure that tranor is properly configured")
	}
	if !c.Confirm(ctx, fmt.Sprintf("Are you sure you want to remove the project %q?", c.name)) {
		return nil
	}
	envNames := config.envNames()
	appNames := make([]string, len(envNames))
	for i, envName := range envNames {
		appNames[i] = fmt.Sprintf("%s-%s", c.name, envName)
	}
	errs, err := deleteApps(appNames, client)
	if err != nil {
		return err
	}
	var notFound int
	for _, err := range errs {
		if err != nil {
			if e, ok := err.(*tsuruerrors.HTTP); ok && e.Code == http.StatusNotFound {
				notFound++
			}
		}
	}
	if notFound == len(errs) {
		return errors.New("project not found")
	}
	fmt.Fprintln(ctx.Stdout, "Project successfully removed!")
	return nil
}

func (c *projectRemove) Flags() *gnuflag.FlagSet {
	if c.fs == nil {
		c.fs = c.ConfirmationCommand.Flags()
		c.fs.StringVar(&c.name, "name", "", "name of the project to b remove")
		c.fs.StringVar(&c.name, "n", "", "name of the project to remove")
	}
	return c.fs
}

type projectList struct{}

func (c *projectList) Info() *cmd.Info {
	return &cmd.Info{
		Name: "project-list",
	}
}

func (c *projectList) Run(ctx *cmd.Context, client *cmd.Client) error {
	config, err := loadConfigFile()
	if err != nil {
		return errors.New("unable to load environments file, please make sure that tranor is properly configured")
	}
	apps, err := listApps(client)
	if err != nil {
		return err
	}
	projects := make(map[string][]app)
	for _, env := range config.Environments {
		for _, app := range apps {
			if len(app.CName) != 1 {
				continue
			}
			partsName := env.nameRegexp().FindStringSubmatch(app.Name)
			partsDNS := env.dnsRegexp().FindStringSubmatch(app.CName[0])
			if len(partsName) == 2 && len(partsDNS) == 2 && partsName[1] == partsDNS[1] {
				projectName := partsDNS[1]
				app.Env = env
				app.Addr = app.CName[0]
				projects[projectName] = append(projects[projectName], app)
			}
		}
	}
	c.render(ctx.Stdout, projects)
	return nil
}

func (c *projectList) render(w io.Writer, projects map[string][]app) {
	var list projectSlice
	for name, apps := range projects {
		list = append(list, struct {
			Name string
			Apps []app
		}{Name: name, Apps: apps})
	}
	sort.Sort(list)
	var table cmd.Table
	table.LineSeparator = true
	table.Headers = cmd.Row{"Project", "Environments", "Address"}
	for _, project := range list {
		var (
			envNames  []string
			addresses []string
		)
		for _, app := range project.Apps {
			envNames = append(envNames, app.Env.Name)
			addresses = append(addresses, app.Addr)
		}
		table.AddRow(cmd.Row{project.Name, strings.Join(envNames, "\n"), strings.Join(addresses, "\n")})
	}
	w.Write(table.Bytes())
}

type projectSlice []struct {
	Name string
	Apps []app
}

func (l projectSlice) Len() int {
	return len(l)
}

func (l projectSlice) Less(i, j int) bool {
	return l[i].Name < l[j].Name
}

func (l projectSlice) Swap(i, j int) {
	l[i], l[j] = l[j], l[i]
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"sync"
)

var (
	defaultProjectMap map[string]*Project
	defaultProjects   = []*Project{
		makeProject("jekyll", "jekyll/jekyll", "master", "jekyll"),
		makeProject("jemoji", "jekyll/jemoji", "master", "jemoji"),
		makeProject("mercenary", "jekyll/mercenary", "master", "mercenary"),
		makeProject("jekyll-import", "jekyll/jekyll-import", "master", "jekyll-import"),
		makeProject("jekyll-feed", "jekyll/jekyll-feed", "master", "jekyll-feed"),
		makeProject("jekyll-sitemap", "jekyll/jekyll-sitemap", "master", "jekyll-sitemap"),
		makeProject("jekyll-mentions", "jekyll/jekyll-mentions", "master", "jekyll-mentions"),
		makeProject("jekyll-watch", "jekyll/jekyll-watch", "master", "jekyll-watch"),
		makeProject("jekyll-compose", "jekyll/jekyll-compose", "master", "jekyll-compose"),
		makeProject("jekyll-paginate", "jekyll/jekyll-paginate", "master", "jekyll-paginate"),
		makeProject("jekyll-gist", "jekyll/jekyll-gist", "master", "jekyll-gist"),
		makeProject("jekyll-coffeescript", "jekyll/jekyll-coffeescript", "master", "jekyll-coffeescript"),
		makeProject("jekyll-opal", "jekyll/jekyll-opal", "master", "jekyll-opal"),
		makeProject("classifier-reborn", "jekyll/classifier-reborn", "master", "classifier-reborn"),
		makeProject("jekyll-sass-converter", "jekyll/jekyll-sass-converter", "master", "jekyll-sass-converter"),
		makeProject("jekyll-textile-converter", "jekyll/jekyll-textile-converter", "master", "jekyll-textile-converter"),
		makeProject("plugins.jekyllrb", "jekyll/plugins", "gh-pages", ""),
		makeProject("jekyll-redirect-from", "jekyll/jekyll-redirect-from", "master", "jekyll-redirect-from"),
		makeProject("github-metadata", "jekyll/github-metadata", "master", "jekyll-github-metadata"),
		makeProject("jekyll docker", "jekyll/docker", "", ""),
	}
)

type Project struct {
	Name    string `json:"name"`
	Nwo     string `json:"nwo"`
	Branch  string `json:"branch"`
	GemName string `json:"gem_name"`

	Gem     *RubyGem      `json:"gem"`
	Travis  *TravisReport `json:"travis"`
	fetched bool
}

func (p *Project) fetch() {
	if !p.fetched {
		rubyGemChan := rubygem(p.GemName)
		travisChan := travis(p.Nwo, p.Branch)
		p.Gem = <-rubyGemChan
		p.Travis = <-travisChan
		p.fetched = true
	}
}

func buildProjectMap() {
	defaultProjectMap = map[string]*Project{}
	for _, p := range defaultProjects {
		defaultProjectMap[p.Name] = p
	}
}

func makeProject(name, nwo, branch, rubygem string) *Project {
	return &Project{
		Name:    name,
		Nwo:     nwo,
		Branch:  branch,
		GemName: rubygem,
	}
}

func getProject(name string) Project {
	if defaultProjectMap == nil {
		buildProjectMap()
	}

	if p, ok := defaultProjectMap[name]; ok {
		if !p.fetched {
			p.fetch()
		}
		return *p
	}
	panic(fmt.Sprintf("no project named '%s'", name))
}

func getAllProjects() []*Project {
	var wg sync.WaitGroup
	for _, p := range defaultProjects {
		wg.Add(1)
		log.Println("fetching:", p)
		go func(project *Project) {
			project.fetch()
			log.Println("just fetched:", project)
			wg.Done()
		}(p)
	}
	wg.Wait()
	return defaultProjects
}
<commit_msg>less logging<commit_after>package main

import (
	"fmt"
	"sync"
)

var (
	defaultProjectMap map[string]*Project
	defaultProjects   = []*Project{
		makeProject("jekyll", "jekyll/jekyll", "master", "jekyll"),
		makeProject("jemoji", "jekyll/jemoji", "master", "jemoji"),
		makeProject("mercenary", "jekyll/mercenary", "master", "mercenary"),
		makeProject("jekyll-import", "jekyll/jekyll-import", "master", "jekyll-import"),
		makeProject("jekyll-feed", "jekyll/jekyll-feed", "master", "jekyll-feed"),
		makeProject("jekyll-sitemap", "jekyll/jekyll-sitemap", "master", "jekyll-sitemap"),
		makeProject("jekyll-mentions", "jekyll/jekyll-mentions", "master", "jekyll-mentions"),
		makeProject("jekyll-watch", "jekyll/jekyll-watch", "master", "jekyll-watch"),
		makeProject("jekyll-compose", "jekyll/jekyll-compose", "master", "jekyll-compose"),
		makeProject("jekyll-paginate", "jekyll/jekyll-paginate", "master", "jekyll-paginate"),
		makeProject("jekyll-gist", "jekyll/jekyll-gist", "master", "jekyll-gist"),
		makeProject("jekyll-coffeescript", "jekyll/jekyll-coffeescript", "master", "jekyll-coffeescript"),
		makeProject("jekyll-opal", "jekyll/jekyll-opal", "master", "jekyll-opal"),
		makeProject("classifier-reborn", "jekyll/classifier-reborn", "master", "classifier-reborn"),
		makeProject("jekyll-sass-converter", "jekyll/jekyll-sass-converter", "master", "jekyll-sass-converter"),
		makeProject("jekyll-textile-converter", "jekyll/jekyll-textile-converter", "master", "jekyll-textile-converter"),
		makeProject("plugins.jekyllrb", "jekyll/plugins", "gh-pages", ""),
		makeProject("jekyll-redirect-from", "jekyll/jekyll-redirect-from", "master", "jekyll-redirect-from"),
		makeProject("github-metadata", "jekyll/github-metadata", "master", "jekyll-github-metadata"),
		makeProject("jekyll docker", "jekyll/docker", "", ""),
	}
)

type Project struct {
	Name    string `json:"name"`
	Nwo     string `json:"nwo"`
	Branch  string `json:"branch"`
	GemName string `json:"gem_name"`

	Gem     *RubyGem      `json:"gem"`
	Travis  *TravisReport `json:"travis"`
	fetched bool
}

func (p *Project) fetch() {
	if !p.fetched {
		rubyGemChan := rubygem(p.GemName)
		travisChan := travis(p.Nwo, p.Branch)
		p.Gem = <-rubyGemChan
		p.Travis = <-travisChan
		p.fetched = true
	}
}

func buildProjectMap() {
	defaultProjectMap = map[string]*Project{}
	for _, p := range defaultProjects {
		defaultProjectMap[p.Name] = p
	}
}

func makeProject(name, nwo, branch, rubygem string) *Project {
	return &Project{
		Name:    name,
		Nwo:     nwo,
		Branch:  branch,
		GemName: rubygem,
	}
}

func getProject(name string) Project {
	if defaultProjectMap == nil {
		buildProjectMap()
	}

	if p, ok := defaultProjectMap[name]; ok {
		if !p.fetched {
			p.fetch()
		}
		return *p
	}
	panic(fmt.Sprintf("no project named '%s'", name))
}

func getAllProjects() []*Project {
	var wg sync.WaitGroup
	for _, p := range defaultProjects {
		wg.Add(1)
		go func(project *Project) {
			project.fetch()
			wg.Done()
		}(p)
	}
	wg.Wait()
	return defaultProjects
}
<|endoftext|>


<commit_before>package apps

import (
	. "github.com/cloudfoundry/cf-acceptance-tests/Godeps/_workspace/src/github.com/onsi/ginkgo"
	. "github.com/cloudfoundry/cf-acceptance-tests/Godeps/_workspace/src/github.com/onsi/gomega"
	. "github.com/cloudfoundry/cf-acceptance-tests/Godeps/_workspace/src/github.com/onsi/gomega/gbytes"
	. "github.com/cloudfoundry/cf-acceptance-tests/Godeps/_workspace/src/github.com/onsi/gomega/gexec"

	"github.com/cloudfoundry/cf-acceptance-tests/Godeps/_workspace/src/github.com/cloudfoundry-incubator/cf-test-helpers/cf"
	"github.com/cloudfoundry/cf-acceptance-tests/Godeps/_workspace/src/github.com/cloudfoundry-incubator/cf-test-helpers/generator"
	"github.com/cloudfoundry/cf-acceptance-tests/Godeps/_workspace/src/github.com/cloudfoundry-incubator/cf-test-helpers/helpers"
	"github.com/cloudfoundry/cf-acceptance-tests/helpers/app_helpers"
	"github.com/cloudfoundry/cf-acceptance-tests/helpers/assets"
)

var _ = Describe("A running application", func() {
	var appName string

	BeforeEach(func() {
		appName = generator.PrefixedRandomName("CATS-APP-")

		Expect(cf.Cf("push", appName, "--no-start", "-b", config.RubyBuildpackName, "-m", DEFAULT_MEMORY_LIMIT, "-p", assets.NewAssets().Dora, "-d", config.AppsDomain).Wait(DEFAULT_TIMEOUT)).To(Exit(0))
		app_helpers.SetBackend(appName)
		Expect(cf.Cf("start", appName).Wait(CF_PUSH_TIMEOUT)).To(Exit(0))
	})

	AfterEach(func() {
		app_helpers.AppReport(appName, DEFAULT_TIMEOUT)

		Expect(cf.Cf("delete", appName, "-f", "-r").Wait(DEFAULT_TIMEOUT)).To(Exit(0))
	})

	It(diegoUnsupportedTag+"can have its files inspected", func() {
		// Currently cannot work with multiple instances since CF always checks instance 0
		files := cf.Cf("files", appName).Wait(DEFAULT_TIMEOUT)
		Expect(files).To(Exit(0))
		Expect(files).To(Say("app/"))

		files = cf.Cf("files", appName, "app/").Wait(DEFAULT_TIMEOUT)
		Expect(files).To(Exit(0))
		Expect(files).To(Say("config.ru"))

		files = cf.Cf("files", appName, "app/config.ru").Wait(DEFAULT_TIMEOUT)
		Expect(files).To(Exit(0))
		Expect(files).To(Say("run Dora"))
	})

	It("shows crash events and recovers from crashes", func() {
		id := helpers.CurlApp(appName, "/id")
		helpers.CurlApp(appName, "/sigterm/KILL")

		Eventually(func() string {
			return string(cf.Cf("events", appName).Wait(DEFAULT_TIMEOUT).Out.Contents())
		}, DEFAULT_TIMEOUT).Should(MatchRegexp("[eE]xited"))

		Eventually(func() string { return helpers.CurlApp(appName, "/id") }).Should(Not(Equal(id)))
	})

	Context("with multiple instances", func() {
		BeforeEach(func() {
			Expect(cf.Cf("scale", appName, "-i", "2").Wait(CF_PUSH_TIMEOUT)).To(Exit(0))
		})

		It("can be queried for state by instance", func() {
			app := cf.Cf("app", appName).Wait(DEFAULT_TIMEOUT)
			Expect(app).To(Exit(0))
			Expect(app).To(Say("#0"))
			Expect(app).To(Say("#1"))
		})
	})
})
<commit_msg>Use CF_PUSH_TIMEOUT when pushing app in dynamic_info_test<commit_after>package apps

import (
	. "github.com/cloudfoundry/cf-acceptance-tests/Godeps/_workspace/src/github.com/onsi/ginkgo"
	. "github.com/cloudfoundry/cf-acceptance-tests/Godeps/_workspace/src/github.com/onsi/gomega"
	. "github.com/cloudfoundry/cf-acceptance-tests/Godeps/_workspace/src/github.com/onsi/gomega/gbytes"
	. "github.com/cloudfoundry/cf-acceptance-tests/Godeps/_workspace/src/github.com/onsi/gomega/gexec"

	"github.com/cloudfoundry/cf-acceptance-tests/Godeps/_workspace/src/github.com/cloudfoundry-incubator/cf-test-helpers/cf"
	"github.com/cloudfoundry/cf-acceptance-tests/Godeps/_workspace/src/github.com/cloudfoundry-incubator/cf-test-helpers/generator"
	"github.com/cloudfoundry/cf-acceptance-tests/Godeps/_workspace/src/github.com/cloudfoundry-incubator/cf-test-helpers/helpers"
	"github.com/cloudfoundry/cf-acceptance-tests/helpers/app_helpers"
	"github.com/cloudfoundry/cf-acceptance-tests/helpers/assets"
)

var _ = Describe("A running application", func() {
	var appName string

	BeforeEach(func() {
		appName = generator.PrefixedRandomName("CATS-APP-")

		Expect(cf.Cf("push", appName, "--no-start", "-b", config.RubyBuildpackName, "-m", DEFAULT_MEMORY_LIMIT, "-p", assets.NewAssets().Dora, "-d", config.AppsDomain).Wait(CF_PUSH_TIMEOUT)).To(Exit(0))
		app_helpers.SetBackend(appName)
		Expect(cf.Cf("start", appName).Wait(CF_PUSH_TIMEOUT)).To(Exit(0))
	})

	AfterEach(func() {
		app_helpers.AppReport(appName, DEFAULT_TIMEOUT)

		Expect(cf.Cf("delete", appName, "-f", "-r").Wait(DEFAULT_TIMEOUT)).To(Exit(0))
	})

	It(diegoUnsupportedTag+"can have its files inspected", func() {
		// Currently cannot work with multiple instances since CF always checks instance 0
		files := cf.Cf("files", appName).Wait(DEFAULT_TIMEOUT)
		Expect(files).To(Exit(0))
		Expect(files).To(Say("app/"))

		files = cf.Cf("files", appName, "app/").Wait(DEFAULT_TIMEOUT)
		Expect(files).To(Exit(0))
		Expect(files).To(Say("config.ru"))

		files = cf.Cf("files", appName, "app/config.ru").Wait(DEFAULT_TIMEOUT)
		Expect(files).To(Exit(0))
		Expect(files).To(Say("run Dora"))
	})

	It("shows crash events and recovers from crashes", func() {
		id := helpers.CurlApp(appName, "/id")
		helpers.CurlApp(appName, "/sigterm/KILL")

		Eventually(func() string {
			return string(cf.Cf("events", appName).Wait(DEFAULT_TIMEOUT).Out.Contents())
		}, DEFAULT_TIMEOUT).Should(MatchRegexp("[eE]xited"))

		Eventually(func() string { return helpers.CurlApp(appName, "/id") }).Should(Not(Equal(id)))
	})

	Context("with multiple instances", func() {
		BeforeEach(func() {
			Expect(cf.Cf("scale", appName, "-i", "2").Wait(CF_PUSH_TIMEOUT)).To(Exit(0))
		})

		It("can be queried for state by instance", func() {
			app := cf.Cf("app", appName).Wait(DEFAULT_TIMEOUT)
			Expect(app).To(Exit(0))
			Expect(app).To(Say("#0"))
			Expect(app).To(Say("#1"))
		})
	})
})
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net"
	"os"

	flags "github.com/jessevdk/go-flags"
)

const version = "1.0.1"

var opts struct {
	Version bool `short:"v" long:"version" description:"Show version"`
}

func main() {
	parser := flags.NewParser(&opts, flags.Default)
	parser.Usage = "HOSTNAME [OPTIONS]"
	args, _ := parser.Parse()

	if len(args) == 0 {
		if opts.Version {
			fmt.Println("ptrhost version", version)
		}
		os.Exit(1)
	}

	hostname := args[0]
	lookupHosts, _ := net.LookupHost(hostname)
	for _, host := range lookupHosts {
		lookupAddrs, err := net.LookupAddr(host)
		if err == nil {
			fmt.Println(host, "->", lookupAddrs[0])
		} else {
			fmt.Println(host, "->", err.(*net.DNSError).Err)
		}
	}
}
<commit_msg>Show help when no args<commit_after>package main

import (
	"fmt"
	"net"
	"os"

	flags "github.com/jessevdk/go-flags"
)

const version = "1.0.1"

var opts struct {
	Version bool `short:"v" long:"version" description:"Show version"`
}

func main() {
	parser := flags.NewParser(&opts, flags.Default)
	parser.Usage = "HOSTNAME [OPTIONS]"
	args, err := parser.Parse()

	if err != nil {
		if ferr, ok := err.(*flags.Error); ok && ferr.Type == flags.ErrHelp {
			os.Exit(0)
		}
		os.Exit(1)
	}

	if len(args) == 0 {
		if opts.Version {
			fmt.Println("ptrhost version", version)
			os.Exit(0)
		}

		parser.WriteHelp(os.Stderr)
		os.Exit(0)
	}

	hostname := args[0]
	lookupHosts, _ := net.LookupHost(hostname)
	for _, host := range lookupHosts {
		lookupAddrs, err := net.LookupAddr(host)
		if err == nil {
			fmt.Println(host, "->", lookupAddrs[0])
		} else {
			fmt.Println(host, "->", err.(*net.DNSError).Err)
		}
	}
}
<|endoftext|>


<commit_before>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package main

import (
	"errors"
	"flag"
	"fmt"
	"net"
	"os"
	"path"
	"path/filepath"
	"strings"

	"github.com/ava-labs/gecko/database/leveldb"
	"github.com/ava-labs/gecko/database/memdb"
	"github.com/ava-labs/gecko/genesis"
	"github.com/ava-labs/gecko/ids"
	"github.com/ava-labs/gecko/nat"
	"github.com/ava-labs/gecko/node"
	"github.com/ava-labs/gecko/snow/networking/router"
	"github.com/ava-labs/gecko/staking"
	"github.com/ava-labs/gecko/utils"
	"github.com/ava-labs/gecko/utils/formatting"
	"github.com/ava-labs/gecko/utils/hashing"
	"github.com/ava-labs/gecko/utils/logging"
	"github.com/ava-labs/gecko/utils/wrappers"
)

const (
	dbVersion = "v0.2.0"
)

// Results of parsing the CLI
var (
	Config                 = node.Config{}
	Err                    error
	defaultDbDir           = os.ExpandEnv(filepath.Join("$HOME", ".gecko", "db"))
	defaultStakingKeyPath  = os.ExpandEnv(filepath.Join("$HOME", ".gecko", "staking", "staker.key"))
	defaultStakingCertPath = os.ExpandEnv(filepath.Join("$HOME", ".gecko", "staking", "staker.crt"))
)

var (
	errBootstrapMismatch = errors.New("more bootstrap IDs provided than bootstrap IPs")
)

// GetIPs returns the default IPs for each network
func GetIPs(networkID uint32) []string {
	switch networkID {
	case genesis.CascadeID:
		return []string{
			"3.227.207.132:21001",
			"34.207.133.167:21001",
			"107.23.241.199:21001",
			"54.197.215.186:21001",
			"18.234.153.22:21001",
		}
	default:
		return nil
	}
}

// Parse the CLI arguments
func init() {
	errs := &wrappers.Errs{}
	defer func() { Err = errs.Err }()

	loggingConfig, err := logging.DefaultConfig()
	if errs.Add(err); errs.Errored() {
		return
	}

	fs := flag.NewFlagSet("gecko", flag.ContinueOnError)

	// NetworkID:
	networkName := fs.String("network-id", genesis.CascadeName, "Network ID this node will connect to")

	// Ava fees:
	fs.Uint64Var(&Config.AvaTxFee, "ava-tx-fee", 0, "Ava transaction fee, in $nAva")

	// Assertions:
	fs.BoolVar(&loggingConfig.Assertions, "assertions-enabled", true, "Turn on assertion execution")

	// Crypto:
	fs.BoolVar(&Config.EnableCrypto, "signature-verification-enabled", true, "Turn on signature verification")

	// Database:
	db := fs.Bool("db-enabled", true, "Turn on persistent storage")
	dbDir := fs.String("db-dir", defaultDbDir, "Database directory for Ava state")

	// IP:
	consensusIP := fs.String("public-ip", "", "Public IP of this node")

	// HTTP Server:
	httpPort := fs.Uint("http-port", 9650, "Port of the HTTP server")
	fs.BoolVar(&Config.EnableHTTPS, "http-tls-enabled", false, "Upgrade the HTTP server to HTTPs")
	fs.StringVar(&Config.HTTPSKeyFile, "http-tls-key-file", "", "TLS private key file for the HTTPs server")
	fs.StringVar(&Config.HTTPSCertFile, "http-tls-cert-file", "", "TLS certificate file for the HTTPs server")

	// Bootstrapping:
	bootstrapIPs := fs.String("bootstrap-ips", "default", "Comma separated list of bootstrap peer ips to connect to. Example: 127.0.0.1:9630,127.0.0.1:9631")
	bootstrapIDs := fs.String("bootstrap-ids", "default", "Comma separated list of bootstrap peer ids to connect to. Example: JR4dVmy6ffUGAKCBDkyCbeZbyHQBeDsET,8CrVPQZ4VSqgL8zTdvL14G8HqAfrBr4z")

	// Staking:
	consensusPort := fs.Uint("staking-port", 9651, "Port of the consensus server")
	fs.BoolVar(&Config.EnableStaking, "staking-tls-enabled", true, "Require TLS to authenticate staking connections")
	fs.StringVar(&Config.StakingKeyFile, "staking-tls-key-file", defaultStakingKeyPath, "TLS private key for staking")
	fs.StringVar(&Config.StakingCertFile, "staking-tls-cert-file", defaultStakingCertPath, "TLS certificate for staking")

	// Plugins:
	fs.StringVar(&Config.PluginDir, "plugin-dir", "./build/plugins", "Plugin directory for Ava VMs")

	// Logging:
	logsDir := fs.String("log-dir", "", "Logging directory for Ava")
	logLevel := fs.String("log-level", "info", "The log level. Should be one of {verbo, debug, info, warn, error, fatal, off}")
	logDisplayLevel := fs.String("log-display-level", "", "The log display level. If left blank, will inherit the value of log-level. Otherwise, should be one of {verbo, debug, info, warn, error, fatal, off}")

	fs.IntVar(&Config.ConsensusParams.K, "snow-sample-size", 5, "Number of nodes to query for each network poll")
	fs.IntVar(&Config.ConsensusParams.Alpha, "snow-quorum-size", 4, "Alpha value to use for required number positive results")
	fs.IntVar(&Config.ConsensusParams.BetaVirtuous, "snow-virtuous-commit-threshold", 20, "Beta value to use for virtuous transactions")
	fs.IntVar(&Config.ConsensusParams.BetaRogue, "snow-rogue-commit-threshold", 30, "Beta value to use for rogue transactions")
	fs.IntVar(&Config.ConsensusParams.Parents, "snow-avalanche-num-parents", 5, "Number of vertexes for reference from each new vertex")
	fs.IntVar(&Config.ConsensusParams.BatchSize, "snow-avalanche-batch-size", 30, "Number of operations to batch in each new vertex")
	fs.IntVar(&Config.ConsensusParams.ConcurrentRepolls, "snow-concurrent-repolls", 1, "Minimum number of concurrent polls for finalizing consensus")

	// Enable/Disable APIs:
	fs.BoolVar(&Config.AdminAPIEnabled, "api-admin-enabled", true, "If true, this node exposes the Admin API")
	fs.BoolVar(&Config.KeystoreAPIEnabled, "api-keystore-enabled", true, "If true, this node exposes the Keystore API")
	fs.BoolVar(&Config.MetricsAPIEnabled, "api-metrics-enabled", true, "If true, this node exposes the Metrics API")
	fs.BoolVar(&Config.IPCEnabled, "api-ipcs-enabled", false, "If true, IPCs can be opened")

	// Throughput Server
	throughputPort := fs.Uint("xput-server-port", 9652, "Port of the deprecated throughput test server")
	fs.BoolVar(&Config.ThroughputServerEnabled, "xput-server-enabled", false, "If true, throughput test server is created")

	ferr := fs.Parse(os.Args[1:])

	if ferr == flag.ErrHelp {
		// display usage/help text and exit successfully
		os.Exit(0)
	}

	if ferr != nil {
		// other type of error occurred when parsing args
		os.Exit(2)
	}

	networkID, err := genesis.NetworkID(*networkName)
	if errs.Add(err); err != nil {
		return
	}

	Config.NetworkID = networkID

	// DB:
	if *db {
		*dbDir = os.ExpandEnv(*dbDir) // parse any env variables
		dbPath := path.Join(*dbDir, genesis.NetworkName(Config.NetworkID), dbVersion)
		db, err := leveldb.New(dbPath, 0, 0, 0)
		if err != nil {
			errs.Add(fmt.Errorf("couldn't create db at %s: %w", dbPath, err))
			return
		}
		Config.DB = db
	} else {
		Config.DB = memdb.New()
	}

	Config.Nat = nat.NewRouter()

	var ip net.IP
	// If public IP is not specified, get it using shell command dig
	if *consensusIP == "" {
		ip, err = Config.Nat.IP()
		if err != nil {
			ip = net.IPv4zero // Couldn't get my IP...set to 0.0.0.0
		}
	} else {
		ip = net.ParseIP(*consensusIP)
	}

	if ip == nil {
		errs.Add(fmt.Errorf("Invalid IP Address %s", *consensusIP))
		return
	}

	Config.StakingIP = utils.IPDesc{
		IP:   ip,
		Port: uint16(*consensusPort),
	}

	// Bootstrapping:
	if *bootstrapIPs == "default" {
		*bootstrapIPs = strings.Join(GetIPs(networkID), ",")
	}
	for _, ip := range strings.Split(*bootstrapIPs, ",") {
		if ip != "" {
			addr, err := utils.ToIPDesc(ip)
			if err != nil {
				errs.Add(fmt.Errorf("couldn't parse ip: %w", err))
				return
			}
			Config.BootstrapPeers = append(Config.BootstrapPeers, &node.Peer{
				IP: addr,
			})
		}
	}

	if *bootstrapIDs == "default" {
		if *bootstrapIPs == "" {
			*bootstrapIDs = ""
		} else {
			*bootstrapIDs = strings.Join(genesis.GetConfig(networkID).StakerIDs, ",")
		}
	}
	if Config.EnableStaking {
		i := 0
		cb58 := formatting.CB58{}
		for _, id := range strings.Split(*bootstrapIDs, ",") {
			if id != "" {
				err = cb58.FromString(id)
				if err != nil {
					if err != nil {
						errs.Add(fmt.Errorf("couldn't parse bootstrap peer id to bytes: %w", err))
						return
					}
				}
				peerID, err := ids.ToShortID(cb58.Bytes)
				if err != nil {
					errs.Add(fmt.Errorf("couldn't parse bootstrap peer id: %w", err))
					return
				}
				if len(Config.BootstrapPeers) <= i {
					errs.Add(errBootstrapMismatch)
					return
				}
				Config.BootstrapPeers[i].ID = peerID
				i++
			}
		}
		if len(Config.BootstrapPeers) != i {
			errs.Add(fmt.Errorf("More bootstrap IPs, %d, provided than bootstrap IDs, %d", len(Config.BootstrapPeers), i))
			return
		}
	} else {
		for _, peer := range Config.BootstrapPeers {
			peer.ID = ids.NewShortID(hashing.ComputeHash160Array([]byte(peer.IP.String())))
		}
	}

	// Staking
	Config.StakingCertFile = os.ExpandEnv(Config.StakingCertFile) // parse any env variable
	Config.StakingKeyFile = os.ExpandEnv(Config.StakingKeyFile)
	switch {
	// If staking key/cert locations are specified but not found, error
	case Config.StakingKeyFile != defaultStakingKeyPath || Config.StakingCertFile != defaultStakingCertPath:
		if _, err := os.Stat(Config.StakingKeyFile); os.IsNotExist(err) {
			errs.Add(fmt.Errorf("couldn't find staking key at %s", Config.StakingKeyFile))
			return
		} else if _, err := os.Stat(Config.StakingCertFile); os.IsNotExist(err) {
			errs.Add(fmt.Errorf("couldn't find staking certificate at %s", Config.StakingCertFile))
			return
		}
	default:
		// Only creates staking key/cert if [stakingKeyPath] doesn't exist
		if err := staking.GenerateStakingKeyCert(Config.StakingKeyFile, Config.StakingCertFile); err != nil {
			errs.Add(fmt.Errorf("couldn't generate staking key/cert: %w", err))
			return
		}
	}

	// HTTP:
	Config.HTTPPort = uint16(*httpPort)

	// Logging:
	if *logsDir != "" {
		loggingConfig.Directory = *logsDir
	}
	logFileLevel, err := logging.ToLevel(*logLevel)
	if errs.Add(err); err != nil {
		return
	}
	loggingConfig.LogLevel = logFileLevel

	if *logDisplayLevel == "" {
		*logDisplayLevel = *logLevel
	}
	displayLevel, err := logging.ToLevel(*logDisplayLevel)
	if errs.Add(err); err != nil {
		return
	}
	loggingConfig.DisplayLevel = displayLevel

	Config.LoggingConfig = loggingConfig

	// Throughput:
	Config.ThroughputPort = uint16(*throughputPort)

	// Router used for consensus
	Config.ConsensusRouter = &router.ChainRouter{}
}
<commit_msg>fix typo<commit_after>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package main

import (
	"errors"
	"flag"
	"fmt"
	"net"
	"os"
	"path"
	"path/filepath"
	"strings"

	"github.com/ava-labs/gecko/database/leveldb"
	"github.com/ava-labs/gecko/database/memdb"
	"github.com/ava-labs/gecko/genesis"
	"github.com/ava-labs/gecko/ids"
	"github.com/ava-labs/gecko/nat"
	"github.com/ava-labs/gecko/node"
	"github.com/ava-labs/gecko/snow/networking/router"
	"github.com/ava-labs/gecko/staking"
	"github.com/ava-labs/gecko/utils"
	"github.com/ava-labs/gecko/utils/formatting"
	"github.com/ava-labs/gecko/utils/hashing"
	"github.com/ava-labs/gecko/utils/logging"
	"github.com/ava-labs/gecko/utils/wrappers"
)

const (
	dbVersion = "v0.2.0"
)

// Results of parsing the CLI
var (
	Config                 = node.Config{}
	Err                    error
	defaultDbDir           = os.ExpandEnv(filepath.Join("$HOME", ".gecko", "db"))
	defaultStakingKeyPath  = os.ExpandEnv(filepath.Join("$HOME", ".gecko", "staking", "staker.key"))
	defaultStakingCertPath = os.ExpandEnv(filepath.Join("$HOME", ".gecko", "staking", "staker.crt"))
)

var (
	errBootstrapMismatch = errors.New("more bootstrap IDs provided than bootstrap IPs")
)

// GetIPs returns the default IPs for each network
func GetIPs(networkID uint32) []string {
	switch networkID {
	case genesis.CascadeID:
		return []string{
			"3.227.207.132:21001",
			"34.207.133.167:21001",
			"107.23.241.199:21001",
			"54.197.215.186:21001",
			"18.234.153.22:21001",
		}
	default:
		return nil
	}
}

// Parse the CLI arguments
func init() {
	errs := &wrappers.Errs{}
	defer func() { Err = errs.Err }()

	loggingConfig, err := logging.DefaultConfig()
	if errs.Add(err); errs.Errored() {
		return
	}

	fs := flag.NewFlagSet("gecko", flag.ContinueOnError)

	// NetworkID:
	networkName := fs.String("network-id", genesis.CascadeName, "Network ID this node will connect to")

	// Ava fees:
	fs.Uint64Var(&Config.AvaTxFee, "ava-tx-fee", 0, "Ava transaction fee, in $nAva")

	// Assertions:
	fs.BoolVar(&loggingConfig.Assertions, "assertions-enabled", true, "Turn on assertion execution")

	// Crypto:
	fs.BoolVar(&Config.EnableCrypto, "signature-verification-enabled", true, "Turn on signature verification")

	// Database:
	db := fs.Bool("db-enabled", true, "Turn on persistent storage")
	dbDir := fs.String("db-dir", defaultDbDir, "Database directory for Ava state")

	// IP:
	consensusIP := fs.String("public-ip", "", "Public IP of this node")

	// HTTP Server:
	httpPort := fs.Uint("http-port", 9650, "Port of the HTTP server")
	fs.BoolVar(&Config.EnableHTTPS, "http-tls-enabled", false, "Upgrade the HTTP server to HTTPs")
	fs.StringVar(&Config.HTTPSKeyFile, "http-tls-key-file", "", "TLS private key file for the HTTPs server")
	fs.StringVar(&Config.HTTPSCertFile, "http-tls-cert-file", "", "TLS certificate file for the HTTPs server")

	// Bootstrapping:
	bootstrapIPs := fs.String("bootstrap-ips", "default", "Comma separated list of bootstrap peer ips to connect to. Example: 127.0.0.1:9630,127.0.0.1:9631")
	bootstrapIDs := fs.String("bootstrap-ids", "default", "Comma separated list of bootstrap peer ids to connect to. Example: JR4dVmy6ffUGAKCBDkyCbeZbyHQBeDsET,8CrVPQZ4VSqgL8zTdvL14G8HqAfrBr4z")

	// Staking:
	consensusPort := fs.Uint("staking-port", 9651, "Port of the consensus server")
	fs.BoolVar(&Config.EnableStaking, "staking-tls-enabled", true, "Require TLS to authenticate staking connections")
	fs.StringVar(&Config.StakingKeyFile, "staking-tls-key-file", defaultStakingKeyPath, "TLS private key for staking")
	fs.StringVar(&Config.StakingCertFile, "staking-tls-cert-file", defaultStakingCertPath, "TLS certificate for staking")

	// Plugins:
	fs.StringVar(&Config.PluginDir, "plugin-dir", "./build/plugins", "Plugin directory for Ava VMs")

	// Logging:
	logsDir := fs.String("log-dir", "", "Logging directory for Ava")
	logLevel := fs.String("log-level", "info", "The log level. Should be one of {verbo, debug, info, warn, error, fatal, off}")
	logDisplayLevel := fs.String("log-display-level", "", "The log display level. If left blank, will inherit the value of log-level. Otherwise, should be one of {verbo, debug, info, warn, error, fatal, off}")

	fs.IntVar(&Config.ConsensusParams.K, "snow-sample-size", 5, "Number of nodes to query for each network poll")
	fs.IntVar(&Config.ConsensusParams.Alpha, "snow-quorum-size", 4, "Alpha value to use for required number positive results")
	fs.IntVar(&Config.ConsensusParams.BetaVirtuous, "snow-virtuous-commit-threshold", 20, "Beta value to use for virtuous transactions")
	fs.IntVar(&Config.ConsensusParams.BetaRogue, "snow-rogue-commit-threshold", 30, "Beta value to use for rogue transactions")
	fs.IntVar(&Config.ConsensusParams.Parents, "snow-avalanche-num-parents", 5, "Number of vertexes for reference from each new vertex")
	fs.IntVar(&Config.ConsensusParams.BatchSize, "snow-avalanche-batch-size", 30, "Number of operations to batch in each new vertex")
	fs.IntVar(&Config.ConsensusParams.ConcurrentRepolls, "snow-concurrent-repolls", 1, "Minimum number of concurrent polls for finalizing consensus")

	// Enable/Disable APIs:
	fs.BoolVar(&Config.AdminAPIEnabled, "api-admin-enabled", true, "If true, this node exposes the Admin API")
	fs.BoolVar(&Config.KeystoreAPIEnabled, "api-keystore-enabled", true, "If true, this node exposes the Keystore API")
	fs.BoolVar(&Config.MetricsAPIEnabled, "api-metrics-enabled", true, "If true, this node exposes the Metrics API")
	fs.BoolVar(&Config.IPCEnabled, "api-ipcs-enabled", false, "If true, IPCs can be opened")

	// Throughput Server
	throughputPort := fs.Uint("xput-server-port", 9652, "Port of the deprecated throughput test server")
	fs.BoolVar(&Config.ThroughputServerEnabled, "xput-server-enabled", false, "If true, throughput test server is created")

	ferr := fs.Parse(os.Args[1:])

	if ferr == flag.ErrHelp {
		// display usage/help text and exit successfully
		os.Exit(0)
	}

	if ferr != nil {
		// other type of error occurred when parsing args
		os.Exit(2)
	}

	networkID, err := genesis.NetworkID(*networkName)
	if errs.Add(err); err != nil {
		return
	}

	Config.NetworkID = networkID

	// DB:
	if *db {
		*dbDir = os.ExpandEnv(*dbDir) // parse any env variables
		dbPath := path.Join(*dbDir, genesis.NetworkName(Config.NetworkID), dbVersion)
		db, err := leveldb.New(dbPath, 0, 0, 0)
		if err != nil {
			errs.Add(fmt.Errorf("couldn't create db at %s: %w", dbPath, err))
			return
		}
		Config.DB = db
	} else {
		Config.DB = memdb.New()
	}

	Config.Nat = nat.NewRouter()

	var ip net.IP
	// If public IP is not specified, get it using shell command dig
	if *consensusIP == "" {
		ip, err = Config.Nat.IP()
		if err != nil {
			ip = net.IPv4zero // Couldn't get my IP...set to 0.0.0.0
		}
	} else {
		ip = net.ParseIP(*consensusIP)
	}

	if ip == nil {
		errs.Add(fmt.Errorf("Invalid IP Address %s", *consensusIP))
		return
	}

	Config.StakingIP = utils.IPDesc{
		IP:   ip,
		Port: uint16(*consensusPort),
	}

	// Bootstrapping:
	if *bootstrapIPs == "default" {
		*bootstrapIPs = strings.Join(GetIPs(networkID), ",")
	}
	for _, ip := range strings.Split(*bootstrapIPs, ",") {
		if ip != "" {
			addr, err := utils.ToIPDesc(ip)
			if err != nil {
				errs.Add(fmt.Errorf("couldn't parse ip: %w", err))
				return
			}
			Config.BootstrapPeers = append(Config.BootstrapPeers, &node.Peer{
				IP: addr,
			})
		}
	}

	if *bootstrapIDs == "default" {
		if *bootstrapIPs == "" {
			*bootstrapIDs = ""
		} else {
			*bootstrapIDs = strings.Join(genesis.GetConfig(networkID).StakerIDs, ",")
		}
	}
	if Config.EnableStaking {
		i := 0
		cb58 := formatting.CB58{}
		for _, id := range strings.Split(*bootstrapIDs, ",") {
			if id != "" {
				err = cb58.FromString(id)
				if err != nil {
					errs.Add(fmt.Errorf("couldn't parse bootstrap peer id to bytes: %w", err))
					return
				}
				peerID, err := ids.ToShortID(cb58.Bytes)
				if err != nil {
					errs.Add(fmt.Errorf("couldn't parse bootstrap peer id: %w", err))
					return
				}
				if len(Config.BootstrapPeers) <= i {
					errs.Add(errBootstrapMismatch)
					return
				}
				Config.BootstrapPeers[i].ID = peerID
				i++
			}
		}
		if len(Config.BootstrapPeers) != i {
			errs.Add(fmt.Errorf("More bootstrap IPs, %d, provided than bootstrap IDs, %d", len(Config.BootstrapPeers), i))
			return
		}
	} else {
		for _, peer := range Config.BootstrapPeers {
			peer.ID = ids.NewShortID(hashing.ComputeHash160Array([]byte(peer.IP.String())))
		}
	}

	// Staking
	Config.StakingCertFile = os.ExpandEnv(Config.StakingCertFile) // parse any env variable
	Config.StakingKeyFile = os.ExpandEnv(Config.StakingKeyFile)
	switch {
	// If staking key/cert locations are specified but not found, error
	case Config.StakingKeyFile != defaultStakingKeyPath || Config.StakingCertFile != defaultStakingCertPath:
		if _, err := os.Stat(Config.StakingKeyFile); os.IsNotExist(err) {
			errs.Add(fmt.Errorf("couldn't find staking key at %s", Config.StakingKeyFile))
			return
		} else if _, err := os.Stat(Config.StakingCertFile); os.IsNotExist(err) {
			errs.Add(fmt.Errorf("couldn't find staking certificate at %s", Config.StakingCertFile))
			return
		}
	default:
		// Only creates staking key/cert if [stakingKeyPath] doesn't exist
		if err := staking.GenerateStakingKeyCert(Config.StakingKeyFile, Config.StakingCertFile); err != nil {
			errs.Add(fmt.Errorf("couldn't generate staking key/cert: %w", err))
			return
		}
	}

	// HTTP:
	Config.HTTPPort = uint16(*httpPort)

	// Logging:
	if *logsDir != "" {
		loggingConfig.Directory = *logsDir
	}
	logFileLevel, err := logging.ToLevel(*logLevel)
	if errs.Add(err); err != nil {
		return
	}
	loggingConfig.LogLevel = logFileLevel

	if *logDisplayLevel == "" {
		*logDisplayLevel = *logLevel
	}
	displayLevel, err := logging.ToLevel(*logDisplayLevel)
	if errs.Add(err); err != nil {
		return
	}
	loggingConfig.DisplayLevel = displayLevel

	Config.LoggingConfig = loggingConfig

	// Throughput:
	Config.ThroughputPort = uint16(*throughputPort)

	// Router used for consensus
	Config.ConsensusRouter = &router.ChainRouter{}
}
<|endoftext|>