<commit_before>package installer

import (
	"github.com/kardianos/osext"

	"bytes"
	"errors"
	"flag"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"text/template"
)

func GetInitDPath(name string) string {
	return "/etc/init.d/" + name
}

func IsInitDScriptExist(name string) bool {
	if _, err := os.Stat(GetInitDPath(name)); os.IsExist(err) {
		return true
	} else {
		return false
	}
}

func GetInitDScript(folder, command, user string) (ret string, err error) {

	initDTemplate, err := template.New("initd").Parse(templateInitD)
	if nil != err {
		return
	}

	buf := new(bytes.Buffer)
	initDTemplate.Execute(buf, TemplateInitD{Dir: folder, User: user, Command: folder + command, ScriptName: command})
	ret = buf.String()

	return
}

func WriteInitD(name, content string) (rErr error) {
	f, err := os.Create(GetInitDPath(name))
	if nil != err {
		rErr = err
		return
	}
	defer f.Close()
	length, err := f.Write([]byte(content))
	if nil != err {
		rErr = err
		return
	}
	if length != len(content) {
		rErr = errors.New("file could not be written completely")
		return
	}
	if err = MakeInitDExecutable(name); nil != err {
		//@TODO: also need rollback here
		rErr = err
		return
	}
	return
}

func MakeInitDExecutable(name string) (rErr error) {
	cmd := exec.Command("chmod", "+x", GetInitDPath(name))
	if _, execErr := cmd.CombinedOutput(); nil != execErr {
		rErr = execErr
	}
	return
}

func Install(user string) (rErr error) {
	pathToExe, err := osext.Executable()
	folder, command := filepath.Split(pathToExe)
	if nil != err {
		panic(err)
	}
	switch runtime.GOOS {
	case "linux":
	default:
		rErr = errors.New("not yet supported")
		return
	}
	var script string
	if IsInitDScriptExist(command) {
		rErr = errors.New(fmt.Sprintf("Script %s already exists in init.d folder", command))
		return
	}
	if script, err = GetInitDScript(folder, command, user); nil != err {
		rErr = err
		return
	}
	if fileWriteError := WriteInitD(command, script); nil != fileWriteError {
		rErr = fileWriteError
		return
	}
	/*
		cmd := exec.Command("update-rc.d", command, "defaults")
		if out, execErr := cmd.CombinedOutput(); nil != execErr {
			//@TODO: rollback , delete init.d script
			rErr = execErr
			return
		} else {
			fmt.Println(string(out))
		}
	*/
	return
}

var install = flag.Bool("install", false, "install this program as service")
var runAsUser = flag.String("installRunAsUser", "", "which user should the service run as")

func Register(parse bool) {
	if parse {
		flag.Parse()
	}
	if !*install {
		return
	}
	if *install && "" == *runAsUser {
		fmt.Println("you must specify a user that the service runs as")
		os.Exit(1)
	}
	if err := Install(*runAsUser); err != nil {
		fmt.Printf("installing as service failed: <%v> \n", err)
		os.Exit(1)
	}
	fmt.Println("successfully installed as service")
	os.Exit(0)
}

type TemplateInitD struct {
	Dir, User, Command, ScriptName string
}

const templateInitD = `#!/bin/sh
### BEGIN INIT INFO
# Provides: {{.ScriptName}}
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Start daemon at boot time
# Description: Enable service provided by daemon.
### END INIT INFO
dir="{{.Dir}}"
user="{{.User}}"
cmd="{{.Command}}"
name=` + "`basename $0`" + `
pid_file="/var/run/$name.pid"
stdout_log="/var/log/$name.log"
stderr_log="/var/log/$name.err"
get_pid() {
cat "$pid_file"
}
is_running() {
[ -f "$pid_file" ] && ps ` + "`get_pid`" + ` > /dev/null 2>&1
}
case "$1" in
start)
if is_running; then
echo "Already started"
else
echo "Starting $name"
cd "$dir"
sudo -u "$user" $cmd >> "$stdout_log" 2>> "$stderr_log" &
echo $! > "$pid_file"
if ! is_running; then
echo "Unable to start, see $stdout_log and $stderr_log"
exit 1
fi
fi
;;
stop)
if is_running; then
echo -n "Stopping $name.."
kill ` + "`get_pid`" + `
for i in {1..10}
do
if ! is_running; then
break
fi
echo -n "."
sleep 1
done
echo
if is_running; then
echo "Not stopped; may still be shutting down or shutdown may have failed"
exit 1
else
echo "Stopped"
if [ -f "$pid_file" ]; then
rm "$pid_file"
fi
fi
else
echo "Not running"
fi
;;
restart)
$0 stop
if is_running; then
echo "Unable to stop, will not attempt to start"
exit 1
fi
$0 start
;;
status)
if is_running; then
echo "Running"
else
echo "Stopped"
exit 1
fi
;;
*)
echo "Usage: $0 {start|stop|restart|status}"
exit 1
;;
esac
exit 0
`
<commit_msg>Added possibility to set a different service name (default is still the binary name)<commit_after>package installer

import (
	"github.com/kardianos/osext"

	"bytes"
	"errors"
	"flag"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"text/template"
)

func GetInitDPath(name string) string {
	return "/etc/init.d/" + name
}

func IsInitDScriptExist(name string) bool {
	if _, err := os.Stat(GetInitDPath(name)); os.IsExist(err) {
		return true
	} else {
		return false
	}
}

func GetInitDScript(folder, command, user string) (ret string, err error) {

	initDTemplate, err := template.New("initd").Parse(templateInitD)
	if nil != err {
		return
	}

	buf := new(bytes.Buffer)
	initDTemplate.Execute(buf, TemplateInitD{Dir: folder, User: user, Command: folder + command, ScriptName: command})
	ret = buf.String()

	return
}

func WriteInitD(name, content string) (rErr error) {
	f, err := os.Create(GetInitDPath(name))
	if nil != err {
		rErr = err
		return
	}
	defer f.Close()
	length, err := f.Write([]byte(content))
	if nil != err {
		rErr = err
		return
	}
	if length != len(content) {
		rErr = errors.New("file could not be written completely")
		return
	}
	if err = MakeInitDExecutable(name); nil != err {
		//@TODO: also need rollback here
		rErr = err
		return
	}
	return
}

func MakeInitDExecutable(name string) (rErr error) {
	cmd := exec.Command("chmod", "+x", GetInitDPath(name))
	if _, execErr := cmd.CombinedOutput(); nil != execErr {
		rErr = execErr
	}
	return
}

func Install(user string, serviceName string) (rErr error) {
	pathToExe, err := osext.Executable()
	folder, command := filepath.Split(pathToExe)
	if nil != err {
		panic(err)
	}
	switch runtime.GOOS {
	case "linux":
	default:
		rErr = errors.New("not yet supported")
		return
	}

	if serviceName == "" {
		serviceName = command
	}

	var script string
	if IsInitDScriptExist(serviceName) {
		rErr = errors.New(fmt.Sprintf("Script %s already exists in init.d folder", command))
		return
	}
	if script, err = GetInitDScript(folder, command, user); nil != err {
		rErr = err
		return
	}
	if fileWriteError := WriteInitD(serviceName, script); nil != fileWriteError {
		rErr = fileWriteError
		return
	}
	/*
		cmd := exec.Command("update-rc.d", command, "defaults")
		if out, execErr := cmd.CombinedOutput(); nil != execErr {
			//@TODO: rollback , delete init.d script
			rErr = execErr
			return
		} else {
			fmt.Println(string(out))
		}
	*/
	return
}

var install = flag.Bool("install", false, "install this program as service")
var runAsUser = flag.String("installRunAsUser", "", "which user should the service run as")
var serviceName = flag.String("serviceName", "", "[optional] service name")

func Register(parse bool) {
	if parse {
		flag.Parse()
	}
	if !*install {
		return
	}
	if *install && "" == *runAsUser {
		fmt.Println("you must specify a user that the service runs as")
		os.Exit(1)
	}
	if err := Install(*runAsUser, *serviceName); err != nil {
		fmt.Printf("installing as service failed: <%v> \n", err)
		os.Exit(1)
	}
	fmt.Println("successfully installed as service")
	os.Exit(0)
}

type TemplateInitD struct {
	Dir, User, Command, ScriptName string
}

const templateInitD = `#!/bin/sh
### BEGIN INIT INFO
# Provides: {{.ScriptName}}
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Start daemon at boot time
# Description: Enable service provided by daemon.
### END INIT INFO
dir="{{.Dir}}"
user="{{.User}}"
cmd="{{.Command}}"
name=` + "`basename $0`" + `
pid_file="/var/run/$name.pid"
stdout_log="/var/log/$name.log"
stderr_log="/var/log/$name.err"
get_pid() {
cat "$pid_file"
}
is_running() {
[ -f "$pid_file" ] && ps ` + "`get_pid`" + ` > /dev/null 2>&1
}
case "$1" in
start)
if is_running; then
echo "Already started"
else
echo "Starting $name"
cd "$dir"
sudo -u "$user" $cmd >> "$stdout_log" 2>> "$stderr_log" &
echo $! > "$pid_file"
if ! is_running; then
echo "Unable to start, see $stdout_log and $stderr_log"
exit 1
fi
fi
;;
stop)
if is_running; then
echo -n "Stopping $name.."
kill ` + "`get_pid`" + `
for i in {1..10}
do
if ! is_running; then
break
fi
echo -n "."
sleep 1
done
echo
if is_running; then
echo "Not stopped; may still be shutting down or shutdown may have failed"
exit 1
else
echo "Stopped"
if [ -f "$pid_file" ]; then
rm "$pid_file"
fi
fi
else
echo "Not running"
fi
;;
restart)
$0 stop
if is_running; then
echo "Unable to stop, will not attempt to start"
exit 1
fi
$0 start
;;
status)
if is_running; then
echo "Running"
else
echo "Stopped"
exit 1
fi
;;
*)
echo "Usage: $0 {start|stop|restart|status}"
exit 1
;;
esac
exit 0
`
<|endoftext|>


<commit_before>// Copyright 2018 The Fractal Team Authors
// This file is part of the fractal project.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

package params

import "fmt"

const (
	//ClientIdentifier Client identifier to advertise over the network
	ClientIdentifier = "ft"
)

const (
	// VersionMajor is Major version component of the current release
	VersionMajor = 0
	// VersionMinor is Minor version component of the current release
	VersionMinor = 0
	// VersionPatch is Patch version component of the current release
	VersionPatch = 1
	// VersionMeta is Version metadata to append to the version string
	VersionMeta = "unstable"
)

// Version holds the textual version string.
var Version = func() string {
	return fmt.Sprintf("%d.%d.%d", VersionMajor, VersionMinor, VersionPatch)
}()

// ArchiveVersion holds the textual version string used for Geth archives.
func ArchiveVersion(gitCommit string) string {
	vsn := Version
	if VersionMeta != "stable" {
		vsn += "-" + VersionMeta
	}
	if len(gitCommit) >= 8 {
		vsn += "-" + gitCommit[:8]
	}
	return vsn
}
<commit_msg>update version<commit_after>// Copyright 2018 The Fractal Team Authors
// This file is part of the fractal project.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

package params

import "fmt"

const (
	//ClientIdentifier Client identifier to advertise over the network
	ClientIdentifier = "ft"
)

const (
	// VersionMajor is Major version component of the current release
	VersionMajor = 0
	// VersionMinor is Minor version component of the current release
	VersionMinor = 0
	// VersionPatch is Patch version component of the current release
	VersionPatch = 4
	// VersionMeta is Version metadata to append to the version string
	VersionMeta = "unstable"
)

// Version holds the textual version string.
var Version = func() string {
	return fmt.Sprintf("%d.%d.%d", VersionMajor, VersionMinor, VersionPatch)
}()

// ArchiveVersion holds the textual version string used for Geth archives.
func ArchiveVersion(gitCommit string) string {
	vsn := Version
	if VersionMeta != "stable" {
		vsn += "-" + VersionMeta
	}
	if len(gitCommit) >= 8 {
		vsn += "-" + gitCommit[:8]
	}
	return vsn
}
<|endoftext|>


<commit_before>package broker

import (
	"time"

	"github.com/kr/beanstalk"
)

const (
	// ListTubeDelay is the time between sending list-tube to beanstalkd
	// to discover and watch newly created tubes.
	ListTubeDelay = 10 * time.Second
)

// BrokerDispatcher manages the running of Broker instances for tubes.  It can
// be manually told tubes to start, or it can poll for tubes as they are
// created. The `perTube` option determines how many brokers are started for
// each tube.
type BrokerDispatcher struct {
	address string
	cmd     string
	conn    *beanstalk.Conn
	perTube uint64
	tubeSet map[string]bool
}

func NewBrokerDispatcher(address, cmd string, perTube uint64) *BrokerDispatcher {
	return &BrokerDispatcher{
		address: address,
		cmd:     cmd,
		perTube: perTube,
		tubeSet: make(map[string]bool),
	}
}

// RunTube runs broker(s) for the specified tube.
// The number of brokers started is determined by the perTube argument to
// NewBrokerDispatcher.
func (bd *BrokerDispatcher) RunTube(tube string) {
	bd.tubeSet[tube] = true
	for i := uint64(0); i < bd.perTube; i++ {
		bd.runBroker(tube, i)
	}
}

// RunTube runs brokers for the specified tubes.
func (bd *BrokerDispatcher) RunTubes(tubes []string) {
	for _, tube := range tubes {
		bd.RunTube(tube)
	}
}

// RunAllTubes polls beanstalkd, running broker as new tubes are created.
func (bd *BrokerDispatcher) RunAllTubes() (err error) {
	conn, err := beanstalk.Dial("tcp", bd.address)
	if err == nil {
		bd.conn = conn
	} else {
		return
	}

	go func() {
		ticker := time.Tick(ListTubeDelay)
		for _ = range ticker {
			if e := bd.watchNewTubes(); e != nil {
				// ignore error
			}
		}
	}()

	return
}

func (bd *BrokerDispatcher) runBroker(tube string, slot uint64) {
	go func() {
		b := New(bd.address, tube, slot, bd.cmd, nil)
		b.Run(nil)
	}()
}

func (bd *BrokerDispatcher) watchNewTubes() (err error) {
	tubes, err := bd.conn.ListTubes()
	if err != nil {
		return
	}

	for _, tube := range tubes {
		if !bd.tubeSet[tube] {
			bd.RunTube(tube)
		}
	}

	return
}
<commit_msg>BrokerDispatcher.RunAllTubes() fires immediately.<commit_after>package broker

import (
	"log"
	"time"

	"github.com/kr/beanstalk"
)

const (
	// ListTubeDelay is the time between sending list-tube to beanstalkd
	// to discover and watch newly created tubes.
	ListTubeDelay = 10 * time.Second
)

// BrokerDispatcher manages the running of Broker instances for tubes.  It can
// be manually told tubes to start, or it can poll for tubes as they are
// created. The `perTube` option determines how many brokers are started for
// each tube.
type BrokerDispatcher struct {
	address string
	cmd     string
	conn    *beanstalk.Conn
	perTube uint64
	tubeSet map[string]bool
}

func NewBrokerDispatcher(address, cmd string, perTube uint64) *BrokerDispatcher {
	return &BrokerDispatcher{
		address: address,
		cmd:     cmd,
		perTube: perTube,
		tubeSet: make(map[string]bool),
	}
}

// RunTube runs broker(s) for the specified tube.
// The number of brokers started is determined by the perTube argument to
// NewBrokerDispatcher.
func (bd *BrokerDispatcher) RunTube(tube string) {
	bd.tubeSet[tube] = true
	for i := uint64(0); i < bd.perTube; i++ {
		bd.runBroker(tube, i)
	}
}

// RunTube runs brokers for the specified tubes.
func (bd *BrokerDispatcher) RunTubes(tubes []string) {
	for _, tube := range tubes {
		bd.RunTube(tube)
	}
}

// RunAllTubes polls beanstalkd, running broker as new tubes are created.
func (bd *BrokerDispatcher) RunAllTubes() (err error) {
	conn, err := beanstalk.Dial("tcp", bd.address)
	if err == nil {
		bd.conn = conn
	} else {
		return
	}

	go func() {
		ticker := instantTicker(ListTubeDelay)
		for _ = range ticker {
			if e := bd.watchNewTubes(); e != nil {
				log.Println(e)
			}
		}
	}()

	return
}

func (bd *BrokerDispatcher) runBroker(tube string, slot uint64) {
	go func() {
		b := New(bd.address, tube, slot, bd.cmd, nil)
		b.Run(nil)
	}()
}

func (bd *BrokerDispatcher) watchNewTubes() (err error) {
	tubes, err := bd.conn.ListTubes()
	if err != nil {
		return
	}

	for _, tube := range tubes {
		if !bd.tubeSet[tube] {
			bd.RunTube(tube)
		}
	}

	return
}

// Like time.Tick() but also fires immediately.
func instantTicker(t time.Duration) <-chan time.Time {
	c := make(chan time.Time)
	ticker := time.NewTicker(t)
	go func() {
		c <- time.Now()
		for t := range ticker.C {
			c <- t
		}
	}()
	return c
}
<|endoftext|>


<commit_before>package engine

import (
	"bytes"
	"capsulecd/pkg/config"
	"capsulecd/pkg/errors"
	"capsulecd/pkg/pipeline"
	"capsulecd/pkg/scm"
	"capsulecd/pkg/utils"
	"fmt"
	"go/ast"
	"go/format"
	"go/parser"
	"go/token"
	"io/ioutil"
	"os/exec"
	"path"
	"strings"
	"os"
)

type golangMetadata struct {
	Version string
}
type engineGolang struct {
	engineBase

	PipelineData    *pipeline.Data
	Scm             scm.Interface //Interface
	CurrentMetadata *golangMetadata
	NextMetadata    *golangMetadata
	GoPath		string
}

func (g *engineGolang) Init(pipelineData *pipeline.Data, config config.Interface, sourceScm scm.Interface) error {
	g.Scm = sourceScm
	g.Config = config
	g.PipelineData = pipelineData
	g.CurrentMetadata = new(golangMetadata)
	g.NextMetadata = new(golangMetadata)

	//golang requires that the package is in GOPATH.
	//we can have multiple workspaces in the gopath by separating them with :
	g.GoPath = g.PipelineData.GitParentPath
	g.PipelineData.GitParentPath = path.Join(g.PipelineData.GitParentPath, "src")
	os.MkdirAll(g.PipelineData.GitParentPath, 0666)
	os.Setenv("GOPATH", fmt.Sprintf("%s:%s", os.Getenv("GOPATH"), g.GoPath))
	//TODO: g.GoPath root will not be deleted (its the parent of GitParentPath).

	//set command defaults (can be overridden by repo/system configuration)
	g.Config.SetDefault("engine_cmd_compile", "go build $(go list ./cmd/...)")
	g.Config.SetDefault("engine_cmd_lint", "gometalinter.v1 ./...")
	g.Config.SetDefault("engine_cmd_fmt", "go fmt $(go list ./... | grep -v /vendor/)")
	g.Config.SetDefault("engine_cmd_test", "go test $(glide novendor)")
	g.Config.SetDefault("engine_cmd_security_check", "exit 0") //TODO: update when there's a dependency checker for Golang/Glide

	return nil
}

func (g *engineGolang) ValidateTools() error {
	if _, kerr := exec.LookPath("go"); kerr != nil {
		return errors.EngineValidateToolError("go binary is missing")
	}

	if _, kerr := exec.LookPath("gometalinter.v1"); kerr != nil {
		return errors.EngineValidateToolError("gometalinter.v1 binary is missing")
	}

	return nil
}

func (g *engineGolang) AssembleStep() error {
	//validate that the chef metadata.rb file exists

	if !utils.FileExists(path.Join(g.PipelineData.GitLocalPath, "pkg", "version", "version.go")) {
		return errors.EngineBuildPackageInvalid("pkg/version/version.go file is required to process Go library")
	}

	//we only support glide as a Go dependency manager right now. Should be easy to add additional ones though.
	if !utils.FileExists(path.Join(g.PipelineData.GitLocalPath, "glide.yaml")) {
		return errors.EngineBuildPackageInvalid("glide.yml file is required to process Go library")
	}

	// bump up the go package version
	if merr := g.retrieveCurrentMetadata(g.PipelineData.GitLocalPath); merr != nil {
		return merr
	}

	if perr := g.populateNextMetadata(); perr != nil {
		return perr
	}

	if nerr := g.writeNextMetadata(g.PipelineData.GitLocalPath); nerr != nil {
		return nerr
	}

	gitignorePath := path.Join(g.PipelineData.GitLocalPath, ".gitignore")
	if !utils.FileExists(gitignorePath) {
		if err := utils.GitGenerateGitIgnore(g.PipelineData.GitLocalPath, "Go"); err != nil {
			return err
		}
	}

	return nil
}

func (g *engineGolang) DependenciesStep() error {
	//TODO: check if glide will complain if the checkout directory isnt the same as the GOPATH
	// the library has already been downloaded. lets make sure all its dependencies are available.
	if cerr := utils.BashCmdExec("glide install", g.PipelineData.GitLocalPath, nil, ""); cerr != nil {
		return errors.EngineTestDependenciesError("glide install failed. Check dependencies")
	}

	return nil
}

func (g *engineGolang) CompileStep() error {
	if g.Config.GetBool("engine_disable_compile") {
		//cmd directory is optional. check if it exists first.
		if !utils.FileExists(path.Join(g.PipelineData.GitLocalPath, "cmd")) {
			return nil
		}

		//code formatter
		compileCmd := g.Config.GetString("engine_cmd_compile")
		if terr := utils.BashCmdExec(compileCmd, g.PipelineData.GitLocalPath, nil, ""); terr != nil {
			return errors.EngineTestRunnerError(fmt.Sprintf("Compile command (%s) failed. Check log for more details.", compileCmd))
		}
	}
	return nil
}

func (g *engineGolang) TestStep() error {
	// go test -v $(go list ./... | grep -v /vendor/)
	// gofmt -s -l $(bash find . -name "*.go" | grep -v vendor | uniq)

	//TODO: the package msut be in the GOPATH for this to work correclty.
	//http://craigwickesser.com/2015/02/golang-cmd-with-custom-environment/
	//http://www.ryanday.net/2012/10/01/installing-go-and-gopath/
	//

	//skip the lint commands if disabled
	if !g.Config.GetBool("engine_disable_lint") {
		//run test command
		lintCmd := g.Config.GetString("engine_cmd_lint")
		if terr := utils.BashCmdExec(lintCmd, g.PipelineData.GitLocalPath, nil, ""); terr != nil {
			return errors.EngineTestRunnerError(fmt.Sprintf("Lint command (%s) failed. Check log for more details.", lintCmd))
		}

		if g.Config.GetBool("engine_enable_code_mutation") {
			//code formatter
			fmtCmd := g.Config.GetString("engine_cmd_fmt")
			if terr := utils.BashCmdExec(fmtCmd, g.PipelineData.GitLocalPath, nil, ""); terr != nil {
				return errors.EngineTestRunnerError(fmt.Sprintf("Format command (%s) failed. Check log for more details.", fmtCmd))
			}
		}
	}

	//skip the test commands if disabled
	if !g.Config.GetBool("engine_disable_test") {
		//run test command
		testCmd :=  g.Config.GetString("engine_cmd_test")
		if terr := utils.BashCmdExec(testCmd, g.PipelineData.GitLocalPath, nil, ""); terr != nil {
			return errors.EngineTestRunnerError(fmt.Sprintf("Test command (%s) failed. Check log for more details.", testCmd))
		}
	}

	//skip the security test commands if disabled
	if !g.Config.GetBool("engine_disable_security_check") {
		//run security check command
		// no Golang security check known for dependencies.
		//code formatter
		vulCmd := g.Config.GetString("engine_cmd_security_check")
		if terr := utils.BashCmdExec(vulCmd, g.PipelineData.GitLocalPath, nil, ""); terr != nil {
			return errors.EngineTestRunnerError(fmt.Sprintf("Format command (%s) failed. Check log for more details.", vulCmd))
		}
	}
	return nil
}

func (g *engineGolang) PackageStep() error {
	if !g.Config.GetBool("engine_package_keep_lock_file") {
		os.Remove(path.Join(g.PipelineData.GitLocalPath, "glide.lock"))
	}

	if cerr := utils.GitCommit(g.PipelineData.GitLocalPath, fmt.Sprintf("(v%s) Automated packaging of release by CapsuleCD", g.NextMetadata.Version)); cerr != nil {
		return cerr
	}
	tagCommit, terr := utils.GitTag(g.PipelineData.GitLocalPath, fmt.Sprintf("v%s", g.NextMetadata.Version))
	if terr != nil {
		return terr
	}

	g.PipelineData.ReleaseCommit = tagCommit
	g.PipelineData.ReleaseVersion = g.NextMetadata.Version
	return nil
}

func (g *engineGolang) DistStep() error {

	// no real packaging for golang.
	// libraries are stored in version control.
	return nil
}

//private Helpers

func (g *engineGolang) retrieveCurrentMetadata(gitLocalPath string) error {

	versionContent, rerr := ioutil.ReadFile(path.Join(g.PipelineData.GitLocalPath, "pkg", "version", "version.go"))
	if rerr != nil {
		return rerr
	}

	//Oh.My.God.

	// Create the AST by parsing src.
	fset := token.NewFileSet() // positions are relative to fset
	f, err := parser.ParseFile(fset, "", string(versionContent), 0)
	if err != nil {
		return err
	}

	version, verr := g.parseGoVersion(f.Decls)
	if verr != nil {
		return verr
	}

	g.CurrentMetadata.Version = version
	return nil
}

func (g *engineGolang) populateNextMetadata() error {

	nextVersion, err := g.BumpVersion(g.CurrentMetadata.Version)
	if err != nil {
		return err
	}

	g.NextMetadata.Version = nextVersion
	return nil
}

func (g *engineGolang) writeNextMetadata(gitLocalPath string) error {
	versionPath := path.Join(g.PipelineData.GitLocalPath, "pkg", "version", "version.go")
	versionContent, rerr := ioutil.ReadFile(versionPath)
	if rerr != nil {
		return rerr
	}

	//Oh.My.God.

	// Create the AST by parsing src.
	fset := token.NewFileSet() // positions are relative to fset
	f, err := parser.ParseFile(fset, "", string(versionContent), parser.ParseComments)
	if err != nil {
		return err
	}

	decls, serr := g.setGoVersion(f.Decls, g.NextMetadata.Version)
	if serr != nil {
		return serr
	}
	f.Decls = decls

	//write the version file again.
	var buf bytes.Buffer
	if err := format.Node(&buf, fset, f); err != nil {
		return err
	}

	return ioutil.WriteFile(versionPath, buf.Bytes(), 0644)
}

func (g *engineGolang) parseGoVersion(list []ast.Decl) (string, error) {
	//find version declaration (uppercase or lowercase)
	for _, decl := range list {
		gen := decl.(*ast.GenDecl)
		if gen.Tok == token.CONST || gen.Tok == token.VAR {
			for _, spec := range gen.Specs {
				valSpec := spec.(*ast.ValueSpec)
				if strings.ToLower(valSpec.Names[0].Name) == "version" {
					//found the version variable.
					return strings.Trim(valSpec.Values[0].(*ast.BasicLit).Value, "\"'"), nil
				}
			}
		}
	}
	return "", errors.EngineBuildPackageFailed("Could not retrieve the version from pkg/version/version.go")
}

func (g *engineGolang) setGoVersion(list []ast.Decl, version string) ([]ast.Decl, error) {
	//find version declaration (uppercase or lowercase)
	for _, decl := range list {
		gen := decl.(*ast.GenDecl)
		if gen.Tok == token.CONST || gen.Tok == token.VAR {
			for _, spec := range gen.Specs {
				valSpec := spec.(*ast.ValueSpec)
				if strings.ToLower(valSpec.Names[0].Name) == "version" {
					//found the version variable.
					valSpec.Values[0].(*ast.BasicLit).Value = fmt.Sprintf(`"%s"`, version)
					return list, nil
				}
			}
		}
	}
	return nil, errors.EngineBuildPackageFailed("Could not set the version in pkg/version/version.go")
}
<commit_msg>disable gocyclo. ignore the vendor folder when running linter.<commit_after>package engine

import (
	"bytes"
	"capsulecd/pkg/config"
	"capsulecd/pkg/errors"
	"capsulecd/pkg/pipeline"
	"capsulecd/pkg/scm"
	"capsulecd/pkg/utils"
	"fmt"
	"go/ast"
	"go/format"
	"go/parser"
	"go/token"
	"io/ioutil"
	"os/exec"
	"path"
	"strings"
	"os"
)

type golangMetadata struct {
	Version string
}
type engineGolang struct {
	engineBase

	PipelineData    *pipeline.Data
	Scm             scm.Interface //Interface
	CurrentMetadata *golangMetadata
	NextMetadata    *golangMetadata
	GoPath		string
}

func (g *engineGolang) Init(pipelineData *pipeline.Data, config config.Interface, sourceScm scm.Interface) error {
	g.Scm = sourceScm
	g.Config = config
	g.PipelineData = pipelineData
	g.CurrentMetadata = new(golangMetadata)
	g.NextMetadata = new(golangMetadata)

	//golang requires that the package is in GOPATH.
	//we can have multiple workspaces in the gopath by separating them with :
	g.GoPath = g.PipelineData.GitParentPath
	g.PipelineData.GitParentPath = path.Join(g.PipelineData.GitParentPath, "src")
	os.MkdirAll(g.PipelineData.GitParentPath, 0666)
	os.Setenv("GOPATH", fmt.Sprintf("%s:%s", os.Getenv("GOPATH"), g.GoPath))
	//TODO: g.GoPath root will not be deleted (its the parent of GitParentPath).

	//set command defaults (can be overridden by repo/system configuration)
	g.Config.SetDefault("engine_cmd_compile", "go build $(go list ./cmd/...)")
	g.Config.SetDefault("engine_cmd_lint", "gometalinter.v1 --vendor --disable=gocyclo  ./...")
	g.Config.SetDefault("engine_cmd_fmt", "go fmt $(go list ./... | grep -v /vendor/)")
	g.Config.SetDefault("engine_cmd_test", "go test $(glide novendor)")
	g.Config.SetDefault("engine_cmd_security_check", "exit 0") //TODO: update when there's a dependency checker for Golang/Glide

	return nil
}

func (g *engineGolang) ValidateTools() error {
	if _, kerr := exec.LookPath("go"); kerr != nil {
		return errors.EngineValidateToolError("go binary is missing")
	}

	if _, kerr := exec.LookPath("gometalinter.v1"); kerr != nil {
		return errors.EngineValidateToolError("gometalinter.v1 binary is missing")
	}

	return nil
}

func (g *engineGolang) AssembleStep() error {
	//validate that the chef metadata.rb file exists

	if !utils.FileExists(path.Join(g.PipelineData.GitLocalPath, "pkg", "version", "version.go")) {
		return errors.EngineBuildPackageInvalid("pkg/version/version.go file is required to process Go library")
	}

	//we only support glide as a Go dependency manager right now. Should be easy to add additional ones though.
	if !utils.FileExists(path.Join(g.PipelineData.GitLocalPath, "glide.yaml")) {
		return errors.EngineBuildPackageInvalid("glide.yml file is required to process Go library")
	}

	// bump up the go package version
	if merr := g.retrieveCurrentMetadata(g.PipelineData.GitLocalPath); merr != nil {
		return merr
	}

	if perr := g.populateNextMetadata(); perr != nil {
		return perr
	}

	if nerr := g.writeNextMetadata(g.PipelineData.GitLocalPath); nerr != nil {
		return nerr
	}

	gitignorePath := path.Join(g.PipelineData.GitLocalPath, ".gitignore")
	if !utils.FileExists(gitignorePath) {
		if err := utils.GitGenerateGitIgnore(g.PipelineData.GitLocalPath, "Go"); err != nil {
			return err
		}
	}

	return nil
}

func (g *engineGolang) DependenciesStep() error {
	//TODO: check if glide will complain if the checkout directory isnt the same as the GOPATH
	// the library has already been downloaded. lets make sure all its dependencies are available.
	if cerr := utils.BashCmdExec("glide install", g.PipelineData.GitLocalPath, nil, ""); cerr != nil {
		return errors.EngineTestDependenciesError("glide install failed. Check dependencies")
	}

	return nil
}

func (g *engineGolang) CompileStep() error {
	if g.Config.GetBool("engine_disable_compile") {
		//cmd directory is optional. check if it exists first.
		if !utils.FileExists(path.Join(g.PipelineData.GitLocalPath, "cmd")) {
			return nil
		}

		//code formatter
		compileCmd := g.Config.GetString("engine_cmd_compile")
		if terr := utils.BashCmdExec(compileCmd, g.PipelineData.GitLocalPath, nil, ""); terr != nil {
			return errors.EngineTestRunnerError(fmt.Sprintf("Compile command (%s) failed. Check log for more details.", compileCmd))
		}
	}
	return nil
}

func (g *engineGolang) TestStep() error {
	// go test -v $(go list ./... | grep -v /vendor/)
	// gofmt -s -l $(bash find . -name "*.go" | grep -v vendor | uniq)

	//TODO: the package msut be in the GOPATH for this to work correclty.
	//http://craigwickesser.com/2015/02/golang-cmd-with-custom-environment/
	//http://www.ryanday.net/2012/10/01/installing-go-and-gopath/
	//

	//skip the lint commands if disabled
	if !g.Config.GetBool("engine_disable_lint") {
		//run test command
		lintCmd := g.Config.GetString("engine_cmd_lint")
		if terr := utils.BashCmdExec(lintCmd, g.PipelineData.GitLocalPath, nil, ""); terr != nil {
			return errors.EngineTestRunnerError(fmt.Sprintf("Lint command (%s) failed. Check log for more details.", lintCmd))
		}

		if g.Config.GetBool("engine_enable_code_mutation") {
			//code formatter
			fmtCmd := g.Config.GetString("engine_cmd_fmt")
			if terr := utils.BashCmdExec(fmtCmd, g.PipelineData.GitLocalPath, nil, ""); terr != nil {
				return errors.EngineTestRunnerError(fmt.Sprintf("Format command (%s) failed. Check log for more details.", fmtCmd))
			}
		}
	}

	//skip the test commands if disabled
	if !g.Config.GetBool("engine_disable_test") {
		//run test command
		testCmd :=  g.Config.GetString("engine_cmd_test")
		if terr := utils.BashCmdExec(testCmd, g.PipelineData.GitLocalPath, nil, ""); terr != nil {
			return errors.EngineTestRunnerError(fmt.Sprintf("Test command (%s) failed. Check log for more details.", testCmd))
		}
	}

	//skip the security test commands if disabled
	if !g.Config.GetBool("engine_disable_security_check") {
		//run security check command
		// no Golang security check known for dependencies.
		//code formatter
		vulCmd := g.Config.GetString("engine_cmd_security_check")
		if terr := utils.BashCmdExec(vulCmd, g.PipelineData.GitLocalPath, nil, ""); terr != nil {
			return errors.EngineTestRunnerError(fmt.Sprintf("Format command (%s) failed. Check log for more details.", vulCmd))
		}
	}
	return nil
}

func (g *engineGolang) PackageStep() error {
	if !g.Config.GetBool("engine_package_keep_lock_file") {
		os.Remove(path.Join(g.PipelineData.GitLocalPath, "glide.lock"))
	}

	if cerr := utils.GitCommit(g.PipelineData.GitLocalPath, fmt.Sprintf("(v%s) Automated packaging of release by CapsuleCD", g.NextMetadata.Version)); cerr != nil {
		return cerr
	}
	tagCommit, terr := utils.GitTag(g.PipelineData.GitLocalPath, fmt.Sprintf("v%s", g.NextMetadata.Version))
	if terr != nil {
		return terr
	}

	g.PipelineData.ReleaseCommit = tagCommit
	g.PipelineData.ReleaseVersion = g.NextMetadata.Version
	return nil
}

func (g *engineGolang) DistStep() error {

	// no real packaging for golang.
	// libraries are stored in version control.
	return nil
}

//private Helpers

func (g *engineGolang) retrieveCurrentMetadata(gitLocalPath string) error {

	versionContent, rerr := ioutil.ReadFile(path.Join(g.PipelineData.GitLocalPath, "pkg", "version", "version.go"))
	if rerr != nil {
		return rerr
	}

	//Oh.My.God.

	// Create the AST by parsing src.
	fset := token.NewFileSet() // positions are relative to fset
	f, err := parser.ParseFile(fset, "", string(versionContent), 0)
	if err != nil {
		return err
	}

	version, verr := g.parseGoVersion(f.Decls)
	if verr != nil {
		return verr
	}

	g.CurrentMetadata.Version = version
	return nil
}

func (g *engineGolang) populateNextMetadata() error {

	nextVersion, err := g.BumpVersion(g.CurrentMetadata.Version)
	if err != nil {
		return err
	}

	g.NextMetadata.Version = nextVersion
	return nil
}

func (g *engineGolang) writeNextMetadata(gitLocalPath string) error {
	versionPath := path.Join(g.PipelineData.GitLocalPath, "pkg", "version", "version.go")
	versionContent, rerr := ioutil.ReadFile(versionPath)
	if rerr != nil {
		return rerr
	}

	//Oh.My.God.

	// Create the AST by parsing src.
	fset := token.NewFileSet() // positions are relative to fset
	f, err := parser.ParseFile(fset, "", string(versionContent), parser.ParseComments)
	if err != nil {
		return err
	}

	decls, serr := g.setGoVersion(f.Decls, g.NextMetadata.Version)
	if serr != nil {
		return serr
	}
	f.Decls = decls

	//write the version file again.
	var buf bytes.Buffer
	if err := format.Node(&buf, fset, f); err != nil {
		return err
	}

	return ioutil.WriteFile(versionPath, buf.Bytes(), 0644)
}

func (g *engineGolang) parseGoVersion(list []ast.Decl) (string, error) {
	//find version declaration (uppercase or lowercase)
	for _, decl := range list {
		gen := decl.(*ast.GenDecl)
		if gen.Tok == token.CONST || gen.Tok == token.VAR {
			for _, spec := range gen.Specs {
				valSpec := spec.(*ast.ValueSpec)
				if strings.ToLower(valSpec.Names[0].Name) == "version" {
					//found the version variable.
					return strings.Trim(valSpec.Values[0].(*ast.BasicLit).Value, "\"'"), nil
				}
			}
		}
	}
	return "", errors.EngineBuildPackageFailed("Could not retrieve the version from pkg/version/version.go")
}

func (g *engineGolang) setGoVersion(list []ast.Decl, version string) ([]ast.Decl, error) {
	//find version declaration (uppercase or lowercase)
	for _, decl := range list {
		gen := decl.(*ast.GenDecl)
		if gen.Tok == token.CONST || gen.Tok == token.VAR {
			for _, spec := range gen.Specs {
				valSpec := spec.(*ast.ValueSpec)
				if strings.ToLower(valSpec.Names[0].Name) == "version" {
					//found the version variable.
					valSpec.Values[0].(*ast.BasicLit).Value = fmt.Sprintf(`"%s"`, version)
					return list, nil
				}
			}
		}
	}
	return nil, errors.EngineBuildPackageFailed("Could not set the version in pkg/version/version.go")
}
<|endoftext|>


<commit_before>// Copyright 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package network

import (
	"fmt"
	"net"
	"sort"

	"github.com/juju/loggo"
)

var logger = loggo.GetLogger("juju.network")

// TODO(dimitern): Remove this once we use spaces as per the model.
const (
	// Id of the default public juju network
	DefaultPublic = "juju-public"

	// Id of the default private juju network
	DefaultPrivate = "juju-private"
)

// Id defines a provider-specific network id.
type Id string

// SubnetInfo describes the bare minimum information for a subnet,
// which the provider knows about but juju might not yet.
type SubnetInfo struct {
	// CIDR of the network, in 123.45.67.89/24 format. Can be empty if
	// unknown.
	CIDR string

	// ProviderId is a provider-specific network id. This the only
	// required field.
	ProviderId Id

	// VLANTag needs to be between 1 and 4094 for VLANs and 0 for
	// normal networks. It's defined by IEEE 802.1Q standard, and used
	// to define a VLAN network. For more information, see:
	// http://en.wikipedia.org/wiki/IEEE_802.1Q.
	VLANTag int

	// AllocatableIPLow and AllocatableIPHigh describe the allocatable
	// portion of the subnet. The provider will only permit allocation
	// between these limits. If they are empty then none of the subnet is
	// allocatable.
	AllocatableIPLow  net.IP
	AllocatableIPHigh net.IP
}

// InterfaceConfigType defines valid network interface configuration
// types. See interfaces(5) for details
type InterfaceConfigType string

const (
	ConfigUnknown InterfaceConfigType = ""
	ConfigDHCP    InterfaceConfigType = "dhcp"
	ConfigStatic  InterfaceConfigType = "static"
	ConfigManual  InterfaceConfigType = "manual"
	// add others when needed
)

// InterfaceInfo describes a single network interface available on an
// instance. For providers that support networks, this will be
// available at StartInstance() time.
type InterfaceInfo struct {
	// DeviceIndex specifies the order in which the network interface
	// appears on the host. The primary interface has an index of 0.
	DeviceIndex int

	// MACAddress is the network interface's hardware MAC address
	// (e.g. "aa:bb:cc:dd:ee:ff").
	MACAddress string

	// CIDR of the network, in 123.45.67.89/24 format.
	CIDR string

	// NetworkName is juju-internal name of the network.
	NetworkName string

	// ProviderId is a provider-specific NIC id.
	ProviderId Id

	// ProviderSubnetId is the provider-specific id for the associated
	// subnet.
	ProviderSubnetId Id

	// VLANTag needs to be between 1 and 4094 for VLANs and 0 for
	// normal networks. It's defined by IEEE 802.1Q standard.
	VLANTag int

	// InterfaceName is the raw OS-specific network device name (e.g.
	// "eth1", even for a VLAN eth1.42 virtual interface).
	InterfaceName string

	// Disabled is true when the interface needs to be disabled on the
	// machine, e.g. not to configure it.
	Disabled bool

	// NoAutoStart is true when the interface should not be configured
	// to start automatically on boot. By default and for
	// backwards-compatibility, interfaces are configured to
	// auto-start.
	NoAutoStart bool

	// ConfigType determines whether the interface should be
	// configured via DHCP, statically, manually, etc. See
	// interfaces(5) for more information.
	ConfigType InterfaceConfigType

	// Address contains an optional static IP address to configure for
	// this network interface. The subnet mask to set will be inferred
	// from the CIDR value.
	Address Address

	// DNSServers contains an optional list of IP addresses and/or
	// hostnames to configure as DNS servers for this network
	// interface.
	DNSServers []Address

	// Gateway address, if set, defines the default gateway to
	// configure for this network interface. For containers this
	// usually is (one of) the host address(es).
	GatewayAddress Address

	// ExtraConfig can contain any valid setting and its value allowed
	// inside an "iface" section of a interfaces(5) config file, e.g.
	// "up", "down", "mtu", etc.
	ExtraConfig map[string]string
}

type interfaceInfoSlice []InterfaceInfo

func (s interfaceInfoSlice) Len() int      { return len(s) }
func (s interfaceInfoSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s interfaceInfoSlice) Less(i, j int) bool {
	iface1 := s[i]
	iface2 := s[j]
	return iface1.DeviceIndex < iface2.DeviceIndex
}

// Sort a slice of InterfaceInfo on DeviceIndex
func SortInterfaceInfo(interfaces []InterfaceInfo) {
	sort.Sort(interfaceInfoSlice(interfaces))
}

// ActualInterfaceName returns raw interface name for raw interface (e.g. "eth0") and
// virtual interface name for virtual interface (e.g. "eth0.42")
func (i *InterfaceInfo) ActualInterfaceName() string {
	if i.VLANTag > 0 {
		return fmt.Sprintf("%s.%d", i.InterfaceName, i.VLANTag)
	}
	return i.InterfaceName
}

// IsVirtual returns true when the interface is a virtual device, as
// opposed to a physical device (e.g. a VLAN or a network alias)
func (i *InterfaceInfo) IsVirtual() bool {
	return i.VLANTag > 0
}

// IsVLAN returns true when the interface is a VLAN interface.
func (i *InterfaceInfo) IsVLAN() bool {
	return i.VLANTag > 0
}

// PreferIPv6Getter will be implemented by both the environment and agent
// config.
type PreferIPv6Getter interface {
	PreferIPv6() bool
}

// InitializeFromConfig needs to be called once after the environment
// or agent configuration is available to configure networking
// settings.
func InitializeFromConfig(config PreferIPv6Getter) {
	globalPreferIPv6 = config.PreferIPv6()
	logger.Infof("setting prefer-ipv6 to %v", globalPreferIPv6)
}
<commit_msg>Nitpicky review comment<commit_after>// Copyright 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package network

import (
	"fmt"
	"net"
	"sort"

	"github.com/juju/loggo"
)

var logger = loggo.GetLogger("juju.network")

// TODO(dimitern): Remove this once we use spaces as per the model.
const (
	// Id of the default public juju network
	DefaultPublic = "juju-public"

	// Id of the default private juju network
	DefaultPrivate = "juju-private"
)

// Id defines a provider-specific network id.
type Id string

// SubnetInfo describes the bare minimum information for a subnet,
// which the provider knows about but juju might not yet.
type SubnetInfo struct {
	// CIDR of the network, in 123.45.67.89/24 format. Can be empty if
	// unknown.
	CIDR string

	// ProviderId is a provider-specific network id. This the only
	// required field.
	ProviderId Id

	// VLANTag needs to be between 1 and 4094 for VLANs and 0 for
	// normal networks. It's defined by IEEE 802.1Q standard, and used
	// to define a VLAN network. For more information, see:
	// http://en.wikipedia.org/wiki/IEEE_802.1Q.
	VLANTag int

	// AllocatableIPLow and AllocatableIPHigh describe the allocatable
	// portion of the subnet. The provider will only permit allocation
	// between these limits. If they are empty then none of the subnet is
	// allocatable.
	AllocatableIPLow  net.IP
	AllocatableIPHigh net.IP
}

// InterfaceConfigType defines valid network interface configuration
// types. See interfaces(5) for details
type InterfaceConfigType string

const (
	ConfigUnknown InterfaceConfigType = ""
	ConfigDHCP    InterfaceConfigType = "dhcp"
	ConfigStatic  InterfaceConfigType = "static"
	ConfigManual  InterfaceConfigType = "manual"
	// add others when needed
)

// InterfaceInfo describes a single network interface available on an
// instance. For providers that support networks, this will be
// available at StartInstance() time.
type InterfaceInfo struct {
	// DeviceIndex specifies the order in which the network interface
	// appears on the host. The primary interface has an index of 0.
	DeviceIndex int

	// MACAddress is the network interface's hardware MAC address
	// (e.g. "aa:bb:cc:dd:ee:ff").
	MACAddress string

	// CIDR of the network, in 123.45.67.89/24 format.
	CIDR string

	// NetworkName is juju-internal name of the network.
	NetworkName string

	// ProviderId is a provider-specific NIC id.
	ProviderId Id

	// ProviderSubnetId is the provider-specific id for the associated
	// subnet.
	ProviderSubnetId Id

	// VLANTag needs to be between 1 and 4094 for VLANs and 0 for
	// normal networks. It's defined by IEEE 802.1Q standard.
	VLANTag int

	// InterfaceName is the raw OS-specific network device name (e.g.
	// "eth1", even for a VLAN eth1.42 virtual interface).
	InterfaceName string

	// Disabled is true when the interface needs to be disabled on the
	// machine, e.g. not to configure it.
	Disabled bool

	// NoAutoStart is true when the interface should not be configured
	// to start automatically on boot. By default and for
	// backwards-compatibility, interfaces are configured to
	// auto-start.
	NoAutoStart bool

	// ConfigType determines whether the interface should be
	// configured via DHCP, statically, manually, etc. See
	// interfaces(5) for more information.
	ConfigType InterfaceConfigType

	// Address contains an optional static IP address to configure for
	// this network interface. The subnet mask to set will be inferred
	// from the CIDR value.
	Address Address

	// DNSServers contains an optional list of IP addresses and/or
	// hostnames to configure as DNS servers for this network
	// interface.
	DNSServers []Address

	// Gateway address, if set, defines the default gateway to
	// configure for this network interface. For containers this
	// usually is (one of) the host address(es).
	GatewayAddress Address

	// ExtraConfig can contain any valid setting and its value allowed
	// inside an "iface" section of a interfaces(5) config file, e.g.
	// "up", "down", "mtu", etc.
	ExtraConfig map[string]string
}

type interfaceInfoSlice []InterfaceInfo

func (s interfaceInfoSlice) Len() int      { return len(s) }
func (s interfaceInfoSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s interfaceInfoSlice) Less(i, j int) bool {
	iface1 := s[i]
	iface2 := s[j]
	return iface1.DeviceIndex < iface2.DeviceIndex
}

// Sort a slice of InterfaceInfo on DeviceIndex in ascending order
func SortInterfaceInfo(interfaces []InterfaceInfo) {
	sort.Sort(interfaceInfoSlice(interfaces))
}

// ActualInterfaceName returns raw interface name for raw interface (e.g. "eth0") and
// virtual interface name for virtual interface (e.g. "eth0.42")
func (i *InterfaceInfo) ActualInterfaceName() string {
	if i.VLANTag > 0 {
		return fmt.Sprintf("%s.%d", i.InterfaceName, i.VLANTag)
	}
	return i.InterfaceName
}

// IsVirtual returns true when the interface is a virtual device, as
// opposed to a physical device (e.g. a VLAN or a network alias)
func (i *InterfaceInfo) IsVirtual() bool {
	return i.VLANTag > 0
}

// IsVLAN returns true when the interface is a VLAN interface.
func (i *InterfaceInfo) IsVLAN() bool {
	return i.VLANTag > 0
}

// PreferIPv6Getter will be implemented by both the environment and agent
// config.
type PreferIPv6Getter interface {
	PreferIPv6() bool
}

// InitializeFromConfig needs to be called once after the environment
// or agent configuration is available to configure networking
// settings.
func InitializeFromConfig(config PreferIPv6Getter) {
	globalPreferIPv6 = config.PreferIPv6()
	logger.Infof("setting prefer-ipv6 to %v", globalPreferIPv6)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"flag"
	"log"
	"net/http"
	"os"
	"text/template"

	"github.com/kardianos/service"
	"github.com/yinghau76/phabricator-to-slack"
)

var logger service.Logger

type server struct{}

func (s *server) Start(service service.Service) error {
	go s.run()
	return nil
}

func (s *server) run() {
	var phabricator = ph2slack.Phabricator{
		Host:  os.Getenv("PHABRICATOR_HOST"),
		Token: os.Getenv("PHABRICATOR_TOKEN"),
	}

	var slack = ph2slack.Slack{
		Token:    os.Getenv("SLACK_TOKEN"),
		Username: "Phabricator",
	}

	channel := os.Getenv("SLACK_CHANNEL")

	var t = template.Must(template.New("message").Parse(`<{{ .URI }}|{{ .Name }}> {{ .Text }}`))

	http.HandleFunc("/story", func(w http.ResponseWriter, r *http.Request) {
		story := r.FormValue("storyID")
		text := r.FormValue("storyText")
		author := r.FormValue("storyAuthorPHID")
		phid := r.FormValue("storyData[objectPHID]")
		logger.Info("New story:", story, author, phid, text)

		if phobj, err := phabricator.PhidQuery(phid); phobj != nil {
			var msg bytes.Buffer
			t.Execute(&msg, struct{ URI, Name, Text string }{phobj["uri"], phobj["name"], text})
			slack.PostMessage(channel, msg.String())
		} else {
			logger.Error("Error:", err.Error())
			slack.PostMessage(channel, text)
		}
	})

	http.ListenAndServe(":"+os.Getenv("PORT"), nil)
}

func (s *server) Stop(service service.Service) error {
	return nil
}

func main() {
	svcConfig := &service.Config{
		Name:        "phabricator-to-slack",
		DisplayName: "phabricator-to-slack",
		Description: "Passing Phabricator notifications to Slack",
	}

	srv := &server{}
	s, err := service.New(srv, svcConfig)
	if err != nil {
		log.Fatal(err)
	}

	flag.Parse()
	if args := flag.Args(); len(args) > 0 {
		verb := args[0]
		switch verb {
		case "install":
			err = s.Install()
			if err != nil {
				log.Fatalln("Failed to install:", err)
			}
			log.Printf("Service \"%s\" installed.\n", svcConfig.DisplayName)
		case "uninstall":
			err = s.Uninstall()
			if err != nil {
				log.Fatalln("Failed to uninstall:", err)
			}
			log.Printf("Service \"%s\" uninstalled.\n", svcConfig.DisplayName)
		}
		return
	}

	logger, err = s.Logger(nil)
	if err != nil {
		log.Fatal(err)
	}
	err = s.Run()
	if err != nil {
		logger.Error(err)
	}
}
<commit_msg>Use shorter service name<commit_after>package main

import (
	"bytes"
	"flag"
	"log"
	"net/http"
	"os"
	"text/template"

	"github.com/kardianos/service"
	"github.com/yinghau76/phabricator-to-slack"
)

var logger service.Logger

type server struct{}

func (s *server) Start(service service.Service) error {
	go s.run()
	return nil
}

func (s *server) run() {
	var phabricator = ph2slack.Phabricator{
		Host:  os.Getenv("PHABRICATOR_HOST"),
		Token: os.Getenv("PHABRICATOR_TOKEN"),
	}

	var slack = ph2slack.Slack{
		Token:    os.Getenv("SLACK_TOKEN"),
		Username: "Phabricator",
	}

	channel := os.Getenv("SLACK_CHANNEL")

	var t = template.Must(template.New("message").Parse(`<{{ .URI }}|{{ .Name }}> {{ .Text }}`))

	http.HandleFunc("/story", func(w http.ResponseWriter, r *http.Request) {
		story := r.FormValue("storyID")
		text := r.FormValue("storyText")
		author := r.FormValue("storyAuthorPHID")
		phid := r.FormValue("storyData[objectPHID]")
		logger.Info("New story:", story, author, phid, text)

		if phobj, err := phabricator.PhidQuery(phid); phobj != nil {
			var msg bytes.Buffer
			t.Execute(&msg, struct{ URI, Name, Text string }{phobj["uri"], phobj["name"], text})
			slack.PostMessage(channel, msg.String())
		} else {
			logger.Error("Error:", err.Error())
			slack.PostMessage(channel, text)
		}
	})

	http.ListenAndServe(":"+os.Getenv("PORT"), nil)
}

func (s *server) Stop(service service.Service) error {
	return nil
}

func main() {
	svcConfig := &service.Config{
		Name:        "ph2slack",
		DisplayName: "phabricator-to-slack",
		Description: "Passing Phabricator notifications to Slack",
	}

	srv := &server{}
	s, err := service.New(srv, svcConfig)
	if err != nil {
		log.Fatal(err)
	}

	flag.Parse()
	if args := flag.Args(); len(args) > 0 {
		verb := args[0]
		switch verb {
		case "install":
			err = s.Install()
			if err != nil {
				log.Fatalln("Failed to install:", err)
			}
			log.Printf("Service \"%s\" installed.\n", svcConfig.DisplayName)
		case "uninstall":
			err = s.Uninstall()
			if err != nil {
				log.Fatalln("Failed to uninstall:", err)
			}
			log.Printf("Service \"%s\" uninstalled.\n", svcConfig.DisplayName)
		}
		return
	}

	logger, err = s.Logger(nil)
	if err != nil {
		log.Fatal(err)
	}
	err = s.Run()
	if err != nil {
		logger.Error(err)
	}
}
<|endoftext|>


<commit_before>package apns

import (
	"crypto/tls"
	"encoding/binary"
	"log"
	"time"
)

//ResponseQueueSize indicates how many APNS responses may be buffered.
var ResponseQueueSize = 10000

//SentBufferSize is the maximum number of sent notifications which may be buffered.
var SentBufferSize = 10000

var maxBackoff = 20 * time.Second

//Connection represents a single connection to APNS.
type Connection struct {
	Client
	conn   *tls.Conn
	queue  chan PushNotification
	errors chan *BadPushNotification
}

//NewConnection initializes an APNS connection. Use Connection.Start() to actually start sending notifications.
func NewConnection(client *Client) *Connection {
	c := new(Connection)
	c.Client = *client
	queue := make(chan PushNotification)
	errors := make(chan *BadPushNotification)
	c.queue = queue
	c.errors = errors
	return c
}

//Response is a reply from APNS - see apns.ApplePushResponses.
type Response struct {
	Status     uint8
	Identifier uint32
}

func newResponse() *Response {
	return new(Response)
}

//BadPushNotification represents a notification which APNS didn't like.
type BadPushNotification struct {
	PushNotification
	Status uint8
}

//Enqueue adds a push notification to the end of the "sending" queue.
func (conn *Connection) Enqueue(pn *PushNotification) {
	go func(pn *PushNotification) {
		conn.queue <- *pn
	}(pn)
}

//Errors gives you a channel of the push notifications Apple rejected.
func (conn *Connection) Errors() (errors <-chan *BadPushNotification) {
	return conn.errors
}

//Start initiates a connection to APNS and asnchronously sends notifications which have been queued.
func (conn *Connection) Start() error {
	//Connect to APNS. The reason this is here as well as in sender is that this probably catches any unavoidable errors in a synchronous fashion, while in sender it can reconnect after temporary errors (which should work most of the time.)
	err := conn.connect()
	if err != nil {
		return err
	}
	//Start sender goroutine
	sent := make(chan PushNotification)
	go conn.sender(conn.queue, sent)
	//Start reader goroutine
	responses := make(chan *Response, ResponseQueueSize)
	go conn.reader(responses)
	//Start limbo goroutine
	return nil
}

//Stop gracefully closes the connection - it waits for the sending queue to clear, and then shuts down.
func (conn *Connection) Stop() {
	//We can't just close the main queue channel, because retries might still need to be sent there.
	//
}

func (conn *Connection) sender(queue <-chan PushNotification, sent chan PushNotification) {
	defer conn.conn.Close()
	var backoff = time.Duration(100)
	for {
		pn, ok := <-conn.queue
		if !ok {
			//That means the Connection is stopped
			//close sent?
			return
		} else {
			//If not connected, connect
			if conn.conn == nil {
				for {
					err := conn.connect()
					if err != nil {
						//Exponential backoff up to a limit
						log.Println("APNS: Error connecting to server: ", err)
						backoff = backoff * 2
						if backoff > maxBackoff {
							backoff = maxBackoff
						}
						time.Sleep(backoff)
					} else {
						backoff = 100
						break
					}
				}
			}
			//Then send the push notification
			//TODO(draaglom): Do buffering as per the APNS docs
			payload, err := pn.ToBytes()
			if err != nil {
				//Should report this on the bad notifications channel probably
			} else {
				_, err = conn.conn.Write(payload)
				if err != nil {
					//Disconnect?
				} else {
					sent <- pn
				}
			}

		}
	}
}

func (conn *Connection) reader(responses chan<- *Response) {
	buffer := make([]byte, 6)
	for {
		_, err := conn.conn.Read(buffer)
		if err != nil {
			log.Println("APNS: Error reading from connection: ", err)
			conn.conn.Close()
			return
		}
		resp := newResponse()
		resp.Identifier = binary.BigEndian.Uint32(buffer[2:6])
		resp.Status = uint8(buffer[1])
		responses <- resp
	}
}

func (conn *Connection) limbo(sent <-chan PushNotification, responses chan Response, errors chan BadPushNotification, queue chan PushNotification) {
	limbo := make(chan PushNotification, SentBufferSize)
	ticker := time.NewTicker(1 * time.Second)
	timeNextNotification := true
	for {
		select {
		case pn := <-sent:
			//Drop it into the array
			limbo <- pn
			if timeNextNotification {
				//Is there a cleaner way of doing this?
				go func(pn PushNotification) {
					<-time.After(TimeoutSeconds * time.Second)
					successResp := newResponse()
					successResp.Identifier = pn.Identifier
					responses <- *successResp
				}(pn)
				timeNextNotification = false
			}
		case resp, ok := <-responses:
			if !ok {
				//If the responses channel is closed,
				//that means we're shutting down the connection.
			}
			switch {
			case resp.Status == 0:
				//Status 0 is a "success" response generated by a timeout in the library.
				for pn := range limbo {
					//Drop all the notifications until we get to the timed-out one.
					//(and leave the others in limbo)
					if pn.Identifier == resp.Identifier {
						break
					}
				}
			default:
				hit := false
				for pn := range limbo {
					switch {
					case pn.Identifier != resp.Identifier && !hit:
						//We haven't seen the identified notification yet
						//so these are all successful (drop silently)
					case pn.Identifier == resp.Identifier:
						hit = true
						if resp.Status != 10 {
							//It was an error, we should report this on the error channel
							bad := BadPushNotification{PushNotification: pn, Status: resp.Status}
							go func(bad BadPushNotification) {
								errors <- bad
							}(bad)
						}
					case pn.Identifier != resp.Identifier && hit:
						//We've already seen the identified notification,
						//so these should be requeued
						conn.Enqueue(&pn)
					}
				}
			}
		case <-ticker.C:
			timeNextNotification = true
		}
	}
}

func (conn *Connection) connect() error {
	if conn.conn != nil {
		conn.conn.Close()
	}

	var cert tls.Certificate
	var err error
	if len(conn.CertificateBase64) == 0 && len(conn.KeyBase64) == 0 {
		// The user did not specify raw block contents, so check the filesystem.
		cert, err = tls.LoadX509KeyPair(conn.CertificateFile, conn.KeyFile)
	} else {
		// The user provided the raw block contents, so use that.
		cert, err = tls.X509KeyPair([]byte(conn.CertificateBase64), []byte(conn.KeyBase64))
	}

	if err != nil {
		return err
	}

	conf := &tls.Config{
		Certificates: []tls.Certificate{cert},
	}

	tlsConn, err := tls.Dial("tcp", conn.Gateway, conf)
	if err != nil {
		return err
	}
	err = tlsConn.Handshake()
	if err != nil {
		_ = tlsConn.Close()
		return err
	}
	conn.conn = tlsConn
	return nil
}
<commit_msg>Fixed inconsistent channel types (pointers vs values); actually start the `limbo` routine; and a little more logging for debugging purposes.<commit_after>package apns

import (
	"crypto/tls"
	"encoding/binary"
	"log"
	"time"
)

//ResponseQueueSize indicates how many APNS responses may be buffered.
var ResponseQueueSize = 10000

//SentBufferSize is the maximum number of sent notifications which may be buffered.
var SentBufferSize = 10000

var maxBackoff = 20 * time.Second

//Connection represents a single connection to APNS.
type Connection struct {
	Client
	conn   *tls.Conn
	queue  chan PushNotification
	errors chan BadPushNotification
}

//NewConnection initializes an APNS connection. Use Connection.Start() to actually start sending notifications.
func NewConnection(client *Client) *Connection {
	c := new(Connection)
	c.Client = *client
	queue := make(chan PushNotification)
	errors := make(chan BadPushNotification)
	c.queue = queue
	c.errors = errors
	return c
}

//Response is a reply from APNS - see apns.ApplePushResponses.
type Response struct {
	Status     uint8
	Identifier uint32
}

func newResponse() Response {
	r := Response{}
	return r
}

//BadPushNotification represents a notification which APNS didn't like.
type BadPushNotification struct {
	PushNotification
	Status uint8
}

//Enqueue adds a push notification to the end of the "sending" queue.
func (conn *Connection) Enqueue(pn *PushNotification) {
	go func(pn *PushNotification) {
		conn.queue <- *pn
	}(pn)
}

//Errors gives you a channel of the push notifications Apple rejected.
func (conn *Connection) Errors() (errors <-chan BadPushNotification) {
	return conn.errors
}

//Start initiates a connection to APNS and asnchronously sends notifications which have been queued.
func (conn *Connection) Start() error {
	//Connect to APNS. The reason this is here as well as in sender is that this probably catches any unavoidable errors in a synchronous fashion, while in sender it can reconnect after temporary errors (which should work most of the time.)
	err := conn.connect()
	if err != nil {
		return err
	}
	//Start sender goroutine
	sent := make(chan PushNotification)
	go conn.sender(conn.queue, sent)
	//Start reader goroutine
	responses := make(chan Response, ResponseQueueSize)
	go conn.reader(responses)
	//Start limbo goroutine
	go conn.limbo(sent, responses, conn.errors, conn.queue)
	return nil
}

//Stop gracefully closes the connection - it waits for the sending queue to clear, and then shuts down.
func (conn *Connection) Stop() {
	//We can't just close the main queue channel, because retries might still need to be sent there.
	//
}

func (conn *Connection) sender(queue <-chan PushNotification, sent chan PushNotification) {
	defer conn.conn.Close()
	var backoff = time.Duration(100)
	for {
		pn, ok := <-conn.queue
		if !ok {
			//That means the Connection is stopped
			//close sent?
			return
		} else {
			//If not connected, connect
			if conn.conn == nil {
				for {
					err := conn.connect()
					if err != nil {
						//Exponential backoff up to a limit
						log.Println("APNS: Error connecting to server: ", err)
						backoff = backoff * 2
						if backoff > maxBackoff {
							backoff = maxBackoff
						}
						time.Sleep(backoff)
					} else {
						backoff = 100
						break
					}
				}
			}
			//Then send the push notification
			//TODO(draaglom): Do buffering as per the APNS docs
			payload, err := pn.ToBytes()
			if err != nil {
				log.Println(err)
				//Should report this on the bad notifications channel probably
			} else {
				_, err = conn.conn.Write(payload)
				if err != nil {
					log.Println(err)
					//Disconnect?
				} else {
					sent <- pn
				}
			}
		}
	}
}

func (conn *Connection) reader(responses chan<- Response) {
	buffer := make([]byte, 6)
	for {
		n, err := conn.conn.Read(buffer)
		if err != nil && n < 6 {
			log.Println("APNS: Error before reading complete response", n, err)
			conn.conn.Close()
			conn.conn = nil
			return
		}
		command := uint8(buffer[0])
		if command != 8 {
			log.Println("Something went wrong: command should have been 8; it was actually", command)
		}
		resp := newResponse()
		resp.Identifier = binary.BigEndian.Uint32(buffer[2:6])
		resp.Status = uint8(buffer[1])
		responses <- resp
		conn.conn.Close()
		conn.conn = nil
		return
	}
}

func (conn *Connection) limbo(sent <-chan PushNotification, responses chan Response, errors chan BadPushNotification, queue chan PushNotification) {
	limbo := make(chan PushNotification, SentBufferSize)
	ticker := time.NewTicker(1 * time.Second)
	timeNextNotification := true
	for {
		select {
		case pn := <-sent:
			//Drop it into the array
			limbo <- pn
			if timeNextNotification {
				//Is there a cleaner way of doing this?
				go func(pn PushNotification) {
					<-time.After(TimeoutSeconds * time.Second)
					successResp := newResponse()
					successResp.Identifier = pn.Identifier
					responses <- successResp
				}(pn)
				timeNextNotification = false
			}
		case resp, ok := <-responses:
			if !ok {
				//If the responses channel is closed,
				//that means we're shutting down the connection.
			}
			switch {
			case resp.Status == 0:
				//Status 0 is a "success" response generated by a timeout in the library.
				for pn := range limbo {
					//Drop all the notifications until we get to the timed-out one.
					//(and leave the others in limbo)
					if pn.Identifier == resp.Identifier {
						break
					}
				}
			default:
				hit := false
				for pn := range limbo {
					switch {
					case pn.Identifier != resp.Identifier && !hit:
						//We haven't seen the identified notification yet
						//so these are all successful (drop silently)
					case pn.Identifier == resp.Identifier:
						hit = true
						if resp.Status != 10 {
							//It was an error, we should report this on the error channel
							bad := BadPushNotification{PushNotification: pn, Status: resp.Status}
							go func(bad BadPushNotification) {
								errors <- bad
							}(bad)
						}
					case pn.Identifier != resp.Identifier && hit:
						//We've already seen the identified notification,
						//so these should be requeued
						conn.Enqueue(&pn)
					}
				}
			}
		case <-ticker.C:
			timeNextNotification = true
		}
	}
}

func (conn *Connection) connect() error {
	if conn.conn != nil {
		conn.conn.Close()
	}

	var cert tls.Certificate
	var err error
	if len(conn.CertificateBase64) == 0 && len(conn.KeyBase64) == 0 {
		// The user did not specify raw block contents, so check the filesystem.
		cert, err = tls.LoadX509KeyPair(conn.CertificateFile, conn.KeyFile)
	} else {
		// The user provided the raw block contents, so use that.
		cert, err = tls.X509KeyPair([]byte(conn.CertificateBase64), []byte(conn.KeyBase64))
	}

	if err != nil {
		return err
	}

	conf := &tls.Config{
		Certificates: []tls.Certificate{cert},
	}

	tlsConn, err := tls.Dial("tcp", conn.Gateway, conf)
	if err != nil {
		return err
	}
	err = tlsConn.Handshake()
	if err != nil {
		_ = tlsConn.Close()
		return err
	}
	conn.conn = tlsConn
	return nil
}
<|endoftext|>


<commit_before>package hoverfly

import (
	"bytes"
	"fmt"

	"github.com/boltdb/bolt"
)

// Metadata - interface to store and retrieve any metadata that is related to Hoverfly
type Metadata interface {
	Set(key, value []byte) error
	Get(key []byte) ([]byte, error)
	Delete(key []byte) error
	GetAll() ([]MetaObject, error)
	CloseDB()
}

// NewBoltDBMetadata - default metadata store
func NewBoltDBMetadata(db *bolt.DB, bucket []byte) *BoltCache {
	return &BoltCache{
		DS:             db,
		RequestsBucket: []byte(bucket),
	}
}

const MetadataBucketName = []byte("metadataBucket")

type BoltMeta struct {
	DS             *bolt.DB
	MetadataBucket []byte
}

// CloseDB - closes database
func (m *BoltMeta) CloseDB() {
	m.DS.Close()
}

// Set - saves given key and value pair to BoltDB
func (m *BoltMeta) Set(key, value []byte) error {
	err := m.DS.Update(func(tx *bolt.Tx) error {
		bucket, err := tx.CreateBucketIfNotExists(m.MetadataBucket)
		if err != nil {
			return err
		}
		err = bucket.Put(key, value)
		if err != nil {
			return err
		}
		return nil
	})

	return err
}
<commit_msg>get function<commit_after>package hoverfly

import (
	"bytes"
	"fmt"

	"github.com/boltdb/bolt"
)

// Metadata - interface to store and retrieve any metadata that is related to Hoverfly
type Metadata interface {
	Set(key, value []byte) error
	Get(key []byte) ([]byte, error)
	Delete(key []byte) error
	GetAll() ([]MetaObject, error)
	CloseDB()
}

// NewBoltDBMetadata - default metadata store
func NewBoltDBMetadata(db *bolt.DB, bucket []byte) *BoltCache {
	return &BoltCache{
		DS:             db,
		RequestsBucket: []byte(bucket),
	}
}

const MetadataBucketName = []byte("metadataBucket")

type BoltMeta struct {
	DS             *bolt.DB
	MetadataBucket []byte
}

// CloseDB - closes database
func (m *BoltMeta) CloseDB() {
	m.DS.Close()
}

// Set - saves given key and value pair to BoltDB
func (m *BoltMeta) Set(key, value []byte) error {
	err := m.DS.Update(func(tx *bolt.Tx) error {
		bucket, err := tx.CreateBucketIfNotExists(m.MetadataBucket)
		if err != nil {
			return err
		}
		err = bucket.Put(key, value)
		if err != nil {
			return err
		}
		return nil
	})

	return err
}

// Get - gets value for given key
func (m *BoltMeta) Get(key []byte) (value []byte, err error) {
	err = m.DS.View(func(tx *bolt.Tx) error {
		bucket := tx.Bucket(m.MetadataBucket)
		if bucket == nil {
			return fmt.Errorf("Bucket %q not found!", m.MetadataBucket)
		}
		var buffer bytes.Buffer
		val := bucket.Get(key)

		// If it doesn't exist then it will return nil
		if val == nil {
			return fmt.Errorf("key %q not found \n", key)
		}

		buffer.Write(val)
		value = buffer.Bytes()
		return nil
	})

	return
}
<|endoftext|>


<commit_before>package ccrdt

import (
	"strconv"

	"github.com/koding/redis"
)

type GCounter struct {
	ccrdt *CCRDT
	key   string
}

func (c *CCRDT) NewGCounter(key string) *GCounter {
	return &GCounter{
		ccrdt: c,
		key:   key,
	}
}

func (g *GCounter) Add(delta int64) error {
	_, err := g.ccrdt.sessions.One().Incrby(g.key, delta)
	return err
}

func (g *GCounter) Sum() (int64, error) {
	var res int64
	for _, c := range g.ccrdt.sessions.All() {
		val, err := c.Get(g.key)
		if err != nil && err != redis.ErrNil {
			return 0, err
		}

		if val == "" {
			val = "0"
		}
		i, err := strconv.ParseInt(val, 10, 64)
		if err != nil {
			return 0, err
		}

		res += i
	}

	return res, nil
}
<commit_msg>CCRDT: add doc for GCounter<commit_after>package ccrdt

import (
	"strconv"

	"github.com/koding/redis"
)

// GCounter is a grow-only counter (inspired by vector clocks) in which only
// increment and merge are possible. Divergent histories are resolved by taking
// the maximum count for the counter.  The value of the counter is the sum of
// all counts.
type GCounter struct {
	ccrdt *CCRDT
	key   string
}

func (c *CCRDT) NewGCounter(key string) *GCounter {
	return &GCounter{
		ccrdt: c,
		key:   key,
	}
}

func (g *GCounter) Add(delta int64) error {
	_, err := g.ccrdt.sessions.One().Incrby(g.key, delta)
	return err
}

func (g *GCounter) Sum() (int64, error) {
	var res int64
	for _, c := range g.ccrdt.sessions.All() {
		val, err := c.Get(g.key)
		if err != nil && err != redis.ErrNil {
			return 0, err
		}

		if val == "" {
			val = "0"
		}
		i, err := strconv.ParseInt(val, 10, 64)
		if err != nil {
			return 0, err
		}

		res += i
	}

	return res, nil
}
<|endoftext|>


<commit_before>package products

import (
	"time"
)

const (
	// GetProductEndpoint is a string representation of the current endpoint for getting product
	GetProductEndpoint = "v1/vulnerability/getProducts"
	// ProductSearchEndpoint is a string representation of the current endpoint for product search
	ProductSearchEndpoint = "v1/product/search"
	// ProductGetProductEndpoint is a string representation of the current endpoint for getting a product
	ProductGetProductEndpoint = "v1/product/getProduct"
	// GetProductVersionsEndpoint is a string representation of the current endpoint for getting a product's versions
	GetProductVersionsEndpoint = "v1/product/getProductVersions"
)

// Product represents a software product within the system for identification
// across multiple sources
type Product struct {
	ID                 int           `json:"id" xml:"id"`
	Name               string        `json:"name" xml:"name"`
	Org                string        `json:"org" xml:"org"`
	Version            string        `json:"version" xml:"version"`
	Up                 string        `json:"up" xml:"up"`
	Edition            string        `json:"edition" xml:"edition"`
	Aliases            interface{}   `json:"aliases" xml:"aliases"`
	CreatedAt          time.Time     `json:"created_at" xml:"created_at"`
	UpdatedAt          time.Time     `json:"updated_at" xml:"updated_at"`
	Title              string        `json:"title" xml:"title"`
	References         []interface{} `json:"references" xml:"references"`
	Part               string        `json:"part" xml:"part"`
	Language           string        `json:"language" xml:"language"`
	ExternalID         string        `json:"external_id" xml:"external_id"`
	Sources            []Source      `json:"source" xml:"source"`
	Confidence         float64       `json:"confidence" xml:"confidence"`
	VulnerabilityCount int           `json:"vulnerability_count" xml:"vulnerability_count"`
}

// Source represents information about where the product data came from
type Source struct {
	ID           int       `json:"id" xml:"id"`
	Name         string    `json:"name" xml:"name"`
	Description  string    `json:"description" xml:"description"`
	CreatedAt    time.Time `json:"created_at" xml:"created_at"`
	UpdatedAt    time.Time `json:"updated_at" xml:"updated_at"`
	Attribution  string    `json:"attribution" xml:"attribution"`
	License      string    `json:"license" xml:"license"`
	CopyrightURL string    `json:"copyright_url" xml:"copyright_url"`
}

// SoftwareEntity represents information about a product as well as
// other info, like Git repository, committer counts, etc
type SoftwareEntity struct {
	Product    *Product             `json:"product,omitempty" xml:"product"`
	Github     *Github              `json:"github,omitempty" xml:"github,omitempty"`
	Package    *Package             `json:"package,omitempty" xml:"package,omitempty"`
	Confidence float64              `json:"confidence" xml:"confidence"`
	Scores     []ProductSearchScore `json:"scores,omitempty" xml:"scores"`
}

// ProductSearchScore represents the TF;IDF score for a given search result
// and a given search term
type ProductSearchScore struct {
	Term  string  `json:"term" xml:"term"`
	Score float64 `json:"score" xml:"score"`
}

// Github represents information from Github about a given repository
type Github struct {
	URI            string `json:"uri" xml:"uri"`
	CommitterCount uint   `json:"committer_count" xml:"committer_count"`
}

// Package represents information about a package from one of
// our supported package management systems like pypi, npm or rubygems
type Package struct {
	Name    string `json:"name" xml:"name"`
	Version string `json:"version" xml:"version"`
	Type    string `json:"type" xml:"type"`
}

// ProductSearchQuery collects all the various searching options that
// the productSearchEndpoint supports for use in a POST request
type ProductSearchQuery struct {
	SearchType        string   `json:"search_type" xml:"search_type"`
	SearchStrategy    string   `json:"search_strategy" xml:"search_strategy"`
	ProductIdentifier string   `json:"product_identifier" xml:"product_identifier"`
	Version           string   `json:"version" xml:"version"`
	Vendor            string   `json:"vendor" xml:"vendor"`
	Terms             []string `json:"terms" xml:"terms"`
}

// IsValid checks some of the constraints on the ProductSearchQuery to
// help the programmer determine if productSearchEndpoint will accept it
func (p *ProductSearchQuery) IsValid() bool {
	if len(p.SearchStrategy) > 0 {
		if p.SearchType == "concatenated" || p.SearchType == "deconcatenated" {
			return true
		}
	}
	return false
}
<commit_msg>Adding field for vulnerability list to products<commit_after>package products

import (
	"time"
)

const (
	// GetProductEndpoint is a string representation of the current endpoint for getting product
	GetProductEndpoint = "v1/vulnerability/getProducts"
	// ProductSearchEndpoint is a string representation of the current endpoint for product search
	ProductSearchEndpoint = "v1/product/search"
	// ProductGetProductEndpoint is a string representation of the current endpoint for getting a product
	ProductGetProductEndpoint = "v1/product/getProduct"
	// GetProductVersionsEndpoint is a string representation of the current endpoint for getting a product's versions
	GetProductVersionsEndpoint = "v1/product/getProductVersions"
)

// Product represents a software product within the system for identification
// across multiple sources
type Product struct {
	ID                 int           `json:"id" xml:"id"`
	Name               string        `json:"name" xml:"name"`
	Org                string        `json:"org" xml:"org"`
	Version            string        `json:"version" xml:"version"`
	Up                 string        `json:"up" xml:"up"`
	Edition            string        `json:"edition" xml:"edition"`
	Aliases            interface{}   `json:"aliases" xml:"aliases"`
	CreatedAt          time.Time     `json:"created_at" xml:"created_at"`
	UpdatedAt          time.Time     `json:"updated_at" xml:"updated_at"`
	Title              string        `json:"title" xml:"title"`
	References         []interface{} `json:"references" xml:"references"`
	Part               string        `json:"part" xml:"part"`
	Language           string        `json:"language" xml:"language"`
	ExternalID         string        `json:"external_id" xml:"external_id"`
	Sources            []Source      `json:"source" xml:"source"`
	Confidence         float64       `json:"confidence" xml:"confidence"`
	VulnerabilityCount int           `json:"vulnerability_count" xml:"vulnerability_count"`
	Vulnerabilities    []interface{} `json:"vulnerabilities,omitempty" xml:"vulnerabilities,omitempty"`
}

// Source represents information about where the product data came from
type Source struct {
	ID           int       `json:"id" xml:"id"`
	Name         string    `json:"name" xml:"name"`
	Description  string    `json:"description" xml:"description"`
	CreatedAt    time.Time `json:"created_at" xml:"created_at"`
	UpdatedAt    time.Time `json:"updated_at" xml:"updated_at"`
	Attribution  string    `json:"attribution" xml:"attribution"`
	License      string    `json:"license" xml:"license"`
	CopyrightURL string    `json:"copyright_url" xml:"copyright_url"`
}

// SoftwareEntity represents information about a product as well as
// other info, like Git repository, committer counts, etc
type SoftwareEntity struct {
	Product    *Product             `json:"product,omitempty" xml:"product"`
	Github     *Github              `json:"github,omitempty" xml:"github,omitempty"`
	Package    *Package             `json:"package,omitempty" xml:"package,omitempty"`
	Confidence float64              `json:"confidence" xml:"confidence"`
	Scores     []ProductSearchScore `json:"scores,omitempty" xml:"scores"`
}

// ProductSearchScore represents the TF;IDF score for a given search result
// and a given search term
type ProductSearchScore struct {
	Term  string  `json:"term" xml:"term"`
	Score float64 `json:"score" xml:"score"`
}

// Github represents information from Github about a given repository
type Github struct {
	URI            string `json:"uri" xml:"uri"`
	CommitterCount uint   `json:"committer_count" xml:"committer_count"`
}

// Package represents information about a package from one of
// our supported package management systems like pypi, npm or rubygems
type Package struct {
	Name    string `json:"name" xml:"name"`
	Version string `json:"version" xml:"version"`
	Type    string `json:"type" xml:"type"`
}

// ProductSearchQuery collects all the various searching options that
// the productSearchEndpoint supports for use in a POST request
type ProductSearchQuery struct {
	SearchType        string   `json:"search_type" xml:"search_type"`
	SearchStrategy    string   `json:"search_strategy" xml:"search_strategy"`
	ProductIdentifier string   `json:"product_identifier" xml:"product_identifier"`
	Version           string   `json:"version" xml:"version"`
	Vendor            string   `json:"vendor" xml:"vendor"`
	Terms             []string `json:"terms" xml:"terms"`
}

// IsValid checks some of the constraints on the ProductSearchQuery to
// help the programmer determine if productSearchEndpoint will accept it
func (p *ProductSearchQuery) IsValid() bool {
	if len(p.SearchStrategy) > 0 {
		if p.SearchType == "concatenated" || p.SearchType == "deconcatenated" {
			return true
		}
	}
	return false
}
<|endoftext|>


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

package main

import (
	"fmt"
	"log"
	"strings"
	"sync"
	"time"

	"github.com/GoogleCloudPlatform/functions-framework-conformance/events"
)

func timeExecution(fn func() error) (time.Duration, error) {
	start := time.Now()
	err := fn()
	return time.Since(start), err
}

// validateConcurrency validates a server can handle concurrent requests by
// valdating that the response time for a single request does not increase
// linearly with n concurrent requests, given a function that:
// 1. Is not CPU-bound (e.g. sleeps)
// 2. Executes for at least 1s to ensure non-trivial measurement differences
func validateConcurrency(url string, functionType string) error {
	log.Printf("%s validation with concurrent requests...", functionType)
	var sendFn func() error
	switch functionType {
	case "http":
		sendFn = func() error {
			return sendHTTP(url, []byte(`{"data": "hello"}`))
		}
	case "cloudevent":
		// Arbitrary payload that conforms to CloudEvent schema
		sendFn = func() error {
			return send(url, events.CloudEvent, []byte(`{
			"specversion": "1.0",
			"type": "google.firebase.auth.user.v1.created",
			"source": "//firebaseauth.googleapis.com/projects/my-project-id",
			"subject": "users/UUpby3s4spZre6kHsgVSPetzQ8l2",
			"id": "aaaaaa-1111-bbbb-2222-cccccccccccc",
			"time": "2020-09-29T11:32:00.123Z",
			"datacontenttype": "application/json",
			"data": {
			  "email": "test@nowhere.com",
			  "metadata": {
				"createTime": "2020-05-26T10:42:27Z",
				"lastSignInTime": "2020-10-24T11:00:00Z"
			  },
			  "providerData": [
				{
				  "email": "test@nowhere.com",
				  "providerId": "password",
				  "uid": "test@nowhere.com"
				}
			  ],
			  "uid": "UUpby3s4spZre6kHsgVSPetzQ8l2"
			}
		  }`))
		}
	case "legacyevent":
		// Arbitrary payload that conforms to Background event schema
		sendFn = func() error {
			return send(url, events.LegacyEvent, []byte(`{
			"data": {
			  "email": "test@nowhere.com",
			  "metadata": {
				"createdAt": "2020-05-26T10:42:27Z",
				"lastSignedInAt": "2020-10-24T11:00:00Z"
			  },
			  "providerData": [
				{
				  "email": "test@nowhere.com",
				  "providerId": "password",
				  "uid": "test@nowhere.com"
				}
			  ],
			  "uid": "UUpby3s4spZre6kHsgVSPetzQ8l2"
			},
			"eventId": "aaaaaa-1111-bbbb-2222-cccccccccccc",
			"eventType": "providers/firebase.auth/eventTypes/user.create",
			"notSupported": {
			},
			"resource": "projects/my-project-id",
			"timestamp": "2020-09-29T11:32:00.123Z"
		  }`))
		}
	default:
		return fmt.Errorf("expected type to be one of 'http', 'cloudevent', or 'legacyevent', got %s", functionType)
	}
	if err := sendConcurrentRequests(sendFn); err != nil {
		return err
	}
	log.Printf("Concurrency validation passed!")
	return nil
}

func sendConcurrentRequests(sendFn func() error) error {
	// Get a benchmark for the time it takes for a single request
	singleReqTime, singleReqErr := timeExecution(func() error {
		return sendFn()
	})
	if singleReqErr != nil {
		return fmt.Errorf("concurrent validation unable to send single request to benchmark response time: %v", singleReqErr)
	}

	minWait := 1 * time.Second
	if singleReqTime < minWait {
		return fmt.Errorf("concurrent validation requires a function that waits at least %s before responding, function responded in %s", minWait, singleReqTime)
	}
	log.Printf("Single request response time benchmarked, took %s for 1 request", singleReqTime)

	// Get a benchmark for the time it takes for concurrent requests
	const numConReqs = 1000
	log.Printf("Starting %d concurrent workers to send requests", numConReqs)

	type workerResponse struct {
		id  int
		err error
	}
	var wg sync.WaitGroup
	respCh := make(chan workerResponse, numConReqs)
	conReqTime, _ := timeExecution(func() error {
		for i := 0; i < numConReqs; i++ {
			wg.Add(1)
			go func(id int) {
				defer wg.Done()
				err := sendFn()
				respCh <- workerResponse{id: id, err: err}
			}(i)
		}

		wg.Wait()
		return nil
	})

	maybeErrMessage := ""
	first10Workers := []string{}
	for i := 0; i < numConReqs; i++ {
		resp := <-respCh
		if resp.err != nil {
			maybeErrMessage += fmt.Sprintf("error #%d: %v\n", i, resp.err)
		}
		if i < 10 {
			first10Workers = append(first10Workers, fmt.Sprintf("Worker #%d", resp.id))
		}
	}
	if maybeErrMessage != "" {
		return fmt.Errorf("at least one concurrent request failed:\n%s", maybeErrMessage)
	}

	log.Printf("First 10 workers done:\n%s", strings.Join(first10Workers, "\n"))

	// Validate that the concurrent requests were handled faster than if all
	// the requests were handled serially, using the single request time
	// as a benchmark. Some buffer is provided by doubling the single request time.
	if conReqTime > 2*singleReqTime {
		return fmt.Errorf("function took too long to complete %d concurrent requests. %d concurrent request time: %s, single request time: %s", numConReqs, numConReqs, conReqTime, singleReqTime)
	}
	log.Printf("Concurrent request response time benchmarked, took %s for %d requests", conReqTime, numConReqs)
	return nil
}
<commit_msg>Revert "feature: test 1000 concurrent requests at a time (#112)" (#114)<commit_after>// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"fmt"
	"log"
	"sync"
	"time"

	"github.com/GoogleCloudPlatform/functions-framework-conformance/events"
)

func timeExecution(fn func() error) (time.Duration, error) {
	start := time.Now()
	err := fn()
	return time.Since(start), err
}

// validateConcurrency validates a server can handle concurrent requests by
// valdating that the response time for a single request does not increase
// linearly with n concurrent requests, given a function that:
// 1. Is not CPU-bound (e.g. sleeps)
// 2. Executes for at least 1s to ensure non-trivial measurement differences
func validateConcurrency(url string, functionType string) error {
	log.Printf("%s validation with concurrent requests...", functionType)
	var sendFn func() error
	switch functionType {
	case "http":
		sendFn = func() error {
			return sendHTTP(url, []byte(`{"data": "hello"}`))
		}
	case "cloudevent":
		// Arbitrary payload that conforms to CloudEvent schema
		sendFn = func() error {
			return send(url, events.CloudEvent, []byte(`{
			"specversion": "1.0",
			"type": "google.firebase.auth.user.v1.created",
			"source": "//firebaseauth.googleapis.com/projects/my-project-id",
			"subject": "users/UUpby3s4spZre6kHsgVSPetzQ8l2",
			"id": "aaaaaa-1111-bbbb-2222-cccccccccccc",
			"time": "2020-09-29T11:32:00.123Z",
			"datacontenttype": "application/json",
			"data": {
			  "email": "test@nowhere.com",
			  "metadata": {
				"createTime": "2020-05-26T10:42:27Z",
				"lastSignInTime": "2020-10-24T11:00:00Z"
			  },
			  "providerData": [
				{
				  "email": "test@nowhere.com",
				  "providerId": "password",
				  "uid": "test@nowhere.com"
				}
			  ],
			  "uid": "UUpby3s4spZre6kHsgVSPetzQ8l2"
			}
		  }`))
		}
	case "legacyevent":
		// Arbitrary payload that conforms to Background event schema
		sendFn = func() error {
			return send(url, events.LegacyEvent, []byte(`{
			"data": {
			  "email": "test@nowhere.com",
			  "metadata": {
				"createdAt": "2020-05-26T10:42:27Z",
				"lastSignedInAt": "2020-10-24T11:00:00Z"
			  },
			  "providerData": [
				{
				  "email": "test@nowhere.com",
				  "providerId": "password",
				  "uid": "test@nowhere.com"
				}
			  ],
			  "uid": "UUpby3s4spZre6kHsgVSPetzQ8l2"
			},
			"eventId": "aaaaaa-1111-bbbb-2222-cccccccccccc",
			"eventType": "providers/firebase.auth/eventTypes/user.create",
			"notSupported": {
			},
			"resource": "projects/my-project-id",
			"timestamp": "2020-09-29T11:32:00.123Z"
		  }`))
		}
	default:
		return fmt.Errorf("expected type to be one of 'http', 'cloudevent', or 'legacyevent', got %s", functionType)
	}
	if err := sendConcurrentRequests(sendFn); err != nil {
		return err
	}
	log.Printf("Concurrency validation passed!")
	return nil
}

func sendConcurrentRequests(sendFn func() error) error {
	// Get a benchmark for the time it takes for a single request
	singleReqTime, singleReqErr := timeExecution(func() error {
		return sendFn()
	})
	if singleReqErr != nil {
		return fmt.Errorf("concurrent validation unable to send single request to benchmark response time: %v", singleReqErr)
	}

	minWait := 1 * time.Second
	if singleReqTime < minWait {
		return fmt.Errorf("concurrent validation requires a function that waits at least %s before responding, function responded in %s", minWait, singleReqTime)
	}
	log.Printf("Single request response time benchmarked, took %s for 1 request", singleReqTime)

	// Get a benchmark for the time it takes for concurrent requests
	const numConReqs = 10
	log.Printf("Starting %d concurrent workers to send requests", numConReqs)

	type workerResponse struct {
		id  int
		err error
	}
	var wg sync.WaitGroup
	respCh := make(chan workerResponse, numConReqs)
	conReqTime, _ := timeExecution(func() error {
		for i := 0; i < numConReqs; i++ {
			wg.Add(1)
			go func(id int) {
				defer wg.Done()
				err := sendFn()
				respCh <- workerResponse{id: id, err: err}
			}(i)
		}

		wg.Wait()
		return nil
	})

	maybeErrMessage := ""
	for i := 0; i < numConReqs; i++ {
		resp := <-respCh
		if resp.err != nil {
			maybeErrMessage += fmt.Sprintf("error #%d: %v\n", i, resp.err)
		} else {
			log.Printf("Worker #%d done", resp.id)
		}
	}

	if maybeErrMessage != "" {
		return fmt.Errorf("at least one concurrent request failed:\n%s", maybeErrMessage)
	}

	// Validate that the concurrent requests were handled faster than if all
	// the requests were handled serially, using the single request time
	// as a benchmark. Some buffer is provided by doubling the single request time.
	if conReqTime > 2*singleReqTime {
		return fmt.Errorf("function took too long to complete %d concurrent requests. %d concurrent request time: %s, single request time: %s", numConReqs, numConReqs, conReqTime, singleReqTime)
	}
	log.Printf("Concurrent request response time benchmarked, took %s for %d requests", conReqTime, numConReqs)
	return nil
}
<|endoftext|>


<commit_before>package forward

import (
	"github.com/spf13/cobra"

	// Explicitly import packages that need to register protocol handlers.
	_ "github.com/mutagen-io/mutagen/pkg/forwarding/protocols/docker"
	_ "github.com/mutagen-io/mutagen/pkg/forwarding/protocols/local"
	_ "github.com/mutagen-io/mutagen/pkg/forwarding/protocols/ssh"
)

func rootMain(command *cobra.Command, arguments []string) error {
	// If no commands were given, then print help information and bail. We don't
	// have to worry about warning about arguments being present here (which
	// would be incorrect usage) because arguments can't even reach this point
	// (they will be mistaken for subcommands and a error will be displayed).
	command.Help()

	// Success.
	return nil
}

var RootCommand = &cobra.Command{
	Use:          "forward",
	Short:        "Create and manage forwarding sessions",
	RunE:         rootMain,
	SilenceUsage: true,
}

var rootConfiguration struct {
	// help indicates whether or not help information should be shown for the
	// command.
	help bool
}

func init() {
	// Grab a handle for the command line flags.
	flags := RootCommand.Flags()

	// Disable alphabetical sorting of flags in help output.
	flags.SortFlags = false

	// Manually add a help flag to override the default message. Cobra will
	// still implement its logic automatically.
	flags.BoolVarP(&rootConfiguration.help, "help", "h", false, "Show help information")

	// Register commands.
	RootCommand.AddCommand(
		createCommand,
		listCommand,
		monitorCommand,
		pauseCommand,
		resumeCommand,
		terminateCommand,
	)
}
<commit_msg>Marked forwarding as experimental<commit_after>package forward

import (
	"github.com/spf13/cobra"

	"github.com/fatih/color"

	// Explicitly import packages that need to register protocol handlers.
	_ "github.com/mutagen-io/mutagen/pkg/forwarding/protocols/docker"
	_ "github.com/mutagen-io/mutagen/pkg/forwarding/protocols/local"
	_ "github.com/mutagen-io/mutagen/pkg/forwarding/protocols/ssh"
)

func rootMain(command *cobra.Command, arguments []string) error {
	// If no commands were given, then print help information and bail. We don't
	// have to worry about warning about arguments being present here (which
	// would be incorrect usage) because arguments can't even reach this point
	// (they will be mistaken for subcommands and a error will be displayed).
	command.Help()

	// Success.
	return nil
}

var RootCommand = &cobra.Command{
	Use:          "forward",
	Short:        "Create and manage forwarding sessions",
	RunE:         rootMain,
	SilenceUsage: true,
}

var rootConfiguration struct {
	// help indicates whether or not help information should be shown for the
	// command.
	help bool
}

func init() {
	// Mark the command as experimental.
	RootCommand.Short = RootCommand.Short + color.YellowString(" [Experimental]")

	// Grab a handle for the command line flags.
	flags := RootCommand.Flags()

	// Disable alphabetical sorting of flags in help output.
	flags.SortFlags = false

	// Manually add a help flag to override the default message. Cobra will
	// still implement its logic automatically.
	flags.BoolVarP(&rootConfiguration.help, "help", "h", false, "Show help information")

	// Register commands.
	RootCommand.AddCommand(
		createCommand,
		listCommand,
		monitorCommand,
		pauseCommand,
		resumeCommand,
		terminateCommand,
	)
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"log"
	"os/exec"

	"github.com/spf13/cobra"
)

// OpenCmd - Open the konnect.yml config file with the default editor.
var OpenCmd = &cobra.Command{
	Use:   "open",
	Short: "Open the config file with the default editor",
	Long:  "Open the config file with the default editor",
	Run: func(cmd *cobra.Command, args []string) {
		// Resolve filename from flags.
		filename, err := resolveFilename(cmd)
		handleErr(err)

		fmt.Printf("Opening config file. (%v)\n", filename)

		if err := exec.Command("open", filename).Run(); err != nil {
			log.Fatal("Error when opening the config file.")
		}
	},
}
<commit_msg>Modify print.<commit_after>package cmd

import (
	"fmt"
	"log"
	"os/exec"

	"github.com/spf13/cobra"
)

// OpenCmd - Open the konnect.yml config file with the default editor.
var OpenCmd = &cobra.Command{
	Use:   "open",
	Short: "Open the config file with the default editor",
	Long:  "Open the config file with the default editor",
	Run: func(cmd *cobra.Command, args []string) {
		// Resolve filename from flags.
		filename, err := resolveFilename(cmd)
		handleErr(err)

		fmt.Printf("Opening config file at %v\n", filename)

		if err := exec.Command("open", filename).Run(); err != nil {
			log.Fatal("Error when opening the config file.")
		}
	},
}
<|endoftext|>